diff --git a/.bundle/config b/.bundle/config new file mode 100644 index 0000000..a2f7a39 --- /dev/null +++ b/.bundle/config @@ -0,0 +1,3 @@ +--- +BUNDLE_PATH: "/home/runner/work/devcontainer/devcontainer/vendor/bundle" +BUNDLE_DEPLOYMENT: "true" diff --git a/.github/ruby-versions.json b/.github/ruby-versions.json index 998187d..910d76b 100644 --- a/.github/ruby-versions.json +++ b/.github/ruby-versions.json @@ -1,4 +1,5 @@ [ + "4.0.2", "4.0.1", "4.0.0", "3.4.9", diff --git a/features/src/ruby/README.md b/features/src/ruby/README.md index ddc5ae1..d74040e 100644 --- a/features/src/ruby/README.md +++ b/features/src/ruby/README.md @@ -40,7 +40,7 @@ Installs Ruby and a version manager (mise or rbenv) along with the dependencies | Options Id | Description | Type | Default Value | |-----|-----|-----|-----| -| version | The version of ruby to be installed | string | 4.0.1 | +| version | The version of ruby to be installed | string | 4.0.2 | | versionManager | The version manager to use for Ruby (mise or rbenv) | string | mise | ## Customizations diff --git a/features/src/ruby/devcontainer-feature.json b/features/src/ruby/devcontainer-feature.json index 95c33e2..972f823 100644 --- a/features/src/ruby/devcontainer-feature.json +++ b/features/src/ruby/devcontainer-feature.json @@ -1,6 +1,6 @@ { "id": "ruby", - "version": "2.1.3", + "version": "2.1.4", "name": "Ruby", "description": "Installs Ruby and a version manager (mise or rbenv) along with libraries needed to build Ruby.", "documentationURL": "https://github.com/rails/devcontainer/tree/main/features/src/ruby", @@ -19,7 +19,7 @@ "options": { "version": { "type": "string", - "default": "4.0.1", + "default": "4.0.2", "description": "The ruby version to be installed" }, "versionManager": { diff --git a/features/test/ruby/test.sh b/features/test/ruby/test.sh index ff6d126..73aa0a1 100644 --- a/features/test/ruby/test.sh +++ b/features/test/ruby/test.sh @@ -8,6 +8,6 @@ check "mise is installed" bash -c "mise --version" check "mise init is sourced in the bashrc" bash -c "grep 'eval \"\$(~/.local/bin/mise activate bash)\"' $HOME/.bashrc" check "mise idiomatic version file is enabled for ruby" bash -c "mise settings | grep idiomatic_version_file_enable_tools | grep ruby" check "Ruby is installed with YJIT" bash -c "RUBY_YJIT_ENABLE=1 ruby -v | grep +YJIT" -check "Ruby version is set to 4.0.1" bash -c "mise use -g ruby | grep 4.0.1" +check "Ruby version is set to 4.0.2" bash -c "mise use -g ruby | grep 4.0.2" reportResults diff --git a/features/test/ruby/with_rbenv.sh b/features/test/ruby/with_rbenv.sh index 22af2c7..9751bbe 100644 --- a/features/test/ruby/with_rbenv.sh +++ b/features/test/ruby/with_rbenv.sh @@ -9,6 +9,6 @@ check "rbenv is installed" bash -c "rbenv --version" check "ruby-build is installed" bash -c "ls -l $HOME/.rbenv/plugins/ruby-build | grep '\-> /usr/local/share/ruby-build'" eval "$(rbenv init -)" check "Ruby is installed with YJIT" bash -c "RUBY_YJIT_ENABLE=1 ruby -v | grep +YJIT" -check "Ruby version is set to 4.0.1" bash -c "rbenv global | grep 4.0.1" +check "Ruby version is set to 4.0.2" bash -c "rbenv global | grep 4.0.2" reportResults diff --git a/vendor/bundle/ruby/3.3.0/bin/minitest b/vendor/bundle/ruby/3.3.0/bin/minitest new file mode 100755 index 0000000..ea1f8f7 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/bin/minitest @@ -0,0 +1,29 @@ +#!/usr/bin/env ruby +# +# This file was generated by RubyGems. +# +# The application 'minitest' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +require 'rubygems' + +Gem.use_gemdeps + +version = ">= 0.a" + +str = ARGV.first +if str + str = str.b[/\A_(.*)_\z/, 1] + if str and Gem::Version.correct?(str) + version = str + ARGV.shift + end +end + +if Gem.respond_to?(:activate_bin_path) +load Gem.activate_bin_path('minitest', 'minitest', version) +else +gem "minitest", version +load Gem.bin_path("minitest", "minitest", version) +end diff --git a/vendor/bundle/ruby/3.3.0/bin/rake b/vendor/bundle/ruby/3.3.0/bin/rake new file mode 100755 index 0000000..3a26cbd --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/bin/rake @@ -0,0 +1,29 @@ +#!/usr/bin/env ruby +# +# This file was generated by RubyGems. +# +# The application 'rake' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +require 'rubygems' + +Gem.use_gemdeps + +version = ">= 0.a" + +str = ARGV.first +if str + str = str.b[/\A_(.*)_\z/, 1] + if str and Gem::Version.correct?(str) + version = str + ARGV.shift + end +end + +if Gem.respond_to?(:activate_bin_path) +load Gem.activate_bin_path('rake', 'rake', version) +else +gem "rake", version +load Gem.bin_path("rake", "rake", version) +end diff --git a/vendor/bundle/ruby/3.3.0/cache/addressable-2.8.8.gem b/vendor/bundle/ruby/3.3.0/cache/addressable-2.8.8.gem new file mode 100644 index 0000000..bee9c05 Binary files /dev/null and b/vendor/bundle/ruby/3.3.0/cache/addressable-2.8.8.gem differ diff --git a/vendor/bundle/ruby/3.3.0/cache/bigdecimal-4.0.1.gem b/vendor/bundle/ruby/3.3.0/cache/bigdecimal-4.0.1.gem new file mode 100644 index 0000000..598e339 Binary files /dev/null and b/vendor/bundle/ruby/3.3.0/cache/bigdecimal-4.0.1.gem differ diff --git a/vendor/bundle/ruby/3.3.0/cache/crack-1.0.1.gem b/vendor/bundle/ruby/3.3.0/cache/crack-1.0.1.gem new file mode 100644 index 0000000..49807f7 Binary files /dev/null and b/vendor/bundle/ruby/3.3.0/cache/crack-1.0.1.gem differ diff --git a/vendor/bundle/ruby/3.3.0/cache/faraday-2.14.0.gem b/vendor/bundle/ruby/3.3.0/cache/faraday-2.14.0.gem new file mode 100644 index 0000000..dc4f66a Binary files /dev/null and b/vendor/bundle/ruby/3.3.0/cache/faraday-2.14.0.gem differ diff --git a/vendor/bundle/ruby/3.3.0/cache/faraday-net_http-3.4.2.gem b/vendor/bundle/ruby/3.3.0/cache/faraday-net_http-3.4.2.gem new file mode 100644 index 0000000..3f46590 Binary files /dev/null and b/vendor/bundle/ruby/3.3.0/cache/faraday-net_http-3.4.2.gem differ diff --git a/vendor/bundle/ruby/3.3.0/cache/hashdiff-1.2.1.gem b/vendor/bundle/ruby/3.3.0/cache/hashdiff-1.2.1.gem new file mode 100644 index 0000000..250f505 Binary files /dev/null and b/vendor/bundle/ruby/3.3.0/cache/hashdiff-1.2.1.gem differ diff --git a/vendor/bundle/ruby/3.3.0/cache/json-2.18.0.gem b/vendor/bundle/ruby/3.3.0/cache/json-2.18.0.gem new file mode 100644 index 0000000..33b7882 Binary files /dev/null and b/vendor/bundle/ruby/3.3.0/cache/json-2.18.0.gem differ diff --git a/vendor/bundle/ruby/3.3.0/cache/logger-1.7.0.gem b/vendor/bundle/ruby/3.3.0/cache/logger-1.7.0.gem new file mode 100644 index 0000000..061f1cc Binary files /dev/null and b/vendor/bundle/ruby/3.3.0/cache/logger-1.7.0.gem differ diff --git a/vendor/bundle/ruby/3.3.0/cache/minitest-6.0.1.gem b/vendor/bundle/ruby/3.3.0/cache/minitest-6.0.1.gem new file mode 100644 index 0000000..1602181 Binary files /dev/null and b/vendor/bundle/ruby/3.3.0/cache/minitest-6.0.1.gem differ diff --git a/vendor/bundle/ruby/3.3.0/cache/mocha-3.0.1.gem b/vendor/bundle/ruby/3.3.0/cache/mocha-3.0.1.gem new file mode 100644 index 0000000..0dab7b1 Binary files /dev/null and b/vendor/bundle/ruby/3.3.0/cache/mocha-3.0.1.gem differ diff --git a/vendor/bundle/ruby/3.3.0/cache/net-http-0.9.1.gem b/vendor/bundle/ruby/3.3.0/cache/net-http-0.9.1.gem new file mode 100644 index 0000000..912cf24 Binary files /dev/null and b/vendor/bundle/ruby/3.3.0/cache/net-http-0.9.1.gem differ diff --git a/vendor/bundle/ruby/3.3.0/cache/octokit-10.0.0.gem b/vendor/bundle/ruby/3.3.0/cache/octokit-10.0.0.gem new file mode 100644 index 0000000..05b18b9 Binary files /dev/null and b/vendor/bundle/ruby/3.3.0/cache/octokit-10.0.0.gem differ diff --git a/vendor/bundle/ruby/3.3.0/cache/prism-1.9.0.gem b/vendor/bundle/ruby/3.3.0/cache/prism-1.9.0.gem new file mode 100644 index 0000000..45fb871 Binary files /dev/null and b/vendor/bundle/ruby/3.3.0/cache/prism-1.9.0.gem differ diff --git a/vendor/bundle/ruby/3.3.0/cache/public_suffix-7.0.2.gem b/vendor/bundle/ruby/3.3.0/cache/public_suffix-7.0.2.gem new file mode 100644 index 0000000..ceaccc0 Binary files /dev/null and b/vendor/bundle/ruby/3.3.0/cache/public_suffix-7.0.2.gem differ diff --git a/vendor/bundle/ruby/3.3.0/cache/rake-13.3.1.gem b/vendor/bundle/ruby/3.3.0/cache/rake-13.3.1.gem new file mode 100644 index 0000000..75b4aab Binary files /dev/null and b/vendor/bundle/ruby/3.3.0/cache/rake-13.3.1.gem differ diff --git a/vendor/bundle/ruby/3.3.0/cache/rexml-3.4.4.gem b/vendor/bundle/ruby/3.3.0/cache/rexml-3.4.4.gem new file mode 100644 index 0000000..46cc01a Binary files /dev/null and b/vendor/bundle/ruby/3.3.0/cache/rexml-3.4.4.gem differ diff --git a/vendor/bundle/ruby/3.3.0/cache/ruby2_keywords-0.0.5.gem b/vendor/bundle/ruby/3.3.0/cache/ruby2_keywords-0.0.5.gem new file mode 100644 index 0000000..d311c5d Binary files /dev/null and b/vendor/bundle/ruby/3.3.0/cache/ruby2_keywords-0.0.5.gem differ diff --git a/vendor/bundle/ruby/3.3.0/cache/sawyer-0.9.3.gem b/vendor/bundle/ruby/3.3.0/cache/sawyer-0.9.3.gem new file mode 100644 index 0000000..1a6b0db Binary files /dev/null and b/vendor/bundle/ruby/3.3.0/cache/sawyer-0.9.3.gem differ diff --git a/vendor/bundle/ruby/3.3.0/cache/uri-1.1.1.gem b/vendor/bundle/ruby/3.3.0/cache/uri-1.1.1.gem new file mode 100644 index 0000000..d1bea0c Binary files /dev/null and b/vendor/bundle/ruby/3.3.0/cache/uri-1.1.1.gem differ diff --git a/vendor/bundle/ruby/3.3.0/cache/webmock-3.26.1.gem b/vendor/bundle/ruby/3.3.0/cache/webmock-3.26.1.gem new file mode 100644 index 0000000..5c0333b Binary files /dev/null and b/vendor/bundle/ruby/3.3.0/cache/webmock-3.26.1.gem differ diff --git a/vendor/bundle/ruby/3.3.0/extensions/x86_64-linux/3.3.0/bigdecimal-4.0.1/bigdecimal.so b/vendor/bundle/ruby/3.3.0/extensions/x86_64-linux/3.3.0/bigdecimal-4.0.1/bigdecimal.so new file mode 100755 index 0000000..12bf3f3 Binary files /dev/null and b/vendor/bundle/ruby/3.3.0/extensions/x86_64-linux/3.3.0/bigdecimal-4.0.1/bigdecimal.so differ diff --git a/vendor/bundle/ruby/3.3.0/extensions/x86_64-linux/3.3.0/bigdecimal-4.0.1/gem.build_complete b/vendor/bundle/ruby/3.3.0/extensions/x86_64-linux/3.3.0/bigdecimal-4.0.1/gem.build_complete new file mode 100644 index 0000000..e69de29 diff --git a/vendor/bundle/ruby/3.3.0/extensions/x86_64-linux/3.3.0/bigdecimal-4.0.1/gem_make.out b/vendor/bundle/ruby/3.3.0/extensions/x86_64-linux/3.3.0/bigdecimal-4.0.1/gem_make.out new file mode 100644 index 0000000..a971f0a --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/extensions/x86_64-linux/3.3.0/bigdecimal-4.0.1/gem_make.out @@ -0,0 +1,38 @@ +current directory: /home/runner/work/devcontainer/devcontainer/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/ext/bigdecimal +/opt/hostedtoolcache/Ruby/3.3.10/x64/bin/ruby extconf.rb +checking for __builtin_clz()... yes +checking for __builtin_clzl()... yes +checking for __builtin_clzll()... yes +checking for float.h... yes +checking for math.h... yes +checking for stdbool.h... yes +checking for stdlib.h... yes +checking for x86intrin.h... yes +checking for _lzcnt_u32() in x86intrin.h... no +checking for _lzcnt_u64() in x86intrin.h... no +checking for intrin.h... no +checking for ruby/atomic.h... yes +checking for ruby/internal/has/builtin.h... yes +checking for ruby/internal/static_assert.h... yes +checking for rb_complex_real() in ruby.h... yes +checking for rb_complex_imag() in ruby.h... yes +checking for rb_opts_exception_p() in ruby.h... yes +checking for rb_category_warn() in ruby.h... yes +checking for RB_WARN_CATEGORY_DEPRECATED in ruby.h... yes +creating Makefile + +current directory: /home/runner/work/devcontainer/devcontainer/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/ext/bigdecimal +make DESTDIR\= sitearchdir\=./.gem.20260202-2234-1mbxre sitelibdir\=./.gem.20260202-2234-1mbxre clean + +current directory: /home/runner/work/devcontainer/devcontainer/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/ext/bigdecimal +make DESTDIR\= sitearchdir\=./.gem.20260202-2234-1mbxre sitelibdir\=./.gem.20260202-2234-1mbxre +compiling bigdecimal.c +compiling missing.c +linking shared-object bigdecimal.so + +current directory: /home/runner/work/devcontainer/devcontainer/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/ext/bigdecimal +make DESTDIR\= sitearchdir\=./.gem.20260202-2234-1mbxre sitelibdir\=./.gem.20260202-2234-1mbxre install +/usr/bin/install -c -m 0755 bigdecimal.so ./.gem.20260202-2234-1mbxre + +current directory: /home/runner/work/devcontainer/devcontainer/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/ext/bigdecimal +make DESTDIR\= sitearchdir\=./.gem.20260202-2234-1mbxre sitelibdir\=./.gem.20260202-2234-1mbxre clean diff --git a/vendor/bundle/ruby/3.3.0/extensions/x86_64-linux/3.3.0/bigdecimal-4.0.1/mkmf.log b/vendor/bundle/ruby/3.3.0/extensions/x86_64-linux/3.3.0/bigdecimal-4.0.1/mkmf.log new file mode 100644 index 0000000..8e5cb19 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/extensions/x86_64-linux/3.3.0/bigdecimal-4.0.1/mkmf.log @@ -0,0 +1,458 @@ +have_builtin_func: checking for __builtin_clz()... -------------------- yes + +LD_LIBRARY_PATH=.:/opt/hostedtoolcache/Ruby/3.3.10/x64/lib "gcc -o conftest -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/ruby/backward -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0 -I. -DENABLE_PATH_CHECK=0 -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef -fPIC conftest.c -L. -L/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -Wl,-rpath,/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -L. -fstack-protector-strong -rdynamic -Wl,-export-dynamic -Wl,--no-as-needed -Wl,-rpath,/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -L/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -lruby -lm -lpthread -lc" +checked program was: +/* begin */ +1: #include "ruby.h" +2: +3: int main(int argc, char **argv) +4: { +5: return !!argv[argc]; +6: } +/* end */ + +LD_LIBRARY_PATH=.:/opt/hostedtoolcache/Ruby/3.3.10/x64/lib "gcc -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/ruby/backward -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0 -I. -DENABLE_PATH_CHECK=0 -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef -fPIC -c conftest.c" +conftest.c: In function ‘main’: +conftest.c:4:5: warning: old-style function definition [-Wold-style-definition] + 4 | int main() { __builtin_clz(0); return 0; } + | ^~~~ +At top level: +cc1: note: unrecognized command-line option ‘-Wno-self-assign’ may have been intended to silence earlier diagnostics +cc1: note: unrecognized command-line option ‘-Wno-parentheses-equality’ may have been intended to silence earlier diagnostics +cc1: note: unrecognized command-line option ‘-Wno-constant-logical-operand’ may have been intended to silence earlier diagnostics +checked program was: +/* begin */ +1: #include "ruby.h" +2: +3: int foo; +4: int main() { __builtin_clz(0); return 0; } +/* end */ + +-------------------- + +have_builtin_func: checking for __builtin_clzl()... -------------------- yes + +LD_LIBRARY_PATH=.:/opt/hostedtoolcache/Ruby/3.3.10/x64/lib "gcc -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/ruby/backward -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0 -I. -DENABLE_PATH_CHECK=0 -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef -fPIC -c conftest.c" +conftest.c: In function ‘main’: +conftest.c:4:5: warning: old-style function definition [-Wold-style-definition] + 4 | int main() { __builtin_clzl(0); return 0; } + | ^~~~ +At top level: +cc1: note: unrecognized command-line option ‘-Wno-self-assign’ may have been intended to silence earlier diagnostics +cc1: note: unrecognized command-line option ‘-Wno-parentheses-equality’ may have been intended to silence earlier diagnostics +cc1: note: unrecognized command-line option ‘-Wno-constant-logical-operand’ may have been intended to silence earlier diagnostics +checked program was: +/* begin */ +1: #include "ruby.h" +2: +3: int foo; +4: int main() { __builtin_clzl(0); return 0; } +/* end */ + +-------------------- + +have_builtin_func: checking for __builtin_clzll()... -------------------- yes + +LD_LIBRARY_PATH=.:/opt/hostedtoolcache/Ruby/3.3.10/x64/lib "gcc -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/ruby/backward -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0 -I. -DENABLE_PATH_CHECK=0 -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef -fPIC -c conftest.c" +conftest.c: In function ‘main’: +conftest.c:4:5: warning: old-style function definition [-Wold-style-definition] + 4 | int main() { __builtin_clzll(0); return 0; } + | ^~~~ +At top level: +cc1: note: unrecognized command-line option ‘-Wno-self-assign’ may have been intended to silence earlier diagnostics +cc1: note: unrecognized command-line option ‘-Wno-parentheses-equality’ may have been intended to silence earlier diagnostics +cc1: note: unrecognized command-line option ‘-Wno-constant-logical-operand’ may have been intended to silence earlier diagnostics +checked program was: +/* begin */ +1: #include "ruby.h" +2: +3: int foo; +4: int main() { __builtin_clzll(0); return 0; } +/* end */ + +-------------------- + +have_header: checking for float.h... -------------------- yes + +LD_LIBRARY_PATH=.:/opt/hostedtoolcache/Ruby/3.3.10/x64/lib "gcc -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/ruby/backward -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0 -I. -DENABLE_PATH_CHECK=0 -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef -fPIC -c conftest.c" +checked program was: +/* begin */ +1: #include "ruby.h" +2: +3: #include +/* end */ + +-------------------- + +have_header: checking for math.h... -------------------- yes + +LD_LIBRARY_PATH=.:/opt/hostedtoolcache/Ruby/3.3.10/x64/lib "gcc -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/ruby/backward -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0 -I. -DENABLE_PATH_CHECK=0 -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef -fPIC -c conftest.c" +checked program was: +/* begin */ +1: #include "ruby.h" +2: +3: #include +/* end */ + +-------------------- + +have_header: checking for stdbool.h... -------------------- yes + +LD_LIBRARY_PATH=.:/opt/hostedtoolcache/Ruby/3.3.10/x64/lib "gcc -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/ruby/backward -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0 -I. -DENABLE_PATH_CHECK=0 -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef -fPIC -c conftest.c" +checked program was: +/* begin */ +1: #include "ruby.h" +2: +3: #include +/* end */ + +-------------------- + +have_header: checking for stdlib.h... -------------------- yes + +LD_LIBRARY_PATH=.:/opt/hostedtoolcache/Ruby/3.3.10/x64/lib "gcc -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/ruby/backward -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0 -I. -DENABLE_PATH_CHECK=0 -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef -fPIC -c conftest.c" +checked program was: +/* begin */ +1: #include "ruby.h" +2: +3: #include +/* end */ + +-------------------- + +have_header: checking for x86intrin.h... -------------------- yes + +LD_LIBRARY_PATH=.:/opt/hostedtoolcache/Ruby/3.3.10/x64/lib "gcc -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/ruby/backward -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0 -I. -DENABLE_PATH_CHECK=0 -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef -fPIC -c conftest.c" +checked program was: +/* begin */ +1: #include "ruby.h" +2: +3: #include +/* end */ + +-------------------- + +have_func: checking for _lzcnt_u32() in x86intrin.h... -------------------- no + +LD_LIBRARY_PATH=.:/opt/hostedtoolcache/Ruby/3.3.10/x64/lib "gcc -o conftest -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/ruby/backward -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0 -I. -DENABLE_PATH_CHECK=0 -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef -fPIC conftest.c -L. -L/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -Wl,-rpath,/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -L. -fstack-protector-strong -rdynamic -Wl,-export-dynamic -Wl,--no-as-needed -Wl,-rpath,/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -L/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -lruby -lm -lpthread -lc" +/usr/bin/ld: /tmp/cc9578cY.o: in function `t': +/home/runner/work/devcontainer/devcontainer/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/ext/bigdecimal/conftest.c:16:(.text+0x7): undefined reference to `_lzcnt_u32' +collect2: error: ld returned 1 exit status +checked program was: +/* begin */ + 1: #include "ruby.h" + 2: + 3: #include + 4: + 5: /*top*/ + 6: extern int t(void); + 7: int main(int argc, char **argv) + 8: { + 9: if (argc > 1000000) { +10: int (* volatile tp)(void)=(int (*)(void))&t; +11: printf("%d", (*tp)()); +12: } +13: +14: return !!argv[argc]; +15: } +16: int t(void) { void ((*volatile p)()); p = (void ((*)()))_lzcnt_u32; return !p; } +/* end */ + +LD_LIBRARY_PATH=.:/opt/hostedtoolcache/Ruby/3.3.10/x64/lib "gcc -o conftest -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/ruby/backward -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0 -I. -DENABLE_PATH_CHECK=0 -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef -fPIC conftest.c -L. -L/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -Wl,-rpath,/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -L. -fstack-protector-strong -rdynamic -Wl,-export-dynamic -Wl,--no-as-needed -Wl,-rpath,/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -L/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -lruby -lm -lpthread -lc" +conftest.c:16:13: error: conflicting types for ‘_lzcnt_u32’; have ‘void()’ + 16 | extern void _lzcnt_u32(); + | ^~~~~~~~~~ +In file included from /usr/lib/gcc/x86_64-linux-gnu/13/include/x86gprintrin.h:61, + from /usr/lib/gcc/x86_64-linux-gnu/13/include/x86intrin.h:27, + from conftest.c:3: +/usr/lib/gcc/x86_64-linux-gnu/13/include/lzcntintrin.h:51:1: note: previous definition of ‘_lzcnt_u32’ with type ‘unsigned int(unsigned int)’ + 51 | _lzcnt_u32 (unsigned int __X) + | ^~~~~~~~~~ +cc1: note: unrecognized command-line option ‘-Wno-self-assign’ may have been intended to silence earlier diagnostics +cc1: note: unrecognized command-line option ‘-Wno-parentheses-equality’ may have been intended to silence earlier diagnostics +cc1: note: unrecognized command-line option ‘-Wno-constant-logical-operand’ may have been intended to silence earlier diagnostics +checked program was: +/* begin */ + 1: #include "ruby.h" + 2: + 3: #include + 4: + 5: /*top*/ + 6: extern int t(void); + 7: int main(int argc, char **argv) + 8: { + 9: if (argc > 1000000) { +10: int (* volatile tp)(void)=(int (*)(void))&t; +11: printf("%d", (*tp)()); +12: } +13: +14: return !!argv[argc]; +15: } +16: extern void _lzcnt_u32(); +17: int t(void) { _lzcnt_u32(); return 0; } +/* end */ + +-------------------- + +have_func: checking for _lzcnt_u64() in x86intrin.h... -------------------- no + +LD_LIBRARY_PATH=.:/opt/hostedtoolcache/Ruby/3.3.10/x64/lib "gcc -o conftest -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/ruby/backward -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0 -I. -DENABLE_PATH_CHECK=0 -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef -fPIC conftest.c -L. -L/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -Wl,-rpath,/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -L. -fstack-protector-strong -rdynamic -Wl,-export-dynamic -Wl,--no-as-needed -Wl,-rpath,/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -L/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -lruby -lm -lpthread -lc" +/usr/bin/ld: /tmp/ccxBg2tS.o: in function `t': +/home/runner/work/devcontainer/devcontainer/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/ext/bigdecimal/conftest.c:16:(.text+0x7): undefined reference to `_lzcnt_u64' +collect2: error: ld returned 1 exit status +checked program was: +/* begin */ + 1: #include "ruby.h" + 2: + 3: #include + 4: + 5: /*top*/ + 6: extern int t(void); + 7: int main(int argc, char **argv) + 8: { + 9: if (argc > 1000000) { +10: int (* volatile tp)(void)=(int (*)(void))&t; +11: printf("%d", (*tp)()); +12: } +13: +14: return !!argv[argc]; +15: } +16: int t(void) { void ((*volatile p)()); p = (void ((*)()))_lzcnt_u64; return !p; } +/* end */ + +LD_LIBRARY_PATH=.:/opt/hostedtoolcache/Ruby/3.3.10/x64/lib "gcc -o conftest -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/ruby/backward -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0 -I. -DENABLE_PATH_CHECK=0 -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef -fPIC conftest.c -L. -L/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -Wl,-rpath,/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -L. -fstack-protector-strong -rdynamic -Wl,-export-dynamic -Wl,--no-as-needed -Wl,-rpath,/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -L/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -lruby -lm -lpthread -lc" +conftest.c:16:13: error: conflicting types for ‘_lzcnt_u64’; have ‘void()’ + 16 | extern void _lzcnt_u64(); + | ^~~~~~~~~~ +In file included from /usr/lib/gcc/x86_64-linux-gnu/13/include/x86gprintrin.h:61, + from /usr/lib/gcc/x86_64-linux-gnu/13/include/x86intrin.h:27, + from conftest.c:3: +/usr/lib/gcc/x86_64-linux-gnu/13/include/lzcntintrin.h:64:1: note: previous definition of ‘_lzcnt_u64’ with type ‘long long unsigned int(long long unsigned int)’ + 64 | _lzcnt_u64 (unsigned long long __X) + | ^~~~~~~~~~ +cc1: note: unrecognized command-line option ‘-Wno-self-assign’ may have been intended to silence earlier diagnostics +cc1: note: unrecognized command-line option ‘-Wno-parentheses-equality’ may have been intended to silence earlier diagnostics +cc1: note: unrecognized command-line option ‘-Wno-constant-logical-operand’ may have been intended to silence earlier diagnostics +checked program was: +/* begin */ + 1: #include "ruby.h" + 2: + 3: #include + 4: + 5: /*top*/ + 6: extern int t(void); + 7: int main(int argc, char **argv) + 8: { + 9: if (argc > 1000000) { +10: int (* volatile tp)(void)=(int (*)(void))&t; +11: printf("%d", (*tp)()); +12: } +13: +14: return !!argv[argc]; +15: } +16: extern void _lzcnt_u64(); +17: int t(void) { _lzcnt_u64(); return 0; } +/* end */ + +-------------------- + +have_header: checking for intrin.h... -------------------- no + +LD_LIBRARY_PATH=.:/opt/hostedtoolcache/Ruby/3.3.10/x64/lib "gcc -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/ruby/backward -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0 -I. -DENABLE_PATH_CHECK=0 -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef -fPIC -c conftest.c" +conftest.c:3:10: fatal error: intrin.h: No such file or directory + 3 | #include + | ^~~~~~~~~~ +compilation terminated. +checked program was: +/* begin */ +1: #include "ruby.h" +2: +3: #include +/* end */ + +-------------------- + +have_header: checking for ruby/atomic.h... -------------------- yes + +LD_LIBRARY_PATH=.:/opt/hostedtoolcache/Ruby/3.3.10/x64/lib "gcc -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/ruby/backward -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0 -I. -DENABLE_PATH_CHECK=0 -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef -fPIC -c conftest.c" +checked program was: +/* begin */ +1: #include "ruby.h" +2: +3: #include +/* end */ + +-------------------- + +have_header: checking for ruby/internal/has/builtin.h... -------------------- yes + +LD_LIBRARY_PATH=.:/opt/hostedtoolcache/Ruby/3.3.10/x64/lib "gcc -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/ruby/backward -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0 -I. -DENABLE_PATH_CHECK=0 -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef -fPIC -c conftest.c" +checked program was: +/* begin */ +1: #include "ruby.h" +2: +3: #include +/* end */ + +-------------------- + +have_header: checking for ruby/internal/static_assert.h... -------------------- yes + +LD_LIBRARY_PATH=.:/opt/hostedtoolcache/Ruby/3.3.10/x64/lib "gcc -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/ruby/backward -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0 -I. -DENABLE_PATH_CHECK=0 -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef -fPIC -c conftest.c" +checked program was: +/* begin */ +1: #include "ruby.h" +2: +3: #include +/* end */ + +-------------------- + +have_func: checking for rb_complex_real() in ruby.h... -------------------- yes + +LD_LIBRARY_PATH=.:/opt/hostedtoolcache/Ruby/3.3.10/x64/lib "gcc -o conftest -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/ruby/backward -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0 -I. -DENABLE_PATH_CHECK=0 -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef -fPIC conftest.c -L. -L/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -Wl,-rpath,/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -L. -fstack-protector-strong -rdynamic -Wl,-export-dynamic -Wl,--no-as-needed -Wl,-rpath,/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -L/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -lruby -lm -lpthread -lc" +checked program was: +/* begin */ + 1: #include "ruby.h" + 2: + 3: #include + 4: + 5: /*top*/ + 6: extern int t(void); + 7: int main(int argc, char **argv) + 8: { + 9: if (argc > 1000000) { +10: int (* volatile tp)(void)=(int (*)(void))&t; +11: printf("%d", (*tp)()); +12: } +13: +14: return !!argv[argc]; +15: } +16: int t(void) { void ((*volatile p)()); p = (void ((*)()))rb_complex_real; return !p; } +/* end */ + +-------------------- + +have_func: checking for rb_complex_imag() in ruby.h... -------------------- yes + +LD_LIBRARY_PATH=.:/opt/hostedtoolcache/Ruby/3.3.10/x64/lib "gcc -o conftest -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/ruby/backward -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0 -I. -DENABLE_PATH_CHECK=0 -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef -fPIC conftest.c -L. -L/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -Wl,-rpath,/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -L. -fstack-protector-strong -rdynamic -Wl,-export-dynamic -Wl,--no-as-needed -Wl,-rpath,/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -L/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -lruby -lm -lpthread -lc" +checked program was: +/* begin */ + 1: #include "ruby.h" + 2: + 3: #include + 4: + 5: /*top*/ + 6: extern int t(void); + 7: int main(int argc, char **argv) + 8: { + 9: if (argc > 1000000) { +10: int (* volatile tp)(void)=(int (*)(void))&t; +11: printf("%d", (*tp)()); +12: } +13: +14: return !!argv[argc]; +15: } +16: int t(void) { void ((*volatile p)()); p = (void ((*)()))rb_complex_imag; return !p; } +/* end */ + +-------------------- + +have_func: checking for rb_opts_exception_p() in ruby.h... -------------------- yes + +LD_LIBRARY_PATH=.:/opt/hostedtoolcache/Ruby/3.3.10/x64/lib "gcc -o conftest -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/ruby/backward -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0 -I. -DENABLE_PATH_CHECK=0 -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef -fPIC conftest.c -L. -L/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -Wl,-rpath,/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -L. -fstack-protector-strong -rdynamic -Wl,-export-dynamic -Wl,--no-as-needed -Wl,-rpath,/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -L/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -lruby -lm -lpthread -lc" +conftest.c: In function ‘t’: +conftest.c:16:57: error: ‘rb_opts_exception_p’ undeclared (first use in this function); did you mean ‘rb_make_exception’? + 16 | int t(void) { void ((*volatile p)()); p = (void ((*)()))rb_opts_exception_p; return !p; } + | ^~~~~~~~~~~~~~~~~~~ + | rb_make_exception +conftest.c:16:57: note: each undeclared identifier is reported only once for each function it appears in +At top level: +cc1: note: unrecognized command-line option ‘-Wno-self-assign’ may have been intended to silence earlier diagnostics +cc1: note: unrecognized command-line option ‘-Wno-parentheses-equality’ may have been intended to silence earlier diagnostics +cc1: note: unrecognized command-line option ‘-Wno-constant-logical-operand’ may have been intended to silence earlier diagnostics +checked program was: +/* begin */ + 1: #include "ruby.h" + 2: + 3: #include + 4: + 5: /*top*/ + 6: extern int t(void); + 7: int main(int argc, char **argv) + 8: { + 9: if (argc > 1000000) { +10: int (* volatile tp)(void)=(int (*)(void))&t; +11: printf("%d", (*tp)()); +12: } +13: +14: return !!argv[argc]; +15: } +16: int t(void) { void ((*volatile p)()); p = (void ((*)()))rb_opts_exception_p; return !p; } +/* end */ + +LD_LIBRARY_PATH=.:/opt/hostedtoolcache/Ruby/3.3.10/x64/lib "gcc -o conftest -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/ruby/backward -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0 -I. -DENABLE_PATH_CHECK=0 -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef -fPIC conftest.c -L. -L/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -Wl,-rpath,/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -L. -fstack-protector-strong -rdynamic -Wl,-export-dynamic -Wl,--no-as-needed -Wl,-rpath,/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -L/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -lruby -lm -lpthread -lc" +checked program was: +/* begin */ + 1: #include "ruby.h" + 2: + 3: #include + 4: + 5: /*top*/ + 6: extern int t(void); + 7: int main(int argc, char **argv) + 8: { + 9: if (argc > 1000000) { +10: int (* volatile tp)(void)=(int (*)(void))&t; +11: printf("%d", (*tp)()); +12: } +13: +14: return !!argv[argc]; +15: } +16: extern void rb_opts_exception_p(); +17: int t(void) { rb_opts_exception_p(); return 0; } +/* end */ + +-------------------- + +have_func: checking for rb_category_warn() in ruby.h... -------------------- yes + +LD_LIBRARY_PATH=.:/opt/hostedtoolcache/Ruby/3.3.10/x64/lib "gcc -o conftest -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/ruby/backward -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0 -I. -DENABLE_PATH_CHECK=0 -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef -fPIC conftest.c -L. -L/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -Wl,-rpath,/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -L. -fstack-protector-strong -rdynamic -Wl,-export-dynamic -Wl,--no-as-needed -Wl,-rpath,/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -L/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -lruby -lm -lpthread -lc" +checked program was: +/* begin */ + 1: #include "ruby.h" + 2: + 3: #include + 4: + 5: /*top*/ + 6: extern int t(void); + 7: int main(int argc, char **argv) + 8: { + 9: if (argc > 1000000) { +10: int (* volatile tp)(void)=(int (*)(void))&t; +11: printf("%d", (*tp)()); +12: } +13: +14: return !!argv[argc]; +15: } +16: int t(void) { void ((*volatile p)()); p = (void ((*)()))rb_category_warn; return !p; } +/* end */ + +-------------------- + +have_const: checking for RB_WARN_CATEGORY_DEPRECATED in ruby.h... -------------------- yes + +LD_LIBRARY_PATH=.:/opt/hostedtoolcache/Ruby/3.3.10/x64/lib "gcc -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/ruby/backward -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0 -I. -DENABLE_PATH_CHECK=0 -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef -fPIC -c conftest.c" +checked program was: +/* begin */ +1: #include "ruby.h" +2: +3: #include +4: +5: /*top*/ +6: typedef int conftest_type; +7: conftest_type conftestval = (int)RB_WARN_CATEGORY_DEPRECATED; +/* end */ + +-------------------- + diff --git a/vendor/bundle/ruby/3.3.0/extensions/x86_64-linux/3.3.0/json-2.18.0/gem.build_complete b/vendor/bundle/ruby/3.3.0/extensions/x86_64-linux/3.3.0/json-2.18.0/gem.build_complete new file mode 100644 index 0000000..e69de29 diff --git a/vendor/bundle/ruby/3.3.0/extensions/x86_64-linux/3.3.0/json-2.18.0/gem_make.out b/vendor/bundle/ruby/3.3.0/extensions/x86_64-linux/3.3.0/json-2.18.0/gem_make.out new file mode 100644 index 0000000..70e1c81 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/extensions/x86_64-linux/3.3.0/json-2.18.0/gem_make.out @@ -0,0 +1,25 @@ +current directory: /home/runner/work/devcontainer/devcontainer/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/ext/json/ext/parser +/opt/hostedtoolcache/Ruby/3.3.10/x64/bin/ruby extconf.rb +checking for rb_enc_interned_str() in ruby/encoding.h... yes +checking for rb_str_to_interned_str() in ruby.h... yes +checking for rb_hash_new_capa() in ruby.h... yes +checking for rb_hash_bulk_insert() in ruby.h... yes +checking for whether -std=c99 is accepted as CFLAGS... yes +checking for x86intrin.h... yes +checking for cpuid.h... yes +creating Makefile + +current directory: /home/runner/work/devcontainer/devcontainer/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/ext/json/ext/parser +make DESTDIR\= sitearchdir\=./.gem.20260202-2234-rn5wr0 sitelibdir\=./.gem.20260202-2234-rn5wr0 clean + +current directory: /home/runner/work/devcontainer/devcontainer/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/ext/json/ext/parser +make DESTDIR\= sitearchdir\=./.gem.20260202-2234-rn5wr0 sitelibdir\=./.gem.20260202-2234-rn5wr0 +compiling parser.c +linking shared-object json/ext/parser.so + +current directory: /home/runner/work/devcontainer/devcontainer/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/ext/json/ext/parser +make DESTDIR\= sitearchdir\=./.gem.20260202-2234-rn5wr0 sitelibdir\=./.gem.20260202-2234-rn5wr0 install +/usr/bin/install -c -m 0755 parser.so ./.gem.20260202-2234-rn5wr0/json/ext + +current directory: /home/runner/work/devcontainer/devcontainer/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/ext/json/ext/parser +make DESTDIR\= sitearchdir\=./.gem.20260202-2234-rn5wr0 sitelibdir\=./.gem.20260202-2234-rn5wr0 clean diff --git a/vendor/bundle/ruby/3.3.0/extensions/x86_64-linux/3.3.0/json-2.18.0/json/ext/generator.so b/vendor/bundle/ruby/3.3.0/extensions/x86_64-linux/3.3.0/json-2.18.0/json/ext/generator.so new file mode 100755 index 0000000..e12d6f9 Binary files /dev/null and b/vendor/bundle/ruby/3.3.0/extensions/x86_64-linux/3.3.0/json-2.18.0/json/ext/generator.so differ diff --git a/vendor/bundle/ruby/3.3.0/extensions/x86_64-linux/3.3.0/json-2.18.0/json/ext/parser.so b/vendor/bundle/ruby/3.3.0/extensions/x86_64-linux/3.3.0/json-2.18.0/json/ext/parser.so new file mode 100755 index 0000000..9b52a04 Binary files /dev/null and b/vendor/bundle/ruby/3.3.0/extensions/x86_64-linux/3.3.0/json-2.18.0/json/ext/parser.so differ diff --git a/vendor/bundle/ruby/3.3.0/extensions/x86_64-linux/3.3.0/json-2.18.0/mkmf.log b/vendor/bundle/ruby/3.3.0/extensions/x86_64-linux/3.3.0/json-2.18.0/mkmf.log new file mode 100644 index 0000000..8d01bfa --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/extensions/x86_64-linux/3.3.0/json-2.18.0/mkmf.log @@ -0,0 +1,165 @@ +have_func: checking for rb_enc_interned_str() in ruby/encoding.h... -------------------- yes + +LD_LIBRARY_PATH=.:/opt/hostedtoolcache/Ruby/3.3.10/x64/lib "gcc -o conftest -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/ruby/backward -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0 -I. -DENABLE_PATH_CHECK=0 -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef -fPIC conftest.c -L. -L/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -Wl,-rpath,/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -L. -fstack-protector-strong -rdynamic -Wl,-export-dynamic -Wl,--no-as-needed -Wl,-rpath,/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -L/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -lruby -lm -lpthread -lc" +checked program was: +/* begin */ +1: #include "ruby.h" +2: +3: int main(int argc, char **argv) +4: { +5: return !!argv[argc]; +6: } +/* end */ + +LD_LIBRARY_PATH=.:/opt/hostedtoolcache/Ruby/3.3.10/x64/lib "gcc -o conftest -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/ruby/backward -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0 -I. -DENABLE_PATH_CHECK=0 -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef -fPIC conftest.c -L. -L/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -Wl,-rpath,/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -L. -fstack-protector-strong -rdynamic -Wl,-export-dynamic -Wl,--no-as-needed -Wl,-rpath,/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -L/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -lruby -lm -lpthread -lc" +checked program was: +/* begin */ + 1: #include "ruby.h" + 2: + 3: #include + 4: + 5: /*top*/ + 6: extern int t(void); + 7: int main(int argc, char **argv) + 8: { + 9: if (argc > 1000000) { +10: int (* volatile tp)(void)=(int (*)(void))&t; +11: printf("%d", (*tp)()); +12: } +13: +14: return !!argv[argc]; +15: } +16: int t(void) { void ((*volatile p)()); p = (void ((*)()))rb_enc_interned_str; return !p; } +/* end */ + +-------------------- + +have_func: checking for rb_str_to_interned_str() in ruby.h... -------------------- yes + +LD_LIBRARY_PATH=.:/opt/hostedtoolcache/Ruby/3.3.10/x64/lib "gcc -o conftest -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/ruby/backward -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0 -I. -DENABLE_PATH_CHECK=0 -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef -fPIC conftest.c -L. -L/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -Wl,-rpath,/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -L. -fstack-protector-strong -rdynamic -Wl,-export-dynamic -Wl,--no-as-needed -Wl,-rpath,/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -L/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -lruby -lm -lpthread -lc" +checked program was: +/* begin */ + 1: #include "ruby.h" + 2: + 3: #include + 4: + 5: /*top*/ + 6: extern int t(void); + 7: int main(int argc, char **argv) + 8: { + 9: if (argc > 1000000) { +10: int (* volatile tp)(void)=(int (*)(void))&t; +11: printf("%d", (*tp)()); +12: } +13: +14: return !!argv[argc]; +15: } +16: int t(void) { void ((*volatile p)()); p = (void ((*)()))rb_str_to_interned_str; return !p; } +/* end */ + +-------------------- + +have_func: checking for rb_hash_new_capa() in ruby.h... -------------------- yes + +LD_LIBRARY_PATH=.:/opt/hostedtoolcache/Ruby/3.3.10/x64/lib "gcc -o conftest -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/ruby/backward -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0 -I. -DENABLE_PATH_CHECK=0 -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef -fPIC conftest.c -L. -L/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -Wl,-rpath,/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -L. -fstack-protector-strong -rdynamic -Wl,-export-dynamic -Wl,--no-as-needed -Wl,-rpath,/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -L/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -lruby -lm -lpthread -lc" +checked program was: +/* begin */ + 1: #include "ruby.h" + 2: + 3: #include + 4: + 5: /*top*/ + 6: extern int t(void); + 7: int main(int argc, char **argv) + 8: { + 9: if (argc > 1000000) { +10: int (* volatile tp)(void)=(int (*)(void))&t; +11: printf("%d", (*tp)()); +12: } +13: +14: return !!argv[argc]; +15: } +16: int t(void) { void ((*volatile p)()); p = (void ((*)()))rb_hash_new_capa; return !p; } +/* end */ + +-------------------- + +have_func: checking for rb_hash_bulk_insert() in ruby.h... -------------------- yes + +LD_LIBRARY_PATH=.:/opt/hostedtoolcache/Ruby/3.3.10/x64/lib "gcc -o conftest -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/ruby/backward -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0 -I. -DENABLE_PATH_CHECK=0 -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef -fPIC conftest.c -L. -L/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -Wl,-rpath,/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -L. -fstack-protector-strong -rdynamic -Wl,-export-dynamic -Wl,--no-as-needed -Wl,-rpath,/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -L/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -lruby -lm -lpthread -lc" +checked program was: +/* begin */ + 1: #include "ruby.h" + 2: + 3: #include + 4: + 5: /*top*/ + 6: extern int t(void); + 7: int main(int argc, char **argv) + 8: { + 9: if (argc > 1000000) { +10: int (* volatile tp)(void)=(int (*)(void))&t; +11: printf("%d", (*tp)()); +12: } +13: +14: return !!argv[argc]; +15: } +16: int t(void) { void ((*volatile p)()); p = (void ((*)()))rb_hash_bulk_insert; return !p; } +/* end */ + +-------------------- + +append_cflags: checking for whether -std=c99 is accepted as CFLAGS... -------------------- yes + +LD_LIBRARY_PATH=.:/opt/hostedtoolcache/Ruby/3.3.10/x64/lib "gcc -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/ruby/backward -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0 -I. -DENABLE_PATH_CHECK=0 -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef -fPIC -std=c99 -Werror -c conftest.c" +checked program was: +/* begin */ +1: #include "ruby.h" +2: +3: int main(int argc, char **argv) +4: { +5: return !!argv[argc]; +6: } +/* end */ + +-------------------- + +have_header: checking for x86intrin.h... -------------------- yes + +LD_LIBRARY_PATH=.:/opt/hostedtoolcache/Ruby/3.3.10/x64/lib "gcc -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/ruby/backward -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0 -I. -DENABLE_PATH_CHECK=0 -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef -fPIC -std=c99 -c conftest.c" +checked program was: +/* begin */ +1: #include "ruby.h" +2: +3: #include +/* end */ + +-------------------- + +LD_LIBRARY_PATH=.:/opt/hostedtoolcache/Ruby/3.3.10/x64/lib "gcc -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/ruby/backward -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0 -I. -DENABLE_PATH_CHECK=0 -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef -fPIC -std=c99 -Werror=implicit-function-declaration -c conftest.c" +checked program was: +/* begin */ + 1: #include "ruby.h" + 2: + 3: #include + 4: + 5: int main(int argc, char **argv) { + 6: __m128i test = _mm_set1_epi8(32); + 7: if (__builtin_cpu_supports("sse2")) { printf("OK"); } + 8: if (argc > 100000) printf("%p", &test); + 9: return 0; +10: } +/* end */ + +have_header: checking for cpuid.h... -------------------- yes + +LD_LIBRARY_PATH=.:/opt/hostedtoolcache/Ruby/3.3.10/x64/lib "gcc -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/ruby/backward -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0 -I. -DENABLE_PATH_CHECK=0 -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef -fPIC -std=c99 -c conftest.c" +checked program was: +/* begin */ +1: #include "ruby.h" +2: +3: #include +/* end */ + +-------------------- + diff --git a/vendor/bundle/ruby/3.3.0/extensions/x86_64-linux/3.3.0/prism-1.9.0/gem.build_complete b/vendor/bundle/ruby/3.3.0/extensions/x86_64-linux/3.3.0/prism-1.9.0/gem.build_complete new file mode 100644 index 0000000..e69de29 diff --git a/vendor/bundle/ruby/3.3.0/extensions/x86_64-linux/3.3.0/prism-1.9.0/gem_make.out b/vendor/bundle/ruby/3.3.0/extensions/x86_64-linux/3.3.0/prism-1.9.0/gem_make.out new file mode 100644 index 0000000..abceeb7 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/extensions/x86_64-linux/3.3.0/prism-1.9.0/gem_make.out @@ -0,0 +1,44 @@ +current directory: /home/runner/work/devcontainer/devcontainer/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/ext/prism +/opt/hostedtoolcache/Ruby/3.3.10/x64/bin/ruby extconf.rb +checking for prism.h in /home/runner/work/devcontainer/devcontainer/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include... yes +checking for prism/extension.h in /home/runner/work/devcontainer/devcontainer/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/ext... yes +checking for whether -fvisibility=hidden is accepted as CFLAGS... yes +creating Makefile + +current directory: /home/runner/work/devcontainer/devcontainer/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/ext/prism +make DESTDIR\= sitearchdir\=./.gem.20260202-2234-u7kra1 sitelibdir\=./.gem.20260202-2234-u7kra1 clean + +current directory: /home/runner/work/devcontainer/devcontainer/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/ext/prism +make DESTDIR\= sitearchdir\=./.gem.20260202-2234-u7kra1 sitelibdir\=./.gem.20260202-2234-u7kra1 +compiling api_node.c +compiling api_pack.c +compiling extension.c +compiling ./../../src/diagnostic.c +compiling ./../../src/encoding.c +compiling ./../../src/node.c +compiling ./../../src/options.c +compiling ./../../src/pack.c +compiling ./../../src/prettyprint.c +compiling ./../../src/prism.c +compiling ./../../src/regexp.c +compiling ./../../src/serialize.c +compiling ./../../src/static_literals.c +compiling ./../../src/token_type.c +compiling ./../../src/util/pm_buffer.c +compiling ./../../src/util/pm_char.c +compiling ./../../src/util/pm_constant_pool.c +compiling ./../../src/util/pm_integer.c +compiling ./../../src/util/pm_list.c +compiling ./../../src/util/pm_memchr.c +compiling ./../../src/util/pm_newline_list.c +compiling ./../../src/util/pm_string.c +compiling ./../../src/util/pm_strncasecmp.c +compiling ./../../src/util/pm_strpbrk.c +linking shared-object prism/prism.so + +current directory: /home/runner/work/devcontainer/devcontainer/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/ext/prism +make DESTDIR\= sitearchdir\=./.gem.20260202-2234-u7kra1 sitelibdir\=./.gem.20260202-2234-u7kra1 install +/usr/bin/install -c -m 0755 prism.so ./.gem.20260202-2234-u7kra1/prism + +current directory: /home/runner/work/devcontainer/devcontainer/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/ext/prism +make DESTDIR\= sitearchdir\=./.gem.20260202-2234-u7kra1 sitelibdir\=./.gem.20260202-2234-u7kra1 clean diff --git a/vendor/bundle/ruby/3.3.0/extensions/x86_64-linux/3.3.0/prism-1.9.0/mkmf.log b/vendor/bundle/ruby/3.3.0/extensions/x86_64-linux/3.3.0/prism-1.9.0/mkmf.log new file mode 100644 index 0000000..f5a7087 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/extensions/x86_64-linux/3.3.0/prism-1.9.0/mkmf.log @@ -0,0 +1,74 @@ +find_header: checking for prism.h in /home/runner/work/devcontainer/devcontainer/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include... -------------------- yes + +LD_LIBRARY_PATH=.:/opt/hostedtoolcache/Ruby/3.3.10/x64/lib "gcc -o conftest -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/ruby/backward -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0 -I. -DENABLE_PATH_CHECK=0 -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef -fPIC conftest.c -L. -L/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -Wl,-rpath,/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -L. -fstack-protector-strong -rdynamic -Wl,-export-dynamic -Wl,--no-as-needed -Wl,-rpath,/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -L/opt/hostedtoolcache/Ruby/3.3.10/x64/lib -lruby -lm -lpthread -lc" +checked program was: +/* begin */ +1: #include "ruby.h" +2: +3: int main(int argc, char **argv) +4: { +5: return !!argv[argc]; +6: } +/* end */ + +LD_LIBRARY_PATH=.:/opt/hostedtoolcache/Ruby/3.3.10/x64/lib "gcc -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/ruby/backward -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0 -I. -DENABLE_PATH_CHECK=0 -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef -fPIC -c conftest.c" +conftest.c:3:10: fatal error: prism.h: No such file or directory + 3 | #include + | ^~~~~~~~~ +compilation terminated. +checked program was: +/* begin */ +1: #include "ruby.h" +2: +3: #include +/* end */ + +LD_LIBRARY_PATH=.:/opt/hostedtoolcache/Ruby/3.3.10/x64/lib "gcc -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/ruby/backward -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0 -I. -DENABLE_PATH_CHECK=0 -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef -fPIC -I/home/runner/work/devcontainer/devcontainer/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include -c conftest.c" +checked program was: +/* begin */ +1: #include "ruby.h" +2: +3: #include +/* end */ + +-------------------- + +find_header: checking for prism/extension.h in /home/runner/work/devcontainer/devcontainer/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/ext... -------------------- yes + +LD_LIBRARY_PATH=.:/opt/hostedtoolcache/Ruby/3.3.10/x64/lib "gcc -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/ruby/backward -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0 -I. -I/home/runner/work/devcontainer/devcontainer/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include -DENABLE_PATH_CHECK=0 -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef -fPIC -c conftest.c" +conftest.c:3:10: fatal error: prism/extension.h: No such file or directory + 3 | #include + | ^~~~~~~~~~~~~~~~~~~ +compilation terminated. +checked program was: +/* begin */ +1: #include "ruby.h" +2: +3: #include +/* end */ + +LD_LIBRARY_PATH=.:/opt/hostedtoolcache/Ruby/3.3.10/x64/lib "gcc -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/ruby/backward -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0 -I. -I/home/runner/work/devcontainer/devcontainer/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include -DENABLE_PATH_CHECK=0 -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef -fPIC -I/home/runner/work/devcontainer/devcontainer/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/ext -c conftest.c" +checked program was: +/* begin */ +1: #include "ruby.h" +2: +3: #include +/* end */ + +-------------------- + +append_cflags: checking for whether -fvisibility=hidden is accepted as CFLAGS... -------------------- yes + +LD_LIBRARY_PATH=.:/opt/hostedtoolcache/Ruby/3.3.10/x64/lib "gcc -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/x86_64-linux -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/ruby/backward -I/opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0 -I. -I/home/runner/work/devcontainer/devcontainer/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include -I/home/runner/work/devcontainer/devcontainer/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/ext -DENABLE_PATH_CHECK=0 -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef -fPIC -fvisibility=hidden -Werror -c conftest.c" +checked program was: +/* begin */ +1: #include "ruby.h" +2: +3: int main(int argc, char **argv) +4: { +5: return !!argv[argc]; +6: } +/* end */ + +-------------------- + diff --git a/vendor/bundle/ruby/3.3.0/extensions/x86_64-linux/3.3.0/prism-1.9.0/prism/prism.so b/vendor/bundle/ruby/3.3.0/extensions/x86_64-linux/3.3.0/prism-1.9.0/prism/prism.so new file mode 100755 index 0000000..45f2b38 Binary files /dev/null and b/vendor/bundle/ruby/3.3.0/extensions/x86_64-linux/3.3.0/prism-1.9.0/prism/prism.so differ diff --git a/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/CHANGELOG.md b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/CHANGELOG.md new file mode 100644 index 0000000..e7757e6 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/CHANGELOG.md @@ -0,0 +1,310 @@ +# Changelog + +## Addressable 2.8.8 +- Replace the `unicode.data` blob by a ruby constant ([#561]) +- Allow `public_suffix` 7 ([#558]) + +[#561]: https://github.com/sporkmonger/addressable/pull/561 +[#535]: https://github.com/sporkmonger/addressable/pull/558 + +## Addressable 2.8.7 +- Allow `public_suffix` 6 ([#535]) + +[#535]: https://github.com/sporkmonger/addressable/pull/535 + +## Addressable 2.8.6 +- Memoize regexps for common character classes ([#524]) + +[#524]: https://github.com/sporkmonger/addressable/pull/524 + +## Addressable 2.8.5 +- Fix thread safety issue with encoding tables ([#515]) +- Define URI::NONE as a module to avoid serialization issues ([#509]) +- Fix YAML serialization ([#508]) + +[#508]: https://github.com/sporkmonger/addressable/pull/508 +[#509]: https://github.com/sporkmonger/addressable/pull/509 +[#515]: https://github.com/sporkmonger/addressable/pull/515 + +## Addressable 2.8.4 +- Restore `Addressable::IDNA.unicode_normalize_kc` as a deprecated method ([#504]) + +[#504]: https://github.com/sporkmonger/addressable/pull/504 + +## Addressable 2.8.3 +- Fix template expand level 2 hash support for non-string objects ([#499], [#498]) + +[#499]: https://github.com/sporkmonger/addressable/pull/499 +[#498]: https://github.com/sporkmonger/addressable/pull/498 + +## Addressable 2.8.2 +- Improve cache hits and JIT friendliness ([#486](https://github.com/sporkmonger/addressable/pull/486)) +- Improve code style and test coverage ([#482](https://github.com/sporkmonger/addressable/pull/482)) +- Ensure reset of deferred validation ([#481](https://github.com/sporkmonger/addressable/pull/481)) +- Resolve normalization differences between `IDNA::Native` and `IDNA::Pure` ([#408](https://github.com/sporkmonger/addressable/issues/408), [#492]) +- Remove redundant colon in `Addressable::URI::CharacterClasses::AUTHORITY` regex ([#438](https://github.com/sporkmonger/addressable/pull/438)) (accidentally reverted by [#449] merge but [added back](https://github.com/sporkmonger/addressable/pull/492#discussion_r1105125280) in [#492]) + +[#492]: https://github.com/sporkmonger/addressable/pull/492 + +## Addressable 2.8.1 +- refactor `Addressable::URI.normalize_path` to address linter offenses ([#430](https://github.com/sporkmonger/addressable/pull/430)) +- update gemspec to reflect supported Ruby versions ([#466], [#464], [#463]) +- compatibility w/ public_suffix 5.x ([#466], [#465], [#460]) +- fixes "invalid byte sequence in UTF-8" exception when unencoding URLs containing non UTF-8 characters ([#459](https://github.com/sporkmonger/addressable/pull/459)) +- `Ractor` compatibility ([#449]) +- use the whole string instead of a single line for template match ([#431](https://github.com/sporkmonger/addressable/pull/431)) +- force UTF-8 encoding only if needed ([#341](https://github.com/sporkmonger/addressable/pull/341)) + +[#449]: https://github.com/sporkmonger/addressable/pull/449 +[#460]: https://github.com/sporkmonger/addressable/pull/460 +[#463]: https://github.com/sporkmonger/addressable/pull/463 +[#464]: https://github.com/sporkmonger/addressable/pull/464 +[#465]: https://github.com/sporkmonger/addressable/pull/465 +[#466]: https://github.com/sporkmonger/addressable/pull/466 + +## Addressable 2.8.0 +- fixes ReDoS vulnerability in Addressable::Template#match +- no longer replaces `+` with spaces in queries for non-http(s) schemes +- fixed encoding ipv6 literals +- the `:compacted` flag for `normalized_query` now dedupes parameters +- fix broken `escape_component` alias +- dropping support for Ruby 2.0 and 2.1 +- adding Ruby 3.0 compatibility for development tasks +- drop support for `rack-mount` and remove Addressable::Template#generate +- performance improvements +- switch CI/CD to GitHub Actions + +## Addressable 2.7.0 +- added `:compacted` flag to `normalized_query` +- `heuristic_parse` handles `mailto:` more intuitively +- dropped explicit support for JRuby 9.0.5.0 +- compatibility w/ public_suffix 4.x +- performance improvements + +## Addressable 2.6.0 +- added `tld=` method to allow assignment to the public suffix +- most `heuristic_parse` patterns are now case-insensitive +- `heuristic_parse` handles more `file://` URI variations +- fixes bug in `heuristic_parse` when uri starts with digit +- fixes bug in `request_uri=` with query strings +- fixes template issues with `nil` and `?` operator +- `frozen_string_literal` pragmas added +- minor performance improvements in regexps +- fixes to eliminate warnings + +## Addressable 2.5.2 +- better support for frozen string literals +- fixed bug w/ uppercase characters in scheme +- IDNA errors w/ emoji URLs +- compatibility w/ public_suffix 3.x + +## Addressable 2.5.1 +- allow unicode normalization to be disabled for URI Template expansion +- removed duplicate test + +## Addressable 2.5.0 +- dropping support for Ruby 1.9 +- adding support for Ruby 2.4 preview +- add support for public suffixes and tld; first runtime dependency +- hostname escaping should match RFC; underscores in hostnames no longer escaped +- paths beginning with // and missing an authority are now considered invalid +- validation now also takes place after setting a path +- handle backslashes in authority more like a browser for `heuristic_parse` +- unescaped backslashes in host now raise an `InvalidURIError` +- `merge!`, `join!`, `omit!` and `normalize!` don't disable deferred validation +- `heuristic_parse` now trims whitespace before parsing +- host parts longer than 63 bytes will be ignored and not passed to libidn +- normalized values always encoded as UTF-8 + +## Addressable 2.4.0 +- support for 1.8.x dropped +- double quotes in a host now raises an error +- newlines in host will no longer get unescaped during normalization +- stricter handling of bogus scheme values +- stricter handling of encoded port values +- calling `require 'addressable'` will now load both the URI and Template files +- assigning to the `hostname` component with an `IPAddr` object is now supported +- assigning to the `origin` component is now supported +- fixed minor bug where an exception would be thrown for a missing ACE suffix +- better partial expansion of URI templates + +## Addressable 2.3.8 +- fix warnings +- update dependency gems +- support for 1.8.x officially deprecated + +## Addressable 2.3.7 +- fix scenario in which invalid URIs don't get an exception until inspected +- handle hostnames with two adjacent periods correctly +- upgrade of RSpec + +## Addressable 2.3.6 +- normalization drops empty query string +- better handling in template extract for missing values +- template modifier for `'?'` now treated as optional +- fixed issue where character class parameters were modified +- templates can now be tested for equality +- added `:sorted` option to normalization of query strings +- fixed issue with normalization of hosts given in `'example.com.'` form + +## Addressable 2.3.5 +- added Addressable::URI#empty? method +- Addressable::URI#hostname methods now strip square brackets from IPv6 hosts +- compatibility with Net::HTTP in Ruby 2.0.0 +- Addressable::URI#route_from should always give relative URIs + +## Addressable 2.3.4 +- fixed issue with encoding altering its inputs +- query string normalization now leaves ';' characters alone +- FakeFS is detected before attempting to load unicode tables +- additional testing to ensure frozen objects don't cause problems + +## Addressable 2.3.3 +- fixed issue with converting common primitives during template expansion +- fixed port encoding issue +- removed a few warnings +- normalize should now ignore %2B in query strings +- the IDNA logic should now be handled by libidn in Ruby 1.9 +- no template match should now result in nil instead of an empty MatchData +- added license information to gemspec + +## Addressable 2.3.2 +- added Addressable::URI#default_port method +- fixed issue with Marshalling Unicode data on Windows +- improved heuristic parsing to better handle IPv4 addresses + +## Addressable 2.3.1 +- fixed missing unicode data file + +## Addressable 2.3.0 +- updated Addressable::Template to use RFC 6570, level 4 +- fixed compatibility problems with some versions of Ruby +- moved unicode tables into a data file for performance reasons +- removing support for multiple query value notations + +## Addressable 2.2.8 +- fixed issues with dot segment removal code +- form encoding can now handle multiple values per key +- updated development environment + +## Addressable 2.2.7 +- fixed issues related to Addressable::URI#query_values= +- the Addressable::URI.parse method is now polymorphic + +## Addressable 2.2.6 +- changed the way ambiguous paths are handled +- fixed bug with frozen URIs +- https supported in heuristic parsing + +## Addressable 2.2.5 +- 'parsing' a pre-parsed URI object is now a dup operation +- introduced conditional support for libidn +- fixed normalization issue on ampersands in query strings +- added additional tests around handling of query strings + +## Addressable 2.2.4 +- added origin support from draft-ietf-websec-origin-00 +- resolved issue with attempting to navigate below root +- fixed bug with string splitting in query strings + +## Addressable 2.2.3 +- added :flat_array notation for query strings + +## Addressable 2.2.2 +- fixed issue with percent escaping of '+' character in query strings + +## Addressable 2.2.1 +- added support for application/x-www-form-urlencoded. + +## Addressable 2.2.0 +- added site methods +- improved documentation + +## Addressable 2.1.2 +- added HTTP request URI methods +- better handling of Windows file paths +- validation_deferred boolean replaced with defer_validation block +- normalization of percent-encoded paths should now be correct +- fixed issue with constructing URIs with relative paths +- fixed warnings + +## Addressable 2.1.1 +- more type checking changes +- fixed issue with unicode normalization +- added method to find template defaults +- symbolic keys are now allowed in template mappings +- numeric values and symbolic values are now allowed in template mappings + +## Addressable 2.1.0 +- refactored URI template support out into its own class +- removed extract method due to being useless and unreliable +- removed Addressable::URI.expand_template +- removed Addressable::URI#extract_mapping +- added partial template expansion +- fixed minor bugs in the parse and heuristic_parse methods +- fixed incompatibility with Ruby 1.9.1 +- fixed bottleneck in Addressable::URI#hash and Addressable::URI#to_s +- fixed unicode normalization exception +- updated query_values methods to better handle subscript notation +- worked around issue with freezing URIs +- improved specs + +## Addressable 2.0.2 +- fixed issue with URI template expansion +- fixed issue with percent escaping characters 0-15 + +## Addressable 2.0.1 +- fixed issue with query string assignment +- fixed issue with improperly encoded components + +## Addressable 2.0.0 +- the initialize method now takes an options hash as its only parameter +- added query_values method to URI class +- completely replaced IDNA implementation with pure Ruby +- renamed Addressable::ADDRESSABLE_VERSION to Addressable::VERSION +- completely reworked the Rakefile +- changed the behavior of the port method significantly +- Addressable::URI.encode_segment, Addressable::URI.unencode_segment renamed +- documentation is now in YARD format +- more rigorous type checking +- to_str method implemented, implicit conversion to Strings now allowed +- Addressable::URI#omit method added, Addressable::URI#merge method replaced +- updated URI Template code to match v 03 of the draft spec +- added a bunch of new specifications + +## Addressable 1.0.4 +- switched to using RSpec's pending system for specs that rely on IDN +- fixed issue with creating URIs with paths that are not prefixed with '/' + +## Addressable 1.0.3 +- implemented a hash method + +## Addressable 1.0.2 +- fixed minor bug with the extract_mapping method + +## Addressable 1.0.1 +- fixed minor bug with the extract_mapping method + +## Addressable 1.0.0 +- heuristic parse method added +- parsing is slightly more strict +- replaced to_h with to_hash +- fixed routing methods +- improved specifications +- improved heckle rake task +- no surviving heckle mutations + +## Addressable 0.1.2 +- improved normalization +- fixed bug in joining algorithm +- updated specifications + +## Addressable 0.1.1 +- updated documentation +- added URI Template variable extraction + +## Addressable 0.1.0 +- initial release +- implementation based on RFC 3986, 3987 +- support for IRIs via libidn +- support for the URI Template draft spec diff --git a/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/Gemfile b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/Gemfile new file mode 100644 index 0000000..87cf771 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/Gemfile @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +source 'https://rubygems.org' + +gemspec + +group :test do + gem 'bigdecimal' if RUBY_VERSION > '2.4' + gem 'rspec', '~> 3.8' + gem 'rspec-its', '~> 1.3' +end + +group :coverage do + gem "coveralls", "> 0.7", require: false, platforms: :mri + gem "simplecov", require: false +end + +group :development do + gem 'launchy', '~> 2.4', '>= 2.4.3' + gem 'redcarpet', :platform => :mri_19 + gem 'yard' +end + +group :test, :development do + gem 'memory_profiler' + gem "rake", ">= 12.3.3" +end + +unless ENV["IDNA_MODE"] == "pure" + gem "idn-ruby", platform: :mri +end diff --git a/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/LICENSE.txt b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/LICENSE.txt new file mode 100644 index 0000000..ef51da2 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +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. diff --git a/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/README.md b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/README.md new file mode 100644 index 0000000..d9fa04f --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/README.md @@ -0,0 +1,121 @@ +# Addressable + +
+
Homepage
github.com/sporkmonger/addressable
+
Author
Bob Aman
+
Copyright
Copyright © Bob Aman
+
License
Apache 2.0
+
+ +[![Gem Version](https://img.shields.io/gem/dt/addressable.svg)][gem] +[![Build Status](https://github.com/sporkmonger/addressable/workflows/CI/badge.svg)][actions] +[![Test Coverage Status](https://img.shields.io/coveralls/sporkmonger/addressable.svg)][coveralls] +[![Documentation Coverage Status](https://inch-ci.org/github/sporkmonger/addressable.svg?branch=master)][inch] + +[gem]: https://rubygems.org/gems/addressable +[actions]: https://github.com/sporkmonger/addressable/actions +[coveralls]: https://coveralls.io/r/sporkmonger/addressable +[inch]: https://inch-ci.org/github/sporkmonger/addressable + +## Description + +Addressable is an alternative implementation to the URI implementation +that is part of Ruby's standard library. It is flexible, offers heuristic +parsing, and additionally provides extensive support for IRIs and URI templates. + +Addressable closely conforms to RFC 3986, RFC 3987, and RFC 6570 (level 4). + +## Reference + +- {Addressable::URI} +- {Addressable::Template} + +## Example usage + +```ruby +require "addressable/uri" + +uri = Addressable::URI.parse("http://example.com/path/to/resource/") +uri.scheme +#=> "http" +uri.host +#=> "example.com" +uri.path +#=> "/path/to/resource/" + +uri = Addressable::URI.parse("http://www.詹姆斯.com/") +uri.normalize +#=> # +``` + + +## URI Templates + +For more details, see [RFC 6570](https://www.rfc-editor.org/rfc/rfc6570.txt). + + +```ruby + +require "addressable/template" + +template = Addressable::Template.new("http://example.com/{?query*}") +template.expand({ + "query" => { + 'foo' => 'bar', + 'color' => 'red' + } +}) +#=> # + +template = Addressable::Template.new("http://example.com/{?one,two,three}") +template.partial_expand({"one" => "1", "three" => 3}).pattern +#=> "http://example.com/?one=1{&two}&three=3" + +template = Addressable::Template.new( + "http://{host}{/segments*}/{?one,two,bogus}{#fragment}" +) +uri = Addressable::URI.parse( + "http://example.com/a/b/c/?one=1&two=2#foo" +) +template.extract(uri) +#=> +# { +# "host" => "example.com", +# "segments" => ["a", "b", "c"], +# "one" => "1", +# "two" => "2", +# "fragment" => "foo" +# } +``` + +## Install + +```console +$ gem install addressable +``` + +You may optionally turn on native IDN support by installing libidn and the +idn gem: + +```console +$ sudo apt-get install libidn11-dev # Debian/Ubuntu +$ brew install libidn # OS X +$ gem install idn-ruby +``` + +## Semantic Versioning + +This project uses [Semantic Versioning](https://semver.org/). You can (and should) specify your +dependency using a pessimistic version constraint covering the major and minor +values: + +```ruby +spec.add_dependency 'addressable', '~> 2.7' +``` + +If you need a specific bug fix, you can also specify minimum tiny versions +without preventing updates to the latest minor release: + +```ruby +spec.add_dependency 'addressable', '~> 2.3', '>= 2.3.7' +``` diff --git a/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/Rakefile b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/Rakefile new file mode 100644 index 0000000..46fbbd0 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/Rakefile @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +require 'rubygems' +require 'rake' + +require File.join(File.dirname(__FILE__), 'lib', 'addressable', 'version') + +PKG_DISPLAY_NAME = 'Addressable' +PKG_NAME = PKG_DISPLAY_NAME.downcase +PKG_VERSION = Addressable::VERSION::STRING +PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}" + +RELEASE_NAME = "REL #{PKG_VERSION}" + +PKG_SUMMARY = "URI Implementation" +PKG_DESCRIPTION = <<-TEXT +Addressable is an alternative implementation to the URI implementation that is +part of Ruby's standard library. It is flexible, offers heuristic parsing, and +additionally provides extensive support for IRIs and URI templates. +TEXT + +PKG_FILES = FileList[ + "data/**/*", + "lib/**/*.rb", + "spec/**/*.rb", + "tasks/**/*.rake", + "addressable.gemspec", + "CHANGELOG.md", + "Gemfile", + "LICENSE.txt", + "README.md", + "Rakefile", +] + +task :default => "spec" + +Dir['tasks/**/*.rake'].each { |rake| load rake } diff --git a/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/addressable.gemspec b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/addressable.gemspec new file mode 100644 index 0000000..8b6c061 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/addressable.gemspec @@ -0,0 +1,28 @@ +# -*- encoding: utf-8 -*- +# stub: addressable 2.8.8 ruby lib + +Gem::Specification.new do |s| + s.name = "addressable".freeze + s.version = "2.8.8".freeze + + s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= + s.metadata = { "changelog_uri" => "https://github.com/sporkmonger/addressable/blob/main/CHANGELOG.md#v2.8.8" } if s.respond_to? :metadata= + s.require_paths = ["lib".freeze] + s.authors = ["Bob Aman".freeze] + s.date = "2025-11-24" + s.description = "Addressable is an alternative implementation to the URI implementation that is\npart of Ruby's standard library. It is flexible, offers heuristic parsing, and\nadditionally provides extensive support for IRIs and URI templates.\n".freeze + s.email = "bob@sporkmonger.com".freeze + s.extra_rdoc_files = ["README.md".freeze] + s.files = ["CHANGELOG.md".freeze, "Gemfile".freeze, "LICENSE.txt".freeze, "README.md".freeze, "Rakefile".freeze, "addressable.gemspec".freeze, "data/unicode.data".freeze, "lib/addressable.rb".freeze, "lib/addressable/idna.rb".freeze, "lib/addressable/idna/native.rb".freeze, "lib/addressable/idna/pure.rb".freeze, "lib/addressable/template.rb".freeze, "lib/addressable/uri.rb".freeze, "lib/addressable/version.rb".freeze, "spec/addressable/idna_spec.rb".freeze, "spec/addressable/net_http_compat_spec.rb".freeze, "spec/addressable/security_spec.rb".freeze, "spec/addressable/template_spec.rb".freeze, "spec/addressable/uri_spec.rb".freeze, "spec/spec_helper.rb".freeze, "tasks/clobber.rake".freeze, "tasks/gem.rake".freeze, "tasks/git.rake".freeze, "tasks/metrics.rake".freeze, "tasks/profile.rake".freeze, "tasks/rspec.rake".freeze, "tasks/yard.rake".freeze] + s.homepage = "https://github.com/sporkmonger/addressable".freeze + s.licenses = ["Apache-2.0".freeze] + s.rdoc_options = ["--main".freeze, "README.md".freeze] + s.required_ruby_version = Gem::Requirement.new(">= 2.2".freeze) + s.rubygems_version = "3.7.2".freeze + s.summary = "URI Implementation".freeze + + s.specification_version = 4 + + s.add_runtime_dependency(%q.freeze, [">= 2.0.2".freeze, "< 8.0".freeze]) + s.add_development_dependency(%q.freeze, [">= 1.0".freeze, "< 3.0".freeze]) +end diff --git a/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/lib/addressable.rb b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/lib/addressable.rb new file mode 100644 index 0000000..b4e98b6 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/lib/addressable.rb @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +require 'addressable/uri' +require 'addressable/template' diff --git a/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/lib/addressable/idna.rb b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/lib/addressable/idna.rb new file mode 100644 index 0000000..2dbd393 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/lib/addressable/idna.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +#-- +# Copyright (C) Bob Aman +# +# 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. +#++ + + +begin + require "addressable/idna/native" +rescue LoadError + # libidn or the idn gem was not available, fall back on a pure-Ruby + # implementation... + require "addressable/idna/pure" +end diff --git a/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/lib/addressable/idna/native.rb b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/lib/addressable/idna/native.rb new file mode 100644 index 0000000..a718364 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/lib/addressable/idna/native.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +#-- +# Copyright (C) Bob Aman +# +# 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. +#++ + + +require "idn" + +module Addressable + module IDNA + def self.punycode_encode(value) + IDN::Punycode.encode(value.to_s) + end + + def self.punycode_decode(value) + IDN::Punycode.decode(value.to_s) + end + + class << self + # @deprecated Use {String#unicode_normalize(:nfkc)} instead + def unicode_normalize_kc(value) + value.to_s.unicode_normalize(:nfkc) + end + + extend Gem::Deprecate + deprecate :unicode_normalize_kc, "String#unicode_normalize(:nfkc)", 2023, 4 + end + + def self.to_ascii(value) + value.to_s.split('.', -1).map do |segment| + if segment.size > 0 && segment.size < 64 + IDN::Idna.toASCII(segment, IDN::Idna::ALLOW_UNASSIGNED) + elsif segment.size >= 64 + segment + else + '' + end + end.join('.') + end + + def self.to_unicode(value) + value.to_s.split('.', -1).map do |segment| + if segment.size > 0 && segment.size < 64 + IDN::Idna.toUnicode(segment, IDN::Idna::ALLOW_UNASSIGNED) + elsif segment.size >= 64 + segment + else + '' + end + end.join('.') + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/lib/addressable/idna/pure.rb b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/lib/addressable/idna/pure.rb new file mode 100644 index 0000000..9dc46f4 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/lib/addressable/idna/pure.rb @@ -0,0 +1,4720 @@ +# frozen_string_literal: true + +#-- +# Copyright (C) Bob Aman +# +# 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. +#++ + + +module Addressable + module IDNA + # This module is loosely based on idn_actionmailer by Mick Staugaard, + # the unicode library by Yoshida Masato, and the punycode implementation + # by Kazuhiro Nishiyama. Most of the code was copied verbatim, but + # some reformatting was done, and some translation from C was done. + # + # Without their code to work from as a base, we'd all still be relying + # on the presence of libidn. Which nobody ever seems to have installed. + # + # Original sources: + # http://github.com/staugaard/idn_actionmailer + # http://www.yoshidam.net/Ruby.html#unicode + # http://rubyforge.org/frs/?group_id=2550 + + ACE_PREFIX = "xn--" + + UTF8_REGEX = /\A(?: + [\x09\x0A\x0D\x20-\x7E] # ASCII + | [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte + | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs + | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte + | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates + | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3 + | [\xF1-\xF3][\x80-\xBF]{3} # planes 4nil5 + | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16 + )*\z/mnx + + UTF8_REGEX_MULTIBYTE = /(?: + [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte + | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs + | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte + | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates + | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3 + | [\xF1-\xF3][\x80-\xBF]{3} # planes 4nil5 + | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16 + )/mnx + + # :startdoc: + + # Converts from a Unicode internationalized domain name to an ASCII + # domain name as described in RFC 3490. + def self.to_ascii(input) + input = input.to_s unless input.is_a?(String) + input = input.dup.force_encoding(Encoding::UTF_8).unicode_normalize(:nfkc) + if input.respond_to?(:force_encoding) + input.force_encoding(Encoding::ASCII_8BIT) + end + if input =~ UTF8_REGEX && input =~ UTF8_REGEX_MULTIBYTE + parts = unicode_downcase(input).split('.') + parts.map! do |part| + if part.respond_to?(:force_encoding) + part.force_encoding(Encoding::ASCII_8BIT) + end + if part =~ UTF8_REGEX && part =~ UTF8_REGEX_MULTIBYTE + ACE_PREFIX + punycode_encode(part) + else + part + end + end + parts.join('.') + else + input + end + end + + # Converts from an ASCII domain name to a Unicode internationalized + # domain name as described in RFC 3490. + def self.to_unicode(input) + input = input.to_s unless input.is_a?(String) + parts = input.split('.') + parts.map! do |part| + if part =~ /^#{ACE_PREFIX}(.+)/ + begin + punycode_decode(part[/^#{ACE_PREFIX}(.+)/, 1]) + rescue Addressable::IDNA::PunycodeBadInput + # toUnicode is explicitly defined as never-fails by the spec + part + end + else + part + end + end + output = parts.join('.') + if output.respond_to?(:force_encoding) + output.force_encoding(Encoding::UTF_8) + end + output + end + + class << self + # @deprecated Use {String#unicode_normalize(:nfkc)} instead + def unicode_normalize_kc(value) + value.to_s.unicode_normalize(:nfkc) + end + + extend Gem::Deprecate + deprecate :unicode_normalize_kc, "String#unicode_normalize(:nfkc)", 2023, 4 + end + + ## + # Unicode aware downcase method. + # + # @api private + # @param [String] input + # The input string. + # @return [String] The downcased result. + def self.unicode_downcase(input) + input = input.to_s unless input.is_a?(String) + unpacked = input.unpack("U*") + unpacked.map! { |codepoint| lookup_unicode_lowercase(codepoint) } + return unpacked.pack("U*") + end + private_class_method :unicode_downcase + + def self.lookup_unicode_lowercase(codepoint) + codepoint_data = UNICODE_DATA[codepoint] + (codepoint_data ? + (codepoint_data[UNICODE_DATA_LOWERCASE] || codepoint) : + codepoint) + end + private_class_method :lookup_unicode_lowercase + + UNICODE_DATA_COMBINING_CLASS = 0 + UNICODE_DATA_EXCLUSION = 1 + UNICODE_DATA_CANONICAL = 2 + UNICODE_DATA_COMPATIBILITY = 3 + UNICODE_DATA_UPPERCASE = 4 + UNICODE_DATA_LOWERCASE = 5 + UNICODE_DATA_TITLECASE = 6 + + UNICODE_DATA = { + 65 => [0, 0, nil, nil, nil, 97, nil], + 66 => [0, 0, nil, nil, nil, 98, nil], + 67 => [0, 0, nil, nil, nil, 99, nil], + 68 => [0, 0, nil, nil, nil, 100, nil], + 69 => [0, 0, nil, nil, nil, 101, nil], + 70 => [0, 0, nil, nil, nil, 102, nil], + 71 => [0, 0, nil, nil, nil, 103, nil], + 72 => [0, 0, nil, nil, nil, 104, nil], + 73 => [0, 0, nil, nil, nil, 105, nil], + 74 => [0, 0, nil, nil, nil, 106, nil], + 75 => [0, 0, nil, nil, nil, 107, nil], + 76 => [0, 0, nil, nil, nil, 108, nil], + 77 => [0, 0, nil, nil, nil, 109, nil], + 78 => [0, 0, nil, nil, nil, 110, nil], + 79 => [0, 0, nil, nil, nil, 111, nil], + 80 => [0, 0, nil, nil, nil, 112, nil], + 81 => [0, 0, nil, nil, nil, 113, nil], + 82 => [0, 0, nil, nil, nil, 114, nil], + 83 => [0, 0, nil, nil, nil, 115, nil], + 84 => [0, 0, nil, nil, nil, 116, nil], + 85 => [0, 0, nil, nil, nil, 117, nil], + 86 => [0, 0, nil, nil, nil, 118, nil], + 87 => [0, 0, nil, nil, nil, 119, nil], + 88 => [0, 0, nil, nil, nil, 120, nil], + 89 => [0, 0, nil, nil, nil, 121, nil], + 90 => [0, 0, nil, nil, nil, 122, nil], + 97 => [0, 0, nil, nil, 65, nil, 65], + 98 => [0, 0, nil, nil, 66, nil, 66], + 99 => [0, 0, nil, nil, 67, nil, 67], + 100 => [0, 0, nil, nil, 68, nil, 68], + 101 => [0, 0, nil, nil, 69, nil, 69], + 102 => [0, 0, nil, nil, 70, nil, 70], + 103 => [0, 0, nil, nil, 71, nil, 71], + 104 => [0, 0, nil, nil, 72, nil, 72], + 105 => [0, 0, nil, nil, 73, nil, 73], + 106 => [0, 0, nil, nil, 74, nil, 74], + 107 => [0, 0, nil, nil, 75, nil, 75], + 108 => [0, 0, nil, nil, 76, nil, 76], + 109 => [0, 0, nil, nil, 77, nil, 77], + 110 => [0, 0, nil, nil, 78, nil, 78], + 111 => [0, 0, nil, nil, 79, nil, 79], + 112 => [0, 0, nil, nil, 80, nil, 80], + 113 => [0, 0, nil, nil, 81, nil, 81], + 114 => [0, 0, nil, nil, 82, nil, 82], + 115 => [0, 0, nil, nil, 83, nil, 83], + 116 => [0, 0, nil, nil, 84, nil, 84], + 117 => [0, 0, nil, nil, 85, nil, 85], + 118 => [0, 0, nil, nil, 86, nil, 86], + 119 => [0, 0, nil, nil, 87, nil, 87], + 120 => [0, 0, nil, nil, 88, nil, 88], + 121 => [0, 0, nil, nil, 89, nil, 89], + 122 => [0, 0, nil, nil, 90, nil, 90], + 160 => [0, 0, nil, " ", nil, nil, nil], + 168 => [0, 0, nil, " ̈", nil, nil, nil], + 170 => [0, 0, nil, "a", nil, nil, nil], + 175 => [0, 0, nil, " ̄", nil, nil, nil], + 178 => [0, 0, nil, "2", nil, nil, nil], + 179 => [0, 0, nil, "3", nil, nil, nil], + 180 => [0, 0, nil, " ́", nil, nil, nil], + 181 => [0, 0, nil, "μ", 924, nil, 924], + 184 => [0, 0, nil, " ̧", nil, nil, nil], + 185 => [0, 0, nil, "1", nil, nil, nil], + 186 => [0, 0, nil, "o", nil, nil, nil], + 188 => [0, 0, nil, "1⁄4", nil, nil, nil], + 189 => [0, 0, nil, "1⁄2", nil, nil, nil], + 190 => [0, 0, nil, "3⁄4", nil, nil, nil], + 192 => [0, 0, "À", "À", nil, 224, nil], + 193 => [0, 0, "Á", "Á", nil, 225, nil], + 194 => [0, 0, "Â", "Â", nil, 226, nil], + 195 => [0, 0, "Ã", "Ã", nil, 227, nil], + 196 => [0, 0, "Ä", "Ä", nil, 228, nil], + 197 => [0, 0, "Å", "Å", nil, 229, nil], + 198 => [0, 0, nil, nil, nil, 230, nil], + 199 => [0, 0, "Ç", "Ç", nil, 231, nil], + 200 => [0, 0, "È", "È", nil, 232, nil], + 201 => [0, 0, "É", "É", nil, 233, nil], + 202 => [0, 0, "Ê", "Ê", nil, 234, nil], + 203 => [0, 0, "Ë", "Ë", nil, 235, nil], + 204 => [0, 0, "Ì", "Ì", nil, 236, nil], + 205 => [0, 0, "Í", "Í", nil, 237, nil], + 206 => [0, 0, "Î", "Î", nil, 238, nil], + 207 => [0, 0, "Ï", "Ï", nil, 239, nil], + 208 => [0, 0, nil, nil, nil, 240, nil], + 209 => [0, 0, "Ñ", "Ñ", nil, 241, nil], + 210 => [0, 0, "Ò", "Ò", nil, 242, nil], + 211 => [0, 0, "Ó", "Ó", nil, 243, nil], + 212 => [0, 0, "Ô", "Ô", nil, 244, nil], + 213 => [0, 0, "Õ", "Õ", nil, 245, nil], + 214 => [0, 0, "Ö", "Ö", nil, 246, nil], + 216 => [0, 0, nil, nil, nil, 248, nil], + 217 => [0, 0, "Ù", "Ù", nil, 249, nil], + 218 => [0, 0, "Ú", "Ú", nil, 250, nil], + 219 => [0, 0, "Û", "Û", nil, 251, nil], + 220 => [0, 0, "Ü", "Ü", nil, 252, nil], + 221 => [0, 0, "Ý", "Ý", nil, 253, nil], + 222 => [0, 0, nil, nil, nil, 254, nil], + 224 => [0, 0, "à", "à", 192, nil, 192], + 225 => [0, 0, "á", "á", 193, nil, 193], + 226 => [0, 0, "â", "â", 194, nil, 194], + 227 => [0, 0, "ã", "ã", 195, nil, 195], + 228 => [0, 0, "ä", "ä", 196, nil, 196], + 229 => [0, 0, "å", "å", 197, nil, 197], + 230 => [0, 0, nil, nil, 198, nil, 198], + 231 => [0, 0, "ç", "ç", 199, nil, 199], + 232 => [0, 0, "è", "è", 200, nil, 200], + 233 => [0, 0, "é", "é", 201, nil, 201], + 234 => [0, 0, "ê", "ê", 202, nil, 202], + 235 => [0, 0, "ë", "ë", 203, nil, 203], + 236 => [0, 0, "ì", "ì", 204, nil, 204], + 237 => [0, 0, "í", "í", 205, nil, 205], + 238 => [0, 0, "î", "î", 206, nil, 206], + 239 => [0, 0, "ï", "ï", 207, nil, 207], + 240 => [0, 0, nil, nil, 208, nil, 208], + 241 => [0, 0, "ñ", "ñ", 209, nil, 209], + 242 => [0, 0, "ò", "ò", 210, nil, 210], + 243 => [0, 0, "ó", "ó", 211, nil, 211], + 244 => [0, 0, "ô", "ô", 212, nil, 212], + 245 => [0, 0, "õ", "õ", 213, nil, 213], + 246 => [0, 0, "ö", "ö", 214, nil, 214], + 248 => [0, 0, nil, nil, 216, nil, 216], + 249 => [0, 0, "ù", "ù", 217, nil, 217], + 250 => [0, 0, "ú", "ú", 218, nil, 218], + 251 => [0, 0, "û", "û", 219, nil, 219], + 252 => [0, 0, "ü", "ü", 220, nil, 220], + 253 => [0, 0, "ý", "ý", 221, nil, 221], + 254 => [0, 0, nil, nil, 222, nil, 222], + 255 => [0, 0, "ÿ", "ÿ", 376, nil, 376], + 256 => [0, 0, "Ā", "Ā", nil, 257, nil], + 257 => [0, 0, "ā", "ā", 256, nil, 256], + 258 => [0, 0, "Ă", "Ă", nil, 259, nil], + 259 => [0, 0, "ă", "ă", 258, nil, 258], + 260 => [0, 0, "Ą", "Ą", nil, 261, nil], + 261 => [0, 0, "ą", "ą", 260, nil, 260], + 262 => [0, 0, "Ć", "Ć", nil, 263, nil], + 263 => [0, 0, "ć", "ć", 262, nil, 262], + 264 => [0, 0, "Ĉ", "Ĉ", nil, 265, nil], + 265 => [0, 0, "ĉ", "ĉ", 264, nil, 264], + 266 => [0, 0, "Ċ", "Ċ", nil, 267, nil], + 267 => [0, 0, "ċ", "ċ", 266, nil, 266], + 268 => [0, 0, "Č", "Č", nil, 269, nil], + 269 => [0, 0, "č", "č", 268, nil, 268], + 270 => [0, 0, "Ď", "Ď", nil, 271, nil], + 271 => [0, 0, "ď", "ď", 270, nil, 270], + 272 => [0, 0, nil, nil, nil, 273, nil], + 273 => [0, 0, nil, nil, 272, nil, 272], + 274 => [0, 0, "Ē", "Ē", nil, 275, nil], + 275 => [0, 0, "ē", "ē", 274, nil, 274], + 276 => [0, 0, "Ĕ", "Ĕ", nil, 277, nil], + 277 => [0, 0, "ĕ", "ĕ", 276, nil, 276], + 278 => [0, 0, "Ė", "Ė", nil, 279, nil], + 279 => [0, 0, "ė", "ė", 278, nil, 278], + 280 => [0, 0, "Ę", "Ę", nil, 281, nil], + 281 => [0, 0, "ę", "ę", 280, nil, 280], + 282 => [0, 0, "Ě", "Ě", nil, 283, nil], + 283 => [0, 0, "ě", "ě", 282, nil, 282], + 284 => [0, 0, "Ĝ", "Ĝ", nil, 285, nil], + 285 => [0, 0, "ĝ", "ĝ", 284, nil, 284], + 286 => [0, 0, "Ğ", "Ğ", nil, 287, nil], + 287 => [0, 0, "ğ", "ğ", 286, nil, 286], + 288 => [0, 0, "Ġ", "Ġ", nil, 289, nil], + 289 => [0, 0, "ġ", "ġ", 288, nil, 288], + 290 => [0, 0, "Ģ", "Ģ", nil, 291, nil], + 291 => [0, 0, "ģ", "ģ", 290, nil, 290], + 292 => [0, 0, "Ĥ", "Ĥ", nil, 293, nil], + 293 => [0, 0, "ĥ", "ĥ", 292, nil, 292], + 294 => [0, 0, nil, nil, nil, 295, nil], + 295 => [0, 0, nil, nil, 294, nil, 294], + 296 => [0, 0, "Ĩ", "Ĩ", nil, 297, nil], + 297 => [0, 0, "ĩ", "ĩ", 296, nil, 296], + 298 => [0, 0, "Ī", "Ī", nil, 299, nil], + 299 => [0, 0, "ī", "ī", 298, nil, 298], + 300 => [0, 0, "Ĭ", "Ĭ", nil, 301, nil], + 301 => [0, 0, "ĭ", "ĭ", 300, nil, 300], + 302 => [0, 0, "Į", "Į", nil, 303, nil], + 303 => [0, 0, "į", "į", 302, nil, 302], + 304 => [0, 0, "İ", "İ", nil, 105, nil], + 305 => [0, 0, nil, nil, 73, nil, 73], + 306 => [0, 0, nil, "IJ", nil, 307, nil], + 307 => [0, 0, nil, "ij", 306, nil, 306], + 308 => [0, 0, "Ĵ", "Ĵ", nil, 309, nil], + 309 => [0, 0, "ĵ", "ĵ", 308, nil, 308], + 310 => [0, 0, "Ķ", "Ķ", nil, 311, nil], + 311 => [0, 0, "ķ", "ķ", 310, nil, 310], + 313 => [0, 0, "Ĺ", "Ĺ", nil, 314, nil], + 314 => [0, 0, "ĺ", "ĺ", 313, nil, 313], + 315 => [0, 0, "Ļ", "Ļ", nil, 316, nil], + 316 => [0, 0, "ļ", "ļ", 315, nil, 315], + 317 => [0, 0, "Ľ", "Ľ", nil, 318, nil], + 318 => [0, 0, "ľ", "ľ", 317, nil, 317], + 319 => [0, 0, nil, "L·", nil, 320, nil], + 320 => [0, 0, nil, "l·", 319, nil, 319], + 321 => [0, 0, nil, nil, nil, 322, nil], + 322 => [0, 0, nil, nil, 321, nil, 321], + 323 => [0, 0, "Ń", "Ń", nil, 324, nil], + 324 => [0, 0, "ń", "ń", 323, nil, 323], + 325 => [0, 0, "Ņ", "Ņ", nil, 326, nil], + 326 => [0, 0, "ņ", "ņ", 325, nil, 325], + 327 => [0, 0, "Ň", "Ň", nil, 328, nil], + 328 => [0, 0, "ň", "ň", 327, nil, 327], + 329 => [0, 0, nil, "ʼn", nil, nil, nil], + 330 => [0, 0, nil, nil, nil, 331, nil], + 331 => [0, 0, nil, nil, 330, nil, 330], + 332 => [0, 0, "Ō", "Ō", nil, 333, nil], + 333 => [0, 0, "ō", "ō", 332, nil, 332], + 334 => [0, 0, "Ŏ", "Ŏ", nil, 335, nil], + 335 => [0, 0, "ŏ", "ŏ", 334, nil, 334], + 336 => [0, 0, "Ő", "Ő", nil, 337, nil], + 337 => [0, 0, "ő", "ő", 336, nil, 336], + 338 => [0, 0, nil, nil, nil, 339, nil], + 339 => [0, 0, nil, nil, 338, nil, 338], + 340 => [0, 0, "Ŕ", "Ŕ", nil, 341, nil], + 341 => [0, 0, "ŕ", "ŕ", 340, nil, 340], + 342 => [0, 0, "Ŗ", "Ŗ", nil, 343, nil], + 343 => [0, 0, "ŗ", "ŗ", 342, nil, 342], + 344 => [0, 0, "Ř", "Ř", nil, 345, nil], + 345 => [0, 0, "ř", "ř", 344, nil, 344], + 346 => [0, 0, "Ś", "Ś", nil, 347, nil], + 347 => [0, 0, "ś", "ś", 346, nil, 346], + 348 => [0, 0, "Ŝ", "Ŝ", nil, 349, nil], + 349 => [0, 0, "ŝ", "ŝ", 348, nil, 348], + 350 => [0, 0, "Ş", "Ş", nil, 351, nil], + 351 => [0, 0, "ş", "ş", 350, nil, 350], + 352 => [0, 0, "Š", "Š", nil, 353, nil], + 353 => [0, 0, "š", "š", 352, nil, 352], + 354 => [0, 0, "Ţ", "Ţ", nil, 355, nil], + 355 => [0, 0, "ţ", "ţ", 354, nil, 354], + 356 => [0, 0, "Ť", "Ť", nil, 357, nil], + 357 => [0, 0, "ť", "ť", 356, nil, 356], + 358 => [0, 0, nil, nil, nil, 359, nil], + 359 => [0, 0, nil, nil, 358, nil, 358], + 360 => [0, 0, "Ũ", "Ũ", nil, 361, nil], + 361 => [0, 0, "ũ", "ũ", 360, nil, 360], + 362 => [0, 0, "Ū", "Ū", nil, 363, nil], + 363 => [0, 0, "ū", "ū", 362, nil, 362], + 364 => [0, 0, "Ŭ", "Ŭ", nil, 365, nil], + 365 => [0, 0, "ŭ", "ŭ", 364, nil, 364], + 366 => [0, 0, "Ů", "Ů", nil, 367, nil], + 367 => [0, 0, "ů", "ů", 366, nil, 366], + 368 => [0, 0, "Ű", "Ű", nil, 369, nil], + 369 => [0, 0, "ű", "ű", 368, nil, 368], + 370 => [0, 0, "Ų", "Ų", nil, 371, nil], + 371 => [0, 0, "ų", "ų", 370, nil, 370], + 372 => [0, 0, "Ŵ", "Ŵ", nil, 373, nil], + 373 => [0, 0, "ŵ", "ŵ", 372, nil, 372], + 374 => [0, 0, "Ŷ", "Ŷ", nil, 375, nil], + 375 => [0, 0, "ŷ", "ŷ", 374, nil, 374], + 376 => [0, 0, "Ÿ", "Ÿ", nil, 255, nil], + 377 => [0, 0, "Ź", "Ź", nil, 378, nil], + 378 => [0, 0, "ź", "ź", 377, nil, 377], + 379 => [0, 0, "Ż", "Ż", nil, 380, nil], + 380 => [0, 0, "ż", "ż", 379, nil, 379], + 381 => [0, 0, "Ž", "Ž", nil, 382, nil], + 382 => [0, 0, "ž", "ž", 381, nil, 381], + 383 => [0, 0, nil, "s", 83, nil, 83], + 385 => [0, 0, nil, nil, nil, 595, nil], + 386 => [0, 0, nil, nil, nil, 387, nil], + 387 => [0, 0, nil, nil, 386, nil, 386], + 388 => [0, 0, nil, nil, nil, 389, nil], + 389 => [0, 0, nil, nil, 388, nil, 388], + 390 => [0, 0, nil, nil, nil, 596, nil], + 391 => [0, 0, nil, nil, nil, 392, nil], + 392 => [0, 0, nil, nil, 391, nil, 391], + 393 => [0, 0, nil, nil, nil, 598, nil], + 394 => [0, 0, nil, nil, nil, 599, nil], + 395 => [0, 0, nil, nil, nil, 396, nil], + 396 => [0, 0, nil, nil, 395, nil, 395], + 398 => [0, 0, nil, nil, nil, 477, nil], + 399 => [0, 0, nil, nil, nil, 601, nil], + 400 => [0, 0, nil, nil, nil, 603, nil], + 401 => [0, 0, nil, nil, nil, 402, nil], + 402 => [0, 0, nil, nil, 401, nil, 401], + 403 => [0, 0, nil, nil, nil, 608, nil], + 404 => [0, 0, nil, nil, nil, 611, nil], + 405 => [0, 0, nil, nil, 502, nil, 502], + 406 => [0, 0, nil, nil, nil, 617, nil], + 407 => [0, 0, nil, nil, nil, 616, nil], + 408 => [0, 0, nil, nil, nil, 409, nil], + 409 => [0, 0, nil, nil, 408, nil, 408], + 412 => [0, 0, nil, nil, nil, 623, nil], + 413 => [0, 0, nil, nil, nil, 626, nil], + 415 => [0, 0, nil, nil, nil, 629, nil], + 416 => [0, 0, "Ơ", "Ơ", nil, 417, nil], + 417 => [0, 0, "ơ", "ơ", 416, nil, 416], + 418 => [0, 0, nil, nil, nil, 419, nil], + 419 => [0, 0, nil, nil, 418, nil, 418], + 420 => [0, 0, nil, nil, nil, 421, nil], + 421 => [0, 0, nil, nil, 420, nil, 420], + 422 => [0, 0, nil, nil, nil, 640, nil], + 423 => [0, 0, nil, nil, nil, 424, nil], + 424 => [0, 0, nil, nil, 423, nil, 423], + 425 => [0, 0, nil, nil, nil, 643, nil], + 428 => [0, 0, nil, nil, nil, 429, nil], + 429 => [0, 0, nil, nil, 428, nil, 428], + 430 => [0, 0, nil, nil, nil, 648, nil], + 431 => [0, 0, "Ư", "Ư", nil, 432, nil], + 432 => [0, 0, "ư", "ư", 431, nil, 431], + 433 => [0, 0, nil, nil, nil, 650, nil], + 434 => [0, 0, nil, nil, nil, 651, nil], + 435 => [0, 0, nil, nil, nil, 436, nil], + 436 => [0, 0, nil, nil, 435, nil, 435], + 437 => [0, 0, nil, nil, nil, 438, nil], + 438 => [0, 0, nil, nil, 437, nil, 437], + 439 => [0, 0, nil, nil, nil, 658, nil], + 440 => [0, 0, nil, nil, nil, 441, nil], + 441 => [0, 0, nil, nil, 440, nil, 440], + 444 => [0, 0, nil, nil, nil, 445, nil], + 445 => [0, 0, nil, nil, 444, nil, 444], + 447 => [0, 0, nil, nil, 503, nil, 503], + 452 => [0, 0, nil, "DŽ", nil, 454, 453], + 453 => [0, 0, nil, "Dž", 452, 454, nil], + 454 => [0, 0, nil, "dž", 452, nil, 453], + 455 => [0, 0, nil, "LJ", nil, 457, 456], + 456 => [0, 0, nil, "Lj", 455, 457, nil], + 457 => [0, 0, nil, "lj", 455, nil, 456], + 458 => [0, 0, nil, "NJ", nil, 460, 459], + 459 => [0, 0, nil, "Nj", 458, 460, nil], + 460 => [0, 0, nil, "nj", 458, nil, 459], + 461 => [0, 0, "Ǎ", "Ǎ", nil, 462, nil], + 462 => [0, 0, "ǎ", "ǎ", 461, nil, 461], + 463 => [0, 0, "Ǐ", "Ǐ", nil, 464, nil], + 464 => [0, 0, "ǐ", "ǐ", 463, nil, 463], + 465 => [0, 0, "Ǒ", "Ǒ", nil, 466, nil], + 466 => [0, 0, "ǒ", "ǒ", 465, nil, 465], + 467 => [0, 0, "Ǔ", "Ǔ", nil, 468, nil], + 468 => [0, 0, "ǔ", "ǔ", 467, nil, 467], + 469 => [0, 0, "Ǖ", "Ǖ", nil, 470, nil], + 470 => [0, 0, "ǖ", "ǖ", 469, nil, 469], + 471 => [0, 0, "Ǘ", "Ǘ", nil, 472, nil], + 472 => [0, 0, "ǘ", "ǘ", 471, nil, 471], + 473 => [0, 0, "Ǚ", "Ǚ", nil, 474, nil], + 474 => [0, 0, "ǚ", "ǚ", 473, nil, 473], + 475 => [0, 0, "Ǜ", "Ǜ", nil, 476, nil], + 476 => [0, 0, "ǜ", "ǜ", 475, nil, 475], + 477 => [0, 0, nil, nil, 398, nil, 398], + 478 => [0, 0, "Ǟ", "Ǟ", nil, 479, nil], + 479 => [0, 0, "ǟ", "ǟ", 478, nil, 478], + 480 => [0, 0, "Ǡ", "Ǡ", nil, 481, nil], + 481 => [0, 0, "ǡ", "ǡ", 480, nil, 480], + 482 => [0, 0, "Ǣ", "Ǣ", nil, 483, nil], + 483 => [0, 0, "ǣ", "ǣ", 482, nil, 482], + 484 => [0, 0, nil, nil, nil, 485, nil], + 485 => [0, 0, nil, nil, 484, nil, 484], + 486 => [0, 0, "Ǧ", "Ǧ", nil, 487, nil], + 487 => [0, 0, "ǧ", "ǧ", 486, nil, 486], + 488 => [0, 0, "Ǩ", "Ǩ", nil, 489, nil], + 489 => [0, 0, "ǩ", "ǩ", 488, nil, 488], + 490 => [0, 0, "Ǫ", "Ǫ", nil, 491, nil], + 491 => [0, 0, "ǫ", "ǫ", 490, nil, 490], + 492 => [0, 0, "Ǭ", "Ǭ", nil, 493, nil], + 493 => [0, 0, "ǭ", "ǭ", 492, nil, 492], + 494 => [0, 0, "Ǯ", "Ǯ", nil, 495, nil], + 495 => [0, 0, "ǯ", "ǯ", 494, nil, 494], + 496 => [0, 0, "ǰ", "ǰ", nil, nil, nil], + 497 => [0, 0, nil, "DZ", nil, 499, 498], + 498 => [0, 0, nil, "Dz", 497, 499, nil], + 499 => [0, 0, nil, "dz", 497, nil, 498], + 500 => [0, 0, "Ǵ", "Ǵ", nil, 501, nil], + 501 => [0, 0, "ǵ", "ǵ", 500, nil, 500], + 502 => [0, 0, nil, nil, nil, 405, nil], + 503 => [0, 0, nil, nil, nil, 447, nil], + 504 => [0, 0, "Ǹ", "Ǹ", nil, 505, nil], + 505 => [0, 0, "ǹ", "ǹ", 504, nil, 504], + 506 => [0, 0, "Ǻ", "Ǻ", nil, 507, nil], + 507 => [0, 0, "ǻ", "ǻ", 506, nil, 506], + 508 => [0, 0, "Ǽ", "Ǽ", nil, 509, nil], + 509 => [0, 0, "ǽ", "ǽ", 508, nil, 508], + 510 => [0, 0, "Ǿ", "Ǿ", nil, 511, nil], + 511 => [0, 0, "ǿ", "ǿ", 510, nil, 510], + 512 => [0, 0, "Ȁ", "Ȁ", nil, 513, nil], + 513 => [0, 0, "ȁ", "ȁ", 512, nil, 512], + 514 => [0, 0, "Ȃ", "Ȃ", nil, 515, nil], + 515 => [0, 0, "ȃ", "ȃ", 514, nil, 514], + 516 => [0, 0, "Ȅ", "Ȅ", nil, 517, nil], + 517 => [0, 0, "ȅ", "ȅ", 516, nil, 516], + 518 => [0, 0, "Ȇ", "Ȇ", nil, 519, nil], + 519 => [0, 0, "ȇ", "ȇ", 518, nil, 518], + 520 => [0, 0, "Ȉ", "Ȉ", nil, 521, nil], + 521 => [0, 0, "ȉ", "ȉ", 520, nil, 520], + 522 => [0, 0, "Ȋ", "Ȋ", nil, 523, nil], + 523 => [0, 0, "ȋ", "ȋ", 522, nil, 522], + 524 => [0, 0, "Ȍ", "Ȍ", nil, 525, nil], + 525 => [0, 0, "ȍ", "ȍ", 524, nil, 524], + 526 => [0, 0, "Ȏ", "Ȏ", nil, 527, nil], + 527 => [0, 0, "ȏ", "ȏ", 526, nil, 526], + 528 => [0, 0, "Ȑ", "Ȑ", nil, 529, nil], + 529 => [0, 0, "ȑ", "ȑ", 528, nil, 528], + 530 => [0, 0, "Ȓ", "Ȓ", nil, 531, nil], + 531 => [0, 0, "ȓ", "ȓ", 530, nil, 530], + 532 => [0, 0, "Ȕ", "Ȕ", nil, 533, nil], + 533 => [0, 0, "ȕ", "ȕ", 532, nil, 532], + 534 => [0, 0, "Ȗ", "Ȗ", nil, 535, nil], + 535 => [0, 0, "ȗ", "ȗ", 534, nil, 534], + 536 => [0, 0, "Ș", "Ș", nil, 537, nil], + 537 => [0, 0, "ș", "ș", 536, nil, 536], + 538 => [0, 0, "Ț", "Ț", nil, 539, nil], + 539 => [0, 0, "ț", "ț", 538, nil, 538], + 540 => [0, 0, nil, nil, nil, 541, nil], + 541 => [0, 0, nil, nil, 540, nil, 540], + 542 => [0, 0, "Ȟ", "Ȟ", nil, 543, nil], + 543 => [0, 0, "ȟ", "ȟ", 542, nil, 542], + 546 => [0, 0, nil, nil, nil, 547, nil], + 547 => [0, 0, nil, nil, 546, nil, 546], + 548 => [0, 0, nil, nil, nil, 549, nil], + 549 => [0, 0, nil, nil, 548, nil, 548], + 550 => [0, 0, "Ȧ", "Ȧ", nil, 551, nil], + 551 => [0, 0, "ȧ", "ȧ", 550, nil, 550], + 552 => [0, 0, "Ȩ", "Ȩ", nil, 553, nil], + 553 => [0, 0, "ȩ", "ȩ", 552, nil, 552], + 554 => [0, 0, "Ȫ", "Ȫ", nil, 555, nil], + 555 => [0, 0, "ȫ", "ȫ", 554, nil, 554], + 556 => [0, 0, "Ȭ", "Ȭ", nil, 557, nil], + 557 => [0, 0, "ȭ", "ȭ", 556, nil, 556], + 558 => [0, 0, "Ȯ", "Ȯ", nil, 559, nil], + 559 => [0, 0, "ȯ", "ȯ", 558, nil, 558], + 560 => [0, 0, "Ȱ", "Ȱ", nil, 561, nil], + 561 => [0, 0, "ȱ", "ȱ", 560, nil, 560], + 562 => [0, 0, "Ȳ", "Ȳ", nil, 563, nil], + 563 => [0, 0, "ȳ", "ȳ", 562, nil, 562], + 595 => [0, 0, nil, nil, 385, nil, 385], + 596 => [0, 0, nil, nil, 390, nil, 390], + 598 => [0, 0, nil, nil, 393, nil, 393], + 599 => [0, 0, nil, nil, 394, nil, 394], + 601 => [0, 0, nil, nil, 399, nil, 399], + 603 => [0, 0, nil, nil, 400, nil, 400], + 608 => [0, 0, nil, nil, 403, nil, 403], + 611 => [0, 0, nil, nil, 404, nil, 404], + 616 => [0, 0, nil, nil, 407, nil, 407], + 617 => [0, 0, nil, nil, 406, nil, 406], + 623 => [0, 0, nil, nil, 412, nil, 412], + 626 => [0, 0, nil, nil, 413, nil, 413], + 629 => [0, 0, nil, nil, 415, nil, 415], + 640 => [0, 0, nil, nil, 422, nil, 422], + 643 => [0, 0, nil, nil, 425, nil, 425], + 648 => [0, 0, nil, nil, 430, nil, 430], + 650 => [0, 0, nil, nil, 433, nil, 433], + 651 => [0, 0, nil, nil, 434, nil, 434], + 658 => [0, 0, nil, nil, 439, nil, 439], + 688 => [0, 0, nil, "h", nil, nil, nil], + 689 => [0, 0, nil, "ɦ", nil, nil, nil], + 690 => [0, 0, nil, "j", nil, nil, nil], + 691 => [0, 0, nil, "r", nil, nil, nil], + 692 => [0, 0, nil, "ɹ", nil, nil, nil], + 693 => [0, 0, nil, "ɻ", nil, nil, nil], + 694 => [0, 0, nil, "ʁ", nil, nil, nil], + 695 => [0, 0, nil, "w", nil, nil, nil], + 696 => [0, 0, nil, "y", nil, nil, nil], + 728 => [0, 0, nil, " ̆", nil, nil, nil], + 729 => [0, 0, nil, " ̇", nil, nil, nil], + 730 => [0, 0, nil, " ̊", nil, nil, nil], + 731 => [0, 0, nil, " ̨", nil, nil, nil], + 732 => [0, 0, nil, " ̃", nil, nil, nil], + 733 => [0, 0, nil, " ̋", nil, nil, nil], + 736 => [0, 0, nil, "ɣ", nil, nil, nil], + 737 => [0, 0, nil, "l", nil, nil, nil], + 738 => [0, 0, nil, "s", nil, nil, nil], + 739 => [0, 0, nil, "x", nil, nil, nil], + 740 => [0, 0, nil, "ʕ", nil, nil, nil], + 768 => [230, 0, nil, nil, nil, nil, nil], + 769 => [230, 0, nil, nil, nil, nil, nil], + 770 => [230, 0, nil, nil, nil, nil, nil], + 771 => [230, 0, nil, nil, nil, nil, nil], + 772 => [230, 0, nil, nil, nil, nil, nil], + 773 => [230, 0, nil, nil, nil, nil, nil], + 774 => [230, 0, nil, nil, nil, nil, nil], + 775 => [230, 0, nil, nil, nil, nil, nil], + 776 => [230, 0, nil, nil, nil, nil, nil], + 777 => [230, 0, nil, nil, nil, nil, nil], + 778 => [230, 0, nil, nil, nil, nil, nil], + 779 => [230, 0, nil, nil, nil, nil, nil], + 780 => [230, 0, nil, nil, nil, nil, nil], + 781 => [230, 0, nil, nil, nil, nil, nil], + 782 => [230, 0, nil, nil, nil, nil, nil], + 783 => [230, 0, nil, nil, nil, nil, nil], + 784 => [230, 0, nil, nil, nil, nil, nil], + 785 => [230, 0, nil, nil, nil, nil, nil], + 786 => [230, 0, nil, nil, nil, nil, nil], + 787 => [230, 0, nil, nil, nil, nil, nil], + 788 => [230, 0, nil, nil, nil, nil, nil], + 789 => [232, 0, nil, nil, nil, nil, nil], + 790 => [220, 0, nil, nil, nil, nil, nil], + 791 => [220, 0, nil, nil, nil, nil, nil], + 792 => [220, 0, nil, nil, nil, nil, nil], + 793 => [220, 0, nil, nil, nil, nil, nil], + 794 => [232, 0, nil, nil, nil, nil, nil], + 795 => [216, 0, nil, nil, nil, nil, nil], + 796 => [220, 0, nil, nil, nil, nil, nil], + 797 => [220, 0, nil, nil, nil, nil, nil], + 798 => [220, 0, nil, nil, nil, nil, nil], + 799 => [220, 0, nil, nil, nil, nil, nil], + 800 => [220, 0, nil, nil, nil, nil, nil], + 801 => [202, 0, nil, nil, nil, nil, nil], + 802 => [202, 0, nil, nil, nil, nil, nil], + 803 => [220, 0, nil, nil, nil, nil, nil], + 804 => [220, 0, nil, nil, nil, nil, nil], + 805 => [220, 0, nil, nil, nil, nil, nil], + 806 => [220, 0, nil, nil, nil, nil, nil], + 807 => [202, 0, nil, nil, nil, nil, nil], + 808 => [202, 0, nil, nil, nil, nil, nil], + 809 => [220, 0, nil, nil, nil, nil, nil], + 810 => [220, 0, nil, nil, nil, nil, nil], + 811 => [220, 0, nil, nil, nil, nil, nil], + 812 => [220, 0, nil, nil, nil, nil, nil], + 813 => [220, 0, nil, nil, nil, nil, nil], + 814 => [220, 0, nil, nil, nil, nil, nil], + 815 => [220, 0, nil, nil, nil, nil, nil], + 816 => [220, 0, nil, nil, nil, nil, nil], + 817 => [220, 0, nil, nil, nil, nil, nil], + 818 => [220, 0, nil, nil, nil, nil, nil], + 819 => [220, 0, nil, nil, nil, nil, nil], + 820 => [1, 0, nil, nil, nil, nil, nil], + 821 => [1, 0, nil, nil, nil, nil, nil], + 822 => [1, 0, nil, nil, nil, nil, nil], + 823 => [1, 0, nil, nil, nil, nil, nil], + 824 => [1, 0, nil, nil, nil, nil, nil], + 825 => [220, 0, nil, nil, nil, nil, nil], + 826 => [220, 0, nil, nil, nil, nil, nil], + 827 => [220, 0, nil, nil, nil, nil, nil], + 828 => [220, 0, nil, nil, nil, nil, nil], + 829 => [230, 0, nil, nil, nil, nil, nil], + 830 => [230, 0, nil, nil, nil, nil, nil], + 831 => [230, 0, nil, nil, nil, nil, nil], + 832 => [230, 2, "̀", "̀", nil, nil, nil], + 833 => [230, 2, "́", "́", nil, nil, nil], + 834 => [230, 0, nil, nil, nil, nil, nil], + 835 => [230, 2, "̓", "̓", nil, nil, nil], + 836 => [230, 3, "̈́", "̈́", nil, nil, nil], + 837 => [240, 0, nil, nil, 921, nil, 921], + 838 => [230, 0, nil, nil, nil, nil, nil], + 839 => [220, 0, nil, nil, nil, nil, nil], + 840 => [220, 0, nil, nil, nil, nil, nil], + 841 => [220, 0, nil, nil, nil, nil, nil], + 842 => [230, 0, nil, nil, nil, nil, nil], + 843 => [230, 0, nil, nil, nil, nil, nil], + 844 => [230, 0, nil, nil, nil, nil, nil], + 845 => [220, 0, nil, nil, nil, nil, nil], + 846 => [220, 0, nil, nil, nil, nil, nil], + 864 => [234, 0, nil, nil, nil, nil, nil], + 865 => [234, 0, nil, nil, nil, nil, nil], + 866 => [233, 0, nil, nil, nil, nil, nil], + 884 => [0, 2, "ʹ", "ʹ", nil, nil, nil], + 890 => [0, 0, nil, " ͅ", nil, nil, nil], + 894 => [0, 2, ";", ";", nil, nil, nil], + 900 => [0, 0, nil, " ́", nil, nil, nil], + 901 => [0, 0, "΅", "΅", nil, nil, nil], + 902 => [0, 0, "Ά", "Ά", nil, 940, nil], + 903 => [0, 2, "·", "·", nil, nil, nil], + 904 => [0, 0, "Έ", "Έ", nil, 941, nil], + 905 => [0, 0, "Ή", "Ή", nil, 942, nil], + 906 => [0, 0, "Ί", "Ί", nil, 943, nil], + 908 => [0, 0, "Ό", "Ό", nil, 972, nil], + 910 => [0, 0, "Ύ", "Ύ", nil, 973, nil], + 911 => [0, 0, "Ώ", "Ώ", nil, 974, nil], + 912 => [0, 0, "ΐ", "ΐ", nil, nil, nil], + 913 => [0, 0, nil, nil, nil, 945, nil], + 914 => [0, 0, nil, nil, nil, 946, nil], + 915 => [0, 0, nil, nil, nil, 947, nil], + 916 => [0, 0, nil, nil, nil, 948, nil], + 917 => [0, 0, nil, nil, nil, 949, nil], + 918 => [0, 0, nil, nil, nil, 950, nil], + 919 => [0, 0, nil, nil, nil, 951, nil], + 920 => [0, 0, nil, nil, nil, 952, nil], + 921 => [0, 0, nil, nil, nil, 953, nil], + 922 => [0, 0, nil, nil, nil, 954, nil], + 923 => [0, 0, nil, nil, nil, 955, nil], + 924 => [0, 0, nil, nil, nil, 956, nil], + 925 => [0, 0, nil, nil, nil, 957, nil], + 926 => [0, 0, nil, nil, nil, 958, nil], + 927 => [0, 0, nil, nil, nil, 959, nil], + 928 => [0, 0, nil, nil, nil, 960, nil], + 929 => [0, 0, nil, nil, nil, 961, nil], + 931 => [0, 0, nil, nil, nil, 963, nil], + 932 => [0, 0, nil, nil, nil, 964, nil], + 933 => [0, 0, nil, nil, nil, 965, nil], + 934 => [0, 0, nil, nil, nil, 966, nil], + 935 => [0, 0, nil, nil, nil, 967, nil], + 936 => [0, 0, nil, nil, nil, 968, nil], + 937 => [0, 0, nil, nil, nil, 969, nil], + 938 => [0, 0, "Ϊ", "Ϊ", nil, 970, nil], + 939 => [0, 0, "Ϋ", "Ϋ", nil, 971, nil], + 940 => [0, 0, "ά", "ά", 902, nil, 902], + 941 => [0, 0, "έ", "έ", 904, nil, 904], + 942 => [0, 0, "ή", "ή", 905, nil, 905], + 943 => [0, 0, "ί", "ί", 906, nil, 906], + 944 => [0, 0, "ΰ", "ΰ", nil, nil, nil], + 945 => [0, 0, nil, nil, 913, nil, 913], + 946 => [0, 0, nil, nil, 914, nil, 914], + 947 => [0, 0, nil, nil, 915, nil, 915], + 948 => [0, 0, nil, nil, 916, nil, 916], + 949 => [0, 0, nil, nil, 917, nil, 917], + 950 => [0, 0, nil, nil, 918, nil, 918], + 951 => [0, 0, nil, nil, 919, nil, 919], + 952 => [0, 0, nil, nil, 920, nil, 920], + 953 => [0, 0, nil, nil, 921, nil, 921], + 954 => [0, 0, nil, nil, 922, nil, 922], + 955 => [0, 0, nil, nil, 923, nil, 923], + 956 => [0, 0, nil, nil, 924, nil, 924], + 957 => [0, 0, nil, nil, 925, nil, 925], + 958 => [0, 0, nil, nil, 926, nil, 926], + 959 => [0, 0, nil, nil, 927, nil, 927], + 960 => [0, 0, nil, nil, 928, nil, 928], + 961 => [0, 0, nil, nil, 929, nil, 929], + 962 => [0, 0, nil, nil, 931, nil, 931], + 963 => [0, 0, nil, nil, 931, nil, 931], + 964 => [0, 0, nil, nil, 932, nil, 932], + 965 => [0, 0, nil, nil, 933, nil, 933], + 966 => [0, 0, nil, nil, 934, nil, 934], + 967 => [0, 0, nil, nil, 935, nil, 935], + 968 => [0, 0, nil, nil, 936, nil, 936], + 969 => [0, 0, nil, nil, 937, nil, 937], + 970 => [0, 0, "ϊ", "ϊ", 938, nil, 938], + 971 => [0, 0, "ϋ", "ϋ", 939, nil, 939], + 972 => [0, 0, "ό", "ό", 908, nil, 908], + 973 => [0, 0, "ύ", "ύ", 910, nil, 910], + 974 => [0, 0, "ώ", "ώ", 911, nil, 911], + 976 => [0, 0, nil, "β", 914, nil, 914], + 977 => [0, 0, nil, "θ", 920, nil, 920], + 978 => [0, 0, nil, "Υ", nil, nil, nil], + 979 => [0, 0, "ϓ", "ϓ", nil, nil, nil], + 980 => [0, 0, "ϔ", "ϔ", nil, nil, nil], + 981 => [0, 0, nil, "φ", 934, nil, 934], + 982 => [0, 0, nil, "π", 928, nil, 928], + 986 => [0, 0, nil, nil, nil, 987, nil], + 987 => [0, 0, nil, nil, 986, nil, 986], + 988 => [0, 0, nil, nil, nil, 989, nil], + 989 => [0, 0, nil, nil, 988, nil, 988], + 990 => [0, 0, nil, nil, nil, 991, nil], + 991 => [0, 0, nil, nil, 990, nil, 990], + 992 => [0, 0, nil, nil, nil, 993, nil], + 993 => [0, 0, nil, nil, 992, nil, 992], + 994 => [0, 0, nil, nil, nil, 995, nil], + 995 => [0, 0, nil, nil, 994, nil, 994], + 996 => [0, 0, nil, nil, nil, 997, nil], + 997 => [0, 0, nil, nil, 996, nil, 996], + 998 => [0, 0, nil, nil, nil, 999, nil], + 999 => [0, 0, nil, nil, 998, nil, 998], + 1000 => [0, 0, nil, nil, nil, 1001, nil], + 1001 => [0, 0, nil, nil, 1000, nil, 1000], + 1002 => [0, 0, nil, nil, nil, 1003, nil], + 1003 => [0, 0, nil, nil, 1002, nil, 1002], + 1004 => [0, 0, nil, nil, nil, 1005, nil], + 1005 => [0, 0, nil, nil, 1004, nil, 1004], + 1006 => [0, 0, nil, nil, nil, 1007, nil], + 1007 => [0, 0, nil, nil, 1006, nil, 1006], + 1008 => [0, 0, nil, "κ", 922, nil, 922], + 1009 => [0, 0, nil, "ρ", 929, nil, 929], + 1010 => [0, 0, nil, "ς", 931, nil, 931], + 1024 => [0, 0, "Ѐ", "Ѐ", nil, 1104, nil], + 1025 => [0, 0, "Ё", "Ё", nil, 1105, nil], + 1026 => [0, 0, nil, nil, nil, 1106, nil], + 1027 => [0, 0, "Ѓ", "Ѓ", nil, 1107, nil], + 1028 => [0, 0, nil, nil, nil, 1108, nil], + 1029 => [0, 0, nil, nil, nil, 1109, nil], + 1030 => [0, 0, nil, nil, nil, 1110, nil], + 1031 => [0, 0, "Ї", "Ї", nil, 1111, nil], + 1032 => [0, 0, nil, nil, nil, 1112, nil], + 1033 => [0, 0, nil, nil, nil, 1113, nil], + 1034 => [0, 0, nil, nil, nil, 1114, nil], + 1035 => [0, 0, nil, nil, nil, 1115, nil], + 1036 => [0, 0, "Ќ", "Ќ", nil, 1116, nil], + 1037 => [0, 0, "Ѝ", "Ѝ", nil, 1117, nil], + 1038 => [0, 0, "Ў", "Ў", nil, 1118, nil], + 1039 => [0, 0, nil, nil, nil, 1119, nil], + 1040 => [0, 0, nil, nil, nil, 1072, nil], + 1041 => [0, 0, nil, nil, nil, 1073, nil], + 1042 => [0, 0, nil, nil, nil, 1074, nil], + 1043 => [0, 0, nil, nil, nil, 1075, nil], + 1044 => [0, 0, nil, nil, nil, 1076, nil], + 1045 => [0, 0, nil, nil, nil, 1077, nil], + 1046 => [0, 0, nil, nil, nil, 1078, nil], + 1047 => [0, 0, nil, nil, nil, 1079, nil], + 1048 => [0, 0, nil, nil, nil, 1080, nil], + 1049 => [0, 0, "Й", "Й", nil, 1081, nil], + 1050 => [0, 0, nil, nil, nil, 1082, nil], + 1051 => [0, 0, nil, nil, nil, 1083, nil], + 1052 => [0, 0, nil, nil, nil, 1084, nil], + 1053 => [0, 0, nil, nil, nil, 1085, nil], + 1054 => [0, 0, nil, nil, nil, 1086, nil], + 1055 => [0, 0, nil, nil, nil, 1087, nil], + 1056 => [0, 0, nil, nil, nil, 1088, nil], + 1057 => [0, 0, nil, nil, nil, 1089, nil], + 1058 => [0, 0, nil, nil, nil, 1090, nil], + 1059 => [0, 0, nil, nil, nil, 1091, nil], + 1060 => [0, 0, nil, nil, nil, 1092, nil], + 1061 => [0, 0, nil, nil, nil, 1093, nil], + 1062 => [0, 0, nil, nil, nil, 1094, nil], + 1063 => [0, 0, nil, nil, nil, 1095, nil], + 1064 => [0, 0, nil, nil, nil, 1096, nil], + 1065 => [0, 0, nil, nil, nil, 1097, nil], + 1066 => [0, 0, nil, nil, nil, 1098, nil], + 1067 => [0, 0, nil, nil, nil, 1099, nil], + 1068 => [0, 0, nil, nil, nil, 1100, nil], + 1069 => [0, 0, nil, nil, nil, 1101, nil], + 1070 => [0, 0, nil, nil, nil, 1102, nil], + 1071 => [0, 0, nil, nil, nil, 1103, nil], + 1072 => [0, 0, nil, nil, 1040, nil, 1040], + 1073 => [0, 0, nil, nil, 1041, nil, 1041], + 1074 => [0, 0, nil, nil, 1042, nil, 1042], + 1075 => [0, 0, nil, nil, 1043, nil, 1043], + 1076 => [0, 0, nil, nil, 1044, nil, 1044], + 1077 => [0, 0, nil, nil, 1045, nil, 1045], + 1078 => [0, 0, nil, nil, 1046, nil, 1046], + 1079 => [0, 0, nil, nil, 1047, nil, 1047], + 1080 => [0, 0, nil, nil, 1048, nil, 1048], + 1081 => [0, 0, "й", "й", 1049, nil, 1049], + 1082 => [0, 0, nil, nil, 1050, nil, 1050], + 1083 => [0, 0, nil, nil, 1051, nil, 1051], + 1084 => [0, 0, nil, nil, 1052, nil, 1052], + 1085 => [0, 0, nil, nil, 1053, nil, 1053], + 1086 => [0, 0, nil, nil, 1054, nil, 1054], + 1087 => [0, 0, nil, nil, 1055, nil, 1055], + 1088 => [0, 0, nil, nil, 1056, nil, 1056], + 1089 => [0, 0, nil, nil, 1057, nil, 1057], + 1090 => [0, 0, nil, nil, 1058, nil, 1058], + 1091 => [0, 0, nil, nil, 1059, nil, 1059], + 1092 => [0, 0, nil, nil, 1060, nil, 1060], + 1093 => [0, 0, nil, nil, 1061, nil, 1061], + 1094 => [0, 0, nil, nil, 1062, nil, 1062], + 1095 => [0, 0, nil, nil, 1063, nil, 1063], + 1096 => [0, 0, nil, nil, 1064, nil, 1064], + 1097 => [0, 0, nil, nil, 1065, nil, 1065], + 1098 => [0, 0, nil, nil, 1066, nil, 1066], + 1099 => [0, 0, nil, nil, 1067, nil, 1067], + 1100 => [0, 0, nil, nil, 1068, nil, 1068], + 1101 => [0, 0, nil, nil, 1069, nil, 1069], + 1102 => [0, 0, nil, nil, 1070, nil, 1070], + 1103 => [0, 0, nil, nil, 1071, nil, 1071], + 1104 => [0, 0, "ѐ", "ѐ", 1024, nil, 1024], + 1105 => [0, 0, "ё", "ё", 1025, nil, 1025], + 1106 => [0, 0, nil, nil, 1026, nil, 1026], + 1107 => [0, 0, "ѓ", "ѓ", 1027, nil, 1027], + 1108 => [0, 0, nil, nil, 1028, nil, 1028], + 1109 => [0, 0, nil, nil, 1029, nil, 1029], + 1110 => [0, 0, nil, nil, 1030, nil, 1030], + 1111 => [0, 0, "ї", "ї", 1031, nil, 1031], + 1112 => [0, 0, nil, nil, 1032, nil, 1032], + 1113 => [0, 0, nil, nil, 1033, nil, 1033], + 1114 => [0, 0, nil, nil, 1034, nil, 1034], + 1115 => [0, 0, nil, nil, 1035, nil, 1035], + 1116 => [0, 0, "ќ", "ќ", 1036, nil, 1036], + 1117 => [0, 0, "ѝ", "ѝ", 1037, nil, 1037], + 1118 => [0, 0, "ў", "ў", 1038, nil, 1038], + 1119 => [0, 0, nil, nil, 1039, nil, 1039], + 1120 => [0, 0, nil, nil, nil, 1121, nil], + 1121 => [0, 0, nil, nil, 1120, nil, 1120], + 1122 => [0, 0, nil, nil, nil, 1123, nil], + 1123 => [0, 0, nil, nil, 1122, nil, 1122], + 1124 => [0, 0, nil, nil, nil, 1125, nil], + 1125 => [0, 0, nil, nil, 1124, nil, 1124], + 1126 => [0, 0, nil, nil, nil, 1127, nil], + 1127 => [0, 0, nil, nil, 1126, nil, 1126], + 1128 => [0, 0, nil, nil, nil, 1129, nil], + 1129 => [0, 0, nil, nil, 1128, nil, 1128], + 1130 => [0, 0, nil, nil, nil, 1131, nil], + 1131 => [0, 0, nil, nil, 1130, nil, 1130], + 1132 => [0, 0, nil, nil, nil, 1133, nil], + 1133 => [0, 0, nil, nil, 1132, nil, 1132], + 1134 => [0, 0, nil, nil, nil, 1135, nil], + 1135 => [0, 0, nil, nil, 1134, nil, 1134], + 1136 => [0, 0, nil, nil, nil, 1137, nil], + 1137 => [0, 0, nil, nil, 1136, nil, 1136], + 1138 => [0, 0, nil, nil, nil, 1139, nil], + 1139 => [0, 0, nil, nil, 1138, nil, 1138], + 1140 => [0, 0, nil, nil, nil, 1141, nil], + 1141 => [0, 0, nil, nil, 1140, nil, 1140], + 1142 => [0, 0, "Ѷ", "Ѷ", nil, 1143, nil], + 1143 => [0, 0, "ѷ", "ѷ", 1142, nil, 1142], + 1144 => [0, 0, nil, nil, nil, 1145, nil], + 1145 => [0, 0, nil, nil, 1144, nil, 1144], + 1146 => [0, 0, nil, nil, nil, 1147, nil], + 1147 => [0, 0, nil, nil, 1146, nil, 1146], + 1148 => [0, 0, nil, nil, nil, 1149, nil], + 1149 => [0, 0, nil, nil, 1148, nil, 1148], + 1150 => [0, 0, nil, nil, nil, 1151, nil], + 1151 => [0, 0, nil, nil, 1150, nil, 1150], + 1152 => [0, 0, nil, nil, nil, 1153, nil], + 1153 => [0, 0, nil, nil, 1152, nil, 1152], + 1155 => [230, 0, nil, nil, nil, nil, nil], + 1156 => [230, 0, nil, nil, nil, nil, nil], + 1157 => [230, 0, nil, nil, nil, nil, nil], + 1158 => [230, 0, nil, nil, nil, nil, nil], + 1164 => [0, 0, nil, nil, nil, 1165, nil], + 1165 => [0, 0, nil, nil, 1164, nil, 1164], + 1166 => [0, 0, nil, nil, nil, 1167, nil], + 1167 => [0, 0, nil, nil, 1166, nil, 1166], + 1168 => [0, 0, nil, nil, nil, 1169, nil], + 1169 => [0, 0, nil, nil, 1168, nil, 1168], + 1170 => [0, 0, nil, nil, nil, 1171, nil], + 1171 => [0, 0, nil, nil, 1170, nil, 1170], + 1172 => [0, 0, nil, nil, nil, 1173, nil], + 1173 => [0, 0, nil, nil, 1172, nil, 1172], + 1174 => [0, 0, nil, nil, nil, 1175, nil], + 1175 => [0, 0, nil, nil, 1174, nil, 1174], + 1176 => [0, 0, nil, nil, nil, 1177, nil], + 1177 => [0, 0, nil, nil, 1176, nil, 1176], + 1178 => [0, 0, nil, nil, nil, 1179, nil], + 1179 => [0, 0, nil, nil, 1178, nil, 1178], + 1180 => [0, 0, nil, nil, nil, 1181, nil], + 1181 => [0, 0, nil, nil, 1180, nil, 1180], + 1182 => [0, 0, nil, nil, nil, 1183, nil], + 1183 => [0, 0, nil, nil, 1182, nil, 1182], + 1184 => [0, 0, nil, nil, nil, 1185, nil], + 1185 => [0, 0, nil, nil, 1184, nil, 1184], + 1186 => [0, 0, nil, nil, nil, 1187, nil], + 1187 => [0, 0, nil, nil, 1186, nil, 1186], + 1188 => [0, 0, nil, nil, nil, 1189, nil], + 1189 => [0, 0, nil, nil, 1188, nil, 1188], + 1190 => [0, 0, nil, nil, nil, 1191, nil], + 1191 => [0, 0, nil, nil, 1190, nil, 1190], + 1192 => [0, 0, nil, nil, nil, 1193, nil], + 1193 => [0, 0, nil, nil, 1192, nil, 1192], + 1194 => [0, 0, nil, nil, nil, 1195, nil], + 1195 => [0, 0, nil, nil, 1194, nil, 1194], + 1196 => [0, 0, nil, nil, nil, 1197, nil], + 1197 => [0, 0, nil, nil, 1196, nil, 1196], + 1198 => [0, 0, nil, nil, nil, 1199, nil], + 1199 => [0, 0, nil, nil, 1198, nil, 1198], + 1200 => [0, 0, nil, nil, nil, 1201, nil], + 1201 => [0, 0, nil, nil, 1200, nil, 1200], + 1202 => [0, 0, nil, nil, nil, 1203, nil], + 1203 => [0, 0, nil, nil, 1202, nil, 1202], + 1204 => [0, 0, nil, nil, nil, 1205, nil], + 1205 => [0, 0, nil, nil, 1204, nil, 1204], + 1206 => [0, 0, nil, nil, nil, 1207, nil], + 1207 => [0, 0, nil, nil, 1206, nil, 1206], + 1208 => [0, 0, nil, nil, nil, 1209, nil], + 1209 => [0, 0, nil, nil, 1208, nil, 1208], + 1210 => [0, 0, nil, nil, nil, 1211, nil], + 1211 => [0, 0, nil, nil, 1210, nil, 1210], + 1212 => [0, 0, nil, nil, nil, 1213, nil], + 1213 => [0, 0, nil, nil, 1212, nil, 1212], + 1214 => [0, 0, nil, nil, nil, 1215, nil], + 1215 => [0, 0, nil, nil, 1214, nil, 1214], + 1217 => [0, 0, "Ӂ", "Ӂ", nil, 1218, nil], + 1218 => [0, 0, "ӂ", "ӂ", 1217, nil, 1217], + 1219 => [0, 0, nil, nil, nil, 1220, nil], + 1220 => [0, 0, nil, nil, 1219, nil, 1219], + 1223 => [0, 0, nil, nil, nil, 1224, nil], + 1224 => [0, 0, nil, nil, 1223, nil, 1223], + 1227 => [0, 0, nil, nil, nil, 1228, nil], + 1228 => [0, 0, nil, nil, 1227, nil, 1227], + 1232 => [0, 0, "Ӑ", "Ӑ", nil, 1233, nil], + 1233 => [0, 0, "ӑ", "ӑ", 1232, nil, 1232], + 1234 => [0, 0, "Ӓ", "Ӓ", nil, 1235, nil], + 1235 => [0, 0, "ӓ", "ӓ", 1234, nil, 1234], + 1236 => [0, 0, nil, nil, nil, 1237, nil], + 1237 => [0, 0, nil, nil, 1236, nil, 1236], + 1238 => [0, 0, "Ӗ", "Ӗ", nil, 1239, nil], + 1239 => [0, 0, "ӗ", "ӗ", 1238, nil, 1238], + 1240 => [0, 0, nil, nil, nil, 1241, nil], + 1241 => [0, 0, nil, nil, 1240, nil, 1240], + 1242 => [0, 0, "Ӛ", "Ӛ", nil, 1243, nil], + 1243 => [0, 0, "ӛ", "ӛ", 1242, nil, 1242], + 1244 => [0, 0, "Ӝ", "Ӝ", nil, 1245, nil], + 1245 => [0, 0, "ӝ", "ӝ", 1244, nil, 1244], + 1246 => [0, 0, "Ӟ", "Ӟ", nil, 1247, nil], + 1247 => [0, 0, "ӟ", "ӟ", 1246, nil, 1246], + 1248 => [0, 0, nil, nil, nil, 1249, nil], + 1249 => [0, 0, nil, nil, 1248, nil, 1248], + 1250 => [0, 0, "Ӣ", "Ӣ", nil, 1251, nil], + 1251 => [0, 0, "ӣ", "ӣ", 1250, nil, 1250], + 1252 => [0, 0, "Ӥ", "Ӥ", nil, 1253, nil], + 1253 => [0, 0, "ӥ", "ӥ", 1252, nil, 1252], + 1254 => [0, 0, "Ӧ", "Ӧ", nil, 1255, nil], + 1255 => [0, 0, "ӧ", "ӧ", 1254, nil, 1254], + 1256 => [0, 0, nil, nil, nil, 1257, nil], + 1257 => [0, 0, nil, nil, 1256, nil, 1256], + 1258 => [0, 0, "Ӫ", "Ӫ", nil, 1259, nil], + 1259 => [0, 0, "ӫ", "ӫ", 1258, nil, 1258], + 1260 => [0, 0, "Ӭ", "Ӭ", nil, 1261, nil], + 1261 => [0, 0, "ӭ", "ӭ", 1260, nil, 1260], + 1262 => [0, 0, "Ӯ", "Ӯ", nil, 1263, nil], + 1263 => [0, 0, "ӯ", "ӯ", 1262, nil, 1262], + 1264 => [0, 0, "Ӱ", "Ӱ", nil, 1265, nil], + 1265 => [0, 0, "ӱ", "ӱ", 1264, nil, 1264], + 1266 => [0, 0, "Ӳ", "Ӳ", nil, 1267, nil], + 1267 => [0, 0, "ӳ", "ӳ", 1266, nil, 1266], + 1268 => [0, 0, "Ӵ", "Ӵ", nil, 1269, nil], + 1269 => [0, 0, "ӵ", "ӵ", 1268, nil, 1268], + 1272 => [0, 0, "Ӹ", "Ӹ", nil, 1273, nil], + 1273 => [0, 0, "ӹ", "ӹ", 1272, nil, 1272], + 1329 => [0, 0, nil, nil, nil, 1377, nil], + 1330 => [0, 0, nil, nil, nil, 1378, nil], + 1331 => [0, 0, nil, nil, nil, 1379, nil], + 1332 => [0, 0, nil, nil, nil, 1380, nil], + 1333 => [0, 0, nil, nil, nil, 1381, nil], + 1334 => [0, 0, nil, nil, nil, 1382, nil], + 1335 => [0, 0, nil, nil, nil, 1383, nil], + 1336 => [0, 0, nil, nil, nil, 1384, nil], + 1337 => [0, 0, nil, nil, nil, 1385, nil], + 1338 => [0, 0, nil, nil, nil, 1386, nil], + 1339 => [0, 0, nil, nil, nil, 1387, nil], + 1340 => [0, 0, nil, nil, nil, 1388, nil], + 1341 => [0, 0, nil, nil, nil, 1389, nil], + 1342 => [0, 0, nil, nil, nil, 1390, nil], + 1343 => [0, 0, nil, nil, nil, 1391, nil], + 1344 => [0, 0, nil, nil, nil, 1392, nil], + 1345 => [0, 0, nil, nil, nil, 1393, nil], + 1346 => [0, 0, nil, nil, nil, 1394, nil], + 1347 => [0, 0, nil, nil, nil, 1395, nil], + 1348 => [0, 0, nil, nil, nil, 1396, nil], + 1349 => [0, 0, nil, nil, nil, 1397, nil], + 1350 => [0, 0, nil, nil, nil, 1398, nil], + 1351 => [0, 0, nil, nil, nil, 1399, nil], + 1352 => [0, 0, nil, nil, nil, 1400, nil], + 1353 => [0, 0, nil, nil, nil, 1401, nil], + 1354 => [0, 0, nil, nil, nil, 1402, nil], + 1355 => [0, 0, nil, nil, nil, 1403, nil], + 1356 => [0, 0, nil, nil, nil, 1404, nil], + 1357 => [0, 0, nil, nil, nil, 1405, nil], + 1358 => [0, 0, nil, nil, nil, 1406, nil], + 1359 => [0, 0, nil, nil, nil, 1407, nil], + 1360 => [0, 0, nil, nil, nil, 1408, nil], + 1361 => [0, 0, nil, nil, nil, 1409, nil], + 1362 => [0, 0, nil, nil, nil, 1410, nil], + 1363 => [0, 0, nil, nil, nil, 1411, nil], + 1364 => [0, 0, nil, nil, nil, 1412, nil], + 1365 => [0, 0, nil, nil, nil, 1413, nil], + 1366 => [0, 0, nil, nil, nil, 1414, nil], + 1377 => [0, 0, nil, nil, 1329, nil, 1329], + 1378 => [0, 0, nil, nil, 1330, nil, 1330], + 1379 => [0, 0, nil, nil, 1331, nil, 1331], + 1380 => [0, 0, nil, nil, 1332, nil, 1332], + 1381 => [0, 0, nil, nil, 1333, nil, 1333], + 1382 => [0, 0, nil, nil, 1334, nil, 1334], + 1383 => [0, 0, nil, nil, 1335, nil, 1335], + 1384 => [0, 0, nil, nil, 1336, nil, 1336], + 1385 => [0, 0, nil, nil, 1337, nil, 1337], + 1386 => [0, 0, nil, nil, 1338, nil, 1338], + 1387 => [0, 0, nil, nil, 1339, nil, 1339], + 1388 => [0, 0, nil, nil, 1340, nil, 1340], + 1389 => [0, 0, nil, nil, 1341, nil, 1341], + 1390 => [0, 0, nil, nil, 1342, nil, 1342], + 1391 => [0, 0, nil, nil, 1343, nil, 1343], + 1392 => [0, 0, nil, nil, 1344, nil, 1344], + 1393 => [0, 0, nil, nil, 1345, nil, 1345], + 1394 => [0, 0, nil, nil, 1346, nil, 1346], + 1395 => [0, 0, nil, nil, 1347, nil, 1347], + 1396 => [0, 0, nil, nil, 1348, nil, 1348], + 1397 => [0, 0, nil, nil, 1349, nil, 1349], + 1398 => [0, 0, nil, nil, 1350, nil, 1350], + 1399 => [0, 0, nil, nil, 1351, nil, 1351], + 1400 => [0, 0, nil, nil, 1352, nil, 1352], + 1401 => [0, 0, nil, nil, 1353, nil, 1353], + 1402 => [0, 0, nil, nil, 1354, nil, 1354], + 1403 => [0, 0, nil, nil, 1355, nil, 1355], + 1404 => [0, 0, nil, nil, 1356, nil, 1356], + 1405 => [0, 0, nil, nil, 1357, nil, 1357], + 1406 => [0, 0, nil, nil, 1358, nil, 1358], + 1407 => [0, 0, nil, nil, 1359, nil, 1359], + 1408 => [0, 0, nil, nil, 1360, nil, 1360], + 1409 => [0, 0, nil, nil, 1361, nil, 1361], + 1410 => [0, 0, nil, nil, 1362, nil, 1362], + 1411 => [0, 0, nil, nil, 1363, nil, 1363], + 1412 => [0, 0, nil, nil, 1364, nil, 1364], + 1413 => [0, 0, nil, nil, 1365, nil, 1365], + 1414 => [0, 0, nil, nil, 1366, nil, 1366], + 1415 => [0, 0, nil, "եւ", nil, nil, nil], + 1425 => [220, 0, nil, nil, nil, nil, nil], + 1426 => [230, 0, nil, nil, nil, nil, nil], + 1427 => [230, 0, nil, nil, nil, nil, nil], + 1428 => [230, 0, nil, nil, nil, nil, nil], + 1429 => [230, 0, nil, nil, nil, nil, nil], + 1430 => [220, 0, nil, nil, nil, nil, nil], + 1431 => [230, 0, nil, nil, nil, nil, nil], + 1432 => [230, 0, nil, nil, nil, nil, nil], + 1433 => [230, 0, nil, nil, nil, nil, nil], + 1434 => [222, 0, nil, nil, nil, nil, nil], + 1435 => [220, 0, nil, nil, nil, nil, nil], + 1436 => [230, 0, nil, nil, nil, nil, nil], + 1437 => [230, 0, nil, nil, nil, nil, nil], + 1438 => [230, 0, nil, nil, nil, nil, nil], + 1439 => [230, 0, nil, nil, nil, nil, nil], + 1440 => [230, 0, nil, nil, nil, nil, nil], + 1441 => [230, 0, nil, nil, nil, nil, nil], + 1443 => [220, 0, nil, nil, nil, nil, nil], + 1444 => [220, 0, nil, nil, nil, nil, nil], + 1445 => [220, 0, nil, nil, nil, nil, nil], + 1446 => [220, 0, nil, nil, nil, nil, nil], + 1447 => [220, 0, nil, nil, nil, nil, nil], + 1448 => [230, 0, nil, nil, nil, nil, nil], + 1449 => [230, 0, nil, nil, nil, nil, nil], + 1450 => [220, 0, nil, nil, nil, nil, nil], + 1451 => [230, 0, nil, nil, nil, nil, nil], + 1452 => [230, 0, nil, nil, nil, nil, nil], + 1453 => [222, 0, nil, nil, nil, nil, nil], + 1454 => [228, 0, nil, nil, nil, nil, nil], + 1455 => [230, 0, nil, nil, nil, nil, nil], + 1456 => [10, 0, nil, nil, nil, nil, nil], + 1457 => [11, 0, nil, nil, nil, nil, nil], + 1458 => [12, 0, nil, nil, nil, nil, nil], + 1459 => [13, 0, nil, nil, nil, nil, nil], + 1460 => [14, 0, nil, nil, nil, nil, nil], + 1461 => [15, 0, nil, nil, nil, nil, nil], + 1462 => [16, 0, nil, nil, nil, nil, nil], + 1463 => [17, 0, nil, nil, nil, nil, nil], + 1464 => [18, 0, nil, nil, nil, nil, nil], + 1465 => [19, 0, nil, nil, nil, nil, nil], + 1467 => [20, 0, nil, nil, nil, nil, nil], + 1468 => [21, 0, nil, nil, nil, nil, nil], + 1469 => [22, 0, nil, nil, nil, nil, nil], + 1471 => [23, 0, nil, nil, nil, nil, nil], + 1473 => [24, 0, nil, nil, nil, nil, nil], + 1474 => [25, 0, nil, nil, nil, nil, nil], + 1476 => [230, 0, nil, nil, nil, nil, nil], + 1570 => [0, 0, "آ", "آ", nil, nil, nil], + 1571 => [0, 0, "أ", "أ", nil, nil, nil], + 1572 => [0, 0, "ؤ", "ؤ", nil, nil, nil], + 1573 => [0, 0, "إ", "إ", nil, nil, nil], + 1574 => [0, 0, "ئ", "ئ", nil, nil, nil], + 1611 => [27, 0, nil, nil, nil, nil, nil], + 1612 => [28, 0, nil, nil, nil, nil, nil], + 1613 => [29, 0, nil, nil, nil, nil, nil], + 1614 => [30, 0, nil, nil, nil, nil, nil], + 1615 => [31, 0, nil, nil, nil, nil, nil], + 1616 => [32, 0, nil, nil, nil, nil, nil], + 1617 => [33, 0, nil, nil, nil, nil, nil], + 1618 => [34, 0, nil, nil, nil, nil, nil], + 1619 => [230, 0, nil, nil, nil, nil, nil], + 1620 => [230, 0, nil, nil, nil, nil, nil], + 1621 => [220, 0, nil, nil, nil, nil, nil], + 1648 => [35, 0, nil, nil, nil, nil, nil], + 1653 => [0, 0, nil, "اٴ", nil, nil, nil], + 1654 => [0, 0, nil, "وٴ", nil, nil, nil], + 1655 => [0, 0, nil, "ۇٴ", nil, nil, nil], + 1656 => [0, 0, nil, "يٴ", nil, nil, nil], + 1728 => [0, 0, "ۀ", "ۀ", nil, nil, nil], + 1730 => [0, 0, "ۂ", "ۂ", nil, nil, nil], + 1747 => [0, 0, "ۓ", "ۓ", nil, nil, nil], + 1750 => [230, 0, nil, nil, nil, nil, nil], + 1751 => [230, 0, nil, nil, nil, nil, nil], + 1752 => [230, 0, nil, nil, nil, nil, nil], + 1753 => [230, 0, nil, nil, nil, nil, nil], + 1754 => [230, 0, nil, nil, nil, nil, nil], + 1755 => [230, 0, nil, nil, nil, nil, nil], + 1756 => [230, 0, nil, nil, nil, nil, nil], + 1759 => [230, 0, nil, nil, nil, nil, nil], + 1760 => [230, 0, nil, nil, nil, nil, nil], + 1761 => [230, 0, nil, nil, nil, nil, nil], + 1762 => [230, 0, nil, nil, nil, nil, nil], + 1763 => [220, 0, nil, nil, nil, nil, nil], + 1764 => [230, 0, nil, nil, nil, nil, nil], + 1767 => [230, 0, nil, nil, nil, nil, nil], + 1768 => [230, 0, nil, nil, nil, nil, nil], + 1770 => [220, 0, nil, nil, nil, nil, nil], + 1771 => [230, 0, nil, nil, nil, nil, nil], + 1772 => [230, 0, nil, nil, nil, nil, nil], + 1773 => [220, 0, nil, nil, nil, nil, nil], + 1809 => [36, 0, nil, nil, nil, nil, nil], + 1840 => [230, 0, nil, nil, nil, nil, nil], + 1841 => [220, 0, nil, nil, nil, nil, nil], + 1842 => [230, 0, nil, nil, nil, nil, nil], + 1843 => [230, 0, nil, nil, nil, nil, nil], + 1844 => [220, 0, nil, nil, nil, nil, nil], + 1845 => [230, 0, nil, nil, nil, nil, nil], + 1846 => [230, 0, nil, nil, nil, nil, nil], + 1847 => [220, 0, nil, nil, nil, nil, nil], + 1848 => [220, 0, nil, nil, nil, nil, nil], + 1849 => [220, 0, nil, nil, nil, nil, nil], + 1850 => [230, 0, nil, nil, nil, nil, nil], + 1851 => [220, 0, nil, nil, nil, nil, nil], + 1852 => [220, 0, nil, nil, nil, nil, nil], + 1853 => [230, 0, nil, nil, nil, nil, nil], + 1854 => [220, 0, nil, nil, nil, nil, nil], + 1855 => [230, 0, nil, nil, nil, nil, nil], + 1856 => [230, 0, nil, nil, nil, nil, nil], + 1857 => [230, 0, nil, nil, nil, nil, nil], + 1858 => [220, 0, nil, nil, nil, nil, nil], + 1859 => [230, 0, nil, nil, nil, nil, nil], + 1860 => [220, 0, nil, nil, nil, nil, nil], + 1861 => [230, 0, nil, nil, nil, nil, nil], + 1862 => [220, 0, nil, nil, nil, nil, nil], + 1863 => [230, 0, nil, nil, nil, nil, nil], + 1864 => [220, 0, nil, nil, nil, nil, nil], + 1865 => [230, 0, nil, nil, nil, nil, nil], + 1866 => [230, 0, nil, nil, nil, nil, nil], + 2345 => [0, 0, "ऩ", "ऩ", nil, nil, nil], + 2353 => [0, 0, "ऱ", "ऱ", nil, nil, nil], + 2356 => [0, 0, "ऴ", "ऴ", nil, nil, nil], + 2364 => [7, 0, nil, nil, nil, nil, nil], + 2381 => [9, 0, nil, nil, nil, nil, nil], + 2385 => [230, 0, nil, nil, nil, nil, nil], + 2386 => [220, 0, nil, nil, nil, nil, nil], + 2387 => [230, 0, nil, nil, nil, nil, nil], + 2388 => [230, 0, nil, nil, nil, nil, nil], + 2392 => [0, 1, "क़", "क़", nil, nil, nil], + 2393 => [0, 1, "ख़", "ख़", nil, nil, nil], + 2394 => [0, 1, "ग़", "ग़", nil, nil, nil], + 2395 => [0, 1, "ज़", "ज़", nil, nil, nil], + 2396 => [0, 1, "ड़", "ड़", nil, nil, nil], + 2397 => [0, 1, "ढ़", "ढ़", nil, nil, nil], + 2398 => [0, 1, "फ़", "फ़", nil, nil, nil], + 2399 => [0, 1, "य़", "य़", nil, nil, nil], + 2492 => [7, 0, nil, nil, nil, nil, nil], + 2507 => [0, 0, "ো", "ো", nil, nil, nil], + 2508 => [0, 0, "ৌ", "ৌ", nil, nil, nil], + 2509 => [9, 0, nil, nil, nil, nil, nil], + 2524 => [0, 1, "ড়", "ড়", nil, nil, nil], + 2525 => [0, 1, "ঢ়", "ঢ়", nil, nil, nil], + 2527 => [0, 1, "য়", "য়", nil, nil, nil], + 2611 => [0, 1, "ਲ਼", "ਲ਼", nil, nil, nil], + 2614 => [0, 1, "ਸ਼", "ਸ਼", nil, nil, nil], + 2620 => [7, 0, nil, nil, nil, nil, nil], + 2637 => [9, 0, nil, nil, nil, nil, nil], + 2649 => [0, 1, "ਖ਼", "ਖ਼", nil, nil, nil], + 2650 => [0, 1, "ਗ਼", "ਗ਼", nil, nil, nil], + 2651 => [0, 1, "ਜ਼", "ਜ਼", nil, nil, nil], + 2654 => [0, 1, "ਫ਼", "ਫ਼", nil, nil, nil], + 2748 => [7, 0, nil, nil, nil, nil, nil], + 2765 => [9, 0, nil, nil, nil, nil, nil], + 2876 => [7, 0, nil, nil, nil, nil, nil], + 2888 => [0, 0, "ୈ", "ୈ", nil, nil, nil], + 2891 => [0, 0, "ୋ", "ୋ", nil, nil, nil], + 2892 => [0, 0, "ୌ", "ୌ", nil, nil, nil], + 2893 => [9, 0, nil, nil, nil, nil, nil], + 2908 => [0, 1, "ଡ଼", "ଡ଼", nil, nil, nil], + 2909 => [0, 1, "ଢ଼", "ଢ଼", nil, nil, nil], + 2964 => [0, 0, "ஔ", "ஔ", nil, nil, nil], + 3018 => [0, 0, "ொ", "ொ", nil, nil, nil], + 3019 => [0, 0, "ோ", "ோ", nil, nil, nil], + 3020 => [0, 0, "ௌ", "ௌ", nil, nil, nil], + 3021 => [9, 0, nil, nil, nil, nil, nil], + 3144 => [0, 0, "ై", "ై", nil, nil, nil], + 3149 => [9, 0, nil, nil, nil, nil, nil], + 3157 => [84, 0, nil, nil, nil, nil, nil], + 3158 => [91, 0, nil, nil, nil, nil, nil], + 3264 => [0, 0, "ೀ", "ೀ", nil, nil, nil], + 3271 => [0, 0, "ೇ", "ೇ", nil, nil, nil], + 3272 => [0, 0, "ೈ", "ೈ", nil, nil, nil], + 3274 => [0, 0, "ೊ", "ೊ", nil, nil, nil], + 3275 => [0, 0, "ೋ", "ೋ", nil, nil, nil], + 3277 => [9, 0, nil, nil, nil, nil, nil], + 3402 => [0, 0, "ൊ", "ൊ", nil, nil, nil], + 3403 => [0, 0, "ോ", "ോ", nil, nil, nil], + 3404 => [0, 0, "ൌ", "ൌ", nil, nil, nil], + 3405 => [9, 0, nil, nil, nil, nil, nil], + 3530 => [9, 0, nil, nil, nil, nil, nil], + 3546 => [0, 0, "ේ", "ේ", nil, nil, nil], + 3548 => [0, 0, "ො", "ො", nil, nil, nil], + 3549 => [0, 0, "ෝ", "ෝ", nil, nil, nil], + 3550 => [0, 0, "ෞ", "ෞ", nil, nil, nil], + 3635 => [0, 0, nil, "ํา", nil, nil, nil], + 3640 => [103, 0, nil, nil, nil, nil, nil], + 3641 => [103, 0, nil, nil, nil, nil, nil], + 3642 => [9, 0, nil, nil, nil, nil, nil], + 3656 => [107, 0, nil, nil, nil, nil, nil], + 3657 => [107, 0, nil, nil, nil, nil, nil], + 3658 => [107, 0, nil, nil, nil, nil, nil], + 3659 => [107, 0, nil, nil, nil, nil, nil], + 3763 => [0, 0, nil, "ໍາ", nil, nil, nil], + 3768 => [118, 0, nil, nil, nil, nil, nil], + 3769 => [118, 0, nil, nil, nil, nil, nil], + 3784 => [122, 0, nil, nil, nil, nil, nil], + 3785 => [122, 0, nil, nil, nil, nil, nil], + 3786 => [122, 0, nil, nil, nil, nil, nil], + 3787 => [122, 0, nil, nil, nil, nil, nil], + 3804 => [0, 0, nil, "ຫນ", nil, nil, nil], + 3805 => [0, 0, nil, "ຫມ", nil, nil, nil], + 3852 => [0, 0, nil, "་", nil, nil, nil], + 3864 => [220, 0, nil, nil, nil, nil, nil], + 3865 => [220, 0, nil, nil, nil, nil, nil], + 3893 => [220, 0, nil, nil, nil, nil, nil], + 3895 => [220, 0, nil, nil, nil, nil, nil], + 3897 => [216, 0, nil, nil, nil, nil, nil], + 3907 => [0, 1, "གྷ", "གྷ", nil, nil, nil], + 3917 => [0, 1, "ཌྷ", "ཌྷ", nil, nil, nil], + 3922 => [0, 1, "དྷ", "དྷ", nil, nil, nil], + 3927 => [0, 1, "བྷ", "བྷ", nil, nil, nil], + 3932 => [0, 1, "ཛྷ", "ཛྷ", nil, nil, nil], + 3945 => [0, 1, "ཀྵ", "ཀྵ", nil, nil, nil], + 3953 => [129, 0, nil, nil, nil, nil, nil], + 3954 => [130, 0, nil, nil, nil, nil, nil], + 3955 => [0, 3, "ཱི", "ཱི", nil, nil, nil], + 3956 => [132, 0, nil, nil, nil, nil, nil], + 3957 => [0, 3, "ཱུ", "ཱུ", nil, nil, nil], + 3958 => [0, 1, "ྲྀ", "ྲྀ", nil, nil, nil], + 3959 => [0, 0, nil, "ྲཱྀ", nil, nil, nil], + 3960 => [0, 1, "ླྀ", "ླྀ", nil, nil, nil], + 3961 => [0, 0, nil, "ླཱྀ", nil, nil, nil], + 3962 => [130, 0, nil, nil, nil, nil, nil], + 3963 => [130, 0, nil, nil, nil, nil, nil], + 3964 => [130, 0, nil, nil, nil, nil, nil], + 3965 => [130, 0, nil, nil, nil, nil, nil], + 3968 => [130, 0, nil, nil, nil, nil, nil], + 3969 => [0, 3, "ཱྀ", "ཱྀ", nil, nil, nil], + 3970 => [230, 0, nil, nil, nil, nil, nil], + 3971 => [230, 0, nil, nil, nil, nil, nil], + 3972 => [9, 0, nil, nil, nil, nil, nil], + 3974 => [230, 0, nil, nil, nil, nil, nil], + 3975 => [230, 0, nil, nil, nil, nil, nil], + 3987 => [0, 1, "ྒྷ", "ྒྷ", nil, nil, nil], + 3997 => [0, 1, "ྜྷ", "ྜྷ", nil, nil, nil], + 4002 => [0, 1, "ྡྷ", "ྡྷ", nil, nil, nil], + 4007 => [0, 1, "ྦྷ", "ྦྷ", nil, nil, nil], + 4012 => [0, 1, "ྫྷ", "ྫྷ", nil, nil, nil], + 4025 => [0, 1, "ྐྵ", "ྐྵ", nil, nil, nil], + 4038 => [220, 0, nil, nil, nil, nil, nil], + 4134 => [0, 0, "ဦ", "ဦ", nil, nil, nil], + 4151 => [7, 0, nil, nil, nil, nil, nil], + 4153 => [9, 0, nil, nil, nil, nil, nil], + 6098 => [9, 0, nil, nil, nil, nil, nil], + 6313 => [228, 0, nil, nil, nil, nil, nil], + 7680 => [0, 0, "Ḁ", "Ḁ", nil, 7681, nil], + 7681 => [0, 0, "ḁ", "ḁ", 7680, nil, 7680], + 7682 => [0, 0, "Ḃ", "Ḃ", nil, 7683, nil], + 7683 => [0, 0, "ḃ", "ḃ", 7682, nil, 7682], + 7684 => [0, 0, "Ḅ", "Ḅ", nil, 7685, nil], + 7685 => [0, 0, "ḅ", "ḅ", 7684, nil, 7684], + 7686 => [0, 0, "Ḇ", "Ḇ", nil, 7687, nil], + 7687 => [0, 0, "ḇ", "ḇ", 7686, nil, 7686], + 7688 => [0, 0, "Ḉ", "Ḉ", nil, 7689, nil], + 7689 => [0, 0, "ḉ", "ḉ", 7688, nil, 7688], + 7690 => [0, 0, "Ḋ", "Ḋ", nil, 7691, nil], + 7691 => [0, 0, "ḋ", "ḋ", 7690, nil, 7690], + 7692 => [0, 0, "Ḍ", "Ḍ", nil, 7693, nil], + 7693 => [0, 0, "ḍ", "ḍ", 7692, nil, 7692], + 7694 => [0, 0, "Ḏ", "Ḏ", nil, 7695, nil], + 7695 => [0, 0, "ḏ", "ḏ", 7694, nil, 7694], + 7696 => [0, 0, "Ḑ", "Ḑ", nil, 7697, nil], + 7697 => [0, 0, "ḑ", "ḑ", 7696, nil, 7696], + 7698 => [0, 0, "Ḓ", "Ḓ", nil, 7699, nil], + 7699 => [0, 0, "ḓ", "ḓ", 7698, nil, 7698], + 7700 => [0, 0, "Ḕ", "Ḕ", nil, 7701, nil], + 7701 => [0, 0, "ḕ", "ḕ", 7700, nil, 7700], + 7702 => [0, 0, "Ḗ", "Ḗ", nil, 7703, nil], + 7703 => [0, 0, "ḗ", "ḗ", 7702, nil, 7702], + 7704 => [0, 0, "Ḙ", "Ḙ", nil, 7705, nil], + 7705 => [0, 0, "ḙ", "ḙ", 7704, nil, 7704], + 7706 => [0, 0, "Ḛ", "Ḛ", nil, 7707, nil], + 7707 => [0, 0, "ḛ", "ḛ", 7706, nil, 7706], + 7708 => [0, 0, "Ḝ", "Ḝ", nil, 7709, nil], + 7709 => [0, 0, "ḝ", "ḝ", 7708, nil, 7708], + 7710 => [0, 0, "Ḟ", "Ḟ", nil, 7711, nil], + 7711 => [0, 0, "ḟ", "ḟ", 7710, nil, 7710], + 7712 => [0, 0, "Ḡ", "Ḡ", nil, 7713, nil], + 7713 => [0, 0, "ḡ", "ḡ", 7712, nil, 7712], + 7714 => [0, 0, "Ḣ", "Ḣ", nil, 7715, nil], + 7715 => [0, 0, "ḣ", "ḣ", 7714, nil, 7714], + 7716 => [0, 0, "Ḥ", "Ḥ", nil, 7717, nil], + 7717 => [0, 0, "ḥ", "ḥ", 7716, nil, 7716], + 7718 => [0, 0, "Ḧ", "Ḧ", nil, 7719, nil], + 7719 => [0, 0, "ḧ", "ḧ", 7718, nil, 7718], + 7720 => [0, 0, "Ḩ", "Ḩ", nil, 7721, nil], + 7721 => [0, 0, "ḩ", "ḩ", 7720, nil, 7720], + 7722 => [0, 0, "Ḫ", "Ḫ", nil, 7723, nil], + 7723 => [0, 0, "ḫ", "ḫ", 7722, nil, 7722], + 7724 => [0, 0, "Ḭ", "Ḭ", nil, 7725, nil], + 7725 => [0, 0, "ḭ", "ḭ", 7724, nil, 7724], + 7726 => [0, 0, "Ḯ", "Ḯ", nil, 7727, nil], + 7727 => [0, 0, "ḯ", "ḯ", 7726, nil, 7726], + 7728 => [0, 0, "Ḱ", "Ḱ", nil, 7729, nil], + 7729 => [0, 0, "ḱ", "ḱ", 7728, nil, 7728], + 7730 => [0, 0, "Ḳ", "Ḳ", nil, 7731, nil], + 7731 => [0, 0, "ḳ", "ḳ", 7730, nil, 7730], + 7732 => [0, 0, "Ḵ", "Ḵ", nil, 7733, nil], + 7733 => [0, 0, "ḵ", "ḵ", 7732, nil, 7732], + 7734 => [0, 0, "Ḷ", "Ḷ", nil, 7735, nil], + 7735 => [0, 0, "ḷ", "ḷ", 7734, nil, 7734], + 7736 => [0, 0, "Ḹ", "Ḹ", nil, 7737, nil], + 7737 => [0, 0, "ḹ", "ḹ", 7736, nil, 7736], + 7738 => [0, 0, "Ḻ", "Ḻ", nil, 7739, nil], + 7739 => [0, 0, "ḻ", "ḻ", 7738, nil, 7738], + 7740 => [0, 0, "Ḽ", "Ḽ", nil, 7741, nil], + 7741 => [0, 0, "ḽ", "ḽ", 7740, nil, 7740], + 7742 => [0, 0, "Ḿ", "Ḿ", nil, 7743, nil], + 7743 => [0, 0, "ḿ", "ḿ", 7742, nil, 7742], + 7744 => [0, 0, "Ṁ", "Ṁ", nil, 7745, nil], + 7745 => [0, 0, "ṁ", "ṁ", 7744, nil, 7744], + 7746 => [0, 0, "Ṃ", "Ṃ", nil, 7747, nil], + 7747 => [0, 0, "ṃ", "ṃ", 7746, nil, 7746], + 7748 => [0, 0, "Ṅ", "Ṅ", nil, 7749, nil], + 7749 => [0, 0, "ṅ", "ṅ", 7748, nil, 7748], + 7750 => [0, 0, "Ṇ", "Ṇ", nil, 7751, nil], + 7751 => [0, 0, "ṇ", "ṇ", 7750, nil, 7750], + 7752 => [0, 0, "Ṉ", "Ṉ", nil, 7753, nil], + 7753 => [0, 0, "ṉ", "ṉ", 7752, nil, 7752], + 7754 => [0, 0, "Ṋ", "Ṋ", nil, 7755, nil], + 7755 => [0, 0, "ṋ", "ṋ", 7754, nil, 7754], + 7756 => [0, 0, "Ṍ", "Ṍ", nil, 7757, nil], + 7757 => [0, 0, "ṍ", "ṍ", 7756, nil, 7756], + 7758 => [0, 0, "Ṏ", "Ṏ", nil, 7759, nil], + 7759 => [0, 0, "ṏ", "ṏ", 7758, nil, 7758], + 7760 => [0, 0, "Ṑ", "Ṑ", nil, 7761, nil], + 7761 => [0, 0, "ṑ", "ṑ", 7760, nil, 7760], + 7762 => [0, 0, "Ṓ", "Ṓ", nil, 7763, nil], + 7763 => [0, 0, "ṓ", "ṓ", 7762, nil, 7762], + 7764 => [0, 0, "Ṕ", "Ṕ", nil, 7765, nil], + 7765 => [0, 0, "ṕ", "ṕ", 7764, nil, 7764], + 7766 => [0, 0, "Ṗ", "Ṗ", nil, 7767, nil], + 7767 => [0, 0, "ṗ", "ṗ", 7766, nil, 7766], + 7768 => [0, 0, "Ṙ", "Ṙ", nil, 7769, nil], + 7769 => [0, 0, "ṙ", "ṙ", 7768, nil, 7768], + 7770 => [0, 0, "Ṛ", "Ṛ", nil, 7771, nil], + 7771 => [0, 0, "ṛ", "ṛ", 7770, nil, 7770], + 7772 => [0, 0, "Ṝ", "Ṝ", nil, 7773, nil], + 7773 => [0, 0, "ṝ", "ṝ", 7772, nil, 7772], + 7774 => [0, 0, "Ṟ", "Ṟ", nil, 7775, nil], + 7775 => [0, 0, "ṟ", "ṟ", 7774, nil, 7774], + 7776 => [0, 0, "Ṡ", "Ṡ", nil, 7777, nil], + 7777 => [0, 0, "ṡ", "ṡ", 7776, nil, 7776], + 7778 => [0, 0, "Ṣ", "Ṣ", nil, 7779, nil], + 7779 => [0, 0, "ṣ", "ṣ", 7778, nil, 7778], + 7780 => [0, 0, "Ṥ", "Ṥ", nil, 7781, nil], + 7781 => [0, 0, "ṥ", "ṥ", 7780, nil, 7780], + 7782 => [0, 0, "Ṧ", "Ṧ", nil, 7783, nil], + 7783 => [0, 0, "ṧ", "ṧ", 7782, nil, 7782], + 7784 => [0, 0, "Ṩ", "Ṩ", nil, 7785, nil], + 7785 => [0, 0, "ṩ", "ṩ", 7784, nil, 7784], + 7786 => [0, 0, "Ṫ", "Ṫ", nil, 7787, nil], + 7787 => [0, 0, "ṫ", "ṫ", 7786, nil, 7786], + 7788 => [0, 0, "Ṭ", "Ṭ", nil, 7789, nil], + 7789 => [0, 0, "ṭ", "ṭ", 7788, nil, 7788], + 7790 => [0, 0, "Ṯ", "Ṯ", nil, 7791, nil], + 7791 => [0, 0, "ṯ", "ṯ", 7790, nil, 7790], + 7792 => [0, 0, "Ṱ", "Ṱ", nil, 7793, nil], + 7793 => [0, 0, "ṱ", "ṱ", 7792, nil, 7792], + 7794 => [0, 0, "Ṳ", "Ṳ", nil, 7795, nil], + 7795 => [0, 0, "ṳ", "ṳ", 7794, nil, 7794], + 7796 => [0, 0, "Ṵ", "Ṵ", nil, 7797, nil], + 7797 => [0, 0, "ṵ", "ṵ", 7796, nil, 7796], + 7798 => [0, 0, "Ṷ", "Ṷ", nil, 7799, nil], + 7799 => [0, 0, "ṷ", "ṷ", 7798, nil, 7798], + 7800 => [0, 0, "Ṹ", "Ṹ", nil, 7801, nil], + 7801 => [0, 0, "ṹ", "ṹ", 7800, nil, 7800], + 7802 => [0, 0, "Ṻ", "Ṻ", nil, 7803, nil], + 7803 => [0, 0, "ṻ", "ṻ", 7802, nil, 7802], + 7804 => [0, 0, "Ṽ", "Ṽ", nil, 7805, nil], + 7805 => [0, 0, "ṽ", "ṽ", 7804, nil, 7804], + 7806 => [0, 0, "Ṿ", "Ṿ", nil, 7807, nil], + 7807 => [0, 0, "ṿ", "ṿ", 7806, nil, 7806], + 7808 => [0, 0, "Ẁ", "Ẁ", nil, 7809, nil], + 7809 => [0, 0, "ẁ", "ẁ", 7808, nil, 7808], + 7810 => [0, 0, "Ẃ", "Ẃ", nil, 7811, nil], + 7811 => [0, 0, "ẃ", "ẃ", 7810, nil, 7810], + 7812 => [0, 0, "Ẅ", "Ẅ", nil, 7813, nil], + 7813 => [0, 0, "ẅ", "ẅ", 7812, nil, 7812], + 7814 => [0, 0, "Ẇ", "Ẇ", nil, 7815, nil], + 7815 => [0, 0, "ẇ", "ẇ", 7814, nil, 7814], + 7816 => [0, 0, "Ẉ", "Ẉ", nil, 7817, nil], + 7817 => [0, 0, "ẉ", "ẉ", 7816, nil, 7816], + 7818 => [0, 0, "Ẋ", "Ẋ", nil, 7819, nil], + 7819 => [0, 0, "ẋ", "ẋ", 7818, nil, 7818], + 7820 => [0, 0, "Ẍ", "Ẍ", nil, 7821, nil], + 7821 => [0, 0, "ẍ", "ẍ", 7820, nil, 7820], + 7822 => [0, 0, "Ẏ", "Ẏ", nil, 7823, nil], + 7823 => [0, 0, "ẏ", "ẏ", 7822, nil, 7822], + 7824 => [0, 0, "Ẑ", "Ẑ", nil, 7825, nil], + 7825 => [0, 0, "ẑ", "ẑ", 7824, nil, 7824], + 7826 => [0, 0, "Ẓ", "Ẓ", nil, 7827, nil], + 7827 => [0, 0, "ẓ", "ẓ", 7826, nil, 7826], + 7828 => [0, 0, "Ẕ", "Ẕ", nil, 7829, nil], + 7829 => [0, 0, "ẕ", "ẕ", 7828, nil, 7828], + 7830 => [0, 0, "ẖ", "ẖ", nil, nil, nil], + 7831 => [0, 0, "ẗ", "ẗ", nil, nil, nil], + 7832 => [0, 0, "ẘ", "ẘ", nil, nil, nil], + 7833 => [0, 0, "ẙ", "ẙ", nil, nil, nil], + 7834 => [0, 0, nil, "aʾ", nil, nil, nil], + 7835 => [0, 0, "ẛ", "ẛ", 7776, nil, 7776], + 7840 => [0, 0, "Ạ", "Ạ", nil, 7841, nil], + 7841 => [0, 0, "ạ", "ạ", 7840, nil, 7840], + 7842 => [0, 0, "Ả", "Ả", nil, 7843, nil], + 7843 => [0, 0, "ả", "ả", 7842, nil, 7842], + 7844 => [0, 0, "Ấ", "Ấ", nil, 7845, nil], + 7845 => [0, 0, "ấ", "ấ", 7844, nil, 7844], + 7846 => [0, 0, "Ầ", "Ầ", nil, 7847, nil], + 7847 => [0, 0, "ầ", "ầ", 7846, nil, 7846], + 7848 => [0, 0, "Ẩ", "Ẩ", nil, 7849, nil], + 7849 => [0, 0, "ẩ", "ẩ", 7848, nil, 7848], + 7850 => [0, 0, "Ẫ", "Ẫ", nil, 7851, nil], + 7851 => [0, 0, "ẫ", "ẫ", 7850, nil, 7850], + 7852 => [0, 0, "Ậ", "Ậ", nil, 7853, nil], + 7853 => [0, 0, "ậ", "ậ", 7852, nil, 7852], + 7854 => [0, 0, "Ắ", "Ắ", nil, 7855, nil], + 7855 => [0, 0, "ắ", "ắ", 7854, nil, 7854], + 7856 => [0, 0, "Ằ", "Ằ", nil, 7857, nil], + 7857 => [0, 0, "ằ", "ằ", 7856, nil, 7856], + 7858 => [0, 0, "Ẳ", "Ẳ", nil, 7859, nil], + 7859 => [0, 0, "ẳ", "ẳ", 7858, nil, 7858], + 7860 => [0, 0, "Ẵ", "Ẵ", nil, 7861, nil], + 7861 => [0, 0, "ẵ", "ẵ", 7860, nil, 7860], + 7862 => [0, 0, "Ặ", "Ặ", nil, 7863, nil], + 7863 => [0, 0, "ặ", "ặ", 7862, nil, 7862], + 7864 => [0, 0, "Ẹ", "Ẹ", nil, 7865, nil], + 7865 => [0, 0, "ẹ", "ẹ", 7864, nil, 7864], + 7866 => [0, 0, "Ẻ", "Ẻ", nil, 7867, nil], + 7867 => [0, 0, "ẻ", "ẻ", 7866, nil, 7866], + 7868 => [0, 0, "Ẽ", "Ẽ", nil, 7869, nil], + 7869 => [0, 0, "ẽ", "ẽ", 7868, nil, 7868], + 7870 => [0, 0, "Ế", "Ế", nil, 7871, nil], + 7871 => [0, 0, "ế", "ế", 7870, nil, 7870], + 7872 => [0, 0, "Ề", "Ề", nil, 7873, nil], + 7873 => [0, 0, "ề", "ề", 7872, nil, 7872], + 7874 => [0, 0, "Ể", "Ể", nil, 7875, nil], + 7875 => [0, 0, "ể", "ể", 7874, nil, 7874], + 7876 => [0, 0, "Ễ", "Ễ", nil, 7877, nil], + 7877 => [0, 0, "ễ", "ễ", 7876, nil, 7876], + 7878 => [0, 0, "Ệ", "Ệ", nil, 7879, nil], + 7879 => [0, 0, "ệ", "ệ", 7878, nil, 7878], + 7880 => [0, 0, "Ỉ", "Ỉ", nil, 7881, nil], + 7881 => [0, 0, "ỉ", "ỉ", 7880, nil, 7880], + 7882 => [0, 0, "Ị", "Ị", nil, 7883, nil], + 7883 => [0, 0, "ị", "ị", 7882, nil, 7882], + 7884 => [0, 0, "Ọ", "Ọ", nil, 7885, nil], + 7885 => [0, 0, "ọ", "ọ", 7884, nil, 7884], + 7886 => [0, 0, "Ỏ", "Ỏ", nil, 7887, nil], + 7887 => [0, 0, "ỏ", "ỏ", 7886, nil, 7886], + 7888 => [0, 0, "Ố", "Ố", nil, 7889, nil], + 7889 => [0, 0, "ố", "ố", 7888, nil, 7888], + 7890 => [0, 0, "Ồ", "Ồ", nil, 7891, nil], + 7891 => [0, 0, "ồ", "ồ", 7890, nil, 7890], + 7892 => [0, 0, "Ổ", "Ổ", nil, 7893, nil], + 7893 => [0, 0, "ổ", "ổ", 7892, nil, 7892], + 7894 => [0, 0, "Ỗ", "Ỗ", nil, 7895, nil], + 7895 => [0, 0, "ỗ", "ỗ", 7894, nil, 7894], + 7896 => [0, 0, "Ộ", "Ộ", nil, 7897, nil], + 7897 => [0, 0, "ộ", "ộ", 7896, nil, 7896], + 7898 => [0, 0, "Ớ", "Ớ", nil, 7899, nil], + 7899 => [0, 0, "ớ", "ớ", 7898, nil, 7898], + 7900 => [0, 0, "Ờ", "Ờ", nil, 7901, nil], + 7901 => [0, 0, "ờ", "ờ", 7900, nil, 7900], + 7902 => [0, 0, "Ở", "Ở", nil, 7903, nil], + 7903 => [0, 0, "ở", "ở", 7902, nil, 7902], + 7904 => [0, 0, "Ỡ", "Ỡ", nil, 7905, nil], + 7905 => [0, 0, "ỡ", "ỡ", 7904, nil, 7904], + 7906 => [0, 0, "Ợ", "Ợ", nil, 7907, nil], + 7907 => [0, 0, "ợ", "ợ", 7906, nil, 7906], + 7908 => [0, 0, "Ụ", "Ụ", nil, 7909, nil], + 7909 => [0, 0, "ụ", "ụ", 7908, nil, 7908], + 7910 => [0, 0, "Ủ", "Ủ", nil, 7911, nil], + 7911 => [0, 0, "ủ", "ủ", 7910, nil, 7910], + 7912 => [0, 0, "Ứ", "Ứ", nil, 7913, nil], + 7913 => [0, 0, "ứ", "ứ", 7912, nil, 7912], + 7914 => [0, 0, "Ừ", "Ừ", nil, 7915, nil], + 7915 => [0, 0, "ừ", "ừ", 7914, nil, 7914], + 7916 => [0, 0, "Ử", "Ử", nil, 7917, nil], + 7917 => [0, 0, "ử", "ử", 7916, nil, 7916], + 7918 => [0, 0, "Ữ", "Ữ", nil, 7919, nil], + 7919 => [0, 0, "ữ", "ữ", 7918, nil, 7918], + 7920 => [0, 0, "Ự", "Ự", nil, 7921, nil], + 7921 => [0, 0, "ự", "ự", 7920, nil, 7920], + 7922 => [0, 0, "Ỳ", "Ỳ", nil, 7923, nil], + 7923 => [0, 0, "ỳ", "ỳ", 7922, nil, 7922], + 7924 => [0, 0, "Ỵ", "Ỵ", nil, 7925, nil], + 7925 => [0, 0, "ỵ", "ỵ", 7924, nil, 7924], + 7926 => [0, 0, "Ỷ", "Ỷ", nil, 7927, nil], + 7927 => [0, 0, "ỷ", "ỷ", 7926, nil, 7926], + 7928 => [0, 0, "Ỹ", "Ỹ", nil, 7929, nil], + 7929 => [0, 0, "ỹ", "ỹ", 7928, nil, 7928], + 7936 => [0, 0, "ἀ", "ἀ", 7944, nil, 7944], + 7937 => [0, 0, "ἁ", "ἁ", 7945, nil, 7945], + 7938 => [0, 0, "ἂ", "ἂ", 7946, nil, 7946], + 7939 => [0, 0, "ἃ", "ἃ", 7947, nil, 7947], + 7940 => [0, 0, "ἄ", "ἄ", 7948, nil, 7948], + 7941 => [0, 0, "ἅ", "ἅ", 7949, nil, 7949], + 7942 => [0, 0, "ἆ", "ἆ", 7950, nil, 7950], + 7943 => [0, 0, "ἇ", "ἇ", 7951, nil, 7951], + 7944 => [0, 0, "Ἀ", "Ἀ", nil, 7936, nil], + 7945 => [0, 0, "Ἁ", "Ἁ", nil, 7937, nil], + 7946 => [0, 0, "Ἂ", "Ἂ", nil, 7938, nil], + 7947 => [0, 0, "Ἃ", "Ἃ", nil, 7939, nil], + 7948 => [0, 0, "Ἄ", "Ἄ", nil, 7940, nil], + 7949 => [0, 0, "Ἅ", "Ἅ", nil, 7941, nil], + 7950 => [0, 0, "Ἆ", "Ἆ", nil, 7942, nil], + 7951 => [0, 0, "Ἇ", "Ἇ", nil, 7943, nil], + 7952 => [0, 0, "ἐ", "ἐ", 7960, nil, 7960], + 7953 => [0, 0, "ἑ", "ἑ", 7961, nil, 7961], + 7954 => [0, 0, "ἒ", "ἒ", 7962, nil, 7962], + 7955 => [0, 0, "ἓ", "ἓ", 7963, nil, 7963], + 7956 => [0, 0, "ἔ", "ἔ", 7964, nil, 7964], + 7957 => [0, 0, "ἕ", "ἕ", 7965, nil, 7965], + 7960 => [0, 0, "Ἐ", "Ἐ", nil, 7952, nil], + 7961 => [0, 0, "Ἑ", "Ἑ", nil, 7953, nil], + 7962 => [0, 0, "Ἒ", "Ἒ", nil, 7954, nil], + 7963 => [0, 0, "Ἓ", "Ἓ", nil, 7955, nil], + 7964 => [0, 0, "Ἔ", "Ἔ", nil, 7956, nil], + 7965 => [0, 0, "Ἕ", "Ἕ", nil, 7957, nil], + 7968 => [0, 0, "ἠ", "ἠ", 7976, nil, 7976], + 7969 => [0, 0, "ἡ", "ἡ", 7977, nil, 7977], + 7970 => [0, 0, "ἢ", "ἢ", 7978, nil, 7978], + 7971 => [0, 0, "ἣ", "ἣ", 7979, nil, 7979], + 7972 => [0, 0, "ἤ", "ἤ", 7980, nil, 7980], + 7973 => [0, 0, "ἥ", "ἥ", 7981, nil, 7981], + 7974 => [0, 0, "ἦ", "ἦ", 7982, nil, 7982], + 7975 => [0, 0, "ἧ", "ἧ", 7983, nil, 7983], + 7976 => [0, 0, "Ἠ", "Ἠ", nil, 7968, nil], + 7977 => [0, 0, "Ἡ", "Ἡ", nil, 7969, nil], + 7978 => [0, 0, "Ἢ", "Ἢ", nil, 7970, nil], + 7979 => [0, 0, "Ἣ", "Ἣ", nil, 7971, nil], + 7980 => [0, 0, "Ἤ", "Ἤ", nil, 7972, nil], + 7981 => [0, 0, "Ἥ", "Ἥ", nil, 7973, nil], + 7982 => [0, 0, "Ἦ", "Ἦ", nil, 7974, nil], + 7983 => [0, 0, "Ἧ", "Ἧ", nil, 7975, nil], + 7984 => [0, 0, "ἰ", "ἰ", 7992, nil, 7992], + 7985 => [0, 0, "ἱ", "ἱ", 7993, nil, 7993], + 7986 => [0, 0, "ἲ", "ἲ", 7994, nil, 7994], + 7987 => [0, 0, "ἳ", "ἳ", 7995, nil, 7995], + 7988 => [0, 0, "ἴ", "ἴ", 7996, nil, 7996], + 7989 => [0, 0, "ἵ", "ἵ", 7997, nil, 7997], + 7990 => [0, 0, "ἶ", "ἶ", 7998, nil, 7998], + 7991 => [0, 0, "ἷ", "ἷ", 7999, nil, 7999], + 7992 => [0, 0, "Ἰ", "Ἰ", nil, 7984, nil], + 7993 => [0, 0, "Ἱ", "Ἱ", nil, 7985, nil], + 7994 => [0, 0, "Ἲ", "Ἲ", nil, 7986, nil], + 7995 => [0, 0, "Ἳ", "Ἳ", nil, 7987, nil], + 7996 => [0, 0, "Ἴ", "Ἴ", nil, 7988, nil], + 7997 => [0, 0, "Ἵ", "Ἵ", nil, 7989, nil], + 7998 => [0, 0, "Ἶ", "Ἶ", nil, 7990, nil], + 7999 => [0, 0, "Ἷ", "Ἷ", nil, 7991, nil], + 8000 => [0, 0, "ὀ", "ὀ", 8008, nil, 8008], + 8001 => [0, 0, "ὁ", "ὁ", 8009, nil, 8009], + 8002 => [0, 0, "ὂ", "ὂ", 8010, nil, 8010], + 8003 => [0, 0, "ὃ", "ὃ", 8011, nil, 8011], + 8004 => [0, 0, "ὄ", "ὄ", 8012, nil, 8012], + 8005 => [0, 0, "ὅ", "ὅ", 8013, nil, 8013], + 8008 => [0, 0, "Ὀ", "Ὀ", nil, 8000, nil], + 8009 => [0, 0, "Ὁ", "Ὁ", nil, 8001, nil], + 8010 => [0, 0, "Ὂ", "Ὂ", nil, 8002, nil], + 8011 => [0, 0, "Ὃ", "Ὃ", nil, 8003, nil], + 8012 => [0, 0, "Ὄ", "Ὄ", nil, 8004, nil], + 8013 => [0, 0, "Ὅ", "Ὅ", nil, 8005, nil], + 8016 => [0, 0, "ὐ", "ὐ", nil, nil, nil], + 8017 => [0, 0, "ὑ", "ὑ", 8025, nil, 8025], + 8018 => [0, 0, "ὒ", "ὒ", nil, nil, nil], + 8019 => [0, 0, "ὓ", "ὓ", 8027, nil, 8027], + 8020 => [0, 0, "ὔ", "ὔ", nil, nil, nil], + 8021 => [0, 0, "ὕ", "ὕ", 8029, nil, 8029], + 8022 => [0, 0, "ὖ", "ὖ", nil, nil, nil], + 8023 => [0, 0, "ὗ", "ὗ", 8031, nil, 8031], + 8025 => [0, 0, "Ὑ", "Ὑ", nil, 8017, nil], + 8027 => [0, 0, "Ὓ", "Ὓ", nil, 8019, nil], + 8029 => [0, 0, "Ὕ", "Ὕ", nil, 8021, nil], + 8031 => [0, 0, "Ὗ", "Ὗ", nil, 8023, nil], + 8032 => [0, 0, "ὠ", "ὠ", 8040, nil, 8040], + 8033 => [0, 0, "ὡ", "ὡ", 8041, nil, 8041], + 8034 => [0, 0, "ὢ", "ὢ", 8042, nil, 8042], + 8035 => [0, 0, "ὣ", "ὣ", 8043, nil, 8043], + 8036 => [0, 0, "ὤ", "ὤ", 8044, nil, 8044], + 8037 => [0, 0, "ὥ", "ὥ", 8045, nil, 8045], + 8038 => [0, 0, "ὦ", "ὦ", 8046, nil, 8046], + 8039 => [0, 0, "ὧ", "ὧ", 8047, nil, 8047], + 8040 => [0, 0, "Ὠ", "Ὠ", nil, 8032, nil], + 8041 => [0, 0, "Ὡ", "Ὡ", nil, 8033, nil], + 8042 => [0, 0, "Ὢ", "Ὢ", nil, 8034, nil], + 8043 => [0, 0, "Ὣ", "Ὣ", nil, 8035, nil], + 8044 => [0, 0, "Ὤ", "Ὤ", nil, 8036, nil], + 8045 => [0, 0, "Ὥ", "Ὥ", nil, 8037, nil], + 8046 => [0, 0, "Ὦ", "Ὦ", nil, 8038, nil], + 8047 => [0, 0, "Ὧ", "Ὧ", nil, 8039, nil], + 8048 => [0, 0, "ὰ", "ὰ", 8122, nil, 8122], + 8049 => [0, 2, "ά", "ά", 8123, nil, 8123], + 8050 => [0, 0, "ὲ", "ὲ", 8136, nil, 8136], + 8051 => [0, 2, "έ", "έ", 8137, nil, 8137], + 8052 => [0, 0, "ὴ", "ὴ", 8138, nil, 8138], + 8053 => [0, 2, "ή", "ή", 8139, nil, 8139], + 8054 => [0, 0, "ὶ", "ὶ", 8154, nil, 8154], + 8055 => [0, 2, "ί", "ί", 8155, nil, 8155], + 8056 => [0, 0, "ὸ", "ὸ", 8184, nil, 8184], + 8057 => [0, 2, "ό", "ό", 8185, nil, 8185], + 8058 => [0, 0, "ὺ", "ὺ", 8170, nil, 8170], + 8059 => [0, 2, "ύ", "ύ", 8171, nil, 8171], + 8060 => [0, 0, "ὼ", "ὼ", 8186, nil, 8186], + 8061 => [0, 2, "ώ", "ώ", 8187, nil, 8187], + 8064 => [0, 0, "ᾀ", "ᾀ", 8072, nil, 8072], + 8065 => [0, 0, "ᾁ", "ᾁ", 8073, nil, 8073], + 8066 => [0, 0, "ᾂ", "ᾂ", 8074, nil, 8074], + 8067 => [0, 0, "ᾃ", "ᾃ", 8075, nil, 8075], + 8068 => [0, 0, "ᾄ", "ᾄ", 8076, nil, 8076], + 8069 => [0, 0, "ᾅ", "ᾅ", 8077, nil, 8077], + 8070 => [0, 0, "ᾆ", "ᾆ", 8078, nil, 8078], + 8071 => [0, 0, "ᾇ", "ᾇ", 8079, nil, 8079], + 8072 => [0, 0, "ᾈ", "ᾈ", nil, 8064, nil], + 8073 => [0, 0, "ᾉ", "ᾉ", nil, 8065, nil], + 8074 => [0, 0, "ᾊ", "ᾊ", nil, 8066, nil], + 8075 => [0, 0, "ᾋ", "ᾋ", nil, 8067, nil], + 8076 => [0, 0, "ᾌ", "ᾌ", nil, 8068, nil], + 8077 => [0, 0, "ᾍ", "ᾍ", nil, 8069, nil], + 8078 => [0, 0, "ᾎ", "ᾎ", nil, 8070, nil], + 8079 => [0, 0, "ᾏ", "ᾏ", nil, 8071, nil], + 8080 => [0, 0, "ᾐ", "ᾐ", 8088, nil, 8088], + 8081 => [0, 0, "ᾑ", "ᾑ", 8089, nil, 8089], + 8082 => [0, 0, "ᾒ", "ᾒ", 8090, nil, 8090], + 8083 => [0, 0, "ᾓ", "ᾓ", 8091, nil, 8091], + 8084 => [0, 0, "ᾔ", "ᾔ", 8092, nil, 8092], + 8085 => [0, 0, "ᾕ", "ᾕ", 8093, nil, 8093], + 8086 => [0, 0, "ᾖ", "ᾖ", 8094, nil, 8094], + 8087 => [0, 0, "ᾗ", "ᾗ", 8095, nil, 8095], + 8088 => [0, 0, "ᾘ", "ᾘ", nil, 8080, nil], + 8089 => [0, 0, "ᾙ", "ᾙ", nil, 8081, nil], + 8090 => [0, 0, "ᾚ", "ᾚ", nil, 8082, nil], + 8091 => [0, 0, "ᾛ", "ᾛ", nil, 8083, nil], + 8092 => [0, 0, "ᾜ", "ᾜ", nil, 8084, nil], + 8093 => [0, 0, "ᾝ", "ᾝ", nil, 8085, nil], + 8094 => [0, 0, "ᾞ", "ᾞ", nil, 8086, nil], + 8095 => [0, 0, "ᾟ", "ᾟ", nil, 8087, nil], + 8096 => [0, 0, "ᾠ", "ᾠ", 8104, nil, 8104], + 8097 => [0, 0, "ᾡ", "ᾡ", 8105, nil, 8105], + 8098 => [0, 0, "ᾢ", "ᾢ", 8106, nil, 8106], + 8099 => [0, 0, "ᾣ", "ᾣ", 8107, nil, 8107], + 8100 => [0, 0, "ᾤ", "ᾤ", 8108, nil, 8108], + 8101 => [0, 0, "ᾥ", "ᾥ", 8109, nil, 8109], + 8102 => [0, 0, "ᾦ", "ᾦ", 8110, nil, 8110], + 8103 => [0, 0, "ᾧ", "ᾧ", 8111, nil, 8111], + 8104 => [0, 0, "ᾨ", "ᾨ", nil, 8096, nil], + 8105 => [0, 0, "ᾩ", "ᾩ", nil, 8097, nil], + 8106 => [0, 0, "ᾪ", "ᾪ", nil, 8098, nil], + 8107 => [0, 0, "ᾫ", "ᾫ", nil, 8099, nil], + 8108 => [0, 0, "ᾬ", "ᾬ", nil, 8100, nil], + 8109 => [0, 0, "ᾭ", "ᾭ", nil, 8101, nil], + 8110 => [0, 0, "ᾮ", "ᾮ", nil, 8102, nil], + 8111 => [0, 0, "ᾯ", "ᾯ", nil, 8103, nil], + 8112 => [0, 0, "ᾰ", "ᾰ", 8120, nil, 8120], + 8113 => [0, 0, "ᾱ", "ᾱ", 8121, nil, 8121], + 8114 => [0, 0, "ᾲ", "ᾲ", nil, nil, nil], + 8115 => [0, 0, "ᾳ", "ᾳ", 8124, nil, 8124], + 8116 => [0, 0, "ᾴ", "ᾴ", nil, nil, nil], + 8118 => [0, 0, "ᾶ", "ᾶ", nil, nil, nil], + 8119 => [0, 0, "ᾷ", "ᾷ", nil, nil, nil], + 8120 => [0, 0, "Ᾰ", "Ᾰ", nil, 8112, nil], + 8121 => [0, 0, "Ᾱ", "Ᾱ", nil, 8113, nil], + 8122 => [0, 0, "Ὰ", "Ὰ", nil, 8048, nil], + 8123 => [0, 2, "Ά", "Ά", nil, 8049, nil], + 8124 => [0, 0, "ᾼ", "ᾼ", nil, 8115, nil], + 8125 => [0, 0, nil, " ̓", nil, nil, nil], + 8126 => [0, 2, "ι", "ι", 921, nil, 921], + 8127 => [0, 0, nil, " ̓", nil, nil, nil], + 8128 => [0, 0, nil, " ͂", nil, nil, nil], + 8129 => [0, 0, "῁", "῁", nil, nil, nil], + 8130 => [0, 0, "ῂ", "ῂ", nil, nil, nil], + 8131 => [0, 0, "ῃ", "ῃ", 8140, nil, 8140], + 8132 => [0, 0, "ῄ", "ῄ", nil, nil, nil], + 8134 => [0, 0, "ῆ", "ῆ", nil, nil, nil], + 8135 => [0, 0, "ῇ", "ῇ", nil, nil, nil], + 8136 => [0, 0, "Ὲ", "Ὲ", nil, 8050, nil], + 8137 => [0, 2, "Έ", "Έ", nil, 8051, nil], + 8138 => [0, 0, "Ὴ", "Ὴ", nil, 8052, nil], + 8139 => [0, 2, "Ή", "Ή", nil, 8053, nil], + 8140 => [0, 0, "ῌ", "ῌ", nil, 8131, nil], + 8141 => [0, 0, "῍", "῍", nil, nil, nil], + 8142 => [0, 0, "῎", "῎", nil, nil, nil], + 8143 => [0, 0, "῏", "῏", nil, nil, nil], + 8144 => [0, 0, "ῐ", "ῐ", 8152, nil, 8152], + 8145 => [0, 0, "ῑ", "ῑ", 8153, nil, 8153], + 8146 => [0, 0, "ῒ", "ῒ", nil, nil, nil], + 8147 => [0, 2, "ΐ", "ΐ", nil, nil, nil], + 8150 => [0, 0, "ῖ", "ῖ", nil, nil, nil], + 8151 => [0, 0, "ῗ", "ῗ", nil, nil, nil], + 8152 => [0, 0, "Ῐ", "Ῐ", nil, 8144, nil], + 8153 => [0, 0, "Ῑ", "Ῑ", nil, 8145, nil], + 8154 => [0, 0, "Ὶ", "Ὶ", nil, 8054, nil], + 8155 => [0, 2, "Ί", "Ί", nil, 8055, nil], + 8157 => [0, 0, "῝", "῝", nil, nil, nil], + 8158 => [0, 0, "῞", "῞", nil, nil, nil], + 8159 => [0, 0, "῟", "῟", nil, nil, nil], + 8160 => [0, 0, "ῠ", "ῠ", 8168, nil, 8168], + 8161 => [0, 0, "ῡ", "ῡ", 8169, nil, 8169], + 8162 => [0, 0, "ῢ", "ῢ", nil, nil, nil], + 8163 => [0, 2, "ΰ", "ΰ", nil, nil, nil], + 8164 => [0, 0, "ῤ", "ῤ", nil, nil, nil], + 8165 => [0, 0, "ῥ", "ῥ", 8172, nil, 8172], + 8166 => [0, 0, "ῦ", "ῦ", nil, nil, nil], + 8167 => [0, 0, "ῧ", "ῧ", nil, nil, nil], + 8168 => [0, 0, "Ῠ", "Ῠ", nil, 8160, nil], + 8169 => [0, 0, "Ῡ", "Ῡ", nil, 8161, nil], + 8170 => [0, 0, "Ὺ", "Ὺ", nil, 8058, nil], + 8171 => [0, 2, "Ύ", "Ύ", nil, 8059, nil], + 8172 => [0, 0, "Ῥ", "Ῥ", nil, 8165, nil], + 8173 => [0, 0, "῭", "῭", nil, nil, nil], + 8174 => [0, 2, "΅", "΅", nil, nil, nil], + 8175 => [0, 2, "`", "`", nil, nil, nil], + 8178 => [0, 0, "ῲ", "ῲ", nil, nil, nil], + 8179 => [0, 0, "ῳ", "ῳ", 8188, nil, 8188], + 8180 => [0, 0, "ῴ", "ῴ", nil, nil, nil], + 8182 => [0, 0, "ῶ", "ῶ", nil, nil, nil], + 8183 => [0, 0, "ῷ", "ῷ", nil, nil, nil], + 8184 => [0, 0, "Ὸ", "Ὸ", nil, 8056, nil], + 8185 => [0, 2, "Ό", "Ό", nil, 8057, nil], + 8186 => [0, 0, "Ὼ", "Ὼ", nil, 8060, nil], + 8187 => [0, 2, "Ώ", "Ώ", nil, 8061, nil], + 8188 => [0, 0, "ῼ", "ῼ", nil, 8179, nil], + 8189 => [0, 2, "´", "´", nil, nil, nil], + 8190 => [0, 0, nil, " ̔", nil, nil, nil], + 8192 => [0, 2, " ", " ", nil, nil, nil], + 8193 => [0, 2, " ", " ", nil, nil, nil], + 8194 => [0, 0, nil, " ", nil, nil, nil], + 8195 => [0, 0, nil, " ", nil, nil, nil], + 8196 => [0, 0, nil, " ", nil, nil, nil], + 8197 => [0, 0, nil, " ", nil, nil, nil], + 8198 => [0, 0, nil, " ", nil, nil, nil], + 8199 => [0, 0, nil, " ", nil, nil, nil], + 8200 => [0, 0, nil, " ", nil, nil, nil], + 8201 => [0, 0, nil, " ", nil, nil, nil], + 8202 => [0, 0, nil, " ", nil, nil, nil], + 8209 => [0, 0, nil, "‐", nil, nil, nil], + 8215 => [0, 0, nil, " ̳", nil, nil, nil], + 8228 => [0, 0, nil, ".", nil, nil, nil], + 8229 => [0, 0, nil, "..", nil, nil, nil], + 8230 => [0, 0, nil, "...", nil, nil, nil], + 8239 => [0, 0, nil, " ", nil, nil, nil], + 8243 => [0, 0, nil, "′′", nil, nil, nil], + 8244 => [0, 0, nil, "′′′", nil, nil, nil], + 8246 => [0, 0, nil, "‵‵", nil, nil, nil], + 8247 => [0, 0, nil, "‵‵‵", nil, nil, nil], + 8252 => [0, 0, nil, "!!", nil, nil, nil], + 8254 => [0, 0, nil, " ̅", nil, nil, nil], + 8264 => [0, 0, nil, "?!", nil, nil, nil], + 8265 => [0, 0, nil, "!?", nil, nil, nil], + 8304 => [0, 0, nil, "0", nil, nil, nil], + 8308 => [0, 0, nil, "4", nil, nil, nil], + 8309 => [0, 0, nil, "5", nil, nil, nil], + 8310 => [0, 0, nil, "6", nil, nil, nil], + 8311 => [0, 0, nil, "7", nil, nil, nil], + 8312 => [0, 0, nil, "8", nil, nil, nil], + 8313 => [0, 0, nil, "9", nil, nil, nil], + 8314 => [0, 0, nil, "+", nil, nil, nil], + 8315 => [0, 0, nil, "−", nil, nil, nil], + 8316 => [0, 0, nil, "=", nil, nil, nil], + 8317 => [0, 0, nil, "(", nil, nil, nil], + 8318 => [0, 0, nil, ")", nil, nil, nil], + 8319 => [0, 0, nil, "n", nil, nil, nil], + 8320 => [0, 0, nil, "0", nil, nil, nil], + 8321 => [0, 0, nil, "1", nil, nil, nil], + 8322 => [0, 0, nil, "2", nil, nil, nil], + 8323 => [0, 0, nil, "3", nil, nil, nil], + 8324 => [0, 0, nil, "4", nil, nil, nil], + 8325 => [0, 0, nil, "5", nil, nil, nil], + 8326 => [0, 0, nil, "6", nil, nil, nil], + 8327 => [0, 0, nil, "7", nil, nil, nil], + 8328 => [0, 0, nil, "8", nil, nil, nil], + 8329 => [0, 0, nil, "9", nil, nil, nil], + 8330 => [0, 0, nil, "+", nil, nil, nil], + 8331 => [0, 0, nil, "−", nil, nil, nil], + 8332 => [0, 0, nil, "=", nil, nil, nil], + 8333 => [0, 0, nil, "(", nil, nil, nil], + 8334 => [0, 0, nil, ")", nil, nil, nil], + 8360 => [0, 0, nil, "Rs", nil, nil, nil], + 8400 => [230, 0, nil, nil, nil, nil, nil], + 8401 => [230, 0, nil, nil, nil, nil, nil], + 8402 => [1, 0, nil, nil, nil, nil, nil], + 8403 => [1, 0, nil, nil, nil, nil, nil], + 8404 => [230, 0, nil, nil, nil, nil, nil], + 8405 => [230, 0, nil, nil, nil, nil, nil], + 8406 => [230, 0, nil, nil, nil, nil, nil], + 8407 => [230, 0, nil, nil, nil, nil, nil], + 8408 => [1, 0, nil, nil, nil, nil, nil], + 8409 => [1, 0, nil, nil, nil, nil, nil], + 8410 => [1, 0, nil, nil, nil, nil, nil], + 8411 => [230, 0, nil, nil, nil, nil, nil], + 8412 => [230, 0, nil, nil, nil, nil, nil], + 8417 => [230, 0, nil, nil, nil, nil, nil], + 8448 => [0, 0, nil, "a/c", nil, nil, nil], + 8449 => [0, 0, nil, "a/s", nil, nil, nil], + 8450 => [0, 0, nil, "C", nil, nil, nil], + 8451 => [0, 0, nil, "°C", nil, nil, nil], + 8453 => [0, 0, nil, "c/o", nil, nil, nil], + 8454 => [0, 0, nil, "c/u", nil, nil, nil], + 8455 => [0, 0, nil, "Ɛ", nil, nil, nil], + 8457 => [0, 0, nil, "°F", nil, nil, nil], + 8458 => [0, 0, nil, "g", nil, nil, nil], + 8459 => [0, 0, nil, "H", nil, nil, nil], + 8460 => [0, 0, nil, "H", nil, nil, nil], + 8461 => [0, 0, nil, "H", nil, nil, nil], + 8462 => [0, 0, nil, "h", nil, nil, nil], + 8463 => [0, 0, nil, "ħ", nil, nil, nil], + 8464 => [0, 0, nil, "I", nil, nil, nil], + 8465 => [0, 0, nil, "I", nil, nil, nil], + 8466 => [0, 0, nil, "L", nil, nil, nil], + 8467 => [0, 0, nil, "l", nil, nil, nil], + 8469 => [0, 0, nil, "N", nil, nil, nil], + 8470 => [0, 0, nil, "No", nil, nil, nil], + 8473 => [0, 0, nil, "P", nil, nil, nil], + 8474 => [0, 0, nil, "Q", nil, nil, nil], + 8475 => [0, 0, nil, "R", nil, nil, nil], + 8476 => [0, 0, nil, "R", nil, nil, nil], + 8477 => [0, 0, nil, "R", nil, nil, nil], + 8480 => [0, 0, nil, "SM", nil, nil, nil], + 8481 => [0, 0, nil, "TEL", nil, nil, nil], + 8482 => [0, 0, nil, "TM", nil, nil, nil], + 8484 => [0, 0, nil, "Z", nil, nil, nil], + 8486 => [0, 2, "Ω", "Ω", nil, 969, nil], + 8488 => [0, 0, nil, "Z", nil, nil, nil], + 8490 => [0, 2, "K", "K", nil, 107, nil], + 8491 => [0, 2, "Å", "Å", nil, 229, nil], + 8492 => [0, 0, nil, "B", nil, nil, nil], + 8493 => [0, 0, nil, "C", nil, nil, nil], + 8495 => [0, 0, nil, "e", nil, nil, nil], + 8496 => [0, 0, nil, "E", nil, nil, nil], + 8497 => [0, 0, nil, "F", nil, nil, nil], + 8499 => [0, 0, nil, "M", nil, nil, nil], + 8500 => [0, 0, nil, "o", nil, nil, nil], + 8501 => [0, 0, nil, "א", nil, nil, nil], + 8502 => [0, 0, nil, "ב", nil, nil, nil], + 8503 => [0, 0, nil, "ג", nil, nil, nil], + 8504 => [0, 0, nil, "ד", nil, nil, nil], + 8505 => [0, 0, nil, "i", nil, nil, nil], + 8531 => [0, 0, nil, "1⁄3", nil, nil, nil], + 8532 => [0, 0, nil, "2⁄3", nil, nil, nil], + 8533 => [0, 0, nil, "1⁄5", nil, nil, nil], + 8534 => [0, 0, nil, "2⁄5", nil, nil, nil], + 8535 => [0, 0, nil, "3⁄5", nil, nil, nil], + 8536 => [0, 0, nil, "4⁄5", nil, nil, nil], + 8537 => [0, 0, nil, "1⁄6", nil, nil, nil], + 8538 => [0, 0, nil, "5⁄6", nil, nil, nil], + 8539 => [0, 0, nil, "1⁄8", nil, nil, nil], + 8540 => [0, 0, nil, "3⁄8", nil, nil, nil], + 8541 => [0, 0, nil, "5⁄8", nil, nil, nil], + 8542 => [0, 0, nil, "7⁄8", nil, nil, nil], + 8543 => [0, 0, nil, "1⁄", nil, nil, nil], + 8544 => [0, 0, nil, "I", nil, 8560, nil], + 8545 => [0, 0, nil, "II", nil, 8561, nil], + 8546 => [0, 0, nil, "III", nil, 8562, nil], + 8547 => [0, 0, nil, "IV", nil, 8563, nil], + 8548 => [0, 0, nil, "V", nil, 8564, nil], + 8549 => [0, 0, nil, "VI", nil, 8565, nil], + 8550 => [0, 0, nil, "VII", nil, 8566, nil], + 8551 => [0, 0, nil, "VIII", nil, 8567, nil], + 8552 => [0, 0, nil, "IX", nil, 8568, nil], + 8553 => [0, 0, nil, "X", nil, 8569, nil], + 8554 => [0, 0, nil, "XI", nil, 8570, nil], + 8555 => [0, 0, nil, "XII", nil, 8571, nil], + 8556 => [0, 0, nil, "L", nil, 8572, nil], + 8557 => [0, 0, nil, "C", nil, 8573, nil], + 8558 => [0, 0, nil, "D", nil, 8574, nil], + 8559 => [0, 0, nil, "M", nil, 8575, nil], + 8560 => [0, 0, nil, "i", 8544, nil, 8544], + 8561 => [0, 0, nil, "ii", 8545, nil, 8545], + 8562 => [0, 0, nil, "iii", 8546, nil, 8546], + 8563 => [0, 0, nil, "iv", 8547, nil, 8547], + 8564 => [0, 0, nil, "v", 8548, nil, 8548], + 8565 => [0, 0, nil, "vi", 8549, nil, 8549], + 8566 => [0, 0, nil, "vii", 8550, nil, 8550], + 8567 => [0, 0, nil, "viii", 8551, nil, 8551], + 8568 => [0, 0, nil, "ix", 8552, nil, 8552], + 8569 => [0, 0, nil, "x", 8553, nil, 8553], + 8570 => [0, 0, nil, "xi", 8554, nil, 8554], + 8571 => [0, 0, nil, "xii", 8555, nil, 8555], + 8572 => [0, 0, nil, "l", 8556, nil, 8556], + 8573 => [0, 0, nil, "c", 8557, nil, 8557], + 8574 => [0, 0, nil, "d", 8558, nil, 8558], + 8575 => [0, 0, nil, "m", 8559, nil, 8559], + 8602 => [0, 0, "↚", "↚", nil, nil, nil], + 8603 => [0, 0, "↛", "↛", nil, nil, nil], + 8622 => [0, 0, "↮", "↮", nil, nil, nil], + 8653 => [0, 0, "⇍", "⇍", nil, nil, nil], + 8654 => [0, 0, "⇎", "⇎", nil, nil, nil], + 8655 => [0, 0, "⇏", "⇏", nil, nil, nil], + 8708 => [0, 0, "∄", "∄", nil, nil, nil], + 8713 => [0, 0, "∉", "∉", nil, nil, nil], + 8716 => [0, 0, "∌", "∌", nil, nil, nil], + 8740 => [0, 0, "∤", "∤", nil, nil, nil], + 8742 => [0, 0, "∦", "∦", nil, nil, nil], + 8748 => [0, 0, nil, "∫∫", nil, nil, nil], + 8749 => [0, 0, nil, "∫∫∫", nil, nil, nil], + 8751 => [0, 0, nil, "∮∮", nil, nil, nil], + 8752 => [0, 0, nil, "∮∮∮", nil, nil, nil], + 8769 => [0, 0, "≁", "≁", nil, nil, nil], + 8772 => [0, 0, "≄", "≄", nil, nil, nil], + 8775 => [0, 0, "≇", "≇", nil, nil, nil], + 8777 => [0, 0, "≉", "≉", nil, nil, nil], + 8800 => [0, 0, "≠", "≠", nil, nil, nil], + 8802 => [0, 0, "≢", "≢", nil, nil, nil], + 8813 => [0, 0, "≭", "≭", nil, nil, nil], + 8814 => [0, 0, "≮", "≮", nil, nil, nil], + 8815 => [0, 0, "≯", "≯", nil, nil, nil], + 8816 => [0, 0, "≰", "≰", nil, nil, nil], + 8817 => [0, 0, "≱", "≱", nil, nil, nil], + 8820 => [0, 0, "≴", "≴", nil, nil, nil], + 8821 => [0, 0, "≵", "≵", nil, nil, nil], + 8824 => [0, 0, "≸", "≸", nil, nil, nil], + 8825 => [0, 0, "≹", "≹", nil, nil, nil], + 8832 => [0, 0, "⊀", "⊀", nil, nil, nil], + 8833 => [0, 0, "⊁", "⊁", nil, nil, nil], + 8836 => [0, 0, "⊄", "⊄", nil, nil, nil], + 8837 => [0, 0, "⊅", "⊅", nil, nil, nil], + 8840 => [0, 0, "⊈", "⊈", nil, nil, nil], + 8841 => [0, 0, "⊉", "⊉", nil, nil, nil], + 8876 => [0, 0, "⊬", "⊬", nil, nil, nil], + 8877 => [0, 0, "⊭", "⊭", nil, nil, nil], + 8878 => [0, 0, "⊮", "⊮", nil, nil, nil], + 8879 => [0, 0, "⊯", "⊯", nil, nil, nil], + 8928 => [0, 0, "⋠", "⋠", nil, nil, nil], + 8929 => [0, 0, "⋡", "⋡", nil, nil, nil], + 8930 => [0, 0, "⋢", "⋢", nil, nil, nil], + 8931 => [0, 0, "⋣", "⋣", nil, nil, nil], + 8938 => [0, 0, "⋪", "⋪", nil, nil, nil], + 8939 => [0, 0, "⋫", "⋫", nil, nil, nil], + 8940 => [0, 0, "⋬", "⋬", nil, nil, nil], + 8941 => [0, 0, "⋭", "⋭", nil, nil, nil], + 9001 => [0, 2, "〈", "〈", nil, nil, nil], + 9002 => [0, 2, "〉", "〉", nil, nil, nil], + 9312 => [0, 0, nil, "1", nil, nil, nil], + 9313 => [0, 0, nil, "2", nil, nil, nil], + 9314 => [0, 0, nil, "3", nil, nil, nil], + 9315 => [0, 0, nil, "4", nil, nil, nil], + 9316 => [0, 0, nil, "5", nil, nil, nil], + 9317 => [0, 0, nil, "6", nil, nil, nil], + 9318 => [0, 0, nil, "7", nil, nil, nil], + 9319 => [0, 0, nil, "8", nil, nil, nil], + 9320 => [0, 0, nil, "9", nil, nil, nil], + 9321 => [0, 0, nil, "10", nil, nil, nil], + 9322 => [0, 0, nil, "11", nil, nil, nil], + 9323 => [0, 0, nil, "12", nil, nil, nil], + 9324 => [0, 0, nil, "13", nil, nil, nil], + 9325 => [0, 0, nil, "14", nil, nil, nil], + 9326 => [0, 0, nil, "15", nil, nil, nil], + 9327 => [0, 0, nil, "16", nil, nil, nil], + 9328 => [0, 0, nil, "17", nil, nil, nil], + 9329 => [0, 0, nil, "18", nil, nil, nil], + 9330 => [0, 0, nil, "19", nil, nil, nil], + 9331 => [0, 0, nil, "20", nil, nil, nil], + 9332 => [0, 0, nil, "(1)", nil, nil, nil], + 9333 => [0, 0, nil, "(2)", nil, nil, nil], + 9334 => [0, 0, nil, "(3)", nil, nil, nil], + 9335 => [0, 0, nil, "(4)", nil, nil, nil], + 9336 => [0, 0, nil, "(5)", nil, nil, nil], + 9337 => [0, 0, nil, "(6)", nil, nil, nil], + 9338 => [0, 0, nil, "(7)", nil, nil, nil], + 9339 => [0, 0, nil, "(8)", nil, nil, nil], + 9340 => [0, 0, nil, "(9)", nil, nil, nil], + 9341 => [0, 0, nil, "(10)", nil, nil, nil], + 9342 => [0, 0, nil, "(11)", nil, nil, nil], + 9343 => [0, 0, nil, "(12)", nil, nil, nil], + 9344 => [0, 0, nil, "(13)", nil, nil, nil], + 9345 => [0, 0, nil, "(14)", nil, nil, nil], + 9346 => [0, 0, nil, "(15)", nil, nil, nil], + 9347 => [0, 0, nil, "(16)", nil, nil, nil], + 9348 => [0, 0, nil, "(17)", nil, nil, nil], + 9349 => [0, 0, nil, "(18)", nil, nil, nil], + 9350 => [0, 0, nil, "(19)", nil, nil, nil], + 9351 => [0, 0, nil, "(20)", nil, nil, nil], + 9352 => [0, 0, nil, "1.", nil, nil, nil], + 9353 => [0, 0, nil, "2.", nil, nil, nil], + 9354 => [0, 0, nil, "3.", nil, nil, nil], + 9355 => [0, 0, nil, "4.", nil, nil, nil], + 9356 => [0, 0, nil, "5.", nil, nil, nil], + 9357 => [0, 0, nil, "6.", nil, nil, nil], + 9358 => [0, 0, nil, "7.", nil, nil, nil], + 9359 => [0, 0, nil, "8.", nil, nil, nil], + 9360 => [0, 0, nil, "9.", nil, nil, nil], + 9361 => [0, 0, nil, "10.", nil, nil, nil], + 9362 => [0, 0, nil, "11.", nil, nil, nil], + 9363 => [0, 0, nil, "12.", nil, nil, nil], + 9364 => [0, 0, nil, "13.", nil, nil, nil], + 9365 => [0, 0, nil, "14.", nil, nil, nil], + 9366 => [0, 0, nil, "15.", nil, nil, nil], + 9367 => [0, 0, nil, "16.", nil, nil, nil], + 9368 => [0, 0, nil, "17.", nil, nil, nil], + 9369 => [0, 0, nil, "18.", nil, nil, nil], + 9370 => [0, 0, nil, "19.", nil, nil, nil], + 9371 => [0, 0, nil, "20.", nil, nil, nil], + 9372 => [0, 0, nil, "(a)", nil, nil, nil], + 9373 => [0, 0, nil, "(b)", nil, nil, nil], + 9374 => [0, 0, nil, "(c)", nil, nil, nil], + 9375 => [0, 0, nil, "(d)", nil, nil, nil], + 9376 => [0, 0, nil, "(e)", nil, nil, nil], + 9377 => [0, 0, nil, "(f)", nil, nil, nil], + 9378 => [0, 0, nil, "(g)", nil, nil, nil], + 9379 => [0, 0, nil, "(h)", nil, nil, nil], + 9380 => [0, 0, nil, "(i)", nil, nil, nil], + 9381 => [0, 0, nil, "(j)", nil, nil, nil], + 9382 => [0, 0, nil, "(k)", nil, nil, nil], + 9383 => [0, 0, nil, "(l)", nil, nil, nil], + 9384 => [0, 0, nil, "(m)", nil, nil, nil], + 9385 => [0, 0, nil, "(n)", nil, nil, nil], + 9386 => [0, 0, nil, "(o)", nil, nil, nil], + 9387 => [0, 0, nil, "(p)", nil, nil, nil], + 9388 => [0, 0, nil, "(q)", nil, nil, nil], + 9389 => [0, 0, nil, "(r)", nil, nil, nil], + 9390 => [0, 0, nil, "(s)", nil, nil, nil], + 9391 => [0, 0, nil, "(t)", nil, nil, nil], + 9392 => [0, 0, nil, "(u)", nil, nil, nil], + 9393 => [0, 0, nil, "(v)", nil, nil, nil], + 9394 => [0, 0, nil, "(w)", nil, nil, nil], + 9395 => [0, 0, nil, "(x)", nil, nil, nil], + 9396 => [0, 0, nil, "(y)", nil, nil, nil], + 9397 => [0, 0, nil, "(z)", nil, nil, nil], + 9398 => [0, 0, nil, "A", nil, 9424, nil], + 9399 => [0, 0, nil, "B", nil, 9425, nil], + 9400 => [0, 0, nil, "C", nil, 9426, nil], + 9401 => [0, 0, nil, "D", nil, 9427, nil], + 9402 => [0, 0, nil, "E", nil, 9428, nil], + 9403 => [0, 0, nil, "F", nil, 9429, nil], + 9404 => [0, 0, nil, "G", nil, 9430, nil], + 9405 => [0, 0, nil, "H", nil, 9431, nil], + 9406 => [0, 0, nil, "I", nil, 9432, nil], + 9407 => [0, 0, nil, "J", nil, 9433, nil], + 9408 => [0, 0, nil, "K", nil, 9434, nil], + 9409 => [0, 0, nil, "L", nil, 9435, nil], + 9410 => [0, 0, nil, "M", nil, 9436, nil], + 9411 => [0, 0, nil, "N", nil, 9437, nil], + 9412 => [0, 0, nil, "O", nil, 9438, nil], + 9413 => [0, 0, nil, "P", nil, 9439, nil], + 9414 => [0, 0, nil, "Q", nil, 9440, nil], + 9415 => [0, 0, nil, "R", nil, 9441, nil], + 9416 => [0, 0, nil, "S", nil, 9442, nil], + 9417 => [0, 0, nil, "T", nil, 9443, nil], + 9418 => [0, 0, nil, "U", nil, 9444, nil], + 9419 => [0, 0, nil, "V", nil, 9445, nil], + 9420 => [0, 0, nil, "W", nil, 9446, nil], + 9421 => [0, 0, nil, "X", nil, 9447, nil], + 9422 => [0, 0, nil, "Y", nil, 9448, nil], + 9423 => [0, 0, nil, "Z", nil, 9449, nil], + 9424 => [0, 0, nil, "a", 9398, nil, 9398], + 9425 => [0, 0, nil, "b", 9399, nil, 9399], + 9426 => [0, 0, nil, "c", 9400, nil, 9400], + 9427 => [0, 0, nil, "d", 9401, nil, 9401], + 9428 => [0, 0, nil, "e", 9402, nil, 9402], + 9429 => [0, 0, nil, "f", 9403, nil, 9403], + 9430 => [0, 0, nil, "g", 9404, nil, 9404], + 9431 => [0, 0, nil, "h", 9405, nil, 9405], + 9432 => [0, 0, nil, "i", 9406, nil, 9406], + 9433 => [0, 0, nil, "j", 9407, nil, 9407], + 9434 => [0, 0, nil, "k", 9408, nil, 9408], + 9435 => [0, 0, nil, "l", 9409, nil, 9409], + 9436 => [0, 0, nil, "m", 9410, nil, 9410], + 9437 => [0, 0, nil, "n", 9411, nil, 9411], + 9438 => [0, 0, nil, "o", 9412, nil, 9412], + 9439 => [0, 0, nil, "p", 9413, nil, 9413], + 9440 => [0, 0, nil, "q", 9414, nil, 9414], + 9441 => [0, 0, nil, "r", 9415, nil, 9415], + 9442 => [0, 0, nil, "s", 9416, nil, 9416], + 9443 => [0, 0, nil, "t", 9417, nil, 9417], + 9444 => [0, 0, nil, "u", 9418, nil, 9418], + 9445 => [0, 0, nil, "v", 9419, nil, 9419], + 9446 => [0, 0, nil, "w", 9420, nil, 9420], + 9447 => [0, 0, nil, "x", 9421, nil, 9421], + 9448 => [0, 0, nil, "y", 9422, nil, 9422], + 9449 => [0, 0, nil, "z", 9423, nil, 9423], + 9450 => [0, 0, nil, "0", nil, nil, nil], + 11935 => [0, 0, nil, "母", nil, nil, nil], + 12019 => [0, 0, nil, "龟", nil, nil, nil], + 12032 => [0, 0, nil, "一", nil, nil, nil], + 12033 => [0, 0, nil, "丨", nil, nil, nil], + 12034 => [0, 0, nil, "丶", nil, nil, nil], + 12035 => [0, 0, nil, "丿", nil, nil, nil], + 12036 => [0, 0, nil, "乙", nil, nil, nil], + 12037 => [0, 0, nil, "亅", nil, nil, nil], + 12038 => [0, 0, nil, "二", nil, nil, nil], + 12039 => [0, 0, nil, "亠", nil, nil, nil], + 12040 => [0, 0, nil, "人", nil, nil, nil], + 12041 => [0, 0, nil, "儿", nil, nil, nil], + 12042 => [0, 0, nil, "入", nil, nil, nil], + 12043 => [0, 0, nil, "八", nil, nil, nil], + 12044 => [0, 0, nil, "冂", nil, nil, nil], + 12045 => [0, 0, nil, "冖", nil, nil, nil], + 12046 => [0, 0, nil, "冫", nil, nil, nil], + 12047 => [0, 0, nil, "几", nil, nil, nil], + 12048 => [0, 0, nil, "凵", nil, nil, nil], + 12049 => [0, 0, nil, "刀", nil, nil, nil], + 12050 => [0, 0, nil, "力", nil, nil, nil], + 12051 => [0, 0, nil, "勹", nil, nil, nil], + 12052 => [0, 0, nil, "匕", nil, nil, nil], + 12053 => [0, 0, nil, "匚", nil, nil, nil], + 12054 => [0, 0, nil, "匸", nil, nil, nil], + 12055 => [0, 0, nil, "十", nil, nil, nil], + 12056 => [0, 0, nil, "卜", nil, nil, nil], + 12057 => [0, 0, nil, "卩", nil, nil, nil], + 12058 => [0, 0, nil, "厂", nil, nil, nil], + 12059 => [0, 0, nil, "厶", nil, nil, nil], + 12060 => [0, 0, nil, "又", nil, nil, nil], + 12061 => [0, 0, nil, "口", nil, nil, nil], + 12062 => [0, 0, nil, "囗", nil, nil, nil], + 12063 => [0, 0, nil, "土", nil, nil, nil], + 12064 => [0, 0, nil, "士", nil, nil, nil], + 12065 => [0, 0, nil, "夂", nil, nil, nil], + 12066 => [0, 0, nil, "夊", nil, nil, nil], + 12067 => [0, 0, nil, "夕", nil, nil, nil], + 12068 => [0, 0, nil, "大", nil, nil, nil], + 12069 => [0, 0, nil, "女", nil, nil, nil], + 12070 => [0, 0, nil, "子", nil, nil, nil], + 12071 => [0, 0, nil, "宀", nil, nil, nil], + 12072 => [0, 0, nil, "寸", nil, nil, nil], + 12073 => [0, 0, nil, "小", nil, nil, nil], + 12074 => [0, 0, nil, "尢", nil, nil, nil], + 12075 => [0, 0, nil, "尸", nil, nil, nil], + 12076 => [0, 0, nil, "屮", nil, nil, nil], + 12077 => [0, 0, nil, "山", nil, nil, nil], + 12078 => [0, 0, nil, "巛", nil, nil, nil], + 12079 => [0, 0, nil, "工", nil, nil, nil], + 12080 => [0, 0, nil, "己", nil, nil, nil], + 12081 => [0, 0, nil, "巾", nil, nil, nil], + 12082 => [0, 0, nil, "干", nil, nil, nil], + 12083 => [0, 0, nil, "幺", nil, nil, nil], + 12084 => [0, 0, nil, "广", nil, nil, nil], + 12085 => [0, 0, nil, "廴", nil, nil, nil], + 12086 => [0, 0, nil, "廾", nil, nil, nil], + 12087 => [0, 0, nil, "弋", nil, nil, nil], + 12088 => [0, 0, nil, "弓", nil, nil, nil], + 12089 => [0, 0, nil, "彐", nil, nil, nil], + 12090 => [0, 0, nil, "彡", nil, nil, nil], + 12091 => [0, 0, nil, "彳", nil, nil, nil], + 12092 => [0, 0, nil, "心", nil, nil, nil], + 12093 => [0, 0, nil, "戈", nil, nil, nil], + 12094 => [0, 0, nil, "戶", nil, nil, nil], + 12095 => [0, 0, nil, "手", nil, nil, nil], + 12096 => [0, 0, nil, "支", nil, nil, nil], + 12097 => [0, 0, nil, "攴", nil, nil, nil], + 12098 => [0, 0, nil, "文", nil, nil, nil], + 12099 => [0, 0, nil, "斗", nil, nil, nil], + 12100 => [0, 0, nil, "斤", nil, nil, nil], + 12101 => [0, 0, nil, "方", nil, nil, nil], + 12102 => [0, 0, nil, "无", nil, nil, nil], + 12103 => [0, 0, nil, "日", nil, nil, nil], + 12104 => [0, 0, nil, "曰", nil, nil, nil], + 12105 => [0, 0, nil, "月", nil, nil, nil], + 12106 => [0, 0, nil, "木", nil, nil, nil], + 12107 => [0, 0, nil, "欠", nil, nil, nil], + 12108 => [0, 0, nil, "止", nil, nil, nil], + 12109 => [0, 0, nil, "歹", nil, nil, nil], + 12110 => [0, 0, nil, "殳", nil, nil, nil], + 12111 => [0, 0, nil, "毋", nil, nil, nil], + 12112 => [0, 0, nil, "比", nil, nil, nil], + 12113 => [0, 0, nil, "毛", nil, nil, nil], + 12114 => [0, 0, nil, "氏", nil, nil, nil], + 12115 => [0, 0, nil, "气", nil, nil, nil], + 12116 => [0, 0, nil, "水", nil, nil, nil], + 12117 => [0, 0, nil, "火", nil, nil, nil], + 12118 => [0, 0, nil, "爪", nil, nil, nil], + 12119 => [0, 0, nil, "父", nil, nil, nil], + 12120 => [0, 0, nil, "爻", nil, nil, nil], + 12121 => [0, 0, nil, "爿", nil, nil, nil], + 12122 => [0, 0, nil, "片", nil, nil, nil], + 12123 => [0, 0, nil, "牙", nil, nil, nil], + 12124 => [0, 0, nil, "牛", nil, nil, nil], + 12125 => [0, 0, nil, "犬", nil, nil, nil], + 12126 => [0, 0, nil, "玄", nil, nil, nil], + 12127 => [0, 0, nil, "玉", nil, nil, nil], + 12128 => [0, 0, nil, "瓜", nil, nil, nil], + 12129 => [0, 0, nil, "瓦", nil, nil, nil], + 12130 => [0, 0, nil, "甘", nil, nil, nil], + 12131 => [0, 0, nil, "生", nil, nil, nil], + 12132 => [0, 0, nil, "用", nil, nil, nil], + 12133 => [0, 0, nil, "田", nil, nil, nil], + 12134 => [0, 0, nil, "疋", nil, nil, nil], + 12135 => [0, 0, nil, "疒", nil, nil, nil], + 12136 => [0, 0, nil, "癶", nil, nil, nil], + 12137 => [0, 0, nil, "白", nil, nil, nil], + 12138 => [0, 0, nil, "皮", nil, nil, nil], + 12139 => [0, 0, nil, "皿", nil, nil, nil], + 12140 => [0, 0, nil, "目", nil, nil, nil], + 12141 => [0, 0, nil, "矛", nil, nil, nil], + 12142 => [0, 0, nil, "矢", nil, nil, nil], + 12143 => [0, 0, nil, "石", nil, nil, nil], + 12144 => [0, 0, nil, "示", nil, nil, nil], + 12145 => [0, 0, nil, "禸", nil, nil, nil], + 12146 => [0, 0, nil, "禾", nil, nil, nil], + 12147 => [0, 0, nil, "穴", nil, nil, nil], + 12148 => [0, 0, nil, "立", nil, nil, nil], + 12149 => [0, 0, nil, "竹", nil, nil, nil], + 12150 => [0, 0, nil, "米", nil, nil, nil], + 12151 => [0, 0, nil, "糸", nil, nil, nil], + 12152 => [0, 0, nil, "缶", nil, nil, nil], + 12153 => [0, 0, nil, "网", nil, nil, nil], + 12154 => [0, 0, nil, "羊", nil, nil, nil], + 12155 => [0, 0, nil, "羽", nil, nil, nil], + 12156 => [0, 0, nil, "老", nil, nil, nil], + 12157 => [0, 0, nil, "而", nil, nil, nil], + 12158 => [0, 0, nil, "耒", nil, nil, nil], + 12159 => [0, 0, nil, "耳", nil, nil, nil], + 12160 => [0, 0, nil, "聿", nil, nil, nil], + 12161 => [0, 0, nil, "肉", nil, nil, nil], + 12162 => [0, 0, nil, "臣", nil, nil, nil], + 12163 => [0, 0, nil, "自", nil, nil, nil], + 12164 => [0, 0, nil, "至", nil, nil, nil], + 12165 => [0, 0, nil, "臼", nil, nil, nil], + 12166 => [0, 0, nil, "舌", nil, nil, nil], + 12167 => [0, 0, nil, "舛", nil, nil, nil], + 12168 => [0, 0, nil, "舟", nil, nil, nil], + 12169 => [0, 0, nil, "艮", nil, nil, nil], + 12170 => [0, 0, nil, "色", nil, nil, nil], + 12171 => [0, 0, nil, "艸", nil, nil, nil], + 12172 => [0, 0, nil, "虍", nil, nil, nil], + 12173 => [0, 0, nil, "虫", nil, nil, nil], + 12174 => [0, 0, nil, "血", nil, nil, nil], + 12175 => [0, 0, nil, "行", nil, nil, nil], + 12176 => [0, 0, nil, "衣", nil, nil, nil], + 12177 => [0, 0, nil, "襾", nil, nil, nil], + 12178 => [0, 0, nil, "見", nil, nil, nil], + 12179 => [0, 0, nil, "角", nil, nil, nil], + 12180 => [0, 0, nil, "言", nil, nil, nil], + 12181 => [0, 0, nil, "谷", nil, nil, nil], + 12182 => [0, 0, nil, "豆", nil, nil, nil], + 12183 => [0, 0, nil, "豕", nil, nil, nil], + 12184 => [0, 0, nil, "豸", nil, nil, nil], + 12185 => [0, 0, nil, "貝", nil, nil, nil], + 12186 => [0, 0, nil, "赤", nil, nil, nil], + 12187 => [0, 0, nil, "走", nil, nil, nil], + 12188 => [0, 0, nil, "足", nil, nil, nil], + 12189 => [0, 0, nil, "身", nil, nil, nil], + 12190 => [0, 0, nil, "車", nil, nil, nil], + 12191 => [0, 0, nil, "辛", nil, nil, nil], + 12192 => [0, 0, nil, "辰", nil, nil, nil], + 12193 => [0, 0, nil, "辵", nil, nil, nil], + 12194 => [0, 0, nil, "邑", nil, nil, nil], + 12195 => [0, 0, nil, "酉", nil, nil, nil], + 12196 => [0, 0, nil, "釆", nil, nil, nil], + 12197 => [0, 0, nil, "里", nil, nil, nil], + 12198 => [0, 0, nil, "金", nil, nil, nil], + 12199 => [0, 0, nil, "長", nil, nil, nil], + 12200 => [0, 0, nil, "門", nil, nil, nil], + 12201 => [0, 0, nil, "阜", nil, nil, nil], + 12202 => [0, 0, nil, "隶", nil, nil, nil], + 12203 => [0, 0, nil, "隹", nil, nil, nil], + 12204 => [0, 0, nil, "雨", nil, nil, nil], + 12205 => [0, 0, nil, "靑", nil, nil, nil], + 12206 => [0, 0, nil, "非", nil, nil, nil], + 12207 => [0, 0, nil, "面", nil, nil, nil], + 12208 => [0, 0, nil, "革", nil, nil, nil], + 12209 => [0, 0, nil, "韋", nil, nil, nil], + 12210 => [0, 0, nil, "韭", nil, nil, nil], + 12211 => [0, 0, nil, "音", nil, nil, nil], + 12212 => [0, 0, nil, "頁", nil, nil, nil], + 12213 => [0, 0, nil, "風", nil, nil, nil], + 12214 => [0, 0, nil, "飛", nil, nil, nil], + 12215 => [0, 0, nil, "食", nil, nil, nil], + 12216 => [0, 0, nil, "首", nil, nil, nil], + 12217 => [0, 0, nil, "香", nil, nil, nil], + 12218 => [0, 0, nil, "馬", nil, nil, nil], + 12219 => [0, 0, nil, "骨", nil, nil, nil], + 12220 => [0, 0, nil, "高", nil, nil, nil], + 12221 => [0, 0, nil, "髟", nil, nil, nil], + 12222 => [0, 0, nil, "鬥", nil, nil, nil], + 12223 => [0, 0, nil, "鬯", nil, nil, nil], + 12224 => [0, 0, nil, "鬲", nil, nil, nil], + 12225 => [0, 0, nil, "鬼", nil, nil, nil], + 12226 => [0, 0, nil, "魚", nil, nil, nil], + 12227 => [0, 0, nil, "鳥", nil, nil, nil], + 12228 => [0, 0, nil, "鹵", nil, nil, nil], + 12229 => [0, 0, nil, "鹿", nil, nil, nil], + 12230 => [0, 0, nil, "麥", nil, nil, nil], + 12231 => [0, 0, nil, "麻", nil, nil, nil], + 12232 => [0, 0, nil, "黃", nil, nil, nil], + 12233 => [0, 0, nil, "黍", nil, nil, nil], + 12234 => [0, 0, nil, "黑", nil, nil, nil], + 12235 => [0, 0, nil, "黹", nil, nil, nil], + 12236 => [0, 0, nil, "黽", nil, nil, nil], + 12237 => [0, 0, nil, "鼎", nil, nil, nil], + 12238 => [0, 0, nil, "鼓", nil, nil, nil], + 12239 => [0, 0, nil, "鼠", nil, nil, nil], + 12240 => [0, 0, nil, "鼻", nil, nil, nil], + 12241 => [0, 0, nil, "齊", nil, nil, nil], + 12242 => [0, 0, nil, "齒", nil, nil, nil], + 12243 => [0, 0, nil, "龍", nil, nil, nil], + 12244 => [0, 0, nil, "龜", nil, nil, nil], + 12245 => [0, 0, nil, "龠", nil, nil, nil], + 12288 => [0, 0, nil, " ", nil, nil, nil], + 12330 => [218, 0, nil, nil, nil, nil, nil], + 12331 => [228, 0, nil, nil, nil, nil, nil], + 12332 => [232, 0, nil, nil, nil, nil, nil], + 12333 => [222, 0, nil, nil, nil, nil, nil], + 12334 => [224, 0, nil, nil, nil, nil, nil], + 12335 => [224, 0, nil, nil, nil, nil, nil], + 12342 => [0, 0, nil, "〒", nil, nil, nil], + 12344 => [0, 0, nil, "十", nil, nil, nil], + 12345 => [0, 0, nil, "卄", nil, nil, nil], + 12346 => [0, 0, nil, "卅", nil, nil, nil], + 12364 => [0, 0, "が", "が", nil, nil, nil], + 12366 => [0, 0, "ぎ", "ぎ", nil, nil, nil], + 12368 => [0, 0, "ぐ", "ぐ", nil, nil, nil], + 12370 => [0, 0, "げ", "げ", nil, nil, nil], + 12372 => [0, 0, "ご", "ご", nil, nil, nil], + 12374 => [0, 0, "ざ", "ざ", nil, nil, nil], + 12376 => [0, 0, "じ", "じ", nil, nil, nil], + 12378 => [0, 0, "ず", "ず", nil, nil, nil], + 12380 => [0, 0, "ぜ", "ぜ", nil, nil, nil], + 12382 => [0, 0, "ぞ", "ぞ", nil, nil, nil], + 12384 => [0, 0, "だ", "だ", nil, nil, nil], + 12386 => [0, 0, "ぢ", "ぢ", nil, nil, nil], + 12389 => [0, 0, "づ", "づ", nil, nil, nil], + 12391 => [0, 0, "で", "で", nil, nil, nil], + 12393 => [0, 0, "ど", "ど", nil, nil, nil], + 12400 => [0, 0, "ば", "ば", nil, nil, nil], + 12401 => [0, 0, "ぱ", "ぱ", nil, nil, nil], + 12403 => [0, 0, "び", "び", nil, nil, nil], + 12404 => [0, 0, "ぴ", "ぴ", nil, nil, nil], + 12406 => [0, 0, "ぶ", "ぶ", nil, nil, nil], + 12407 => [0, 0, "ぷ", "ぷ", nil, nil, nil], + 12409 => [0, 0, "べ", "べ", nil, nil, nil], + 12410 => [0, 0, "ぺ", "ぺ", nil, nil, nil], + 12412 => [0, 0, "ぼ", "ぼ", nil, nil, nil], + 12413 => [0, 0, "ぽ", "ぽ", nil, nil, nil], + 12436 => [0, 0, "ゔ", "ゔ", nil, nil, nil], + 12441 => [8, 0, nil, nil, nil, nil, nil], + 12442 => [8, 0, nil, nil, nil, nil, nil], + 12443 => [0, 0, nil, " ゙", nil, nil, nil], + 12444 => [0, 0, nil, " ゚", nil, nil, nil], + 12446 => [0, 0, "ゞ", "ゞ", nil, nil, nil], + 12460 => [0, 0, "ガ", "ガ", nil, nil, nil], + 12462 => [0, 0, "ギ", "ギ", nil, nil, nil], + 12464 => [0, 0, "グ", "グ", nil, nil, nil], + 12466 => [0, 0, "ゲ", "ゲ", nil, nil, nil], + 12468 => [0, 0, "ゴ", "ゴ", nil, nil, nil], + 12470 => [0, 0, "ザ", "ザ", nil, nil, nil], + 12472 => [0, 0, "ジ", "ジ", nil, nil, nil], + 12474 => [0, 0, "ズ", "ズ", nil, nil, nil], + 12476 => [0, 0, "ゼ", "ゼ", nil, nil, nil], + 12478 => [0, 0, "ゾ", "ゾ", nil, nil, nil], + 12480 => [0, 0, "ダ", "ダ", nil, nil, nil], + 12482 => [0, 0, "ヂ", "ヂ", nil, nil, nil], + 12485 => [0, 0, "ヅ", "ヅ", nil, nil, nil], + 12487 => [0, 0, "デ", "デ", nil, nil, nil], + 12489 => [0, 0, "ド", "ド", nil, nil, nil], + 12496 => [0, 0, "バ", "バ", nil, nil, nil], + 12497 => [0, 0, "パ", "パ", nil, nil, nil], + 12499 => [0, 0, "ビ", "ビ", nil, nil, nil], + 12500 => [0, 0, "ピ", "ピ", nil, nil, nil], + 12502 => [0, 0, "ブ", "ブ", nil, nil, nil], + 12503 => [0, 0, "プ", "プ", nil, nil, nil], + 12505 => [0, 0, "ベ", "ベ", nil, nil, nil], + 12506 => [0, 0, "ペ", "ペ", nil, nil, nil], + 12508 => [0, 0, "ボ", "ボ", nil, nil, nil], + 12509 => [0, 0, "ポ", "ポ", nil, nil, nil], + 12532 => [0, 0, "ヴ", "ヴ", nil, nil, nil], + 12535 => [0, 0, "ヷ", "ヷ", nil, nil, nil], + 12536 => [0, 0, "ヸ", "ヸ", nil, nil, nil], + 12537 => [0, 0, "ヹ", "ヹ", nil, nil, nil], + 12538 => [0, 0, "ヺ", "ヺ", nil, nil, nil], + 12542 => [0, 0, "ヾ", "ヾ", nil, nil, nil], + 12593 => [0, 0, nil, "ᄀ", nil, nil, nil], + 12594 => [0, 0, nil, "ᄁ", nil, nil, nil], + 12595 => [0, 0, nil, "ᆪ", nil, nil, nil], + 12596 => [0, 0, nil, "ᄂ", nil, nil, nil], + 12597 => [0, 0, nil, "ᆬ", nil, nil, nil], + 12598 => [0, 0, nil, "ᆭ", nil, nil, nil], + 12599 => [0, 0, nil, "ᄃ", nil, nil, nil], + 12600 => [0, 0, nil, "ᄄ", nil, nil, nil], + 12601 => [0, 0, nil, "ᄅ", nil, nil, nil], + 12602 => [0, 0, nil, "ᆰ", nil, nil, nil], + 12603 => [0, 0, nil, "ᆱ", nil, nil, nil], + 12604 => [0, 0, nil, "ᆲ", nil, nil, nil], + 12605 => [0, 0, nil, "ᆳ", nil, nil, nil], + 12606 => [0, 0, nil, "ᆴ", nil, nil, nil], + 12607 => [0, 0, nil, "ᆵ", nil, nil, nil], + 12608 => [0, 0, nil, "ᄚ", nil, nil, nil], + 12609 => [0, 0, nil, "ᄆ", nil, nil, nil], + 12610 => [0, 0, nil, "ᄇ", nil, nil, nil], + 12611 => [0, 0, nil, "ᄈ", nil, nil, nil], + 12612 => [0, 0, nil, "ᄡ", nil, nil, nil], + 12613 => [0, 0, nil, "ᄉ", nil, nil, nil], + 12614 => [0, 0, nil, "ᄊ", nil, nil, nil], + 12615 => [0, 0, nil, "ᄋ", nil, nil, nil], + 12616 => [0, 0, nil, "ᄌ", nil, nil, nil], + 12617 => [0, 0, nil, "ᄍ", nil, nil, nil], + 12618 => [0, 0, nil, "ᄎ", nil, nil, nil], + 12619 => [0, 0, nil, "ᄏ", nil, nil, nil], + 12620 => [0, 0, nil, "ᄐ", nil, nil, nil], + 12621 => [0, 0, nil, "ᄑ", nil, nil, nil], + 12622 => [0, 0, nil, "ᄒ", nil, nil, nil], + 12623 => [0, 0, nil, "ᅡ", nil, nil, nil], + 12624 => [0, 0, nil, "ᅢ", nil, nil, nil], + 12625 => [0, 0, nil, "ᅣ", nil, nil, nil], + 12626 => [0, 0, nil, "ᅤ", nil, nil, nil], + 12627 => [0, 0, nil, "ᅥ", nil, nil, nil], + 12628 => [0, 0, nil, "ᅦ", nil, nil, nil], + 12629 => [0, 0, nil, "ᅧ", nil, nil, nil], + 12630 => [0, 0, nil, "ᅨ", nil, nil, nil], + 12631 => [0, 0, nil, "ᅩ", nil, nil, nil], + 12632 => [0, 0, nil, "ᅪ", nil, nil, nil], + 12633 => [0, 0, nil, "ᅫ", nil, nil, nil], + 12634 => [0, 0, nil, "ᅬ", nil, nil, nil], + 12635 => [0, 0, nil, "ᅭ", nil, nil, nil], + 12636 => [0, 0, nil, "ᅮ", nil, nil, nil], + 12637 => [0, 0, nil, "ᅯ", nil, nil, nil], + 12638 => [0, 0, nil, "ᅰ", nil, nil, nil], + 12639 => [0, 0, nil, "ᅱ", nil, nil, nil], + 12640 => [0, 0, nil, "ᅲ", nil, nil, nil], + 12641 => [0, 0, nil, "ᅳ", nil, nil, nil], + 12642 => [0, 0, nil, "ᅴ", nil, nil, nil], + 12643 => [0, 0, nil, "ᅵ", nil, nil, nil], + 12644 => [0, 0, nil, "ᅠ", nil, nil, nil], + 12645 => [0, 0, nil, "ᄔ", nil, nil, nil], + 12646 => [0, 0, nil, "ᄕ", nil, nil, nil], + 12647 => [0, 0, nil, "ᇇ", nil, nil, nil], + 12648 => [0, 0, nil, "ᇈ", nil, nil, nil], + 12649 => [0, 0, nil, "ᇌ", nil, nil, nil], + 12650 => [0, 0, nil, "ᇎ", nil, nil, nil], + 12651 => [0, 0, nil, "ᇓ", nil, nil, nil], + 12652 => [0, 0, nil, "ᇗ", nil, nil, nil], + 12653 => [0, 0, nil, "ᇙ", nil, nil, nil], + 12654 => [0, 0, nil, "ᄜ", nil, nil, nil], + 12655 => [0, 0, nil, "ᇝ", nil, nil, nil], + 12656 => [0, 0, nil, "ᇟ", nil, nil, nil], + 12657 => [0, 0, nil, "ᄝ", nil, nil, nil], + 12658 => [0, 0, nil, "ᄞ", nil, nil, nil], + 12659 => [0, 0, nil, "ᄠ", nil, nil, nil], + 12660 => [0, 0, nil, "ᄢ", nil, nil, nil], + 12661 => [0, 0, nil, "ᄣ", nil, nil, nil], + 12662 => [0, 0, nil, "ᄧ", nil, nil, nil], + 12663 => [0, 0, nil, "ᄩ", nil, nil, nil], + 12664 => [0, 0, nil, "ᄫ", nil, nil, nil], + 12665 => [0, 0, nil, "ᄬ", nil, nil, nil], + 12666 => [0, 0, nil, "ᄭ", nil, nil, nil], + 12667 => [0, 0, nil, "ᄮ", nil, nil, nil], + 12668 => [0, 0, nil, "ᄯ", nil, nil, nil], + 12669 => [0, 0, nil, "ᄲ", nil, nil, nil], + 12670 => [0, 0, nil, "ᄶ", nil, nil, nil], + 12671 => [0, 0, nil, "ᅀ", nil, nil, nil], + 12672 => [0, 0, nil, "ᅇ", nil, nil, nil], + 12673 => [0, 0, nil, "ᅌ", nil, nil, nil], + 12674 => [0, 0, nil, "ᇱ", nil, nil, nil], + 12675 => [0, 0, nil, "ᇲ", nil, nil, nil], + 12676 => [0, 0, nil, "ᅗ", nil, nil, nil], + 12677 => [0, 0, nil, "ᅘ", nil, nil, nil], + 12678 => [0, 0, nil, "ᅙ", nil, nil, nil], + 12679 => [0, 0, nil, "ᆄ", nil, nil, nil], + 12680 => [0, 0, nil, "ᆅ", nil, nil, nil], + 12681 => [0, 0, nil, "ᆈ", nil, nil, nil], + 12682 => [0, 0, nil, "ᆑ", nil, nil, nil], + 12683 => [0, 0, nil, "ᆒ", nil, nil, nil], + 12684 => [0, 0, nil, "ᆔ", nil, nil, nil], + 12685 => [0, 0, nil, "ᆞ", nil, nil, nil], + 12686 => [0, 0, nil, "ᆡ", nil, nil, nil], + 12690 => [0, 0, nil, "一", nil, nil, nil], + 12691 => [0, 0, nil, "二", nil, nil, nil], + 12692 => [0, 0, nil, "三", nil, nil, nil], + 12693 => [0, 0, nil, "四", nil, nil, nil], + 12694 => [0, 0, nil, "上", nil, nil, nil], + 12695 => [0, 0, nil, "中", nil, nil, nil], + 12696 => [0, 0, nil, "下", nil, nil, nil], + 12697 => [0, 0, nil, "甲", nil, nil, nil], + 12698 => [0, 0, nil, "乙", nil, nil, nil], + 12699 => [0, 0, nil, "丙", nil, nil, nil], + 12700 => [0, 0, nil, "丁", nil, nil, nil], + 12701 => [0, 0, nil, "天", nil, nil, nil], + 12702 => [0, 0, nil, "地", nil, nil, nil], + 12703 => [0, 0, nil, "人", nil, nil, nil], + 12800 => [0, 0, nil, "(ᄀ)", nil, nil, nil], + 12801 => [0, 0, nil, "(ᄂ)", nil, nil, nil], + 12802 => [0, 0, nil, "(ᄃ)", nil, nil, nil], + 12803 => [0, 0, nil, "(ᄅ)", nil, nil, nil], + 12804 => [0, 0, nil, "(ᄆ)", nil, nil, nil], + 12805 => [0, 0, nil, "(ᄇ)", nil, nil, nil], + 12806 => [0, 0, nil, "(ᄉ)", nil, nil, nil], + 12807 => [0, 0, nil, "(ᄋ)", nil, nil, nil], + 12808 => [0, 0, nil, "(ᄌ)", nil, nil, nil], + 12809 => [0, 0, nil, "(ᄎ)", nil, nil, nil], + 12810 => [0, 0, nil, "(ᄏ)", nil, nil, nil], + 12811 => [0, 0, nil, "(ᄐ)", nil, nil, nil], + 12812 => [0, 0, nil, "(ᄑ)", nil, nil, nil], + 12813 => [0, 0, nil, "(ᄒ)", nil, nil, nil], + 12814 => [0, 0, nil, "(가)", nil, nil, nil], + 12815 => [0, 0, nil, "(나)", nil, nil, nil], + 12816 => [0, 0, nil, "(다)", nil, nil, nil], + 12817 => [0, 0, nil, "(라)", nil, nil, nil], + 12818 => [0, 0, nil, "(마)", nil, nil, nil], + 12819 => [0, 0, nil, "(바)", nil, nil, nil], + 12820 => [0, 0, nil, "(사)", nil, nil, nil], + 12821 => [0, 0, nil, "(아)", nil, nil, nil], + 12822 => [0, 0, nil, "(자)", nil, nil, nil], + 12823 => [0, 0, nil, "(차)", nil, nil, nil], + 12824 => [0, 0, nil, "(카)", nil, nil, nil], + 12825 => [0, 0, nil, "(타)", nil, nil, nil], + 12826 => [0, 0, nil, "(파)", nil, nil, nil], + 12827 => [0, 0, nil, "(하)", nil, nil, nil], + 12828 => [0, 0, nil, "(주)", nil, nil, nil], + 12832 => [0, 0, nil, "(一)", nil, nil, nil], + 12833 => [0, 0, nil, "(二)", nil, nil, nil], + 12834 => [0, 0, nil, "(三)", nil, nil, nil], + 12835 => [0, 0, nil, "(四)", nil, nil, nil], + 12836 => [0, 0, nil, "(五)", nil, nil, nil], + 12837 => [0, 0, nil, "(六)", nil, nil, nil], + 12838 => [0, 0, nil, "(七)", nil, nil, nil], + 12839 => [0, 0, nil, "(八)", nil, nil, nil], + 12840 => [0, 0, nil, "(九)", nil, nil, nil], + 12841 => [0, 0, nil, "(十)", nil, nil, nil], + 12842 => [0, 0, nil, "(月)", nil, nil, nil], + 12843 => [0, 0, nil, "(火)", nil, nil, nil], + 12844 => [0, 0, nil, "(水)", nil, nil, nil], + 12845 => [0, 0, nil, "(木)", nil, nil, nil], + 12846 => [0, 0, nil, "(金)", nil, nil, nil], + 12847 => [0, 0, nil, "(土)", nil, nil, nil], + 12848 => [0, 0, nil, "(日)", nil, nil, nil], + 12849 => [0, 0, nil, "(株)", nil, nil, nil], + 12850 => [0, 0, nil, "(有)", nil, nil, nil], + 12851 => [0, 0, nil, "(社)", nil, nil, nil], + 12852 => [0, 0, nil, "(名)", nil, nil, nil], + 12853 => [0, 0, nil, "(特)", nil, nil, nil], + 12854 => [0, 0, nil, "(財)", nil, nil, nil], + 12855 => [0, 0, nil, "(祝)", nil, nil, nil], + 12856 => [0, 0, nil, "(労)", nil, nil, nil], + 12857 => [0, 0, nil, "(代)", nil, nil, nil], + 12858 => [0, 0, nil, "(呼)", nil, nil, nil], + 12859 => [0, 0, nil, "(学)", nil, nil, nil], + 12860 => [0, 0, nil, "(監)", nil, nil, nil], + 12861 => [0, 0, nil, "(企)", nil, nil, nil], + 12862 => [0, 0, nil, "(資)", nil, nil, nil], + 12863 => [0, 0, nil, "(協)", nil, nil, nil], + 12864 => [0, 0, nil, "(祭)", nil, nil, nil], + 12865 => [0, 0, nil, "(休)", nil, nil, nil], + 12866 => [0, 0, nil, "(自)", nil, nil, nil], + 12867 => [0, 0, nil, "(至)", nil, nil, nil], + 12896 => [0, 0, nil, "ᄀ", nil, nil, nil], + 12897 => [0, 0, nil, "ᄂ", nil, nil, nil], + 12898 => [0, 0, nil, "ᄃ", nil, nil, nil], + 12899 => [0, 0, nil, "ᄅ", nil, nil, nil], + 12900 => [0, 0, nil, "ᄆ", nil, nil, nil], + 12901 => [0, 0, nil, "ᄇ", nil, nil, nil], + 12902 => [0, 0, nil, "ᄉ", nil, nil, nil], + 12903 => [0, 0, nil, "ᄋ", nil, nil, nil], + 12904 => [0, 0, nil, "ᄌ", nil, nil, nil], + 12905 => [0, 0, nil, "ᄎ", nil, nil, nil], + 12906 => [0, 0, nil, "ᄏ", nil, nil, nil], + 12907 => [0, 0, nil, "ᄐ", nil, nil, nil], + 12908 => [0, 0, nil, "ᄑ", nil, nil, nil], + 12909 => [0, 0, nil, "ᄒ", nil, nil, nil], + 12910 => [0, 0, nil, "가", nil, nil, nil], + 12911 => [0, 0, nil, "나", nil, nil, nil], + 12912 => [0, 0, nil, "다", nil, nil, nil], + 12913 => [0, 0, nil, "라", nil, nil, nil], + 12914 => [0, 0, nil, "마", nil, nil, nil], + 12915 => [0, 0, nil, "바", nil, nil, nil], + 12916 => [0, 0, nil, "사", nil, nil, nil], + 12917 => [0, 0, nil, "아", nil, nil, nil], + 12918 => [0, 0, nil, "자", nil, nil, nil], + 12919 => [0, 0, nil, "차", nil, nil, nil], + 12920 => [0, 0, nil, "카", nil, nil, nil], + 12921 => [0, 0, nil, "타", nil, nil, nil], + 12922 => [0, 0, nil, "파", nil, nil, nil], + 12923 => [0, 0, nil, "하", nil, nil, nil], + 12928 => [0, 0, nil, "一", nil, nil, nil], + 12929 => [0, 0, nil, "二", nil, nil, nil], + 12930 => [0, 0, nil, "三", nil, nil, nil], + 12931 => [0, 0, nil, "四", nil, nil, nil], + 12932 => [0, 0, nil, "五", nil, nil, nil], + 12933 => [0, 0, nil, "六", nil, nil, nil], + 12934 => [0, 0, nil, "七", nil, nil, nil], + 12935 => [0, 0, nil, "八", nil, nil, nil], + 12936 => [0, 0, nil, "九", nil, nil, nil], + 12937 => [0, 0, nil, "十", nil, nil, nil], + 12938 => [0, 0, nil, "月", nil, nil, nil], + 12939 => [0, 0, nil, "火", nil, nil, nil], + 12940 => [0, 0, nil, "水", nil, nil, nil], + 12941 => [0, 0, nil, "木", nil, nil, nil], + 12942 => [0, 0, nil, "金", nil, nil, nil], + 12943 => [0, 0, nil, "土", nil, nil, nil], + 12944 => [0, 0, nil, "日", nil, nil, nil], + 12945 => [0, 0, nil, "株", nil, nil, nil], + 12946 => [0, 0, nil, "有", nil, nil, nil], + 12947 => [0, 0, nil, "社", nil, nil, nil], + 12948 => [0, 0, nil, "名", nil, nil, nil], + 12949 => [0, 0, nil, "特", nil, nil, nil], + 12950 => [0, 0, nil, "財", nil, nil, nil], + 12951 => [0, 0, nil, "祝", nil, nil, nil], + 12952 => [0, 0, nil, "労", nil, nil, nil], + 12953 => [0, 0, nil, "秘", nil, nil, nil], + 12954 => [0, 0, nil, "男", nil, nil, nil], + 12955 => [0, 0, nil, "女", nil, nil, nil], + 12956 => [0, 0, nil, "適", nil, nil, nil], + 12957 => [0, 0, nil, "優", nil, nil, nil], + 12958 => [0, 0, nil, "印", nil, nil, nil], + 12959 => [0, 0, nil, "注", nil, nil, nil], + 12960 => [0, 0, nil, "項", nil, nil, nil], + 12961 => [0, 0, nil, "休", nil, nil, nil], + 12962 => [0, 0, nil, "写", nil, nil, nil], + 12963 => [0, 0, nil, "正", nil, nil, nil], + 12964 => [0, 0, nil, "上", nil, nil, nil], + 12965 => [0, 0, nil, "中", nil, nil, nil], + 12966 => [0, 0, nil, "下", nil, nil, nil], + 12967 => [0, 0, nil, "左", nil, nil, nil], + 12968 => [0, 0, nil, "右", nil, nil, nil], + 12969 => [0, 0, nil, "医", nil, nil, nil], + 12970 => [0, 0, nil, "宗", nil, nil, nil], + 12971 => [0, 0, nil, "学", nil, nil, nil], + 12972 => [0, 0, nil, "監", nil, nil, nil], + 12973 => [0, 0, nil, "企", nil, nil, nil], + 12974 => [0, 0, nil, "資", nil, nil, nil], + 12975 => [0, 0, nil, "協", nil, nil, nil], + 12976 => [0, 0, nil, "夜", nil, nil, nil], + 12992 => [0, 0, nil, "1月", nil, nil, nil], + 12993 => [0, 0, nil, "2月", nil, nil, nil], + 12994 => [0, 0, nil, "3月", nil, nil, nil], + 12995 => [0, 0, nil, "4月", nil, nil, nil], + 12996 => [0, 0, nil, "5月", nil, nil, nil], + 12997 => [0, 0, nil, "6月", nil, nil, nil], + 12998 => [0, 0, nil, "7月", nil, nil, nil], + 12999 => [0, 0, nil, "8月", nil, nil, nil], + 13000 => [0, 0, nil, "9月", nil, nil, nil], + 13001 => [0, 0, nil, "10月", nil, nil, nil], + 13002 => [0, 0, nil, "11月", nil, nil, nil], + 13003 => [0, 0, nil, "12月", nil, nil, nil], + 13008 => [0, 0, nil, "ア", nil, nil, nil], + 13009 => [0, 0, nil, "イ", nil, nil, nil], + 13010 => [0, 0, nil, "ウ", nil, nil, nil], + 13011 => [0, 0, nil, "エ", nil, nil, nil], + 13012 => [0, 0, nil, "オ", nil, nil, nil], + 13013 => [0, 0, nil, "カ", nil, nil, nil], + 13014 => [0, 0, nil, "キ", nil, nil, nil], + 13015 => [0, 0, nil, "ク", nil, nil, nil], + 13016 => [0, 0, nil, "ケ", nil, nil, nil], + 13017 => [0, 0, nil, "コ", nil, nil, nil], + 13018 => [0, 0, nil, "サ", nil, nil, nil], + 13019 => [0, 0, nil, "シ", nil, nil, nil], + 13020 => [0, 0, nil, "ス", nil, nil, nil], + 13021 => [0, 0, nil, "セ", nil, nil, nil], + 13022 => [0, 0, nil, "ソ", nil, nil, nil], + 13023 => [0, 0, nil, "タ", nil, nil, nil], + 13024 => [0, 0, nil, "チ", nil, nil, nil], + 13025 => [0, 0, nil, "ツ", nil, nil, nil], + 13026 => [0, 0, nil, "テ", nil, nil, nil], + 13027 => [0, 0, nil, "ト", nil, nil, nil], + 13028 => [0, 0, nil, "ナ", nil, nil, nil], + 13029 => [0, 0, nil, "ニ", nil, nil, nil], + 13030 => [0, 0, nil, "ヌ", nil, nil, nil], + 13031 => [0, 0, nil, "ネ", nil, nil, nil], + 13032 => [0, 0, nil, "ノ", nil, nil, nil], + 13033 => [0, 0, nil, "ハ", nil, nil, nil], + 13034 => [0, 0, nil, "ヒ", nil, nil, nil], + 13035 => [0, 0, nil, "フ", nil, nil, nil], + 13036 => [0, 0, nil, "ヘ", nil, nil, nil], + 13037 => [0, 0, nil, "ホ", nil, nil, nil], + 13038 => [0, 0, nil, "マ", nil, nil, nil], + 13039 => [0, 0, nil, "ミ", nil, nil, nil], + 13040 => [0, 0, nil, "ム", nil, nil, nil], + 13041 => [0, 0, nil, "メ", nil, nil, nil], + 13042 => [0, 0, nil, "モ", nil, nil, nil], + 13043 => [0, 0, nil, "ヤ", nil, nil, nil], + 13044 => [0, 0, nil, "ユ", nil, nil, nil], + 13045 => [0, 0, nil, "ヨ", nil, nil, nil], + 13046 => [0, 0, nil, "ラ", nil, nil, nil], + 13047 => [0, 0, nil, "リ", nil, nil, nil], + 13048 => [0, 0, nil, "ル", nil, nil, nil], + 13049 => [0, 0, nil, "レ", nil, nil, nil], + 13050 => [0, 0, nil, "ロ", nil, nil, nil], + 13051 => [0, 0, nil, "ワ", nil, nil, nil], + 13052 => [0, 0, nil, "ヰ", nil, nil, nil], + 13053 => [0, 0, nil, "ヱ", nil, nil, nil], + 13054 => [0, 0, nil, "ヲ", nil, nil, nil], + 13056 => [0, 0, nil, "アパート", nil, nil, nil], + 13057 => [0, 0, nil, "アルファ", nil, nil, nil], + 13058 => [0, 0, nil, "アンペア", nil, nil, nil], + 13059 => [0, 0, nil, "アール", nil, nil, nil], + 13060 => [0, 0, nil, "イニング", nil, nil, nil], + 13061 => [0, 0, nil, "インチ", nil, nil, nil], + 13062 => [0, 0, nil, "ウォン", nil, nil, nil], + 13063 => [0, 0, nil, "エスクード", nil, nil, nil], + 13064 => [0, 0, nil, "エーカー", nil, nil, nil], + 13065 => [0, 0, nil, "オンス", nil, nil, nil], + 13066 => [0, 0, nil, "オーム", nil, nil, nil], + 13067 => [0, 0, nil, "カイリ", nil, nil, nil], + 13068 => [0, 0, nil, "カラット", nil, nil, nil], + 13069 => [0, 0, nil, "カロリー", nil, nil, nil], + 13070 => [0, 0, nil, "ガロン", nil, nil, nil], + 13071 => [0, 0, nil, "ガンマ", nil, nil, nil], + 13072 => [0, 0, nil, "ギガ", nil, nil, nil], + 13073 => [0, 0, nil, "ギニー", nil, nil, nil], + 13074 => [0, 0, nil, "キュリー", nil, nil, nil], + 13075 => [0, 0, nil, "ギルダー", nil, nil, nil], + 13076 => [0, 0, nil, "キロ", nil, nil, nil], + 13077 => [0, 0, nil, "キログラム", nil, nil, nil], + 13078 => [0, 0, nil, "キロメートル", nil, nil, nil], + 13079 => [0, 0, nil, "キロワット", nil, nil, nil], + 13080 => [0, 0, nil, "グラム", nil, nil, nil], + 13081 => [0, 0, nil, "グラムトン", nil, nil, nil], + 13082 => [0, 0, nil, "クルゼイロ", nil, nil, nil], + 13083 => [0, 0, nil, "クローネ", nil, nil, nil], + 13084 => [0, 0, nil, "ケース", nil, nil, nil], + 13085 => [0, 0, nil, "コルナ", nil, nil, nil], + 13086 => [0, 0, nil, "コーポ", nil, nil, nil], + 13087 => [0, 0, nil, "サイクル", nil, nil, nil], + 13088 => [0, 0, nil, "サンチーム", nil, nil, nil], + 13089 => [0, 0, nil, "シリング", nil, nil, nil], + 13090 => [0, 0, nil, "センチ", nil, nil, nil], + 13091 => [0, 0, nil, "セント", nil, nil, nil], + 13092 => [0, 0, nil, "ダース", nil, nil, nil], + 13093 => [0, 0, nil, "デシ", nil, nil, nil], + 13094 => [0, 0, nil, "ドル", nil, nil, nil], + 13095 => [0, 0, nil, "トン", nil, nil, nil], + 13096 => [0, 0, nil, "ナノ", nil, nil, nil], + 13097 => [0, 0, nil, "ノット", nil, nil, nil], + 13098 => [0, 0, nil, "ハイツ", nil, nil, nil], + 13099 => [0, 0, nil, "パーセント", nil, nil, nil], + 13100 => [0, 0, nil, "パーツ", nil, nil, nil], + 13101 => [0, 0, nil, "バーレル", nil, nil, nil], + 13102 => [0, 0, nil, "ピアストル", nil, nil, nil], + 13103 => [0, 0, nil, "ピクル", nil, nil, nil], + 13104 => [0, 0, nil, "ピコ", nil, nil, nil], + 13105 => [0, 0, nil, "ビル", nil, nil, nil], + 13106 => [0, 0, nil, "ファラッド", nil, nil, nil], + 13107 => [0, 0, nil, "フィート", nil, nil, nil], + 13108 => [0, 0, nil, "ブッシェル", nil, nil, nil], + 13109 => [0, 0, nil, "フラン", nil, nil, nil], + 13110 => [0, 0, nil, "ヘクタール", nil, nil, nil], + 13111 => [0, 0, nil, "ペソ", nil, nil, nil], + 13112 => [0, 0, nil, "ペニヒ", nil, nil, nil], + 13113 => [0, 0, nil, "ヘルツ", nil, nil, nil], + 13114 => [0, 0, nil, "ペンス", nil, nil, nil], + 13115 => [0, 0, nil, "ページ", nil, nil, nil], + 13116 => [0, 0, nil, "ベータ", nil, nil, nil], + 13117 => [0, 0, nil, "ポイント", nil, nil, nil], + 13118 => [0, 0, nil, "ボルト", nil, nil, nil], + 13119 => [0, 0, nil, "ホン", nil, nil, nil], + 13120 => [0, 0, nil, "ポンド", nil, nil, nil], + 13121 => [0, 0, nil, "ホール", nil, nil, nil], + 13122 => [0, 0, nil, "ホーン", nil, nil, nil], + 13123 => [0, 0, nil, "マイクロ", nil, nil, nil], + 13124 => [0, 0, nil, "マイル", nil, nil, nil], + 13125 => [0, 0, nil, "マッハ", nil, nil, nil], + 13126 => [0, 0, nil, "マルク", nil, nil, nil], + 13127 => [0, 0, nil, "マンション", nil, nil, nil], + 13128 => [0, 0, nil, "ミクロン", nil, nil, nil], + 13129 => [0, 0, nil, "ミリ", nil, nil, nil], + 13130 => [0, 0, nil, "ミリバール", nil, nil, nil], + 13131 => [0, 0, nil, "メガ", nil, nil, nil], + 13132 => [0, 0, nil, "メガトン", nil, nil, nil], + 13133 => [0, 0, nil, "メートル", nil, nil, nil], + 13134 => [0, 0, nil, "ヤード", nil, nil, nil], + 13135 => [0, 0, nil, "ヤール", nil, nil, nil], + 13136 => [0, 0, nil, "ユアン", nil, nil, nil], + 13137 => [0, 0, nil, "リットル", nil, nil, nil], + 13138 => [0, 0, nil, "リラ", nil, nil, nil], + 13139 => [0, 0, nil, "ルピー", nil, nil, nil], + 13140 => [0, 0, nil, "ルーブル", nil, nil, nil], + 13141 => [0, 0, nil, "レム", nil, nil, nil], + 13142 => [0, 0, nil, "レントゲン", nil, nil, nil], + 13143 => [0, 0, nil, "ワット", nil, nil, nil], + 13144 => [0, 0, nil, "0点", nil, nil, nil], + 13145 => [0, 0, nil, "1点", nil, nil, nil], + 13146 => [0, 0, nil, "2点", nil, nil, nil], + 13147 => [0, 0, nil, "3点", nil, nil, nil], + 13148 => [0, 0, nil, "4点", nil, nil, nil], + 13149 => [0, 0, nil, "5点", nil, nil, nil], + 13150 => [0, 0, nil, "6点", nil, nil, nil], + 13151 => [0, 0, nil, "7点", nil, nil, nil], + 13152 => [0, 0, nil, "8点", nil, nil, nil], + 13153 => [0, 0, nil, "9点", nil, nil, nil], + 13154 => [0, 0, nil, "10点", nil, nil, nil], + 13155 => [0, 0, nil, "11点", nil, nil, nil], + 13156 => [0, 0, nil, "12点", nil, nil, nil], + 13157 => [0, 0, nil, "13点", nil, nil, nil], + 13158 => [0, 0, nil, "14点", nil, nil, nil], + 13159 => [0, 0, nil, "15点", nil, nil, nil], + 13160 => [0, 0, nil, "16点", nil, nil, nil], + 13161 => [0, 0, nil, "17点", nil, nil, nil], + 13162 => [0, 0, nil, "18点", nil, nil, nil], + 13163 => [0, 0, nil, "19点", nil, nil, nil], + 13164 => [0, 0, nil, "20点", nil, nil, nil], + 13165 => [0, 0, nil, "21点", nil, nil, nil], + 13166 => [0, 0, nil, "22点", nil, nil, nil], + 13167 => [0, 0, nil, "23点", nil, nil, nil], + 13168 => [0, 0, nil, "24点", nil, nil, nil], + 13169 => [0, 0, nil, "hPa", nil, nil, nil], + 13170 => [0, 0, nil, "da", nil, nil, nil], + 13171 => [0, 0, nil, "AU", nil, nil, nil], + 13172 => [0, 0, nil, "bar", nil, nil, nil], + 13173 => [0, 0, nil, "oV", nil, nil, nil], + 13174 => [0, 0, nil, "pc", nil, nil, nil], + 13179 => [0, 0, nil, "平成", nil, nil, nil], + 13180 => [0, 0, nil, "昭和", nil, nil, nil], + 13181 => [0, 0, nil, "大正", nil, nil, nil], + 13182 => [0, 0, nil, "明治", nil, nil, nil], + 13183 => [0, 0, nil, "株式会社", nil, nil, nil], + 13184 => [0, 0, nil, "pA", nil, nil, nil], + 13185 => [0, 0, nil, "nA", nil, nil, nil], + 13186 => [0, 0, nil, "μA", nil, nil, nil], + 13187 => [0, 0, nil, "mA", nil, nil, nil], + 13188 => [0, 0, nil, "kA", nil, nil, nil], + 13189 => [0, 0, nil, "KB", nil, nil, nil], + 13190 => [0, 0, nil, "MB", nil, nil, nil], + 13191 => [0, 0, nil, "GB", nil, nil, nil], + 13192 => [0, 0, nil, "cal", nil, nil, nil], + 13193 => [0, 0, nil, "kcal", nil, nil, nil], + 13194 => [0, 0, nil, "pF", nil, nil, nil], + 13195 => [0, 0, nil, "nF", nil, nil, nil], + 13196 => [0, 0, nil, "μF", nil, nil, nil], + 13197 => [0, 0, nil, "μg", nil, nil, nil], + 13198 => [0, 0, nil, "mg", nil, nil, nil], + 13199 => [0, 0, nil, "kg", nil, nil, nil], + 13200 => [0, 0, nil, "Hz", nil, nil, nil], + 13201 => [0, 0, nil, "kHz", nil, nil, nil], + 13202 => [0, 0, nil, "MHz", nil, nil, nil], + 13203 => [0, 0, nil, "GHz", nil, nil, nil], + 13204 => [0, 0, nil, "THz", nil, nil, nil], + 13205 => [0, 0, nil, "μℓ", nil, nil, nil], + 13206 => [0, 0, nil, "mℓ", nil, nil, nil], + 13207 => [0, 0, nil, "dℓ", nil, nil, nil], + 13208 => [0, 0, nil, "kℓ", nil, nil, nil], + 13209 => [0, 0, nil, "fm", nil, nil, nil], + 13210 => [0, 0, nil, "nm", nil, nil, nil], + 13211 => [0, 0, nil, "μm", nil, nil, nil], + 13212 => [0, 0, nil, "mm", nil, nil, nil], + 13213 => [0, 0, nil, "cm", nil, nil, nil], + 13214 => [0, 0, nil, "km", nil, nil, nil], + 13215 => [0, 0, nil, "mm²", nil, nil, nil], + 13216 => [0, 0, nil, "cm²", nil, nil, nil], + 13217 => [0, 0, nil, "m²", nil, nil, nil], + 13218 => [0, 0, nil, "km²", nil, nil, nil], + 13219 => [0, 0, nil, "mm³", nil, nil, nil], + 13220 => [0, 0, nil, "cm³", nil, nil, nil], + 13221 => [0, 0, nil, "m³", nil, nil, nil], + 13222 => [0, 0, nil, "km³", nil, nil, nil], + 13223 => [0, 0, nil, "m∕s", nil, nil, nil], + 13224 => [0, 0, nil, "m∕s²", nil, nil, nil], + 13225 => [0, 0, nil, "Pa", nil, nil, nil], + 13226 => [0, 0, nil, "kPa", nil, nil, nil], + 13227 => [0, 0, nil, "MPa", nil, nil, nil], + 13228 => [0, 0, nil, "GPa", nil, nil, nil], + 13229 => [0, 0, nil, "rad", nil, nil, nil], + 13230 => [0, 0, nil, "rad∕s", nil, nil, nil], + 13231 => [0, 0, nil, "rad∕s²", nil, nil, nil], + 13232 => [0, 0, nil, "ps", nil, nil, nil], + 13233 => [0, 0, nil, "ns", nil, nil, nil], + 13234 => [0, 0, nil, "μs", nil, nil, nil], + 13235 => [0, 0, nil, "ms", nil, nil, nil], + 13236 => [0, 0, nil, "pV", nil, nil, nil], + 13237 => [0, 0, nil, "nV", nil, nil, nil], + 13238 => [0, 0, nil, "μV", nil, nil, nil], + 13239 => [0, 0, nil, "mV", nil, nil, nil], + 13240 => [0, 0, nil, "kV", nil, nil, nil], + 13241 => [0, 0, nil, "MV", nil, nil, nil], + 13242 => [0, 0, nil, "pW", nil, nil, nil], + 13243 => [0, 0, nil, "nW", nil, nil, nil], + 13244 => [0, 0, nil, "μW", nil, nil, nil], + 13245 => [0, 0, nil, "mW", nil, nil, nil], + 13246 => [0, 0, nil, "kW", nil, nil, nil], + 13247 => [0, 0, nil, "MW", nil, nil, nil], + 13248 => [0, 0, nil, "kΩ", nil, nil, nil], + 13249 => [0, 0, nil, "MΩ", nil, nil, nil], + 13250 => [0, 0, nil, "a.m.", nil, nil, nil], + 13251 => [0, 0, nil, "Bq", nil, nil, nil], + 13252 => [0, 0, nil, "cc", nil, nil, nil], + 13253 => [0, 0, nil, "cd", nil, nil, nil], + 13254 => [0, 0, nil, "C∕kg", nil, nil, nil], + 13255 => [0, 0, nil, "Co.", nil, nil, nil], + 13256 => [0, 0, nil, "dB", nil, nil, nil], + 13257 => [0, 0, nil, "Gy", nil, nil, nil], + 13258 => [0, 0, nil, "ha", nil, nil, nil], + 13259 => [0, 0, nil, "HP", nil, nil, nil], + 13260 => [0, 0, nil, "in", nil, nil, nil], + 13261 => [0, 0, nil, "KK", nil, nil, nil], + 13262 => [0, 0, nil, "KM", nil, nil, nil], + 13263 => [0, 0, nil, "kt", nil, nil, nil], + 13264 => [0, 0, nil, "lm", nil, nil, nil], + 13265 => [0, 0, nil, "ln", nil, nil, nil], + 13266 => [0, 0, nil, "log", nil, nil, nil], + 13267 => [0, 0, nil, "lx", nil, nil, nil], + 13268 => [0, 0, nil, "mb", nil, nil, nil], + 13269 => [0, 0, nil, "mil", nil, nil, nil], + 13270 => [0, 0, nil, "mol", nil, nil, nil], + 13271 => [0, 0, nil, "PH", nil, nil, nil], + 13272 => [0, 0, nil, "p.m.", nil, nil, nil], + 13273 => [0, 0, nil, "PPM", nil, nil, nil], + 13274 => [0, 0, nil, "PR", nil, nil, nil], + 13275 => [0, 0, nil, "sr", nil, nil, nil], + 13276 => [0, 0, nil, "Sv", nil, nil, nil], + 13277 => [0, 0, nil, "Wb", nil, nil, nil], + 13280 => [0, 0, nil, "1日", nil, nil, nil], + 13281 => [0, 0, nil, "2日", nil, nil, nil], + 13282 => [0, 0, nil, "3日", nil, nil, nil], + 13283 => [0, 0, nil, "4日", nil, nil, nil], + 13284 => [0, 0, nil, "5日", nil, nil, nil], + 13285 => [0, 0, nil, "6日", nil, nil, nil], + 13286 => [0, 0, nil, "7日", nil, nil, nil], + 13287 => [0, 0, nil, "8日", nil, nil, nil], + 13288 => [0, 0, nil, "9日", nil, nil, nil], + 13289 => [0, 0, nil, "10日", nil, nil, nil], + 13290 => [0, 0, nil, "11日", nil, nil, nil], + 13291 => [0, 0, nil, "12日", nil, nil, nil], + 13292 => [0, 0, nil, "13日", nil, nil, nil], + 13293 => [0, 0, nil, "14日", nil, nil, nil], + 13294 => [0, 0, nil, "15日", nil, nil, nil], + 13295 => [0, 0, nil, "16日", nil, nil, nil], + 13296 => [0, 0, nil, "17日", nil, nil, nil], + 13297 => [0, 0, nil, "18日", nil, nil, nil], + 13298 => [0, 0, nil, "19日", nil, nil, nil], + 13299 => [0, 0, nil, "20日", nil, nil, nil], + 13300 => [0, 0, nil, "21日", nil, nil, nil], + 13301 => [0, 0, nil, "22日", nil, nil, nil], + 13302 => [0, 0, nil, "23日", nil, nil, nil], + 13303 => [0, 0, nil, "24日", nil, nil, nil], + 13304 => [0, 0, nil, "25日", nil, nil, nil], + 13305 => [0, 0, nil, "26日", nil, nil, nil], + 13306 => [0, 0, nil, "27日", nil, nil, nil], + 13307 => [0, 0, nil, "28日", nil, nil, nil], + 13308 => [0, 0, nil, "29日", nil, nil, nil], + 13309 => [0, 0, nil, "30日", nil, nil, nil], + 13310 => [0, 0, nil, "31日", nil, nil, nil], + 63744 => [0, 2, "豈", "豈", nil, nil, nil], + 63745 => [0, 2, "更", "更", nil, nil, nil], + 63746 => [0, 2, "車", "車", nil, nil, nil], + 63747 => [0, 2, "賈", "賈", nil, nil, nil], + 63748 => [0, 2, "滑", "滑", nil, nil, nil], + 63749 => [0, 2, "串", "串", nil, nil, nil], + 63750 => [0, 2, "句", "句", nil, nil, nil], + 63751 => [0, 2, "龜", "龜", nil, nil, nil], + 63752 => [0, 2, "龜", "龜", nil, nil, nil], + 63753 => [0, 2, "契", "契", nil, nil, nil], + 63754 => [0, 2, "金", "金", nil, nil, nil], + 63755 => [0, 2, "喇", "喇", nil, nil, nil], + 63756 => [0, 2, "奈", "奈", nil, nil, nil], + 63757 => [0, 2, "懶", "懶", nil, nil, nil], + 63758 => [0, 2, "癩", "癩", nil, nil, nil], + 63759 => [0, 2, "羅", "羅", nil, nil, nil], + 63760 => [0, 2, "蘿", "蘿", nil, nil, nil], + 63761 => [0, 2, "螺", "螺", nil, nil, nil], + 63762 => [0, 2, "裸", "裸", nil, nil, nil], + 63763 => [0, 2, "邏", "邏", nil, nil, nil], + 63764 => [0, 2, "樂", "樂", nil, nil, nil], + 63765 => [0, 2, "洛", "洛", nil, nil, nil], + 63766 => [0, 2, "烙", "烙", nil, nil, nil], + 63767 => [0, 2, "珞", "珞", nil, nil, nil], + 63768 => [0, 2, "落", "落", nil, nil, nil], + 63769 => [0, 2, "酪", "酪", nil, nil, nil], + 63770 => [0, 2, "駱", "駱", nil, nil, nil], + 63771 => [0, 2, "亂", "亂", nil, nil, nil], + 63772 => [0, 2, "卵", "卵", nil, nil, nil], + 63773 => [0, 2, "欄", "欄", nil, nil, nil], + 63774 => [0, 2, "爛", "爛", nil, nil, nil], + 63775 => [0, 2, "蘭", "蘭", nil, nil, nil], + 63776 => [0, 2, "鸞", "鸞", nil, nil, nil], + 63777 => [0, 2, "嵐", "嵐", nil, nil, nil], + 63778 => [0, 2, "濫", "濫", nil, nil, nil], + 63779 => [0, 2, "藍", "藍", nil, nil, nil], + 63780 => [0, 2, "襤", "襤", nil, nil, nil], + 63781 => [0, 2, "拉", "拉", nil, nil, nil], + 63782 => [0, 2, "臘", "臘", nil, nil, nil], + 63783 => [0, 2, "蠟", "蠟", nil, nil, nil], + 63784 => [0, 2, "廊", "廊", nil, nil, nil], + 63785 => [0, 2, "朗", "朗", nil, nil, nil], + 63786 => [0, 2, "浪", "浪", nil, nil, nil], + 63787 => [0, 2, "狼", "狼", nil, nil, nil], + 63788 => [0, 2, "郎", "郎", nil, nil, nil], + 63789 => [0, 2, "來", "來", nil, nil, nil], + 63790 => [0, 2, "冷", "冷", nil, nil, nil], + 63791 => [0, 2, "勞", "勞", nil, nil, nil], + 63792 => [0, 2, "擄", "擄", nil, nil, nil], + 63793 => [0, 2, "櫓", "櫓", nil, nil, nil], + 63794 => [0, 2, "爐", "爐", nil, nil, nil], + 63795 => [0, 2, "盧", "盧", nil, nil, nil], + 63796 => [0, 2, "老", "老", nil, nil, nil], + 63797 => [0, 2, "蘆", "蘆", nil, nil, nil], + 63798 => [0, 2, "虜", "虜", nil, nil, nil], + 63799 => [0, 2, "路", "路", nil, nil, nil], + 63800 => [0, 2, "露", "露", nil, nil, nil], + 63801 => [0, 2, "魯", "魯", nil, nil, nil], + 63802 => [0, 2, "鷺", "鷺", nil, nil, nil], + 63803 => [0, 2, "碌", "碌", nil, nil, nil], + 63804 => [0, 2, "祿", "祿", nil, nil, nil], + 63805 => [0, 2, "綠", "綠", nil, nil, nil], + 63806 => [0, 2, "菉", "菉", nil, nil, nil], + 63807 => [0, 2, "錄", "錄", nil, nil, nil], + 63808 => [0, 2, "鹿", "鹿", nil, nil, nil], + 63809 => [0, 2, "論", "論", nil, nil, nil], + 63810 => [0, 2, "壟", "壟", nil, nil, nil], + 63811 => [0, 2, "弄", "弄", nil, nil, nil], + 63812 => [0, 2, "籠", "籠", nil, nil, nil], + 63813 => [0, 2, "聾", "聾", nil, nil, nil], + 63814 => [0, 2, "牢", "牢", nil, nil, nil], + 63815 => [0, 2, "磊", "磊", nil, nil, nil], + 63816 => [0, 2, "賂", "賂", nil, nil, nil], + 63817 => [0, 2, "雷", "雷", nil, nil, nil], + 63818 => [0, 2, "壘", "壘", nil, nil, nil], + 63819 => [0, 2, "屢", "屢", nil, nil, nil], + 63820 => [0, 2, "樓", "樓", nil, nil, nil], + 63821 => [0, 2, "淚", "淚", nil, nil, nil], + 63822 => [0, 2, "漏", "漏", nil, nil, nil], + 63823 => [0, 2, "累", "累", nil, nil, nil], + 63824 => [0, 2, "縷", "縷", nil, nil, nil], + 63825 => [0, 2, "電", "電", nil, nil, nil], + 63826 => [0, 2, "勒", "勒", nil, nil, nil], + 63827 => [0, 2, "肋", "肋", nil, nil, nil], + 63828 => [0, 2, "凜", "凜", nil, nil, nil], + 63829 => [0, 2, "凌", "凌", nil, nil, nil], + 63830 => [0, 2, "稜", "稜", nil, nil, nil], + 63831 => [0, 2, "綾", "綾", nil, nil, nil], + 63832 => [0, 2, "菱", "菱", nil, nil, nil], + 63833 => [0, 2, "陵", "陵", nil, nil, nil], + 63834 => [0, 2, "讀", "讀", nil, nil, nil], + 63835 => [0, 2, "拏", "拏", nil, nil, nil], + 63836 => [0, 2, "樂", "樂", nil, nil, nil], + 63837 => [0, 2, "諾", "諾", nil, nil, nil], + 63838 => [0, 2, "丹", "丹", nil, nil, nil], + 63839 => [0, 2, "寧", "寧", nil, nil, nil], + 63840 => [0, 2, "怒", "怒", nil, nil, nil], + 63841 => [0, 2, "率", "率", nil, nil, nil], + 63842 => [0, 2, "異", "異", nil, nil, nil], + 63843 => [0, 2, "北", "北", nil, nil, nil], + 63844 => [0, 2, "磻", "磻", nil, nil, nil], + 63845 => [0, 2, "便", "便", nil, nil, nil], + 63846 => [0, 2, "復", "復", nil, nil, nil], + 63847 => [0, 2, "不", "不", nil, nil, nil], + 63848 => [0, 2, "泌", "泌", nil, nil, nil], + 63849 => [0, 2, "數", "數", nil, nil, nil], + 63850 => [0, 2, "索", "索", nil, nil, nil], + 63851 => [0, 2, "參", "參", nil, nil, nil], + 63852 => [0, 2, "塞", "塞", nil, nil, nil], + 63853 => [0, 2, "省", "省", nil, nil, nil], + 63854 => [0, 2, "葉", "葉", nil, nil, nil], + 63855 => [0, 2, "說", "說", nil, nil, nil], + 63856 => [0, 2, "殺", "殺", nil, nil, nil], + 63857 => [0, 2, "辰", "辰", nil, nil, nil], + 63858 => [0, 2, "沈", "沈", nil, nil, nil], + 63859 => [0, 2, "拾", "拾", nil, nil, nil], + 63860 => [0, 2, "若", "若", nil, nil, nil], + 63861 => [0, 2, "掠", "掠", nil, nil, nil], + 63862 => [0, 2, "略", "略", nil, nil, nil], + 63863 => [0, 2, "亮", "亮", nil, nil, nil], + 63864 => [0, 2, "兩", "兩", nil, nil, nil], + 63865 => [0, 2, "凉", "凉", nil, nil, nil], + 63866 => [0, 2, "梁", "梁", nil, nil, nil], + 63867 => [0, 2, "糧", "糧", nil, nil, nil], + 63868 => [0, 2, "良", "良", nil, nil, nil], + 63869 => [0, 2, "諒", "諒", nil, nil, nil], + 63870 => [0, 2, "量", "量", nil, nil, nil], + 63871 => [0, 2, "勵", "勵", nil, nil, nil], + 63872 => [0, 2, "呂", "呂", nil, nil, nil], + 63873 => [0, 2, "女", "女", nil, nil, nil], + 63874 => [0, 2, "廬", "廬", nil, nil, nil], + 63875 => [0, 2, "旅", "旅", nil, nil, nil], + 63876 => [0, 2, "濾", "濾", nil, nil, nil], + 63877 => [0, 2, "礪", "礪", nil, nil, nil], + 63878 => [0, 2, "閭", "閭", nil, nil, nil], + 63879 => [0, 2, "驪", "驪", nil, nil, nil], + 63880 => [0, 2, "麗", "麗", nil, nil, nil], + 63881 => [0, 2, "黎", "黎", nil, nil, nil], + 63882 => [0, 2, "力", "力", nil, nil, nil], + 63883 => [0, 2, "曆", "曆", nil, nil, nil], + 63884 => [0, 2, "歷", "歷", nil, nil, nil], + 63885 => [0, 2, "轢", "轢", nil, nil, nil], + 63886 => [0, 2, "年", "年", nil, nil, nil], + 63887 => [0, 2, "憐", "憐", nil, nil, nil], + 63888 => [0, 2, "戀", "戀", nil, nil, nil], + 63889 => [0, 2, "撚", "撚", nil, nil, nil], + 63890 => [0, 2, "漣", "漣", nil, nil, nil], + 63891 => [0, 2, "煉", "煉", nil, nil, nil], + 63892 => [0, 2, "璉", "璉", nil, nil, nil], + 63893 => [0, 2, "秊", "秊", nil, nil, nil], + 63894 => [0, 2, "練", "練", nil, nil, nil], + 63895 => [0, 2, "聯", "聯", nil, nil, nil], + 63896 => [0, 2, "輦", "輦", nil, nil, nil], + 63897 => [0, 2, "蓮", "蓮", nil, nil, nil], + 63898 => [0, 2, "連", "連", nil, nil, nil], + 63899 => [0, 2, "鍊", "鍊", nil, nil, nil], + 63900 => [0, 2, "列", "列", nil, nil, nil], + 63901 => [0, 2, "劣", "劣", nil, nil, nil], + 63902 => [0, 2, "咽", "咽", nil, nil, nil], + 63903 => [0, 2, "烈", "烈", nil, nil, nil], + 63904 => [0, 2, "裂", "裂", nil, nil, nil], + 63905 => [0, 2, "說", "說", nil, nil, nil], + 63906 => [0, 2, "廉", "廉", nil, nil, nil], + 63907 => [0, 2, "念", "念", nil, nil, nil], + 63908 => [0, 2, "捻", "捻", nil, nil, nil], + 63909 => [0, 2, "殮", "殮", nil, nil, nil], + 63910 => [0, 2, "簾", "簾", nil, nil, nil], + 63911 => [0, 2, "獵", "獵", nil, nil, nil], + 63912 => [0, 2, "令", "令", nil, nil, nil], + 63913 => [0, 2, "囹", "囹", nil, nil, nil], + 63914 => [0, 2, "寧", "寧", nil, nil, nil], + 63915 => [0, 2, "嶺", "嶺", nil, nil, nil], + 63916 => [0, 2, "怜", "怜", nil, nil, nil], + 63917 => [0, 2, "玲", "玲", nil, nil, nil], + 63918 => [0, 2, "瑩", "瑩", nil, nil, nil], + 63919 => [0, 2, "羚", "羚", nil, nil, nil], + 63920 => [0, 2, "聆", "聆", nil, nil, nil], + 63921 => [0, 2, "鈴", "鈴", nil, nil, nil], + 63922 => [0, 2, "零", "零", nil, nil, nil], + 63923 => [0, 2, "靈", "靈", nil, nil, nil], + 63924 => [0, 2, "領", "領", nil, nil, nil], + 63925 => [0, 2, "例", "例", nil, nil, nil], + 63926 => [0, 2, "禮", "禮", nil, nil, nil], + 63927 => [0, 2, "醴", "醴", nil, nil, nil], + 63928 => [0, 2, "隸", "隸", nil, nil, nil], + 63929 => [0, 2, "惡", "惡", nil, nil, nil], + 63930 => [0, 2, "了", "了", nil, nil, nil], + 63931 => [0, 2, "僚", "僚", nil, nil, nil], + 63932 => [0, 2, "寮", "寮", nil, nil, nil], + 63933 => [0, 2, "尿", "尿", nil, nil, nil], + 63934 => [0, 2, "料", "料", nil, nil, nil], + 63935 => [0, 2, "樂", "樂", nil, nil, nil], + 63936 => [0, 2, "燎", "燎", nil, nil, nil], + 63937 => [0, 2, "療", "療", nil, nil, nil], + 63938 => [0, 2, "蓼", "蓼", nil, nil, nil], + 63939 => [0, 2, "遼", "遼", nil, nil, nil], + 63940 => [0, 2, "龍", "龍", nil, nil, nil], + 63941 => [0, 2, "暈", "暈", nil, nil, nil], + 63942 => [0, 2, "阮", "阮", nil, nil, nil], + 63943 => [0, 2, "劉", "劉", nil, nil, nil], + 63944 => [0, 2, "杻", "杻", nil, nil, nil], + 63945 => [0, 2, "柳", "柳", nil, nil, nil], + 63946 => [0, 2, "流", "流", nil, nil, nil], + 63947 => [0, 2, "溜", "溜", nil, nil, nil], + 63948 => [0, 2, "琉", "琉", nil, nil, nil], + 63949 => [0, 2, "留", "留", nil, nil, nil], + 63950 => [0, 2, "硫", "硫", nil, nil, nil], + 63951 => [0, 2, "紐", "紐", nil, nil, nil], + 63952 => [0, 2, "類", "類", nil, nil, nil], + 63953 => [0, 2, "六", "六", nil, nil, nil], + 63954 => [0, 2, "戮", "戮", nil, nil, nil], + 63955 => [0, 2, "陸", "陸", nil, nil, nil], + 63956 => [0, 2, "倫", "倫", nil, nil, nil], + 63957 => [0, 2, "崙", "崙", nil, nil, nil], + 63958 => [0, 2, "淪", "淪", nil, nil, nil], + 63959 => [0, 2, "輪", "輪", nil, nil, nil], + 63960 => [0, 2, "律", "律", nil, nil, nil], + 63961 => [0, 2, "慄", "慄", nil, nil, nil], + 63962 => [0, 2, "栗", "栗", nil, nil, nil], + 63963 => [0, 2, "率", "率", nil, nil, nil], + 63964 => [0, 2, "隆", "隆", nil, nil, nil], + 63965 => [0, 2, "利", "利", nil, nil, nil], + 63966 => [0, 2, "吏", "吏", nil, nil, nil], + 63967 => [0, 2, "履", "履", nil, nil, nil], + 63968 => [0, 2, "易", "易", nil, nil, nil], + 63969 => [0, 2, "李", "李", nil, nil, nil], + 63970 => [0, 2, "梨", "梨", nil, nil, nil], + 63971 => [0, 2, "泥", "泥", nil, nil, nil], + 63972 => [0, 2, "理", "理", nil, nil, nil], + 63973 => [0, 2, "痢", "痢", nil, nil, nil], + 63974 => [0, 2, "罹", "罹", nil, nil, nil], + 63975 => [0, 2, "裏", "裏", nil, nil, nil], + 63976 => [0, 2, "裡", "裡", nil, nil, nil], + 63977 => [0, 2, "里", "里", nil, nil, nil], + 63978 => [0, 2, "離", "離", nil, nil, nil], + 63979 => [0, 2, "匿", "匿", nil, nil, nil], + 63980 => [0, 2, "溺", "溺", nil, nil, nil], + 63981 => [0, 2, "吝", "吝", nil, nil, nil], + 63982 => [0, 2, "燐", "燐", nil, nil, nil], + 63983 => [0, 2, "璘", "璘", nil, nil, nil], + 63984 => [0, 2, "藺", "藺", nil, nil, nil], + 63985 => [0, 2, "隣", "隣", nil, nil, nil], + 63986 => [0, 2, "鱗", "鱗", nil, nil, nil], + 63987 => [0, 2, "麟", "麟", nil, nil, nil], + 63988 => [0, 2, "林", "林", nil, nil, nil], + 63989 => [0, 2, "淋", "淋", nil, nil, nil], + 63990 => [0, 2, "臨", "臨", nil, nil, nil], + 63991 => [0, 2, "立", "立", nil, nil, nil], + 63992 => [0, 2, "笠", "笠", nil, nil, nil], + 63993 => [0, 2, "粒", "粒", nil, nil, nil], + 63994 => [0, 2, "狀", "狀", nil, nil, nil], + 63995 => [0, 2, "炙", "炙", nil, nil, nil], + 63996 => [0, 2, "識", "識", nil, nil, nil], + 63997 => [0, 2, "什", "什", nil, nil, nil], + 63998 => [0, 2, "茶", "茶", nil, nil, nil], + 63999 => [0, 2, "刺", "刺", nil, nil, nil], + 64000 => [0, 2, "切", "切", nil, nil, nil], + 64001 => [0, 2, "度", "度", nil, nil, nil], + 64002 => [0, 2, "拓", "拓", nil, nil, nil], + 64003 => [0, 2, "糖", "糖", nil, nil, nil], + 64004 => [0, 2, "宅", "宅", nil, nil, nil], + 64005 => [0, 2, "洞", "洞", nil, nil, nil], + 64006 => [0, 2, "暴", "暴", nil, nil, nil], + 64007 => [0, 2, "輻", "輻", nil, nil, nil], + 64008 => [0, 2, "行", "行", nil, nil, nil], + 64009 => [0, 2, "降", "降", nil, nil, nil], + 64010 => [0, 2, "見", "見", nil, nil, nil], + 64011 => [0, 2, "廓", "廓", nil, nil, nil], + 64012 => [0, 2, "兀", "兀", nil, nil, nil], + 64013 => [0, 2, "嗀", "嗀", nil, nil, nil], + 64016 => [0, 2, "塚", "塚", nil, nil, nil], + 64018 => [0, 2, "晴", "晴", nil, nil, nil], + 64021 => [0, 2, "凞", "凞", nil, nil, nil], + 64022 => [0, 2, "猪", "猪", nil, nil, nil], + 64023 => [0, 2, "益", "益", nil, nil, nil], + 64024 => [0, 2, "礼", "礼", nil, nil, nil], + 64025 => [0, 2, "神", "神", nil, nil, nil], + 64026 => [0, 2, "祥", "祥", nil, nil, nil], + 64027 => [0, 2, "福", "福", nil, nil, nil], + 64028 => [0, 2, "靖", "靖", nil, nil, nil], + 64029 => [0, 2, "精", "精", nil, nil, nil], + 64030 => [0, 2, "羽", "羽", nil, nil, nil], + 64032 => [0, 2, "蘒", "蘒", nil, nil, nil], + 64034 => [0, 2, "諸", "諸", nil, nil, nil], + 64037 => [0, 2, "逸", "逸", nil, nil, nil], + 64038 => [0, 2, "都", "都", nil, nil, nil], + 64042 => [0, 2, "飯", "飯", nil, nil, nil], + 64043 => [0, 2, "飼", "飼", nil, nil, nil], + 64044 => [0, 2, "館", "館", nil, nil, nil], + 64045 => [0, 2, "鶴", "鶴", nil, nil, nil], + 64256 => [0, 0, nil, "ff", nil, nil, nil], + 64257 => [0, 0, nil, "fi", nil, nil, nil], + 64258 => [0, 0, nil, "fl", nil, nil, nil], + 64259 => [0, 0, nil, "ffi", nil, nil, nil], + 64260 => [0, 0, nil, "ffl", nil, nil, nil], + 64261 => [0, 0, nil, "ſt", nil, nil, nil], + 64262 => [0, 0, nil, "st", nil, nil, nil], + 64275 => [0, 0, nil, "մն", nil, nil, nil], + 64276 => [0, 0, nil, "մե", nil, nil, nil], + 64277 => [0, 0, nil, "մի", nil, nil, nil], + 64278 => [0, 0, nil, "վն", nil, nil, nil], + 64279 => [0, 0, nil, "մխ", nil, nil, nil], + 64285 => [0, 0, "יִ", "יִ", nil, nil, nil], + 64286 => [26, 0, nil, nil, nil, nil, nil], + 64287 => [0, 1, "ײַ", "ײַ", nil, nil, nil], + 64288 => [0, 0, nil, "ע", nil, nil, nil], + 64289 => [0, 0, nil, "א", nil, nil, nil], + 64290 => [0, 0, nil, "ד", nil, nil, nil], + 64291 => [0, 0, nil, "ה", nil, nil, nil], + 64292 => [0, 0, nil, "כ", nil, nil, nil], + 64293 => [0, 0, nil, "ל", nil, nil, nil], + 64294 => [0, 0, nil, "ם", nil, nil, nil], + 64295 => [0, 0, nil, "ר", nil, nil, nil], + 64296 => [0, 0, nil, "ת", nil, nil, nil], + 64297 => [0, 0, nil, "+", nil, nil, nil], + 64298 => [0, 1, "שׁ", "שׁ", nil, nil, nil], + 64299 => [0, 1, "שׂ", "שׂ", nil, nil, nil], + 64300 => [0, 1, "שּׁ", "שּׁ", nil, nil, nil], + 64301 => [0, 1, "שּׂ", "שּׂ", nil, nil, nil], + 64302 => [0, 1, "אַ", "אַ", nil, nil, nil], + 64303 => [0, 1, "אָ", "אָ", nil, nil, nil], + 64304 => [0, 1, "אּ", "אּ", nil, nil, nil], + 64305 => [0, 1, "בּ", "בּ", nil, nil, nil], + 64306 => [0, 1, "גּ", "גּ", nil, nil, nil], + 64307 => [0, 1, "דּ", "דּ", nil, nil, nil], + 64308 => [0, 1, "הּ", "הּ", nil, nil, nil], + 64309 => [0, 1, "וּ", "וּ", nil, nil, nil], + 64310 => [0, 1, "זּ", "זּ", nil, nil, nil], + 64312 => [0, 1, "טּ", "טּ", nil, nil, nil], + 64313 => [0, 1, "יּ", "יּ", nil, nil, nil], + 64314 => [0, 1, "ךּ", "ךּ", nil, nil, nil], + 64315 => [0, 1, "כּ", "כּ", nil, nil, nil], + 64316 => [0, 1, "לּ", "לּ", nil, nil, nil], + 64318 => [0, 1, "מּ", "מּ", nil, nil, nil], + 64320 => [0, 1, "נּ", "נּ", nil, nil, nil], + 64321 => [0, 1, "סּ", "סּ", nil, nil, nil], + 64323 => [0, 1, "ףּ", "ףּ", nil, nil, nil], + 64324 => [0, 1, "פּ", "פּ", nil, nil, nil], + 64326 => [0, 1, "צּ", "צּ", nil, nil, nil], + 64327 => [0, 1, "קּ", "קּ", nil, nil, nil], + 64328 => [0, 1, "רּ", "רּ", nil, nil, nil], + 64329 => [0, 1, "שּ", "שּ", nil, nil, nil], + 64330 => [0, 1, "תּ", "תּ", nil, nil, nil], + 64331 => [0, 1, "וֹ", "וֹ", nil, nil, nil], + 64332 => [0, 1, "בֿ", "בֿ", nil, nil, nil], + 64333 => [0, 1, "כֿ", "כֿ", nil, nil, nil], + 64334 => [0, 1, "פֿ", "פֿ", nil, nil, nil], + 64335 => [0, 0, nil, "אל", nil, nil, nil], + 64336 => [0, 0, nil, "ٱ", nil, nil, nil], + 64337 => [0, 0, nil, "ٱ", nil, nil, nil], + 64338 => [0, 0, nil, "ٻ", nil, nil, nil], + 64339 => [0, 0, nil, "ٻ", nil, nil, nil], + 64340 => [0, 0, nil, "ٻ", nil, nil, nil], + 64341 => [0, 0, nil, "ٻ", nil, nil, nil], + 64342 => [0, 0, nil, "پ", nil, nil, nil], + 64343 => [0, 0, nil, "پ", nil, nil, nil], + 64344 => [0, 0, nil, "پ", nil, nil, nil], + 64345 => [0, 0, nil, "پ", nil, nil, nil], + 64346 => [0, 0, nil, "ڀ", nil, nil, nil], + 64347 => [0, 0, nil, "ڀ", nil, nil, nil], + 64348 => [0, 0, nil, "ڀ", nil, nil, nil], + 64349 => [0, 0, nil, "ڀ", nil, nil, nil], + 64350 => [0, 0, nil, "ٺ", nil, nil, nil], + 64351 => [0, 0, nil, "ٺ", nil, nil, nil], + 64352 => [0, 0, nil, "ٺ", nil, nil, nil], + 64353 => [0, 0, nil, "ٺ", nil, nil, nil], + 64354 => [0, 0, nil, "ٿ", nil, nil, nil], + 64355 => [0, 0, nil, "ٿ", nil, nil, nil], + 64356 => [0, 0, nil, "ٿ", nil, nil, nil], + 64357 => [0, 0, nil, "ٿ", nil, nil, nil], + 64358 => [0, 0, nil, "ٹ", nil, nil, nil], + 64359 => [0, 0, nil, "ٹ", nil, nil, nil], + 64360 => [0, 0, nil, "ٹ", nil, nil, nil], + 64361 => [0, 0, nil, "ٹ", nil, nil, nil], + 64362 => [0, 0, nil, "ڤ", nil, nil, nil], + 64363 => [0, 0, nil, "ڤ", nil, nil, nil], + 64364 => [0, 0, nil, "ڤ", nil, nil, nil], + 64365 => [0, 0, nil, "ڤ", nil, nil, nil], + 64366 => [0, 0, nil, "ڦ", nil, nil, nil], + 64367 => [0, 0, nil, "ڦ", nil, nil, nil], + 64368 => [0, 0, nil, "ڦ", nil, nil, nil], + 64369 => [0, 0, nil, "ڦ", nil, nil, nil], + 64370 => [0, 0, nil, "ڄ", nil, nil, nil], + 64371 => [0, 0, nil, "ڄ", nil, nil, nil], + 64372 => [0, 0, nil, "ڄ", nil, nil, nil], + 64373 => [0, 0, nil, "ڄ", nil, nil, nil], + 64374 => [0, 0, nil, "ڃ", nil, nil, nil], + 64375 => [0, 0, nil, "ڃ", nil, nil, nil], + 64376 => [0, 0, nil, "ڃ", nil, nil, nil], + 64377 => [0, 0, nil, "ڃ", nil, nil, nil], + 64378 => [0, 0, nil, "چ", nil, nil, nil], + 64379 => [0, 0, nil, "چ", nil, nil, nil], + 64380 => [0, 0, nil, "چ", nil, nil, nil], + 64381 => [0, 0, nil, "چ", nil, nil, nil], + 64382 => [0, 0, nil, "ڇ", nil, nil, nil], + 64383 => [0, 0, nil, "ڇ", nil, nil, nil], + 64384 => [0, 0, nil, "ڇ", nil, nil, nil], + 64385 => [0, 0, nil, "ڇ", nil, nil, nil], + 64386 => [0, 0, nil, "ڍ", nil, nil, nil], + 64387 => [0, 0, nil, "ڍ", nil, nil, nil], + 64388 => [0, 0, nil, "ڌ", nil, nil, nil], + 64389 => [0, 0, nil, "ڌ", nil, nil, nil], + 64390 => [0, 0, nil, "ڎ", nil, nil, nil], + 64391 => [0, 0, nil, "ڎ", nil, nil, nil], + 64392 => [0, 0, nil, "ڈ", nil, nil, nil], + 64393 => [0, 0, nil, "ڈ", nil, nil, nil], + 64394 => [0, 0, nil, "ژ", nil, nil, nil], + 64395 => [0, 0, nil, "ژ", nil, nil, nil], + 64396 => [0, 0, nil, "ڑ", nil, nil, nil], + 64397 => [0, 0, nil, "ڑ", nil, nil, nil], + 64398 => [0, 0, nil, "ک", nil, nil, nil], + 64399 => [0, 0, nil, "ک", nil, nil, nil], + 64400 => [0, 0, nil, "ک", nil, nil, nil], + 64401 => [0, 0, nil, "ک", nil, nil, nil], + 64402 => [0, 0, nil, "گ", nil, nil, nil], + 64403 => [0, 0, nil, "گ", nil, nil, nil], + 64404 => [0, 0, nil, "گ", nil, nil, nil], + 64405 => [0, 0, nil, "گ", nil, nil, nil], + 64406 => [0, 0, nil, "ڳ", nil, nil, nil], + 64407 => [0, 0, nil, "ڳ", nil, nil, nil], + 64408 => [0, 0, nil, "ڳ", nil, nil, nil], + 64409 => [0, 0, nil, "ڳ", nil, nil, nil], + 64410 => [0, 0, nil, "ڱ", nil, nil, nil], + 64411 => [0, 0, nil, "ڱ", nil, nil, nil], + 64412 => [0, 0, nil, "ڱ", nil, nil, nil], + 64413 => [0, 0, nil, "ڱ", nil, nil, nil], + 64414 => [0, 0, nil, "ں", nil, nil, nil], + 64415 => [0, 0, nil, "ں", nil, nil, nil], + 64416 => [0, 0, nil, "ڻ", nil, nil, nil], + 64417 => [0, 0, nil, "ڻ", nil, nil, nil], + 64418 => [0, 0, nil, "ڻ", nil, nil, nil], + 64419 => [0, 0, nil, "ڻ", nil, nil, nil], + 64420 => [0, 0, nil, "ۀ", nil, nil, nil], + 64421 => [0, 0, nil, "ۀ", nil, nil, nil], + 64422 => [0, 0, nil, "ہ", nil, nil, nil], + 64423 => [0, 0, nil, "ہ", nil, nil, nil], + 64424 => [0, 0, nil, "ہ", nil, nil, nil], + 64425 => [0, 0, nil, "ہ", nil, nil, nil], + 64426 => [0, 0, nil, "ھ", nil, nil, nil], + 64427 => [0, 0, nil, "ھ", nil, nil, nil], + 64428 => [0, 0, nil, "ھ", nil, nil, nil], + 64429 => [0, 0, nil, "ھ", nil, nil, nil], + 64430 => [0, 0, nil, "ے", nil, nil, nil], + 64431 => [0, 0, nil, "ے", nil, nil, nil], + 64432 => [0, 0, nil, "ۓ", nil, nil, nil], + 64433 => [0, 0, nil, "ۓ", nil, nil, nil], + 64467 => [0, 0, nil, "ڭ", nil, nil, nil], + 64468 => [0, 0, nil, "ڭ", nil, nil, nil], + 64469 => [0, 0, nil, "ڭ", nil, nil, nil], + 64470 => [0, 0, nil, "ڭ", nil, nil, nil], + 64471 => [0, 0, nil, "ۇ", nil, nil, nil], + 64472 => [0, 0, nil, "ۇ", nil, nil, nil], + 64473 => [0, 0, nil, "ۆ", nil, nil, nil], + 64474 => [0, 0, nil, "ۆ", nil, nil, nil], + 64475 => [0, 0, nil, "ۈ", nil, nil, nil], + 64476 => [0, 0, nil, "ۈ", nil, nil, nil], + 64477 => [0, 0, nil, "ٷ", nil, nil, nil], + 64478 => [0, 0, nil, "ۋ", nil, nil, nil], + 64479 => [0, 0, nil, "ۋ", nil, nil, nil], + 64480 => [0, 0, nil, "ۅ", nil, nil, nil], + 64481 => [0, 0, nil, "ۅ", nil, nil, nil], + 64482 => [0, 0, nil, "ۉ", nil, nil, nil], + 64483 => [0, 0, nil, "ۉ", nil, nil, nil], + 64484 => [0, 0, nil, "ې", nil, nil, nil], + 64485 => [0, 0, nil, "ې", nil, nil, nil], + 64486 => [0, 0, nil, "ې", nil, nil, nil], + 64487 => [0, 0, nil, "ې", nil, nil, nil], + 64488 => [0, 0, nil, "ى", nil, nil, nil], + 64489 => [0, 0, nil, "ى", nil, nil, nil], + 64490 => [0, 0, nil, "ئا", nil, nil, nil], + 64491 => [0, 0, nil, "ئا", nil, nil, nil], + 64492 => [0, 0, nil, "ئە", nil, nil, nil], + 64493 => [0, 0, nil, "ئە", nil, nil, nil], + 64494 => [0, 0, nil, "ئو", nil, nil, nil], + 64495 => [0, 0, nil, "ئو", nil, nil, nil], + 64496 => [0, 0, nil, "ئۇ", nil, nil, nil], + 64497 => [0, 0, nil, "ئۇ", nil, nil, nil], + 64498 => [0, 0, nil, "ئۆ", nil, nil, nil], + 64499 => [0, 0, nil, "ئۆ", nil, nil, nil], + 64500 => [0, 0, nil, "ئۈ", nil, nil, nil], + 64501 => [0, 0, nil, "ئۈ", nil, nil, nil], + 64502 => [0, 0, nil, "ئې", nil, nil, nil], + 64503 => [0, 0, nil, "ئې", nil, nil, nil], + 64504 => [0, 0, nil, "ئې", nil, nil, nil], + 64505 => [0, 0, nil, "ئى", nil, nil, nil], + 64506 => [0, 0, nil, "ئى", nil, nil, nil], + 64507 => [0, 0, nil, "ئى", nil, nil, nil], + 64508 => [0, 0, nil, "ی", nil, nil, nil], + 64509 => [0, 0, nil, "ی", nil, nil, nil], + 64510 => [0, 0, nil, "ی", nil, nil, nil], + 64511 => [0, 0, nil, "ی", nil, nil, nil], + 64512 => [0, 0, nil, "ئج", nil, nil, nil], + 64513 => [0, 0, nil, "ئح", nil, nil, nil], + 64514 => [0, 0, nil, "ئم", nil, nil, nil], + 64515 => [0, 0, nil, "ئى", nil, nil, nil], + 64516 => [0, 0, nil, "ئي", nil, nil, nil], + 64517 => [0, 0, nil, "بج", nil, nil, nil], + 64518 => [0, 0, nil, "بح", nil, nil, nil], + 64519 => [0, 0, nil, "بخ", nil, nil, nil], + 64520 => [0, 0, nil, "بم", nil, nil, nil], + 64521 => [0, 0, nil, "بى", nil, nil, nil], + 64522 => [0, 0, nil, "بي", nil, nil, nil], + 64523 => [0, 0, nil, "تج", nil, nil, nil], + 64524 => [0, 0, nil, "تح", nil, nil, nil], + 64525 => [0, 0, nil, "تخ", nil, nil, nil], + 64526 => [0, 0, nil, "تم", nil, nil, nil], + 64527 => [0, 0, nil, "تى", nil, nil, nil], + 64528 => [0, 0, nil, "تي", nil, nil, nil], + 64529 => [0, 0, nil, "ثج", nil, nil, nil], + 64530 => [0, 0, nil, "ثم", nil, nil, nil], + 64531 => [0, 0, nil, "ثى", nil, nil, nil], + 64532 => [0, 0, nil, "ثي", nil, nil, nil], + 64533 => [0, 0, nil, "جح", nil, nil, nil], + 64534 => [0, 0, nil, "جم", nil, nil, nil], + 64535 => [0, 0, nil, "حج", nil, nil, nil], + 64536 => [0, 0, nil, "حم", nil, nil, nil], + 64537 => [0, 0, nil, "خج", nil, nil, nil], + 64538 => [0, 0, nil, "خح", nil, nil, nil], + 64539 => [0, 0, nil, "خم", nil, nil, nil], + 64540 => [0, 0, nil, "سج", nil, nil, nil], + 64541 => [0, 0, nil, "سح", nil, nil, nil], + 64542 => [0, 0, nil, "سخ", nil, nil, nil], + 64543 => [0, 0, nil, "سم", nil, nil, nil], + 64544 => [0, 0, nil, "صح", nil, nil, nil], + 64545 => [0, 0, nil, "صم", nil, nil, nil], + 64546 => [0, 0, nil, "ضج", nil, nil, nil], + 64547 => [0, 0, nil, "ضح", nil, nil, nil], + 64548 => [0, 0, nil, "ضخ", nil, nil, nil], + 64549 => [0, 0, nil, "ضم", nil, nil, nil], + 64550 => [0, 0, nil, "طح", nil, nil, nil], + 64551 => [0, 0, nil, "طم", nil, nil, nil], + 64552 => [0, 0, nil, "ظم", nil, nil, nil], + 64553 => [0, 0, nil, "عج", nil, nil, nil], + 64554 => [0, 0, nil, "عم", nil, nil, nil], + 64555 => [0, 0, nil, "غج", nil, nil, nil], + 64556 => [0, 0, nil, "غم", nil, nil, nil], + 64557 => [0, 0, nil, "فج", nil, nil, nil], + 64558 => [0, 0, nil, "فح", nil, nil, nil], + 64559 => [0, 0, nil, "فخ", nil, nil, nil], + 64560 => [0, 0, nil, "فم", nil, nil, nil], + 64561 => [0, 0, nil, "فى", nil, nil, nil], + 64562 => [0, 0, nil, "في", nil, nil, nil], + 64563 => [0, 0, nil, "قح", nil, nil, nil], + 64564 => [0, 0, nil, "قم", nil, nil, nil], + 64565 => [0, 0, nil, "قى", nil, nil, nil], + 64566 => [0, 0, nil, "قي", nil, nil, nil], + 64567 => [0, 0, nil, "كا", nil, nil, nil], + 64568 => [0, 0, nil, "كج", nil, nil, nil], + 64569 => [0, 0, nil, "كح", nil, nil, nil], + 64570 => [0, 0, nil, "كخ", nil, nil, nil], + 64571 => [0, 0, nil, "كل", nil, nil, nil], + 64572 => [0, 0, nil, "كم", nil, nil, nil], + 64573 => [0, 0, nil, "كى", nil, nil, nil], + 64574 => [0, 0, nil, "كي", nil, nil, nil], + 64575 => [0, 0, nil, "لج", nil, nil, nil], + 64576 => [0, 0, nil, "لح", nil, nil, nil], + 64577 => [0, 0, nil, "لخ", nil, nil, nil], + 64578 => [0, 0, nil, "لم", nil, nil, nil], + 64579 => [0, 0, nil, "لى", nil, nil, nil], + 64580 => [0, 0, nil, "لي", nil, nil, nil], + 64581 => [0, 0, nil, "مج", nil, nil, nil], + 64582 => [0, 0, nil, "مح", nil, nil, nil], + 64583 => [0, 0, nil, "مخ", nil, nil, nil], + 64584 => [0, 0, nil, "مم", nil, nil, nil], + 64585 => [0, 0, nil, "مى", nil, nil, nil], + 64586 => [0, 0, nil, "مي", nil, nil, nil], + 64587 => [0, 0, nil, "نج", nil, nil, nil], + 64588 => [0, 0, nil, "نح", nil, nil, nil], + 64589 => [0, 0, nil, "نخ", nil, nil, nil], + 64590 => [0, 0, nil, "نم", nil, nil, nil], + 64591 => [0, 0, nil, "نى", nil, nil, nil], + 64592 => [0, 0, nil, "ني", nil, nil, nil], + 64593 => [0, 0, nil, "هج", nil, nil, nil], + 64594 => [0, 0, nil, "هم", nil, nil, nil], + 64595 => [0, 0, nil, "هى", nil, nil, nil], + 64596 => [0, 0, nil, "هي", nil, nil, nil], + 64597 => [0, 0, nil, "يج", nil, nil, nil], + 64598 => [0, 0, nil, "يح", nil, nil, nil], + 64599 => [0, 0, nil, "يخ", nil, nil, nil], + 64600 => [0, 0, nil, "يم", nil, nil, nil], + 64601 => [0, 0, nil, "يى", nil, nil, nil], + 64602 => [0, 0, nil, "يي", nil, nil, nil], + 64603 => [0, 0, nil, "ذٰ", nil, nil, nil], + 64604 => [0, 0, nil, "رٰ", nil, nil, nil], + 64605 => [0, 0, nil, "ىٰ", nil, nil, nil], + 64606 => [0, 0, nil, " ٌّ", nil, nil, nil], + 64607 => [0, 0, nil, " ٍّ", nil, nil, nil], + 64608 => [0, 0, nil, " َّ", nil, nil, nil], + 64609 => [0, 0, nil, " ُّ", nil, nil, nil], + 64610 => [0, 0, nil, " ِّ", nil, nil, nil], + 64611 => [0, 0, nil, " ّٰ", nil, nil, nil], + 64612 => [0, 0, nil, "ئر", nil, nil, nil], + 64613 => [0, 0, nil, "ئز", nil, nil, nil], + 64614 => [0, 0, nil, "ئم", nil, nil, nil], + 64615 => [0, 0, nil, "ئن", nil, nil, nil], + 64616 => [0, 0, nil, "ئى", nil, nil, nil], + 64617 => [0, 0, nil, "ئي", nil, nil, nil], + 64618 => [0, 0, nil, "بر", nil, nil, nil], + 64619 => [0, 0, nil, "بز", nil, nil, nil], + 64620 => [0, 0, nil, "بم", nil, nil, nil], + 64621 => [0, 0, nil, "بن", nil, nil, nil], + 64622 => [0, 0, nil, "بى", nil, nil, nil], + 64623 => [0, 0, nil, "بي", nil, nil, nil], + 64624 => [0, 0, nil, "تر", nil, nil, nil], + 64625 => [0, 0, nil, "تز", nil, nil, nil], + 64626 => [0, 0, nil, "تم", nil, nil, nil], + 64627 => [0, 0, nil, "تن", nil, nil, nil], + 64628 => [0, 0, nil, "تى", nil, nil, nil], + 64629 => [0, 0, nil, "تي", nil, nil, nil], + 64630 => [0, 0, nil, "ثر", nil, nil, nil], + 64631 => [0, 0, nil, "ثز", nil, nil, nil], + 64632 => [0, 0, nil, "ثم", nil, nil, nil], + 64633 => [0, 0, nil, "ثن", nil, nil, nil], + 64634 => [0, 0, nil, "ثى", nil, nil, nil], + 64635 => [0, 0, nil, "ثي", nil, nil, nil], + 64636 => [0, 0, nil, "فى", nil, nil, nil], + 64637 => [0, 0, nil, "في", nil, nil, nil], + 64638 => [0, 0, nil, "قى", nil, nil, nil], + 64639 => [0, 0, nil, "قي", nil, nil, nil], + 64640 => [0, 0, nil, "كا", nil, nil, nil], + 64641 => [0, 0, nil, "كل", nil, nil, nil], + 64642 => [0, 0, nil, "كم", nil, nil, nil], + 64643 => [0, 0, nil, "كى", nil, nil, nil], + 64644 => [0, 0, nil, "كي", nil, nil, nil], + 64645 => [0, 0, nil, "لم", nil, nil, nil], + 64646 => [0, 0, nil, "لى", nil, nil, nil], + 64647 => [0, 0, nil, "لي", nil, nil, nil], + 64648 => [0, 0, nil, "ما", nil, nil, nil], + 64649 => [0, 0, nil, "مم", nil, nil, nil], + 64650 => [0, 0, nil, "نر", nil, nil, nil], + 64651 => [0, 0, nil, "نز", nil, nil, nil], + 64652 => [0, 0, nil, "نم", nil, nil, nil], + 64653 => [0, 0, nil, "نن", nil, nil, nil], + 64654 => [0, 0, nil, "نى", nil, nil, nil], + 64655 => [0, 0, nil, "ني", nil, nil, nil], + 64656 => [0, 0, nil, "ىٰ", nil, nil, nil], + 64657 => [0, 0, nil, "ير", nil, nil, nil], + 64658 => [0, 0, nil, "يز", nil, nil, nil], + 64659 => [0, 0, nil, "يم", nil, nil, nil], + 64660 => [0, 0, nil, "ين", nil, nil, nil], + 64661 => [0, 0, nil, "يى", nil, nil, nil], + 64662 => [0, 0, nil, "يي", nil, nil, nil], + 64663 => [0, 0, nil, "ئج", nil, nil, nil], + 64664 => [0, 0, nil, "ئح", nil, nil, nil], + 64665 => [0, 0, nil, "ئخ", nil, nil, nil], + 64666 => [0, 0, nil, "ئم", nil, nil, nil], + 64667 => [0, 0, nil, "ئه", nil, nil, nil], + 64668 => [0, 0, nil, "بج", nil, nil, nil], + 64669 => [0, 0, nil, "بح", nil, nil, nil], + 64670 => [0, 0, nil, "بخ", nil, nil, nil], + 64671 => [0, 0, nil, "بم", nil, nil, nil], + 64672 => [0, 0, nil, "به", nil, nil, nil], + 64673 => [0, 0, nil, "تج", nil, nil, nil], + 64674 => [0, 0, nil, "تح", nil, nil, nil], + 64675 => [0, 0, nil, "تخ", nil, nil, nil], + 64676 => [0, 0, nil, "تم", nil, nil, nil], + 64677 => [0, 0, nil, "ته", nil, nil, nil], + 64678 => [0, 0, nil, "ثم", nil, nil, nil], + 64679 => [0, 0, nil, "جح", nil, nil, nil], + 64680 => [0, 0, nil, "جم", nil, nil, nil], + 64681 => [0, 0, nil, "حج", nil, nil, nil], + 64682 => [0, 0, nil, "حم", nil, nil, nil], + 64683 => [0, 0, nil, "خج", nil, nil, nil], + 64684 => [0, 0, nil, "خم", nil, nil, nil], + 64685 => [0, 0, nil, "سج", nil, nil, nil], + 64686 => [0, 0, nil, "سح", nil, nil, nil], + 64687 => [0, 0, nil, "سخ", nil, nil, nil], + 64688 => [0, 0, nil, "سم", nil, nil, nil], + 64689 => [0, 0, nil, "صح", nil, nil, nil], + 64690 => [0, 0, nil, "صخ", nil, nil, nil], + 64691 => [0, 0, nil, "صم", nil, nil, nil], + 64692 => [0, 0, nil, "ضج", nil, nil, nil], + 64693 => [0, 0, nil, "ضح", nil, nil, nil], + 64694 => [0, 0, nil, "ضخ", nil, nil, nil], + 64695 => [0, 0, nil, "ضم", nil, nil, nil], + 64696 => [0, 0, nil, "طح", nil, nil, nil], + 64697 => [0, 0, nil, "ظم", nil, nil, nil], + 64698 => [0, 0, nil, "عج", nil, nil, nil], + 64699 => [0, 0, nil, "عم", nil, nil, nil], + 64700 => [0, 0, nil, "غج", nil, nil, nil], + 64701 => [0, 0, nil, "غم", nil, nil, nil], + 64702 => [0, 0, nil, "فج", nil, nil, nil], + 64703 => [0, 0, nil, "فح", nil, nil, nil], + 64704 => [0, 0, nil, "فخ", nil, nil, nil], + 64705 => [0, 0, nil, "فم", nil, nil, nil], + 64706 => [0, 0, nil, "قح", nil, nil, nil], + 64707 => [0, 0, nil, "قم", nil, nil, nil], + 64708 => [0, 0, nil, "كج", nil, nil, nil], + 64709 => [0, 0, nil, "كح", nil, nil, nil], + 64710 => [0, 0, nil, "كخ", nil, nil, nil], + 64711 => [0, 0, nil, "كل", nil, nil, nil], + 64712 => [0, 0, nil, "كم", nil, nil, nil], + 64713 => [0, 0, nil, "لج", nil, nil, nil], + 64714 => [0, 0, nil, "لح", nil, nil, nil], + 64715 => [0, 0, nil, "لخ", nil, nil, nil], + 64716 => [0, 0, nil, "لم", nil, nil, nil], + 64717 => [0, 0, nil, "له", nil, nil, nil], + 64718 => [0, 0, nil, "مج", nil, nil, nil], + 64719 => [0, 0, nil, "مح", nil, nil, nil], + 64720 => [0, 0, nil, "مخ", nil, nil, nil], + 64721 => [0, 0, nil, "مم", nil, nil, nil], + 64722 => [0, 0, nil, "نج", nil, nil, nil], + 64723 => [0, 0, nil, "نح", nil, nil, nil], + 64724 => [0, 0, nil, "نخ", nil, nil, nil], + 64725 => [0, 0, nil, "نم", nil, nil, nil], + 64726 => [0, 0, nil, "نه", nil, nil, nil], + 64727 => [0, 0, nil, "هج", nil, nil, nil], + 64728 => [0, 0, nil, "هم", nil, nil, nil], + 64729 => [0, 0, nil, "هٰ", nil, nil, nil], + 64730 => [0, 0, nil, "يج", nil, nil, nil], + 64731 => [0, 0, nil, "يح", nil, nil, nil], + 64732 => [0, 0, nil, "يخ", nil, nil, nil], + 64733 => [0, 0, nil, "يم", nil, nil, nil], + 64734 => [0, 0, nil, "يه", nil, nil, nil], + 64735 => [0, 0, nil, "ئم", nil, nil, nil], + 64736 => [0, 0, nil, "ئه", nil, nil, nil], + 64737 => [0, 0, nil, "بم", nil, nil, nil], + 64738 => [0, 0, nil, "به", nil, nil, nil], + 64739 => [0, 0, nil, "تم", nil, nil, nil], + 64740 => [0, 0, nil, "ته", nil, nil, nil], + 64741 => [0, 0, nil, "ثم", nil, nil, nil], + 64742 => [0, 0, nil, "ثه", nil, nil, nil], + 64743 => [0, 0, nil, "سم", nil, nil, nil], + 64744 => [0, 0, nil, "سه", nil, nil, nil], + 64745 => [0, 0, nil, "شم", nil, nil, nil], + 64746 => [0, 0, nil, "شه", nil, nil, nil], + 64747 => [0, 0, nil, "كل", nil, nil, nil], + 64748 => [0, 0, nil, "كم", nil, nil, nil], + 64749 => [0, 0, nil, "لم", nil, nil, nil], + 64750 => [0, 0, nil, "نم", nil, nil, nil], + 64751 => [0, 0, nil, "نه", nil, nil, nil], + 64752 => [0, 0, nil, "يم", nil, nil, nil], + 64753 => [0, 0, nil, "يه", nil, nil, nil], + 64754 => [0, 0, nil, "ـَّ", nil, nil, nil], + 64755 => [0, 0, nil, "ـُّ", nil, nil, nil], + 64756 => [0, 0, nil, "ـِّ", nil, nil, nil], + 64757 => [0, 0, nil, "طى", nil, nil, nil], + 64758 => [0, 0, nil, "طي", nil, nil, nil], + 64759 => [0, 0, nil, "عى", nil, nil, nil], + 64760 => [0, 0, nil, "عي", nil, nil, nil], + 64761 => [0, 0, nil, "غى", nil, nil, nil], + 64762 => [0, 0, nil, "غي", nil, nil, nil], + 64763 => [0, 0, nil, "سى", nil, nil, nil], + 64764 => [0, 0, nil, "سي", nil, nil, nil], + 64765 => [0, 0, nil, "شى", nil, nil, nil], + 64766 => [0, 0, nil, "شي", nil, nil, nil], + 64767 => [0, 0, nil, "حى", nil, nil, nil], + 64768 => [0, 0, nil, "حي", nil, nil, nil], + 64769 => [0, 0, nil, "جى", nil, nil, nil], + 64770 => [0, 0, nil, "جي", nil, nil, nil], + 64771 => [0, 0, nil, "خى", nil, nil, nil], + 64772 => [0, 0, nil, "خي", nil, nil, nil], + 64773 => [0, 0, nil, "صى", nil, nil, nil], + 64774 => [0, 0, nil, "صي", nil, nil, nil], + 64775 => [0, 0, nil, "ضى", nil, nil, nil], + 64776 => [0, 0, nil, "ضي", nil, nil, nil], + 64777 => [0, 0, nil, "شج", nil, nil, nil], + 64778 => [0, 0, nil, "شح", nil, nil, nil], + 64779 => [0, 0, nil, "شخ", nil, nil, nil], + 64780 => [0, 0, nil, "شم", nil, nil, nil], + 64781 => [0, 0, nil, "شر", nil, nil, nil], + 64782 => [0, 0, nil, "سر", nil, nil, nil], + 64783 => [0, 0, nil, "صر", nil, nil, nil], + 64784 => [0, 0, nil, "ضر", nil, nil, nil], + 64785 => [0, 0, nil, "طى", nil, nil, nil], + 64786 => [0, 0, nil, "طي", nil, nil, nil], + 64787 => [0, 0, nil, "عى", nil, nil, nil], + 64788 => [0, 0, nil, "عي", nil, nil, nil], + 64789 => [0, 0, nil, "غى", nil, nil, nil], + 64790 => [0, 0, nil, "غي", nil, nil, nil], + 64791 => [0, 0, nil, "سى", nil, nil, nil], + 64792 => [0, 0, nil, "سي", nil, nil, nil], + 64793 => [0, 0, nil, "شى", nil, nil, nil], + 64794 => [0, 0, nil, "شي", nil, nil, nil], + 64795 => [0, 0, nil, "حى", nil, nil, nil], + 64796 => [0, 0, nil, "حي", nil, nil, nil], + 64797 => [0, 0, nil, "جى", nil, nil, nil], + 64798 => [0, 0, nil, "جي", nil, nil, nil], + 64799 => [0, 0, nil, "خى", nil, nil, nil], + 64800 => [0, 0, nil, "خي", nil, nil, nil], + 64801 => [0, 0, nil, "صى", nil, nil, nil], + 64802 => [0, 0, nil, "صي", nil, nil, nil], + 64803 => [0, 0, nil, "ضى", nil, nil, nil], + 64804 => [0, 0, nil, "ضي", nil, nil, nil], + 64805 => [0, 0, nil, "شج", nil, nil, nil], + 64806 => [0, 0, nil, "شح", nil, nil, nil], + 64807 => [0, 0, nil, "شخ", nil, nil, nil], + 64808 => [0, 0, nil, "شم", nil, nil, nil], + 64809 => [0, 0, nil, "شر", nil, nil, nil], + 64810 => [0, 0, nil, "سر", nil, nil, nil], + 64811 => [0, 0, nil, "صر", nil, nil, nil], + 64812 => [0, 0, nil, "ضر", nil, nil, nil], + 64813 => [0, 0, nil, "شج", nil, nil, nil], + 64814 => [0, 0, nil, "شح", nil, nil, nil], + 64815 => [0, 0, nil, "شخ", nil, nil, nil], + 64816 => [0, 0, nil, "شم", nil, nil, nil], + 64817 => [0, 0, nil, "سه", nil, nil, nil], + 64818 => [0, 0, nil, "شه", nil, nil, nil], + 64819 => [0, 0, nil, "طم", nil, nil, nil], + 64820 => [0, 0, nil, "سج", nil, nil, nil], + 64821 => [0, 0, nil, "سح", nil, nil, nil], + 64822 => [0, 0, nil, "سخ", nil, nil, nil], + 64823 => [0, 0, nil, "شج", nil, nil, nil], + 64824 => [0, 0, nil, "شح", nil, nil, nil], + 64825 => [0, 0, nil, "شخ", nil, nil, nil], + 64826 => [0, 0, nil, "طم", nil, nil, nil], + 64827 => [0, 0, nil, "ظم", nil, nil, nil], + 64828 => [0, 0, nil, "اً", nil, nil, nil], + 64829 => [0, 0, nil, "اً", nil, nil, nil], + 64848 => [0, 0, nil, "تجم", nil, nil, nil], + 64849 => [0, 0, nil, "تحج", nil, nil, nil], + 64850 => [0, 0, nil, "تحج", nil, nil, nil], + 64851 => [0, 0, nil, "تحم", nil, nil, nil], + 64852 => [0, 0, nil, "تخم", nil, nil, nil], + 64853 => [0, 0, nil, "تمج", nil, nil, nil], + 64854 => [0, 0, nil, "تمح", nil, nil, nil], + 64855 => [0, 0, nil, "تمخ", nil, nil, nil], + 64856 => [0, 0, nil, "جمح", nil, nil, nil], + 64857 => [0, 0, nil, "جمح", nil, nil, nil], + 64858 => [0, 0, nil, "حمي", nil, nil, nil], + 64859 => [0, 0, nil, "حمى", nil, nil, nil], + 64860 => [0, 0, nil, "سحج", nil, nil, nil], + 64861 => [0, 0, nil, "سجح", nil, nil, nil], + 64862 => [0, 0, nil, "سجى", nil, nil, nil], + 64863 => [0, 0, nil, "سمح", nil, nil, nil], + 64864 => [0, 0, nil, "سمح", nil, nil, nil], + 64865 => [0, 0, nil, "سمج", nil, nil, nil], + 64866 => [0, 0, nil, "سمم", nil, nil, nil], + 64867 => [0, 0, nil, "سمم", nil, nil, nil], + 64868 => [0, 0, nil, "صحح", nil, nil, nil], + 64869 => [0, 0, nil, "صحح", nil, nil, nil], + 64870 => [0, 0, nil, "صمم", nil, nil, nil], + 64871 => [0, 0, nil, "شحم", nil, nil, nil], + 64872 => [0, 0, nil, "شحم", nil, nil, nil], + 64873 => [0, 0, nil, "شجي", nil, nil, nil], + 64874 => [0, 0, nil, "شمخ", nil, nil, nil], + 64875 => [0, 0, nil, "شمخ", nil, nil, nil], + 64876 => [0, 0, nil, "شمم", nil, nil, nil], + 64877 => [0, 0, nil, "شمم", nil, nil, nil], + 64878 => [0, 0, nil, "ضحى", nil, nil, nil], + 64879 => [0, 0, nil, "ضخم", nil, nil, nil], + 64880 => [0, 0, nil, "ضخم", nil, nil, nil], + 64881 => [0, 0, nil, "طمح", nil, nil, nil], + 64882 => [0, 0, nil, "طمح", nil, nil, nil], + 64883 => [0, 0, nil, "طمم", nil, nil, nil], + 64884 => [0, 0, nil, "طمي", nil, nil, nil], + 64885 => [0, 0, nil, "عجم", nil, nil, nil], + 64886 => [0, 0, nil, "عمم", nil, nil, nil], + 64887 => [0, 0, nil, "عمم", nil, nil, nil], + 64888 => [0, 0, nil, "عمى", nil, nil, nil], + 64889 => [0, 0, nil, "غمم", nil, nil, nil], + 64890 => [0, 0, nil, "غمي", nil, nil, nil], + 64891 => [0, 0, nil, "غمى", nil, nil, nil], + 64892 => [0, 0, nil, "فخم", nil, nil, nil], + 64893 => [0, 0, nil, "فخم", nil, nil, nil], + 64894 => [0, 0, nil, "قمح", nil, nil, nil], + 64895 => [0, 0, nil, "قمم", nil, nil, nil], + 64896 => [0, 0, nil, "لحم", nil, nil, nil], + 64897 => [0, 0, nil, "لحي", nil, nil, nil], + 64898 => [0, 0, nil, "لحى", nil, nil, nil], + 64899 => [0, 0, nil, "لجج", nil, nil, nil], + 64900 => [0, 0, nil, "لجج", nil, nil, nil], + 64901 => [0, 0, nil, "لخم", nil, nil, nil], + 64902 => [0, 0, nil, "لخم", nil, nil, nil], + 64903 => [0, 0, nil, "لمح", nil, nil, nil], + 64904 => [0, 0, nil, "لمح", nil, nil, nil], + 64905 => [0, 0, nil, "محج", nil, nil, nil], + 64906 => [0, 0, nil, "محم", nil, nil, nil], + 64907 => [0, 0, nil, "محي", nil, nil, nil], + 64908 => [0, 0, nil, "مجح", nil, nil, nil], + 64909 => [0, 0, nil, "مجم", nil, nil, nil], + 64910 => [0, 0, nil, "مخج", nil, nil, nil], + 64911 => [0, 0, nil, "مخم", nil, nil, nil], + 64914 => [0, 0, nil, "مجخ", nil, nil, nil], + 64915 => [0, 0, nil, "همج", nil, nil, nil], + 64916 => [0, 0, nil, "همم", nil, nil, nil], + 64917 => [0, 0, nil, "نحم", nil, nil, nil], + 64918 => [0, 0, nil, "نحى", nil, nil, nil], + 64919 => [0, 0, nil, "نجم", nil, nil, nil], + 64920 => [0, 0, nil, "نجم", nil, nil, nil], + 64921 => [0, 0, nil, "نجى", nil, nil, nil], + 64922 => [0, 0, nil, "نمي", nil, nil, nil], + 64923 => [0, 0, nil, "نمى", nil, nil, nil], + 64924 => [0, 0, nil, "يمم", nil, nil, nil], + 64925 => [0, 0, nil, "يمم", nil, nil, nil], + 64926 => [0, 0, nil, "بخي", nil, nil, nil], + 64927 => [0, 0, nil, "تجي", nil, nil, nil], + 64928 => [0, 0, nil, "تجى", nil, nil, nil], + 64929 => [0, 0, nil, "تخي", nil, nil, nil], + 64930 => [0, 0, nil, "تخى", nil, nil, nil], + 64931 => [0, 0, nil, "تمي", nil, nil, nil], + 64932 => [0, 0, nil, "تمى", nil, nil, nil], + 64933 => [0, 0, nil, "جمي", nil, nil, nil], + 64934 => [0, 0, nil, "جحى", nil, nil, nil], + 64935 => [0, 0, nil, "جمى", nil, nil, nil], + 64936 => [0, 0, nil, "سخى", nil, nil, nil], + 64937 => [0, 0, nil, "صحي", nil, nil, nil], + 64938 => [0, 0, nil, "شحي", nil, nil, nil], + 64939 => [0, 0, nil, "ضحي", nil, nil, nil], + 64940 => [0, 0, nil, "لجي", nil, nil, nil], + 64941 => [0, 0, nil, "لمي", nil, nil, nil], + 64942 => [0, 0, nil, "يحي", nil, nil, nil], + 64943 => [0, 0, nil, "يجي", nil, nil, nil], + 64944 => [0, 0, nil, "يمي", nil, nil, nil], + 64945 => [0, 0, nil, "ممي", nil, nil, nil], + 64946 => [0, 0, nil, "قمي", nil, nil, nil], + 64947 => [0, 0, nil, "نحي", nil, nil, nil], + 64948 => [0, 0, nil, "قمح", nil, nil, nil], + 64949 => [0, 0, nil, "لحم", nil, nil, nil], + 64950 => [0, 0, nil, "عمي", nil, nil, nil], + 64951 => [0, 0, nil, "كمي", nil, nil, nil], + 64952 => [0, 0, nil, "نجح", nil, nil, nil], + 64953 => [0, 0, nil, "مخي", nil, nil, nil], + 64954 => [0, 0, nil, "لجم", nil, nil, nil], + 64955 => [0, 0, nil, "كمم", nil, nil, nil], + 64956 => [0, 0, nil, "لجم", nil, nil, nil], + 64957 => [0, 0, nil, "نجح", nil, nil, nil], + 64958 => [0, 0, nil, "جحي", nil, nil, nil], + 64959 => [0, 0, nil, "حجي", nil, nil, nil], + 64960 => [0, 0, nil, "مجي", nil, nil, nil], + 64961 => [0, 0, nil, "فمي", nil, nil, nil], + 64962 => [0, 0, nil, "بحي", nil, nil, nil], + 64963 => [0, 0, nil, "كمم", nil, nil, nil], + 64964 => [0, 0, nil, "عجم", nil, nil, nil], + 64965 => [0, 0, nil, "صمم", nil, nil, nil], + 64966 => [0, 0, nil, "سخي", nil, nil, nil], + 64967 => [0, 0, nil, "نجي", nil, nil, nil], + 65008 => [0, 0, nil, "صلے", nil, nil, nil], + 65009 => [0, 0, nil, "قلے", nil, nil, nil], + 65010 => [0, 0, nil, "الله", nil, nil, nil], + 65011 => [0, 0, nil, "اكبر", nil, nil, nil], + 65012 => [0, 0, nil, "محمد", nil, nil, nil], + 65013 => [0, 0, nil, "صلعم", nil, nil, nil], + 65014 => [0, 0, nil, "رسول", nil, nil, nil], + 65015 => [0, 0, nil, "عليه", nil, nil, nil], + 65016 => [0, 0, nil, "وسلم", nil, nil, nil], + 65017 => [0, 0, nil, "صلى", nil, nil, nil], + 65018 => [0, 0, nil, "صلى الله عليه وسلم", nil, nil, nil], + 65019 => [0, 0, nil, "جل جلاله", nil, nil, nil], + 65056 => [230, 0, nil, nil, nil, nil, nil], + 65057 => [230, 0, nil, nil, nil, nil, nil], + 65058 => [230, 0, nil, nil, nil, nil, nil], + 65059 => [230, 0, nil, nil, nil, nil, nil], + 65072 => [0, 0, nil, "‥", nil, nil, nil], + 65073 => [0, 0, nil, "—", nil, nil, nil], + 65074 => [0, 0, nil, "–", nil, nil, nil], + 65075 => [0, 0, nil, "_", nil, nil, nil], + 65076 => [0, 0, nil, "_", nil, nil, nil], + 65077 => [0, 0, nil, "(", nil, nil, nil], + 65078 => [0, 0, nil, ")", nil, nil, nil], + 65079 => [0, 0, nil, "{", nil, nil, nil], + 65080 => [0, 0, nil, "}", nil, nil, nil], + 65081 => [0, 0, nil, "〔", nil, nil, nil], + 65082 => [0, 0, nil, "〕", nil, nil, nil], + 65083 => [0, 0, nil, "【", nil, nil, nil], + 65084 => [0, 0, nil, "】", nil, nil, nil], + 65085 => [0, 0, nil, "《", nil, nil, nil], + 65086 => [0, 0, nil, "》", nil, nil, nil], + 65087 => [0, 0, nil, "〈", nil, nil, nil], + 65088 => [0, 0, nil, "〉", nil, nil, nil], + 65089 => [0, 0, nil, "「", nil, nil, nil], + 65090 => [0, 0, nil, "」", nil, nil, nil], + 65091 => [0, 0, nil, "『", nil, nil, nil], + 65092 => [0, 0, nil, "』", nil, nil, nil], + 65097 => [0, 0, nil, "‾", nil, nil, nil], + 65098 => [0, 0, nil, "‾", nil, nil, nil], + 65099 => [0, 0, nil, "‾", nil, nil, nil], + 65100 => [0, 0, nil, "‾", nil, nil, nil], + 65101 => [0, 0, nil, "_", nil, nil, nil], + 65102 => [0, 0, nil, "_", nil, nil, nil], + 65103 => [0, 0, nil, "_", nil, nil, nil], + 65104 => [0, 0, nil, ",", nil, nil, nil], + 65105 => [0, 0, nil, "、", nil, nil, nil], + 65106 => [0, 0, nil, ".", nil, nil, nil], + 65108 => [0, 0, nil, ";", nil, nil, nil], + 65109 => [0, 0, nil, ":", nil, nil, nil], + 65110 => [0, 0, nil, "?", nil, nil, nil], + 65111 => [0, 0, nil, "!", nil, nil, nil], + 65112 => [0, 0, nil, "—", nil, nil, nil], + 65113 => [0, 0, nil, "(", nil, nil, nil], + 65114 => [0, 0, nil, ")", nil, nil, nil], + 65115 => [0, 0, nil, "{", nil, nil, nil], + 65116 => [0, 0, nil, "}", nil, nil, nil], + 65117 => [0, 0, nil, "〔", nil, nil, nil], + 65118 => [0, 0, nil, "〕", nil, nil, nil], + 65119 => [0, 0, nil, "#", nil, nil, nil], + 65120 => [0, 0, nil, "&", nil, nil, nil], + 65121 => [0, 0, nil, "*", nil, nil, nil], + 65122 => [0, 0, nil, "+", nil, nil, nil], + 65123 => [0, 0, nil, "-", nil, nil, nil], + 65124 => [0, 0, nil, "<", nil, nil, nil], + 65125 => [0, 0, nil, ">", nil, nil, nil], + 65126 => [0, 0, nil, "=", nil, nil, nil], + 65128 => [0, 0, nil, "\\", nil, nil, nil], + 65129 => [0, 0, nil, "$", nil, nil, nil], + 65130 => [0, 0, nil, "%", nil, nil, nil], + 65131 => [0, 0, nil, "@", nil, nil, nil], + 65136 => [0, 0, nil, " ً", nil, nil, nil], + 65137 => [0, 0, nil, "ـً", nil, nil, nil], + 65138 => [0, 0, nil, " ٌ", nil, nil, nil], + 65140 => [0, 0, nil, " ٍ", nil, nil, nil], + 65142 => [0, 0, nil, " َ", nil, nil, nil], + 65143 => [0, 0, nil, "ـَ", nil, nil, nil], + 65144 => [0, 0, nil, " ُ", nil, nil, nil], + 65145 => [0, 0, nil, "ـُ", nil, nil, nil], + 65146 => [0, 0, nil, " ِ", nil, nil, nil], + 65147 => [0, 0, nil, "ـِ", nil, nil, nil], + 65148 => [0, 0, nil, " ّ", nil, nil, nil], + 65149 => [0, 0, nil, "ـّ", nil, nil, nil], + 65150 => [0, 0, nil, " ْ", nil, nil, nil], + 65151 => [0, 0, nil, "ـْ", nil, nil, nil], + 65152 => [0, 0, nil, "ء", nil, nil, nil], + 65153 => [0, 0, nil, "آ", nil, nil, nil], + 65154 => [0, 0, nil, "آ", nil, nil, nil], + 65155 => [0, 0, nil, "أ", nil, nil, nil], + 65156 => [0, 0, nil, "أ", nil, nil, nil], + 65157 => [0, 0, nil, "ؤ", nil, nil, nil], + 65158 => [0, 0, nil, "ؤ", nil, nil, nil], + 65159 => [0, 0, nil, "إ", nil, nil, nil], + 65160 => [0, 0, nil, "إ", nil, nil, nil], + 65161 => [0, 0, nil, "ئ", nil, nil, nil], + 65162 => [0, 0, nil, "ئ", nil, nil, nil], + 65163 => [0, 0, nil, "ئ", nil, nil, nil], + 65164 => [0, 0, nil, "ئ", nil, nil, nil], + 65165 => [0, 0, nil, "ا", nil, nil, nil], + 65166 => [0, 0, nil, "ا", nil, nil, nil], + 65167 => [0, 0, nil, "ب", nil, nil, nil], + 65168 => [0, 0, nil, "ب", nil, nil, nil], + 65169 => [0, 0, nil, "ب", nil, nil, nil], + 65170 => [0, 0, nil, "ب", nil, nil, nil], + 65171 => [0, 0, nil, "ة", nil, nil, nil], + 65172 => [0, 0, nil, "ة", nil, nil, nil], + 65173 => [0, 0, nil, "ت", nil, nil, nil], + 65174 => [0, 0, nil, "ت", nil, nil, nil], + 65175 => [0, 0, nil, "ت", nil, nil, nil], + 65176 => [0, 0, nil, "ت", nil, nil, nil], + 65177 => [0, 0, nil, "ث", nil, nil, nil], + 65178 => [0, 0, nil, "ث", nil, nil, nil], + 65179 => [0, 0, nil, "ث", nil, nil, nil], + 65180 => [0, 0, nil, "ث", nil, nil, nil], + 65181 => [0, 0, nil, "ج", nil, nil, nil], + 65182 => [0, 0, nil, "ج", nil, nil, nil], + 65183 => [0, 0, nil, "ج", nil, nil, nil], + 65184 => [0, 0, nil, "ج", nil, nil, nil], + 65185 => [0, 0, nil, "ح", nil, nil, nil], + 65186 => [0, 0, nil, "ح", nil, nil, nil], + 65187 => [0, 0, nil, "ح", nil, nil, nil], + 65188 => [0, 0, nil, "ح", nil, nil, nil], + 65189 => [0, 0, nil, "خ", nil, nil, nil], + 65190 => [0, 0, nil, "خ", nil, nil, nil], + 65191 => [0, 0, nil, "خ", nil, nil, nil], + 65192 => [0, 0, nil, "خ", nil, nil, nil], + 65193 => [0, 0, nil, "د", nil, nil, nil], + 65194 => [0, 0, nil, "د", nil, nil, nil], + 65195 => [0, 0, nil, "ذ", nil, nil, nil], + 65196 => [0, 0, nil, "ذ", nil, nil, nil], + 65197 => [0, 0, nil, "ر", nil, nil, nil], + 65198 => [0, 0, nil, "ر", nil, nil, nil], + 65199 => [0, 0, nil, "ز", nil, nil, nil], + 65200 => [0, 0, nil, "ز", nil, nil, nil], + 65201 => [0, 0, nil, "س", nil, nil, nil], + 65202 => [0, 0, nil, "س", nil, nil, nil], + 65203 => [0, 0, nil, "س", nil, nil, nil], + 65204 => [0, 0, nil, "س", nil, nil, nil], + 65205 => [0, 0, nil, "ش", nil, nil, nil], + 65206 => [0, 0, nil, "ش", nil, nil, nil], + 65207 => [0, 0, nil, "ش", nil, nil, nil], + 65208 => [0, 0, nil, "ش", nil, nil, nil], + 65209 => [0, 0, nil, "ص", nil, nil, nil], + 65210 => [0, 0, nil, "ص", nil, nil, nil], + 65211 => [0, 0, nil, "ص", nil, nil, nil], + 65212 => [0, 0, nil, "ص", nil, nil, nil], + 65213 => [0, 0, nil, "ض", nil, nil, nil], + 65214 => [0, 0, nil, "ض", nil, nil, nil], + 65215 => [0, 0, nil, "ض", nil, nil, nil], + 65216 => [0, 0, nil, "ض", nil, nil, nil], + 65217 => [0, 0, nil, "ط", nil, nil, nil], + 65218 => [0, 0, nil, "ط", nil, nil, nil], + 65219 => [0, 0, nil, "ط", nil, nil, nil], + 65220 => [0, 0, nil, "ط", nil, nil, nil], + 65221 => [0, 0, nil, "ظ", nil, nil, nil], + 65222 => [0, 0, nil, "ظ", nil, nil, nil], + 65223 => [0, 0, nil, "ظ", nil, nil, nil], + 65224 => [0, 0, nil, "ظ", nil, nil, nil], + 65225 => [0, 0, nil, "ع", nil, nil, nil], + 65226 => [0, 0, nil, "ع", nil, nil, nil], + 65227 => [0, 0, nil, "ع", nil, nil, nil], + 65228 => [0, 0, nil, "ع", nil, nil, nil], + 65229 => [0, 0, nil, "غ", nil, nil, nil], + 65230 => [0, 0, nil, "غ", nil, nil, nil], + 65231 => [0, 0, nil, "غ", nil, nil, nil], + 65232 => [0, 0, nil, "غ", nil, nil, nil], + 65233 => [0, 0, nil, "ف", nil, nil, nil], + 65234 => [0, 0, nil, "ف", nil, nil, nil], + 65235 => [0, 0, nil, "ف", nil, nil, nil], + 65236 => [0, 0, nil, "ف", nil, nil, nil], + 65237 => [0, 0, nil, "ق", nil, nil, nil], + 65238 => [0, 0, nil, "ق", nil, nil, nil], + 65239 => [0, 0, nil, "ق", nil, nil, nil], + 65240 => [0, 0, nil, "ق", nil, nil, nil], + 65241 => [0, 0, nil, "ك", nil, nil, nil], + 65242 => [0, 0, nil, "ك", nil, nil, nil], + 65243 => [0, 0, nil, "ك", nil, nil, nil], + 65244 => [0, 0, nil, "ك", nil, nil, nil], + 65245 => [0, 0, nil, "ل", nil, nil, nil], + 65246 => [0, 0, nil, "ل", nil, nil, nil], + 65247 => [0, 0, nil, "ل", nil, nil, nil], + 65248 => [0, 0, nil, "ل", nil, nil, nil], + 65249 => [0, 0, nil, "م", nil, nil, nil], + 65250 => [0, 0, nil, "م", nil, nil, nil], + 65251 => [0, 0, nil, "م", nil, nil, nil], + 65252 => [0, 0, nil, "م", nil, nil, nil], + 65253 => [0, 0, nil, "ن", nil, nil, nil], + 65254 => [0, 0, nil, "ن", nil, nil, nil], + 65255 => [0, 0, nil, "ن", nil, nil, nil], + 65256 => [0, 0, nil, "ن", nil, nil, nil], + 65257 => [0, 0, nil, "ه", nil, nil, nil], + 65258 => [0, 0, nil, "ه", nil, nil, nil], + 65259 => [0, 0, nil, "ه", nil, nil, nil], + 65260 => [0, 0, nil, "ه", nil, nil, nil], + 65261 => [0, 0, nil, "و", nil, nil, nil], + 65262 => [0, 0, nil, "و", nil, nil, nil], + 65263 => [0, 0, nil, "ى", nil, nil, nil], + 65264 => [0, 0, nil, "ى", nil, nil, nil], + 65265 => [0, 0, nil, "ي", nil, nil, nil], + 65266 => [0, 0, nil, "ي", nil, nil, nil], + 65267 => [0, 0, nil, "ي", nil, nil, nil], + 65268 => [0, 0, nil, "ي", nil, nil, nil], + 65269 => [0, 0, nil, "لآ", nil, nil, nil], + 65270 => [0, 0, nil, "لآ", nil, nil, nil], + 65271 => [0, 0, nil, "لأ", nil, nil, nil], + 65272 => [0, 0, nil, "لأ", nil, nil, nil], + 65273 => [0, 0, nil, "لإ", nil, nil, nil], + 65274 => [0, 0, nil, "لإ", nil, nil, nil], + 65275 => [0, 0, nil, "لا", nil, nil, nil], + 65276 => [0, 0, nil, "لا", nil, nil, nil], + 65281 => [0, 0, nil, "!", nil, nil, nil], + 65282 => [0, 0, nil, "\"", nil, nil, nil], + 65283 => [0, 0, nil, "#", nil, nil, nil], + 65284 => [0, 0, nil, "$", nil, nil, nil], + 65285 => [0, 0, nil, "%", nil, nil, nil], + 65286 => [0, 0, nil, "&", nil, nil, nil], + 65287 => [0, 0, nil, "'", nil, nil, nil], + 65288 => [0, 0, nil, "(", nil, nil, nil], + 65289 => [0, 0, nil, ")", nil, nil, nil], + 65290 => [0, 0, nil, "*", nil, nil, nil], + 65291 => [0, 0, nil, "+", nil, nil, nil], + 65292 => [0, 0, nil, ",", nil, nil, nil], + 65293 => [0, 0, nil, "-", nil, nil, nil], + 65294 => [0, 0, nil, ".", nil, nil, nil], + 65295 => [0, 0, nil, "/", nil, nil, nil], + 65296 => [0, 0, nil, "0", nil, nil, nil], + 65297 => [0, 0, nil, "1", nil, nil, nil], + 65298 => [0, 0, nil, "2", nil, nil, nil], + 65299 => [0, 0, nil, "3", nil, nil, nil], + 65300 => [0, 0, nil, "4", nil, nil, nil], + 65301 => [0, 0, nil, "5", nil, nil, nil], + 65302 => [0, 0, nil, "6", nil, nil, nil], + 65303 => [0, 0, nil, "7", nil, nil, nil], + 65304 => [0, 0, nil, "8", nil, nil, nil], + 65305 => [0, 0, nil, "9", nil, nil, nil], + 65306 => [0, 0, nil, ":", nil, nil, nil], + 65307 => [0, 0, nil, ";", nil, nil, nil], + 65308 => [0, 0, nil, "<", nil, nil, nil], + 65309 => [0, 0, nil, "=", nil, nil, nil], + 65310 => [0, 0, nil, ">", nil, nil, nil], + 65311 => [0, 0, nil, "?", nil, nil, nil], + 65312 => [0, 0, nil, "@", nil, nil, nil], + 65313 => [0, 0, nil, "A", nil, 65345, nil], + 65314 => [0, 0, nil, "B", nil, 65346, nil], + 65315 => [0, 0, nil, "C", nil, 65347, nil], + 65316 => [0, 0, nil, "D", nil, 65348, nil], + 65317 => [0, 0, nil, "E", nil, 65349, nil], + 65318 => [0, 0, nil, "F", nil, 65350, nil], + 65319 => [0, 0, nil, "G", nil, 65351, nil], + 65320 => [0, 0, nil, "H", nil, 65352, nil], + 65321 => [0, 0, nil, "I", nil, 65353, nil], + 65322 => [0, 0, nil, "J", nil, 65354, nil], + 65323 => [0, 0, nil, "K", nil, 65355, nil], + 65324 => [0, 0, nil, "L", nil, 65356, nil], + 65325 => [0, 0, nil, "M", nil, 65357, nil], + 65326 => [0, 0, nil, "N", nil, 65358, nil], + 65327 => [0, 0, nil, "O", nil, 65359, nil], + 65328 => [0, 0, nil, "P", nil, 65360, nil], + 65329 => [0, 0, nil, "Q", nil, 65361, nil], + 65330 => [0, 0, nil, "R", nil, 65362, nil], + 65331 => [0, 0, nil, "S", nil, 65363, nil], + 65332 => [0, 0, nil, "T", nil, 65364, nil], + 65333 => [0, 0, nil, "U", nil, 65365, nil], + 65334 => [0, 0, nil, "V", nil, 65366, nil], + 65335 => [0, 0, nil, "W", nil, 65367, nil], + 65336 => [0, 0, nil, "X", nil, 65368, nil], + 65337 => [0, 0, nil, "Y", nil, 65369, nil], + 65338 => [0, 0, nil, "Z", nil, 65370, nil], + 65339 => [0, 0, nil, "[", nil, nil, nil], + 65340 => [0, 0, nil, "\\", nil, nil, nil], + 65341 => [0, 0, nil, "]", nil, nil, nil], + 65342 => [0, 0, nil, "^", nil, nil, nil], + 65343 => [0, 0, nil, "_", nil, nil, nil], + 65344 => [0, 0, nil, "`", nil, nil, nil], + 65345 => [0, 0, nil, "a", 65313, nil, 65313], + 65346 => [0, 0, nil, "b", 65314, nil, 65314], + 65347 => [0, 0, nil, "c", 65315, nil, 65315], + 65348 => [0, 0, nil, "d", 65316, nil, 65316], + 65349 => [0, 0, nil, "e", 65317, nil, 65317], + 65350 => [0, 0, nil, "f", 65318, nil, 65318], + 65351 => [0, 0, nil, "g", 65319, nil, 65319], + 65352 => [0, 0, nil, "h", 65320, nil, 65320], + 65353 => [0, 0, nil, "i", 65321, nil, 65321], + 65354 => [0, 0, nil, "j", 65322, nil, 65322], + 65355 => [0, 0, nil, "k", 65323, nil, 65323], + 65356 => [0, 0, nil, "l", 65324, nil, 65324], + 65357 => [0, 0, nil, "m", 65325, nil, 65325], + 65358 => [0, 0, nil, "n", 65326, nil, 65326], + 65359 => [0, 0, nil, "o", 65327, nil, 65327], + 65360 => [0, 0, nil, "p", 65328, nil, 65328], + 65361 => [0, 0, nil, "q", 65329, nil, 65329], + 65362 => [0, 0, nil, "r", 65330, nil, 65330], + 65363 => [0, 0, nil, "s", 65331, nil, 65331], + 65364 => [0, 0, nil, "t", 65332, nil, 65332], + 65365 => [0, 0, nil, "u", 65333, nil, 65333], + 65366 => [0, 0, nil, "v", 65334, nil, 65334], + 65367 => [0, 0, nil, "w", 65335, nil, 65335], + 65368 => [0, 0, nil, "x", 65336, nil, 65336], + 65369 => [0, 0, nil, "y", 65337, nil, 65337], + 65370 => [0, 0, nil, "z", 65338, nil, 65338], + 65371 => [0, 0, nil, "{", nil, nil, nil], + 65372 => [0, 0, nil, "|", nil, nil, nil], + 65373 => [0, 0, nil, "}", nil, nil, nil], + 65374 => [0, 0, nil, "~", nil, nil, nil], + 65377 => [0, 0, nil, "。", nil, nil, nil], + 65378 => [0, 0, nil, "「", nil, nil, nil], + 65379 => [0, 0, nil, "」", nil, nil, nil], + 65380 => [0, 0, nil, "、", nil, nil, nil], + 65381 => [0, 0, nil, "・", nil, nil, nil], + 65382 => [0, 0, nil, "ヲ", nil, nil, nil], + 65383 => [0, 0, nil, "ァ", nil, nil, nil], + 65384 => [0, 0, nil, "ィ", nil, nil, nil], + 65385 => [0, 0, nil, "ゥ", nil, nil, nil], + 65386 => [0, 0, nil, "ェ", nil, nil, nil], + 65387 => [0, 0, nil, "ォ", nil, nil, nil], + 65388 => [0, 0, nil, "ャ", nil, nil, nil], + 65389 => [0, 0, nil, "ュ", nil, nil, nil], + 65390 => [0, 0, nil, "ョ", nil, nil, nil], + 65391 => [0, 0, nil, "ッ", nil, nil, nil], + 65392 => [0, 0, nil, "ー", nil, nil, nil], + 65393 => [0, 0, nil, "ア", nil, nil, nil], + 65394 => [0, 0, nil, "イ", nil, nil, nil], + 65395 => [0, 0, nil, "ウ", nil, nil, nil], + 65396 => [0, 0, nil, "エ", nil, nil, nil], + 65397 => [0, 0, nil, "オ", nil, nil, nil], + 65398 => [0, 0, nil, "カ", nil, nil, nil], + 65399 => [0, 0, nil, "キ", nil, nil, nil], + 65400 => [0, 0, nil, "ク", nil, nil, nil], + 65401 => [0, 0, nil, "ケ", nil, nil, nil], + 65402 => [0, 0, nil, "コ", nil, nil, nil], + 65403 => [0, 0, nil, "サ", nil, nil, nil], + 65404 => [0, 0, nil, "シ", nil, nil, nil], + 65405 => [0, 0, nil, "ス", nil, nil, nil], + 65406 => [0, 0, nil, "セ", nil, nil, nil], + 65407 => [0, 0, nil, "ソ", nil, nil, nil], + 65408 => [0, 0, nil, "タ", nil, nil, nil], + 65409 => [0, 0, nil, "チ", nil, nil, nil], + 65410 => [0, 0, nil, "ツ", nil, nil, nil], + 65411 => [0, 0, nil, "テ", nil, nil, nil], + 65412 => [0, 0, nil, "ト", nil, nil, nil], + 65413 => [0, 0, nil, "ナ", nil, nil, nil], + 65414 => [0, 0, nil, "ニ", nil, nil, nil], + 65415 => [0, 0, nil, "ヌ", nil, nil, nil], + 65416 => [0, 0, nil, "ネ", nil, nil, nil], + 65417 => [0, 0, nil, "ノ", nil, nil, nil], + 65418 => [0, 0, nil, "ハ", nil, nil, nil], + 65419 => [0, 0, nil, "ヒ", nil, nil, nil], + 65420 => [0, 0, nil, "フ", nil, nil, nil], + 65421 => [0, 0, nil, "ヘ", nil, nil, nil], + 65422 => [0, 0, nil, "ホ", nil, nil, nil], + 65423 => [0, 0, nil, "マ", nil, nil, nil], + 65424 => [0, 0, nil, "ミ", nil, nil, nil], + 65425 => [0, 0, nil, "ム", nil, nil, nil], + 65426 => [0, 0, nil, "メ", nil, nil, nil], + 65427 => [0, 0, nil, "モ", nil, nil, nil], + 65428 => [0, 0, nil, "ヤ", nil, nil, nil], + 65429 => [0, 0, nil, "ユ", nil, nil, nil], + 65430 => [0, 0, nil, "ヨ", nil, nil, nil], + 65431 => [0, 0, nil, "ラ", nil, nil, nil], + 65432 => [0, 0, nil, "リ", nil, nil, nil], + 65433 => [0, 0, nil, "ル", nil, nil, nil], + 65434 => [0, 0, nil, "レ", nil, nil, nil], + 65435 => [0, 0, nil, "ロ", nil, nil, nil], + 65436 => [0, 0, nil, "ワ", nil, nil, nil], + 65437 => [0, 0, nil, "ン", nil, nil, nil], + 65438 => [0, 0, nil, "゙", nil, nil, nil], + 65439 => [0, 0, nil, "゚", nil, nil, nil], + 65440 => [0, 0, nil, "ㅤ", nil, nil, nil], + 65441 => [0, 0, nil, "ㄱ", nil, nil, nil], + 65442 => [0, 0, nil, "ㄲ", nil, nil, nil], + 65443 => [0, 0, nil, "ㄳ", nil, nil, nil], + 65444 => [0, 0, nil, "ㄴ", nil, nil, nil], + 65445 => [0, 0, nil, "ㄵ", nil, nil, nil], + 65446 => [0, 0, nil, "ㄶ", nil, nil, nil], + 65447 => [0, 0, nil, "ㄷ", nil, nil, nil], + 65448 => [0, 0, nil, "ㄸ", nil, nil, nil], + 65449 => [0, 0, nil, "ㄹ", nil, nil, nil], + 65450 => [0, 0, nil, "ㄺ", nil, nil, nil], + 65451 => [0, 0, nil, "ㄻ", nil, nil, nil], + 65452 => [0, 0, nil, "ㄼ", nil, nil, nil], + 65453 => [0, 0, nil, "ㄽ", nil, nil, nil], + 65454 => [0, 0, nil, "ㄾ", nil, nil, nil], + 65455 => [0, 0, nil, "ㄿ", nil, nil, nil], + 65456 => [0, 0, nil, "ㅀ", nil, nil, nil], + 65457 => [0, 0, nil, "ㅁ", nil, nil, nil], + 65458 => [0, 0, nil, "ㅂ", nil, nil, nil], + 65459 => [0, 0, nil, "ㅃ", nil, nil, nil], + 65460 => [0, 0, nil, "ㅄ", nil, nil, nil], + 65461 => [0, 0, nil, "ㅅ", nil, nil, nil], + 65462 => [0, 0, nil, "ㅆ", nil, nil, nil], + 65463 => [0, 0, nil, "ㅇ", nil, nil, nil], + 65464 => [0, 0, nil, "ㅈ", nil, nil, nil], + 65465 => [0, 0, nil, "ㅉ", nil, nil, nil], + 65466 => [0, 0, nil, "ㅊ", nil, nil, nil], + 65467 => [0, 0, nil, "ㅋ", nil, nil, nil], + 65468 => [0, 0, nil, "ㅌ", nil, nil, nil], + 65469 => [0, 0, nil, "ㅍ", nil, nil, nil], + 65470 => [0, 0, nil, "ㅎ", nil, nil, nil], + 65474 => [0, 0, nil, "ㅏ", nil, nil, nil], + 65475 => [0, 0, nil, "ㅐ", nil, nil, nil], + 65476 => [0, 0, nil, "ㅑ", nil, nil, nil], + 65477 => [0, 0, nil, "ㅒ", nil, nil, nil], + 65478 => [0, 0, nil, "ㅓ", nil, nil, nil], + 65479 => [0, 0, nil, "ㅔ", nil, nil, nil], + 65482 => [0, 0, nil, "ㅕ", nil, nil, nil], + 65483 => [0, 0, nil, "ㅖ", nil, nil, nil], + 65484 => [0, 0, nil, "ㅗ", nil, nil, nil], + 65485 => [0, 0, nil, "ㅘ", nil, nil, nil], + 65486 => [0, 0, nil, "ㅙ", nil, nil, nil], + 65487 => [0, 0, nil, "ㅚ", nil, nil, nil], + 65490 => [0, 0, nil, "ㅛ", nil, nil, nil], + 65491 => [0, 0, nil, "ㅜ", nil, nil, nil], + 65492 => [0, 0, nil, "ㅝ", nil, nil, nil], + 65493 => [0, 0, nil, "ㅞ", nil, nil, nil], + 65494 => [0, 0, nil, "ㅟ", nil, nil, nil], + 65495 => [0, 0, nil, "ㅠ", nil, nil, nil], + 65498 => [0, 0, nil, "ㅡ", nil, nil, nil], + 65499 => [0, 0, nil, "ㅢ", nil, nil, nil], + 65500 => [0, 0, nil, "ㅣ", nil, nil, nil], + 65504 => [0, 0, nil, "¢", nil, nil, nil], + 65505 => [0, 0, nil, "£", nil, nil, nil], + 65506 => [0, 0, nil, "¬", nil, nil, nil], + 65507 => [0, 0, nil, "¯", nil, nil, nil], + 65508 => [0, 0, nil, "¦", nil, nil, nil], + 65509 => [0, 0, nil, "¥", nil, nil, nil], + 65510 => [0, 0, nil, "₩", nil, nil, nil], + 65512 => [0, 0, nil, "│", nil, nil, nil], + 65513 => [0, 0, nil, "←", nil, nil, nil], + 65514 => [0, 0, nil, "↑", nil, nil, nil], + 65515 => [0, 0, nil, "→", nil, nil, nil], + 65516 => [0, 0, nil, "↓", nil, nil, nil], + 65517 => [0, 0, nil, "■", nil, nil, nil], + 65518 => [0, 0, nil, "○", nil, nil, nil], + } + + COMPOSITION_TABLE = {} + UNICODE_DATA.each do |codepoint, data| + canonical = data[UNICODE_DATA_CANONICAL] + exclusion = data[UNICODE_DATA_EXCLUSION] + + if canonical && exclusion == 0 + COMPOSITION_TABLE[canonical.unpack("C*")] = codepoint + end + end + + UNICODE_MAX_LENGTH = 256 + ACE_MAX_LENGTH = 256 + + PUNYCODE_BASE = 36 + PUNYCODE_TMIN = 1 + PUNYCODE_TMAX = 26 + PUNYCODE_SKEW = 38 + PUNYCODE_DAMP = 700 + PUNYCODE_INITIAL_BIAS = 72 + PUNYCODE_INITIAL_N = 0x80 + PUNYCODE_DELIMITER = 0x2D + + PUNYCODE_MAXINT = 1 << 64 + + PUNYCODE_PRINT_ASCII = + "\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" + + " !\"\#$%&'()*+,-./" + + "0123456789:;<=>?" + + "@ABCDEFGHIJKLMNO" + + "PQRSTUVWXYZ[\\]^_" + + "`abcdefghijklmno" + + "pqrstuvwxyz{|}~\n" + + # Input is invalid. + class PunycodeBadInput < StandardError; end + # Output would exceed the space provided. + class PunycodeBigOutput < StandardError; end + # Input needs wider integers to process. + class PunycodeOverflow < StandardError; end + + def self.punycode_encode(unicode) + unicode = unicode.to_s unless unicode.is_a?(String) + input = unicode.unpack("U*") + output = [0] * (ACE_MAX_LENGTH + 1) + input_length = input.size + output_length = [ACE_MAX_LENGTH] + + # Initialize the state + n = PUNYCODE_INITIAL_N + delta = out = 0 + max_out = output_length[0] + bias = PUNYCODE_INITIAL_BIAS + + # Handle the basic code points: + input_length.times do |j| + if punycode_basic?(input[j]) + if max_out - out < 2 + raise PunycodeBigOutput, + "Output would exceed the space provided." + end + output[out] = input[j] + out += 1 + end + end + + h = b = out + + # h is the number of code points that have been handled, b is the + # number of basic code points, and out is the number of characters + # that have been output. + + if b > 0 + output[out] = PUNYCODE_DELIMITER + out += 1 + end + + # Main encoding loop: + + while h < input_length + # All non-basic code points < n have been + # handled already. Find the next larger one: + + m = PUNYCODE_MAXINT + input_length.times do |j| + m = input[j] if (n...m) === input[j] + end + + # Increase delta enough to advance the decoder's + # state to , but guard against overflow: + + if m - n > (PUNYCODE_MAXINT - delta) / (h + 1) + raise PunycodeOverflow, "Input needs wider integers to process." + end + delta += (m - n) * (h + 1) + n = m + + input_length.times do |j| + # Punycode does not need to check whether input[j] is basic: + if input[j] < n + delta += 1 + if delta == 0 + raise PunycodeOverflow, + "Input needs wider integers to process." + end + end + + if input[j] == n + # Represent delta as a generalized variable-length integer: + + q = delta; k = PUNYCODE_BASE + while true + if out >= max_out + raise PunycodeBigOutput, + "Output would exceed the space provided." + end + t = ( + if k <= bias + PUNYCODE_TMIN + elsif k >= bias + PUNYCODE_TMAX + PUNYCODE_TMAX + else + k - bias + end + ) + break if q < t + output[out] = + punycode_encode_digit(t + (q - t) % (PUNYCODE_BASE - t)) + out += 1 + q = (q - t) / (PUNYCODE_BASE - t) + k += PUNYCODE_BASE + end + + output[out] = punycode_encode_digit(q) + out += 1 + bias = punycode_adapt(delta, h + 1, h == b) + delta = 0 + h += 1 + end + end + + delta += 1 + n += 1 + end + + output_length[0] = out + + outlen = out + outlen.times do |j| + c = output[j] + unless c >= 0 && c <= 127 + raise StandardError, "Invalid output char." + end + unless PUNYCODE_PRINT_ASCII[c] + raise PunycodeBadInput, "Input is invalid." + end + end + + output[0..outlen].map { |x| x.chr }.join("").sub(/\0+\z/, "") + end + private_class_method :punycode_encode + + def self.punycode_decode(punycode) + input = [] + output = [] + + if ACE_MAX_LENGTH * 2 < punycode.size + raise PunycodeBigOutput, "Output would exceed the space provided." + end + punycode.each_byte do |c| + unless c >= 0 && c <= 127 + raise PunycodeBadInput, "Input is invalid." + end + input.push(c) + end + + input_length = input.length + output_length = [UNICODE_MAX_LENGTH] + + # Initialize the state + n = PUNYCODE_INITIAL_N + + out = i = 0 + max_out = output_length[0] + bias = PUNYCODE_INITIAL_BIAS + + # Handle the basic code points: Let b be the number of input code + # points before the last delimiter, or 0 if there is none, then + # copy the first b code points to the output. + + b = 0 + input_length.times do |j| + b = j if punycode_delimiter?(input[j]) + end + if b > max_out + raise PunycodeBigOutput, "Output would exceed the space provided." + end + + b.times do |j| + unless punycode_basic?(input[j]) + raise PunycodeBadInput, "Input is invalid." + end + output[out] = input[j] + out+=1 + end + + # Main decoding loop: Start just after the last delimiter if any + # basic code points were copied; start at the beginning otherwise. + + in_ = b > 0 ? b + 1 : 0 + while in_ < input_length + + # in_ is the index of the next character to be consumed, and + # out is the number of code points in the output array. + + # Decode a generalized variable-length integer into delta, + # which gets added to i. The overflow checking is easier + # if we increase i as we go, then subtract off its starting + # value at the end to obtain delta. + + oldi = i; w = 1; k = PUNYCODE_BASE + while true + if in_ >= input_length + raise PunycodeBadInput, "Input is invalid." + end + digit = punycode_decode_digit(input[in_]) + in_+=1 + if digit >= PUNYCODE_BASE + raise PunycodeBadInput, "Input is invalid." + end + if digit > (PUNYCODE_MAXINT - i) / w + raise PunycodeOverflow, "Input needs wider integers to process." + end + i += digit * w + t = ( + if k <= bias + PUNYCODE_TMIN + elsif k >= bias + PUNYCODE_TMAX + PUNYCODE_TMAX + else + k - bias + end + ) + break if digit < t + if w > PUNYCODE_MAXINT / (PUNYCODE_BASE - t) + raise PunycodeOverflow, "Input needs wider integers to process." + end + w *= PUNYCODE_BASE - t + k += PUNYCODE_BASE + end + + bias = punycode_adapt(i - oldi, out + 1, oldi == 0) + + # I was supposed to wrap around from out + 1 to 0, + # incrementing n each time, so we'll fix that now: + + if i / (out + 1) > PUNYCODE_MAXINT - n + raise PunycodeOverflow, "Input needs wider integers to process." + end + n += i / (out + 1) + i %= out + 1 + + # Insert n at position i of the output: + + # not needed for Punycode: + # raise PUNYCODE_INVALID_INPUT if decode_digit(n) <= base + if out >= max_out + raise PunycodeBigOutput, "Output would exceed the space provided." + end + + #memmove(output + i + 1, output + i, (out - i) * sizeof *output) + output[i + 1, out - i] = output[i, out - i] + output[i] = n + i += 1 + + out += 1 + end + + output_length[0] = out + + output.pack("U*") + end + private_class_method :punycode_decode + + def self.punycode_basic?(codepoint) + codepoint < 0x80 + end + private_class_method :punycode_basic? + + def self.punycode_delimiter?(codepoint) + codepoint == PUNYCODE_DELIMITER + end + private_class_method :punycode_delimiter? + + def self.punycode_encode_digit(d) + d + 22 + 75 * ((d < 26) ? 1 : 0) + end + private_class_method :punycode_encode_digit + + # Returns the numeric value of a basic codepoint + # (for use in representing integers) in the range 0 to + # base - 1, or PUNYCODE_BASE if codepoint does not represent a value. + def self.punycode_decode_digit(codepoint) + if codepoint - 48 < 10 + codepoint - 22 + elsif codepoint - 65 < 26 + codepoint - 65 + elsif codepoint - 97 < 26 + codepoint - 97 + else + PUNYCODE_BASE + end + end + private_class_method :punycode_decode_digit + + # Bias adaptation method + def self.punycode_adapt(delta, numpoints, firsttime) + delta = firsttime ? delta / PUNYCODE_DAMP : delta >> 1 + # delta >> 1 is a faster way of doing delta / 2 + delta += delta / numpoints + difference = PUNYCODE_BASE - PUNYCODE_TMIN + + k = 0 + while delta > (difference * PUNYCODE_TMAX) / 2 + delta /= difference + k += PUNYCODE_BASE + end + + k + (difference + 1) * delta / (delta + PUNYCODE_SKEW) + end + private_class_method :punycode_adapt + end + # :startdoc: +end diff --git a/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/lib/addressable/template.rb b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/lib/addressable/template.rb new file mode 100644 index 0000000..08556d9 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/lib/addressable/template.rb @@ -0,0 +1,1029 @@ +# frozen_string_literal: true + +#-- +# Copyright (C) Bob Aman +# +# 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. +#++ + + +require "addressable/version" +require "addressable/uri" + +module Addressable + ## + # This is an implementation of a URI template based on + # RFC 6570 (http://tools.ietf.org/html/rfc6570). + class Template + # Constants used throughout the template code. + anything = + Addressable::URI::CharacterClasses::RESERVED + + Addressable::URI::CharacterClasses::UNRESERVED + + + variable_char_class = + Addressable::URI::CharacterClasses::ALPHA + + Addressable::URI::CharacterClasses::DIGIT + '_' + + var_char = + "(?>(?:[#{variable_char_class}]|%[a-fA-F0-9][a-fA-F0-9])+)" + RESERVED = + "(?:[#{anything}]|%[a-fA-F0-9][a-fA-F0-9])" + UNRESERVED = + "(?:[#{ + Addressable::URI::CharacterClasses::UNRESERVED + }]|%[a-fA-F0-9][a-fA-F0-9])" + variable = + "(?:#{var_char}(?:\\.?#{var_char})*)" + varspec = + "(?:(#{variable})(\\*|:\\d+)?)" + VARNAME = + /^#{variable}$/ + VARSPEC = + /^#{varspec}$/ + VARIABLE_LIST = + /^#{varspec}(?:,#{varspec})*$/ + operator = + "+#./;?&=,!@|" + EXPRESSION = + /\{([#{operator}])?(#{varspec}(?:,#{varspec})*)\}/ + + + LEADERS = { + '?' => '?', + '/' => '/', + '#' => '#', + '.' => '.', + ';' => ';', + '&' => '&' + } + JOINERS = { + '?' => '&', + '.' => '.', + ';' => ';', + '&' => '&', + '/' => '/' + } + + ## + # Raised if an invalid template value is supplied. + class InvalidTemplateValueError < StandardError + end + + ## + # Raised if an invalid template operator is used in a pattern. + class InvalidTemplateOperatorError < StandardError + end + + ## + # Raised if an invalid template operator is used in a pattern. + class TemplateOperatorAbortedError < StandardError + end + + ## + # This class represents the data that is extracted when a Template + # is matched against a URI. + class MatchData + ## + # Creates a new MatchData object. + # MatchData objects should never be instantiated directly. + # + # @param [Addressable::URI] uri + # The URI that the template was matched against. + def initialize(uri, template, mapping) + @uri = uri.dup.freeze + @template = template + @mapping = mapping.dup.freeze + end + + ## + # @return [Addressable::URI] + # The URI that the Template was matched against. + attr_reader :uri + + ## + # @return [Addressable::Template] + # The Template used for the match. + attr_reader :template + + ## + # @return [Hash] + # The mapping that resulted from the match. + # Note that this mapping does not include keys or values for + # variables that appear in the Template, but are not present + # in the URI. + attr_reader :mapping + + ## + # @return [Array] + # The list of variables that were present in the Template. + # Note that this list will include variables which do not appear + # in the mapping because they were not present in URI. + def variables + self.template.variables + end + alias_method :keys, :variables + alias_method :names, :variables + + ## + # @return [Array] + # The list of values that were captured by the Template. + # Note that this list will include nils for any variables which + # were in the Template, but did not appear in the URI. + def values + @values ||= self.variables.inject([]) do |accu, key| + accu << self.mapping[key] + accu + end + end + alias_method :captures, :values + + ## + # Accesses captured values by name or by index. + # + # @param [String, Symbol, Fixnum] key + # Capture index or name. Note that when accessing by with index + # of 0, the full URI will be returned. The intention is to mimic + # the ::MatchData#[] behavior. + # + # @param [#to_int, nil] len + # If provided, an array of values will be returned with the given + # parameter used as length. + # + # @return [Array, String, nil] + # The captured value corresponding to the index or name. If the + # value was not provided or the key is unknown, nil will be + # returned. + # + # If the second parameter is provided, an array of that length will + # be returned instead. + def [](key, len = nil) + if len + to_a[key, len] + elsif String === key or Symbol === key + mapping[key.to_s] + else + to_a[key] + end + end + + ## + # @return [Array] + # Array with the matched URI as first element followed by the captured + # values. + def to_a + [to_s, *values] + end + + ## + # @return [String] + # The matched URI as String. + def to_s + uri.to_s + end + alias_method :string, :to_s + + # Returns multiple captured values at once. + # + # @param [String, Symbol, Fixnum] *indexes + # Indices of the captures to be returned + # + # @return [Array] + # Values corresponding to given indices. + # + # @see Addressable::Template::MatchData#[] + def values_at(*indexes) + indexes.map { |i| self[i] } + end + + ## + # Returns a String representation of the MatchData's state. + # + # @return [String] The MatchData's state, as a String. + def inspect + sprintf("#<%s:%#0x RESULT:%s>", + self.class.to_s, self.object_id, self.mapping.inspect) + end + + ## + # Dummy method for code expecting a ::MatchData instance + # + # @return [String] An empty string. + def pre_match + "" + end + alias_method :post_match, :pre_match + end + + ## + # Creates a new Addressable::Template object. + # + # @param [#to_str] pattern The URI Template pattern. + # + # @return [Addressable::Template] The initialized Template object. + def initialize(pattern) + if !pattern.respond_to?(:to_str) + raise TypeError, "Can't convert #{pattern.class} into String." + end + @pattern = pattern.to_str.dup.freeze + end + + ## + # Freeze URI, initializing instance variables. + # + # @return [Addressable::URI] The frozen URI object. + def freeze + self.variables + self.variable_defaults + self.named_captures + super + end + + ## + # @return [String] The Template object's pattern. + attr_reader :pattern + + ## + # Returns a String representation of the Template object's state. + # + # @return [String] The Template object's state, as a String. + def inspect + sprintf("#<%s:%#0x PATTERN:%s>", + self.class.to_s, self.object_id, self.pattern) + end + + ## + # Returns true if the Template objects are equal. This method + # does NOT normalize either Template before doing the comparison. + # + # @param [Object] template The Template to compare. + # + # @return [TrueClass, FalseClass] + # true if the Templates are equivalent, false + # otherwise. + def ==(template) + return false unless template.kind_of?(Template) + return self.pattern == template.pattern + end + + ## + # Addressable::Template makes no distinction between `==` and `eql?`. + # + # @see #== + alias_method :eql?, :== + + ## + # Extracts a mapping from the URI using a URI Template pattern. + # + # @param [Addressable::URI, #to_str] uri + # The URI to extract from. + # + # @param [#restore, #match] processor + # A template processor object may optionally be supplied. + # + # The object should respond to either the restore or + # match messages or both. The restore method should + # take two parameters: `[String] name` and `[String] value`. + # The restore method should reverse any transformations that + # have been performed on the value to ensure a valid URI. + # The match method should take a single + # parameter: `[String] name`. The match method should return + # a String containing a regular expression capture group for + # matching on that particular variable. The default value is `".*?"`. + # The match method has no effect on multivariate operator + # expansions. + # + # @return [Hash, NilClass] + # The Hash mapping that was extracted from the URI, or + # nil if the URI didn't match the template. + # + # @example + # class ExampleProcessor + # def self.restore(name, value) + # return value.gsub(/\+/, " ") if name == "query" + # return value + # end + # + # def self.match(name) + # return ".*?" if name == "first" + # return ".*" + # end + # end + # + # uri = Addressable::URI.parse( + # "http://example.com/search/an+example+search+query/" + # ) + # Addressable::Template.new( + # "http://example.com/search/{query}/" + # ).extract(uri, ExampleProcessor) + # #=> {"query" => "an example search query"} + # + # uri = Addressable::URI.parse("http://example.com/a/b/c/") + # Addressable::Template.new( + # "http://example.com/{first}/{second}/" + # ).extract(uri, ExampleProcessor) + # #=> {"first" => "a", "second" => "b/c"} + # + # uri = Addressable::URI.parse("http://example.com/a/b/c/") + # Addressable::Template.new( + # "http://example.com/{first}/{-list|/|second}/" + # ).extract(uri) + # #=> {"first" => "a", "second" => ["b", "c"]} + def extract(uri, processor=nil) + match_data = self.match(uri, processor) + return (match_data ? match_data.mapping : nil) + end + + ## + # Extracts match data from the URI using a URI Template pattern. + # + # @param [Addressable::URI, #to_str] uri + # The URI to extract from. + # + # @param [#restore, #match] processor + # A template processor object may optionally be supplied. + # + # The object should respond to either the restore or + # match messages or both. The restore method should + # take two parameters: `[String] name` and `[String] value`. + # The restore method should reverse any transformations that + # have been performed on the value to ensure a valid URI. + # The match method should take a single + # parameter: `[String] name`. The match method should return + # a String containing a regular expression capture group for + # matching on that particular variable. The default value is `".*?"`. + # The match method has no effect on multivariate operator + # expansions. + # + # @return [Hash, NilClass] + # The Hash mapping that was extracted from the URI, or + # nil if the URI didn't match the template. + # + # @example + # class ExampleProcessor + # def self.restore(name, value) + # return value.gsub(/\+/, " ") if name == "query" + # return value + # end + # + # def self.match(name) + # return ".*?" if name == "first" + # return ".*" + # end + # end + # + # uri = Addressable::URI.parse( + # "http://example.com/search/an+example+search+query/" + # ) + # match = Addressable::Template.new( + # "http://example.com/search/{query}/" + # ).match(uri, ExampleProcessor) + # match.variables + # #=> ["query"] + # match.captures + # #=> ["an example search query"] + # + # uri = Addressable::URI.parse("http://example.com/a/b/c/") + # match = Addressable::Template.new( + # "http://example.com/{first}/{+second}/" + # ).match(uri, ExampleProcessor) + # match.variables + # #=> ["first", "second"] + # match.captures + # #=> ["a", "b/c"] + # + # uri = Addressable::URI.parse("http://example.com/a/b/c/") + # match = Addressable::Template.new( + # "http://example.com/{first}{/second*}/" + # ).match(uri) + # match.variables + # #=> ["first", "second"] + # match.captures + # #=> ["a", ["b", "c"]] + def match(uri, processor=nil) + uri = Addressable::URI.parse(uri) unless uri.is_a?(Addressable::URI) + mapping = {} + + # First, we need to process the pattern, and extract the values. + expansions, expansion_regexp = + parse_template_pattern(pattern, processor) + + return nil unless uri.to_str.match(expansion_regexp) + unparsed_values = uri.to_str.scan(expansion_regexp).flatten + + if uri.to_str == pattern + return Addressable::Template::MatchData.new(uri, self, mapping) + elsif expansions.size > 0 + index = 0 + expansions.each do |expansion| + _, operator, varlist = *expansion.match(EXPRESSION) + varlist.split(',').each do |varspec| + _, name, modifier = *varspec.match(VARSPEC) + mapping[name] ||= nil + case operator + when nil, '+', '#', '/', '.' + unparsed_value = unparsed_values[index] + name = varspec[VARSPEC, 1] + value = unparsed_value + value = value.split(JOINERS[operator]) if value && modifier == '*' + when ';', '?', '&' + if modifier == '*' + if unparsed_values[index] + value = unparsed_values[index].split(JOINERS[operator]) + value = value.inject({}) do |acc, v| + key, val = v.split('=') + val = "" if val.nil? + acc[key] = val + acc + end + end + else + if (unparsed_values[index]) + name, value = unparsed_values[index].split('=') + value = "" if value.nil? + end + end + end + if processor != nil && processor.respond_to?(:restore) + value = processor.restore(name, value) + end + if processor == nil + if value.is_a?(Hash) + value = value.inject({}){|acc, (k, v)| + acc[Addressable::URI.unencode_component(k)] = + Addressable::URI.unencode_component(v) + acc + } + elsif value.is_a?(Array) + value = value.map{|v| Addressable::URI.unencode_component(v) } + else + value = Addressable::URI.unencode_component(value) + end + end + if !mapping.has_key?(name) || mapping[name].nil? + # Doesn't exist, set to value (even if value is nil) + mapping[name] = value + end + index = index + 1 + end + end + return Addressable::Template::MatchData.new(uri, self, mapping) + else + return nil + end + end + + ## + # Expands a URI template into another URI template. + # + # @param [Hash] mapping The mapping that corresponds to the pattern. + # @param [#validate, #transform] processor + # An optional processor object may be supplied. + # @param [Boolean] normalize_values + # Optional flag to enable/disable unicode normalization. Default: true + # + # The object should respond to either the validate or + # transform messages or both. Both the validate and + # transform methods should take two parameters: name and + # value. The validate method should return true + # or false; true if the value of the variable is valid, + # false otherwise. An InvalidTemplateValueError + # exception will be raised if the value is invalid. The transform + # method should return the transformed variable value as a String. + # If a transform method is used, the value will not be percent + # encoded automatically. Unicode normalization will be performed both + # before and after sending the value to the transform method. + # + # @return [Addressable::Template] The partially expanded URI template. + # + # @example + # Addressable::Template.new( + # "http://example.com/{one}/{two}/" + # ).partial_expand({"one" => "1"}).pattern + # #=> "http://example.com/1/{two}/" + # + # Addressable::Template.new( + # "http://example.com/{?one,two}/" + # ).partial_expand({"one" => "1"}).pattern + # #=> "http://example.com/?one=1{&two}/" + # + # Addressable::Template.new( + # "http://example.com/{?one,two,three}/" + # ).partial_expand({"one" => "1", "three" => 3}).pattern + # #=> "http://example.com/?one=1{&two}&three=3" + def partial_expand(mapping, processor=nil, normalize_values=true) + result = self.pattern.dup + mapping = normalize_keys(mapping) + result.gsub!( EXPRESSION ) do |capture| + transform_partial_capture(mapping, capture, processor, normalize_values) + end + return Addressable::Template.new(result) + end + + ## + # Expands a URI template into a full URI. + # + # @param [Hash] mapping The mapping that corresponds to the pattern. + # @param [#validate, #transform] processor + # An optional processor object may be supplied. + # @param [Boolean] normalize_values + # Optional flag to enable/disable unicode normalization. Default: true + # + # The object should respond to either the validate or + # transform messages or both. Both the validate and + # transform methods should take two parameters: name and + # value. The validate method should return true + # or false; true if the value of the variable is valid, + # false otherwise. An InvalidTemplateValueError + # exception will be raised if the value is invalid. The transform + # method should return the transformed variable value as a String. + # If a transform method is used, the value will not be percent + # encoded automatically. Unicode normalization will be performed both + # before and after sending the value to the transform method. + # + # @return [Addressable::URI] The expanded URI template. + # + # @example + # class ExampleProcessor + # def self.validate(name, value) + # return !!(value =~ /^[\w ]+$/) if name == "query" + # return true + # end + # + # def self.transform(name, value) + # return value.gsub(/ /, "+") if name == "query" + # return value + # end + # end + # + # Addressable::Template.new( + # "http://example.com/search/{query}/" + # ).expand( + # {"query" => "an example search query"}, + # ExampleProcessor + # ).to_str + # #=> "http://example.com/search/an+example+search+query/" + # + # Addressable::Template.new( + # "http://example.com/search/{query}/" + # ).expand( + # {"query" => "an example search query"} + # ).to_str + # #=> "http://example.com/search/an%20example%20search%20query/" + # + # Addressable::Template.new( + # "http://example.com/search/{query}/" + # ).expand( + # {"query" => "bogus!"}, + # ExampleProcessor + # ).to_str + # #=> Addressable::Template::InvalidTemplateValueError + def expand(mapping, processor=nil, normalize_values=true) + result = self.pattern.dup + mapping = normalize_keys(mapping) + result.gsub!( EXPRESSION ) do |capture| + transform_capture(mapping, capture, processor, normalize_values) + end + return Addressable::URI.parse(result) + end + + ## + # Returns an Array of variables used within the template pattern. + # The variables are listed in the Array in the order they appear within + # the pattern. Multiple occurrences of a variable within a pattern are + # not represented in this Array. + # + # @return [Array] The variables present in the template's pattern. + def variables + @variables ||= ordered_variable_defaults.map { |var, val| var }.uniq + end + alias_method :keys, :variables + alias_method :names, :variables + + ## + # Returns a mapping of variables to their default values specified + # in the template. Variables without defaults are not returned. + # + # @return [Hash] Mapping of template variables to their defaults + def variable_defaults + @variable_defaults ||= + Hash[*ordered_variable_defaults.reject { |k, v| v.nil? }.flatten] + end + + ## + # Coerces a template into a `Regexp` object. This regular expression will + # behave very similarly to the actual template, and should match the same + # URI values, but it cannot fully handle, for example, values that would + # extract to an `Array`. + # + # @return [Regexp] A regular expression which should match the template. + def to_regexp + _, source = parse_template_pattern(pattern) + Regexp.new(source) + end + + ## + # Returns the source of the coerced `Regexp`. + # + # @return [String] The source of the `Regexp` given by {#to_regexp}. + # + # @api private + def source + self.to_regexp.source + end + + ## + # Returns the named captures of the coerced `Regexp`. + # + # @return [Hash] The named captures of the `Regexp` given by {#to_regexp}. + # + # @api private + def named_captures + self.to_regexp.named_captures + end + + private + def ordered_variable_defaults + @ordered_variable_defaults ||= begin + expansions, _ = parse_template_pattern(pattern) + expansions.flat_map do |capture| + _, _, varlist = *capture.match(EXPRESSION) + varlist.split(',').map do |varspec| + varspec[VARSPEC, 1] + end + end + end + end + + + ## + # Loops through each capture and expands any values available in mapping + # + # @param [Hash] mapping + # Set of keys to expand + # @param [String] capture + # The expression to expand + # @param [#validate, #transform] processor + # An optional processor object may be supplied. + # @param [Boolean] normalize_values + # Optional flag to enable/disable unicode normalization. Default: true + # + # The object should respond to either the validate or + # transform messages or both. Both the validate and + # transform methods should take two parameters: name and + # value. The validate method should return true + # or false; true if the value of the variable is valid, + # false otherwise. An InvalidTemplateValueError exception + # will be raised if the value is invalid. The transform method + # should return the transformed variable value as a String. If a + # transform method is used, the value will not be percent encoded + # automatically. Unicode normalization will be performed both before and + # after sending the value to the transform method. + # + # @return [String] The expanded expression + def transform_partial_capture(mapping, capture, processor = nil, + normalize_values = true) + _, operator, varlist = *capture.match(EXPRESSION) + + vars = varlist.split(",") + + if operator == "?" + # partial expansion of form style query variables sometimes requires a + # slight reordering of the variables to produce a valid url. + first_to_expand = vars.find { |varspec| + _, name, _ = *varspec.match(VARSPEC) + mapping.key?(name) && !mapping[name].nil? + } + + vars = [first_to_expand] + vars.reject {|varspec| varspec == first_to_expand} if first_to_expand + end + + vars. + inject("".dup) do |acc, varspec| + _, name, _ = *varspec.match(VARSPEC) + next_val = if mapping.key? name + transform_capture(mapping, "{#{operator}#{varspec}}", + processor, normalize_values) + else + "{#{operator}#{varspec}}" + end + # If we've already expanded at least one '?' operator with non-empty + # value, change to '&' + operator = "&" if (operator == "?") && (next_val != "") + acc << next_val + end + end + + ## + # Transforms a mapped value so that values can be substituted into the + # template. + # + # @param [Hash] mapping The mapping to replace captures + # @param [String] capture + # The expression to replace + # @param [#validate, #transform] processor + # An optional processor object may be supplied. + # @param [Boolean] normalize_values + # Optional flag to enable/disable unicode normalization. Default: true + # + # + # The object should respond to either the validate or + # transform messages or both. Both the validate and + # transform methods should take two parameters: name and + # value. The validate method should return true + # or false; true if the value of the variable is valid, + # false otherwise. An InvalidTemplateValueError exception + # will be raised if the value is invalid. The transform method + # should return the transformed variable value as a String. If a + # transform method is used, the value will not be percent encoded + # automatically. Unicode normalization will be performed both before and + # after sending the value to the transform method. + # + # @return [String] The expanded expression + def transform_capture(mapping, capture, processor=nil, + normalize_values=true) + _, operator, varlist = *capture.match(EXPRESSION) + return_value = varlist.split(',').inject([]) do |acc, varspec| + _, name, modifier = *varspec.match(VARSPEC) + value = mapping[name] + unless value == nil || value == {} + allow_reserved = %w(+ #).include?(operator) + # Common primitives where the .to_s output is well-defined + if Numeric === value || Symbol === value || + value == true || value == false + value = value.to_s + end + length = modifier.gsub(':', '').to_i if modifier =~ /^:\d+/ + + unless (Hash === value) || + value.respond_to?(:to_ary) || value.respond_to?(:to_str) + raise TypeError, + "Can't convert #{value.class} into String or Array." + end + + value = normalize_value(value) if normalize_values + + if processor == nil || !processor.respond_to?(:transform) + # Handle percent escaping + if allow_reserved + encode_map = + Addressable::URI::CharacterClasses::RESERVED + + Addressable::URI::CharacterClasses::UNRESERVED + else + encode_map = Addressable::URI::CharacterClasses::UNRESERVED + end + if value.kind_of?(Array) + transformed_value = value.map do |val| + if length + Addressable::URI.encode_component(val[0...length], encode_map) + else + Addressable::URI.encode_component(val, encode_map) + end + end + unless modifier == "*" + transformed_value = transformed_value.join(',') + end + elsif value.kind_of?(Hash) + transformed_value = value.map do |key, val| + if modifier == "*" + "#{ + Addressable::URI.encode_component( key, encode_map) + }=#{ + Addressable::URI.encode_component( val, encode_map) + }" + else + "#{ + Addressable::URI.encode_component( key, encode_map) + },#{ + Addressable::URI.encode_component( val, encode_map) + }" + end + end + unless modifier == "*" + transformed_value = transformed_value.join(',') + end + else + if length + transformed_value = Addressable::URI.encode_component( + value[0...length], encode_map) + else + transformed_value = Addressable::URI.encode_component( + value, encode_map) + end + end + end + + # Process, if we've got a processor + if processor != nil + if processor.respond_to?(:validate) + if !processor.validate(name, value) + display_value = value.kind_of?(Array) ? value.inspect : value + raise InvalidTemplateValueError, + "#{name}=#{display_value} is an invalid template value." + end + end + if processor.respond_to?(:transform) + transformed_value = processor.transform(name, value) + if normalize_values + transformed_value = normalize_value(transformed_value) + end + end + end + acc << [name, transformed_value] + end + acc + end + return "" if return_value.empty? + join_values(operator, return_value) + end + + ## + # Takes a set of values, and joins them together based on the + # operator. + # + # @param [String, Nil] operator One of the operators from the set + # (?,&,+,#,;,/,.), or nil if there wasn't one. + # @param [Array] return_value + # The set of return values (as [variable_name, value] tuples) that will + # be joined together. + # + # @return [String] The transformed mapped value + def join_values(operator, return_value) + leader = LEADERS.fetch(operator, '') + joiner = JOINERS.fetch(operator, ',') + case operator + when '&', '?' + leader + return_value.map{|k,v| + if v.is_a?(Array) && v.first =~ /=/ + v.join(joiner) + elsif v.is_a?(Array) + v.map{|inner_value| "#{k}=#{inner_value}"}.join(joiner) + else + "#{k}=#{v}" + end + }.join(joiner) + when ';' + return_value.map{|k,v| + if v.is_a?(Array) && v.first =~ /=/ + ';' + v.join(";") + elsif v.is_a?(Array) + ';' + v.map{|inner_value| "#{k}=#{inner_value}"}.join(";") + else + v && v != '' ? ";#{k}=#{v}" : ";#{k}" + end + }.join + else + leader + return_value.map{|k,v| v}.join(joiner) + end + end + + ## + # Takes a set of values, and joins them together based on the + # operator. + # + # @param [Hash, Array, String] value + # Normalizes unicode keys and values with String#unicode_normalize (NFC) + # + # @return [Hash, Array, String] The normalized values + def normalize_value(value) + # Handle unicode normalization + if value.respond_to?(:to_ary) + value.to_ary.map! { |val| normalize_value(val) } + elsif value.kind_of?(Hash) + value = value.inject({}) { |acc, (k, v)| + acc[normalize_value(k)] = normalize_value(v) + acc + } + else + value = value.to_s if !value.kind_of?(String) + if value.encoding != Encoding::UTF_8 + value = value.dup.force_encoding(Encoding::UTF_8) + end + value = value.unicode_normalize(:nfc) + end + value + end + + ## + # Generates a hash with string keys + # + # @param [Hash] mapping A mapping hash to normalize + # + # @return [Hash] + # A hash with stringified keys + def normalize_keys(mapping) + return mapping.inject({}) do |accu, pair| + name, value = pair + if Symbol === name + name = name.to_s + elsif name.respond_to?(:to_str) + name = name.to_str + else + raise TypeError, + "Can't convert #{name.class} into String." + end + accu[name] = value + accu + end + end + + ## + # Generates the Regexp that parses a template pattern. Memoizes the + # value if template processor not set (processors may not be deterministic) + # + # @param [String] pattern The URI template pattern. + # @param [#match] processor The template processor to use. + # + # @return [Array, Regexp] + # An array of expansion variables nad a regular expression which may be + # used to parse a template pattern + def parse_template_pattern(pattern, processor = nil) + if processor.nil? && pattern == @pattern + @cached_template_parse ||= + parse_new_template_pattern(pattern, processor) + else + parse_new_template_pattern(pattern, processor) + end + end + + ## + # Generates the Regexp that parses a template pattern. + # + # @param [String] pattern The URI template pattern. + # @param [#match] processor The template processor to use. + # + # @return [Array, Regexp] + # An array of expansion variables nad a regular expression which may be + # used to parse a template pattern + def parse_new_template_pattern(pattern, processor = nil) + # Escape the pattern. The two gsubs restore the escaped curly braces + # back to their original form. Basically, escape everything that isn't + # within an expansion. + escaped_pattern = Regexp.escape( + pattern + ).gsub(/\\\{(.*?)\\\}/) do |escaped| + escaped.gsub(/\\(.)/, "\\1") + end + + expansions = [] + + # Create a regular expression that captures the values of the + # variables in the URI. + regexp_string = escaped_pattern.gsub( EXPRESSION ) do |expansion| + + expansions << expansion + _, operator, varlist = *expansion.match(EXPRESSION) + leader = Regexp.escape(LEADERS.fetch(operator, '')) + joiner = Regexp.escape(JOINERS.fetch(operator, ',')) + combined = varlist.split(',').map do |varspec| + _, name, modifier = *varspec.match(VARSPEC) + + result = processor && processor.respond_to?(:match) ? processor.match(name) : nil + if result + "(?<#{name}>#{ result })" + else + group = case operator + when '+' + "#{ RESERVED }*?" + when '#' + "#{ RESERVED }*?" + when '/' + "#{ UNRESERVED }*?" + when '.' + "#{ UNRESERVED.gsub('\.', '') }*?" + when ';' + "#{ UNRESERVED }*=?#{ UNRESERVED }*?" + when '?' + "#{ UNRESERVED }*=#{ UNRESERVED }*?" + when '&' + "#{ UNRESERVED }*=#{ UNRESERVED }*?" + else + "#{ UNRESERVED }*?" + end + if modifier == '*' + "(?<#{name}>#{group}(?:#{joiner}?#{group})*)?" + else + "(?<#{name}>#{group})?" + end + end + end.join("#{joiner}?") + "(?:|#{leader}#{combined})" + end + + # Ensure that the regular expression matches the whole URI. + regexp_string = "\\A#{regexp_string}\\z" + return expansions, Regexp.new(regexp_string) + end + + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/lib/addressable/uri.rb b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/lib/addressable/uri.rb new file mode 100644 index 0000000..07c9aeb --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/lib/addressable/uri.rb @@ -0,0 +1,2602 @@ +# frozen_string_literal: true + +#-- +# Copyright (C) Bob Aman +# +# 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. +#++ + + +require "addressable/version" +require "addressable/idna" +require "public_suffix" + +## +# Addressable is a library for processing links and URIs. +module Addressable + ## + # This is an implementation of a URI parser based on + # RFC 3986, + # RFC 3987. + class URI + ## + # Raised if something other than a uri is supplied. + class InvalidURIError < StandardError + end + + ## + # Container for the character classes specified in + # RFC 3986. + # + # Note: Concatenated and interpolated `String`s are not affected by the + # `frozen_string_literal` directive and must be frozen explicitly. + # + # Interpolated `String`s *were* frozen this way before Ruby 3.0: + # https://bugs.ruby-lang.org/issues/17104 + module CharacterClasses + ALPHA = "a-zA-Z" + DIGIT = "0-9" + GEN_DELIMS = "\\:\\/\\?\\#\\[\\]\\@" + SUB_DELIMS = "\\!\\$\\&\\'\\(\\)\\*\\+\\,\\;\\=" + RESERVED = (GEN_DELIMS + SUB_DELIMS).freeze + UNRESERVED = (ALPHA + DIGIT + "\\-\\.\\_\\~").freeze + RESERVED_AND_UNRESERVED = RESERVED + UNRESERVED + PCHAR = (UNRESERVED + SUB_DELIMS + "\\:\\@").freeze + SCHEME = (ALPHA + DIGIT + "\\-\\+\\.").freeze + HOST = (UNRESERVED + SUB_DELIMS + "\\[\\:\\]").freeze + AUTHORITY = (PCHAR + "\\[\\]").freeze + PATH = (PCHAR + "\\/").freeze + QUERY = (PCHAR + "\\/\\?").freeze + FRAGMENT = (PCHAR + "\\/\\?").freeze + end + + module NormalizeCharacterClasses + HOST = /[^#{CharacterClasses::HOST}]/ + UNRESERVED = /[^#{CharacterClasses::UNRESERVED}]/ + PCHAR = /[^#{CharacterClasses::PCHAR}]/ + SCHEME = /[^#{CharacterClasses::SCHEME}]/ + FRAGMENT = /[^#{CharacterClasses::FRAGMENT}]/ + QUERY = %r{[^a-zA-Z0-9\-\.\_\~\!\$\'\(\)\*\+\,\=\:\@\/\?%]|%(?!2B|2b)} + end + + module CharacterClassesRegexps + AUTHORITY = /[^#{CharacterClasses::AUTHORITY}]/ + FRAGMENT = /[^#{CharacterClasses::FRAGMENT}]/ + HOST = /[^#{CharacterClasses::HOST}]/ + PATH = /[^#{CharacterClasses::PATH}]/ + QUERY = /[^#{CharacterClasses::QUERY}]/ + RESERVED = /[^#{CharacterClasses::RESERVED}]/ + RESERVED_AND_UNRESERVED = /[^#{CharacterClasses::RESERVED_AND_UNRESERVED}]/ + SCHEME = /[^#{CharacterClasses::SCHEME}]/ + UNRESERVED = /[^#{CharacterClasses::UNRESERVED}]/ + end + + SLASH = '/' + EMPTY_STR = '' + + URIREGEX = /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/ + + PORT_MAPPING = { + "http" => 80, + "https" => 443, + "ftp" => 21, + "tftp" => 69, + "sftp" => 22, + "ssh" => 22, + "svn+ssh" => 22, + "telnet" => 23, + "nntp" => 119, + "gopher" => 70, + "wais" => 210, + "ldap" => 389, + "prospero" => 1525 + }.freeze + + ## + # Returns a URI object based on the parsed string. + # + # @param [String, Addressable::URI, #to_str] uri + # The URI string to parse. + # No parsing is performed if the object is already an + # Addressable::URI. + # + # @return [Addressable::URI] The parsed URI. + def self.parse(uri) + # If we were given nil, return nil. + return nil unless uri + # If a URI object is passed, just return itself. + return uri.dup if uri.kind_of?(self) + + # If a URI object of the Ruby standard library variety is passed, + # convert it to a string, then parse the string. + # We do the check this way because we don't want to accidentally + # cause a missing constant exception to be thrown. + if uri.class.name =~ /^URI\b/ + uri = uri.to_s + end + + # Otherwise, convert to a String + begin + uri = uri.to_str + rescue TypeError, NoMethodError + raise TypeError, "Can't convert #{uri.class} into String." + end unless uri.is_a?(String) + + # This Regexp supplied as an example in RFC 3986, and it works great. + scan = uri.scan(URIREGEX) + fragments = scan[0] + scheme = fragments[1] + authority = fragments[3] + path = fragments[4] + query = fragments[6] + fragment = fragments[8] + user = nil + password = nil + host = nil + port = nil + if authority != nil + # The Regexp above doesn't split apart the authority. + userinfo = authority[/^([^\[\]]*)@/, 1] + if userinfo != nil + user = userinfo.strip[/^([^:]*):?/, 1] + password = userinfo.strip[/:(.*)$/, 1] + end + + host = authority.sub( + /^([^\[\]]*)@/, EMPTY_STR + ).sub( + /:([^:@\[\]]*?)$/, EMPTY_STR + ) + + port = authority[/:([^:@\[\]]*?)$/, 1] + port = nil if port == EMPTY_STR + end + + return new( + :scheme => scheme, + :user => user, + :password => password, + :host => host, + :port => port, + :path => path, + :query => query, + :fragment => fragment + ) + end + + ## + # Converts an input to a URI. The input does not have to be a valid + # URI — the method will use heuristics to guess what URI was intended. + # This is not standards-compliant, merely user-friendly. + # + # @param [String, Addressable::URI, #to_str] uri + # The URI string to parse. + # No parsing is performed if the object is already an + # Addressable::URI. + # @param [Hash] hints + # A Hash of hints to the heuristic parser. + # Defaults to {:scheme => "http"}. + # + # @return [Addressable::URI] The parsed URI. + def self.heuristic_parse(uri, hints={}) + # If we were given nil, return nil. + return nil unless uri + # If a URI object is passed, just return itself. + return uri.dup if uri.kind_of?(self) + + # If a URI object of the Ruby standard library variety is passed, + # convert it to a string, then parse the string. + # We do the check this way because we don't want to accidentally + # cause a missing constant exception to be thrown. + if uri.class.name =~ /^URI\b/ + uri = uri.to_s + end + + unless uri.respond_to?(:to_str) + raise TypeError, "Can't convert #{uri.class} into String." + end + # Otherwise, convert to a String + uri = uri.to_str.dup.strip + hints = { + :scheme => "http" + }.merge(hints) + case uri + when /^http:\//i + uri.sub!(/^http:\/+/i, "http://") + when /^https:\//i + uri.sub!(/^https:\/+/i, "https://") + when /^feed:\/+http:\//i + uri.sub!(/^feed:\/+http:\/+/i, "feed:http://") + when /^feed:\//i + uri.sub!(/^feed:\/+/i, "feed://") + when %r[^file:/{4}]i + uri.sub!(%r[^file:/+]i, "file:////") + when %r[^file://localhost/]i + uri.sub!(%r[^file://localhost/+]i, "file:///") + when %r[^file:/+]i + uri.sub!(%r[^file:/+]i, "file:///") + when /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/ + uri.sub!(/^/, hints[:scheme] + "://") + when /\A\d+\..*:\d+\z/ + uri = "#{hints[:scheme]}://#{uri}" + end + match = uri.match(URIREGEX) + fragments = match.captures + authority = fragments[3] + if authority && authority.length > 0 + new_authority = authority.tr("\\", "/").gsub(" ", "%20") + # NOTE: We want offset 4, not 3! + offset = match.offset(4) + uri = uri.dup + uri[offset[0]...offset[1]] = new_authority + end + parsed = self.parse(uri) + if parsed.scheme =~ /^[^\/?#\.]+\.[^\/?#]+$/ + parsed = self.parse(hints[:scheme] + "://" + uri) + end + if parsed.path.include?(".") + if parsed.path[/\b@\b/] + parsed.scheme = "mailto" unless parsed.scheme + elsif new_host = parsed.path[/^([^\/]+\.[^\/]*)/, 1] + parsed.defer_validation do + new_path = parsed.path.sub( + Regexp.new("^" + Regexp.escape(new_host)), EMPTY_STR) + parsed.host = new_host + parsed.path = new_path + parsed.scheme = hints[:scheme] unless parsed.scheme + end + end + end + return parsed + end + + ## + # Converts a path to a file scheme URI. If the path supplied is + # relative, it will be returned as a relative URI. If the path supplied + # is actually a non-file URI, it will parse the URI as if it had been + # parsed with Addressable::URI.parse. Handles all of the + # various Microsoft-specific formats for specifying paths. + # + # @param [String, Addressable::URI, #to_str] path + # Typically a String path to a file or directory, but + # will return a sensible return value if an absolute URI is supplied + # instead. + # + # @return [Addressable::URI] + # The parsed file scheme URI or the original URI if some other URI + # scheme was provided. + # + # @example + # base = Addressable::URI.convert_path("/absolute/path/") + # uri = Addressable::URI.convert_path("relative/path") + # (base + uri).to_s + # #=> "file:///absolute/path/relative/path" + # + # Addressable::URI.convert_path( + # "c:\\windows\\My Documents 100%20\\foo.txt" + # ).to_s + # #=> "file:///c:/windows/My%20Documents%20100%20/foo.txt" + # + # Addressable::URI.convert_path("http://example.com/").to_s + # #=> "http://example.com/" + def self.convert_path(path) + # If we were given nil, return nil. + return nil unless path + # If a URI object is passed, just return itself. + return path if path.kind_of?(self) + unless path.respond_to?(:to_str) + raise TypeError, "Can't convert #{path.class} into String." + end + # Otherwise, convert to a String + path = path.to_str.strip + + path.sub!(/^file:\/?\/?/, EMPTY_STR) if path =~ /^file:\/?\/?/ + path = SLASH + path if path =~ /^([a-zA-Z])[\|:]/ + uri = self.parse(path) + + if uri.scheme == nil + # Adjust windows-style uris + uri.path.sub!(/^\/?([a-zA-Z])[\|:][\\\/]/) do + "/#{$1.downcase}:/" + end + uri.path.tr!("\\", SLASH) + if File.exist?(uri.path) && + File.stat(uri.path).directory? + uri.path.chomp!(SLASH) + uri.path = uri.path + '/' + end + + # If the path is absolute, set the scheme and host. + if uri.path.start_with?(SLASH) + uri.scheme = "file" + uri.host = EMPTY_STR + end + uri.normalize! + end + + return uri + end + + ## + # Joins several URIs together. + # + # @param [String, Addressable::URI, #to_str] *uris + # The URIs to join. + # + # @return [Addressable::URI] The joined URI. + # + # @example + # base = "http://example.com/" + # uri = Addressable::URI.parse("relative/path") + # Addressable::URI.join(base, uri) + # #=> # + def self.join(*uris) + uri_objects = uris.collect do |uri| + unless uri.respond_to?(:to_str) + raise TypeError, "Can't convert #{uri.class} into String." + end + uri.kind_of?(self) ? uri : self.parse(uri.to_str) + end + result = uri_objects.shift.dup + uri_objects.each do |uri| + result.join!(uri) + end + return result + end + + ## + # Tables used to optimize encoding operations in `self.encode_component` + # and `self.normalize_component` + SEQUENCE_ENCODING_TABLE = (0..255).map do |byte| + format("%02x", byte).freeze + end.freeze + + SEQUENCE_UPCASED_PERCENT_ENCODING_TABLE = (0..255).map do |byte| + format("%%%02X", byte).freeze + end.freeze + + ## + # Percent encodes a URI component. + # + # @param [String, #to_str] component The URI component to encode. + # + # @param [String, Regexp] character_class + # The characters which are not percent encoded. If a String + # is passed, the String must be formatted as a regular + # expression character class. (Do not include the surrounding square + # brackets.) For example, "b-zB-Z0-9" would cause + # everything but the letters 'b' through 'z' and the numbers '0' through + # '9' to be percent encoded. If a Regexp is passed, the + # value /[^b-zB-Z0-9]/ would have the same effect. A set of + # useful String values may be found in the + # Addressable::URI::CharacterClasses module. The default + # value is the reserved plus unreserved character classes specified in + # RFC 3986. + # + # @param [Regexp] upcase_encoded + # A string of characters that may already be percent encoded, and whose + # encodings should be upcased. This allows normalization of percent + # encodings for characters not included in the + # character_class. + # + # @return [String] The encoded component. + # + # @example + # Addressable::URI.encode_component("simple/example", "b-zB-Z0-9") + # => "simple%2Fex%61mple" + # Addressable::URI.encode_component("simple/example", /[^b-zB-Z0-9]/) + # => "simple%2Fex%61mple" + # Addressable::URI.encode_component( + # "simple/example", Addressable::URI::CharacterClasses::UNRESERVED + # ) + # => "simple%2Fexample" + def self.encode_component(component, character_class=CharacterClassesRegexps::RESERVED_AND_UNRESERVED, upcase_encoded='') + return nil if component.nil? + + begin + if component.kind_of?(Symbol) || + component.kind_of?(Numeric) || + component.kind_of?(TrueClass) || + component.kind_of?(FalseClass) + component = component.to_s + else + component = component.to_str + end + rescue TypeError, NoMethodError + raise TypeError, "Can't convert #{component.class} into String." + end if !component.is_a? String + + if ![String, Regexp].include?(character_class.class) + raise TypeError, + "Expected String or Regexp, got #{character_class.inspect}" + end + if character_class.kind_of?(String) + character_class = /[^#{character_class}]/ + end + # We can't perform regexps on invalid UTF sequences, but + # here we need to, so switch to ASCII. + component = component.dup + component.force_encoding(Encoding::ASCII_8BIT) + # Avoiding gsub! because there are edge cases with frozen strings + component = component.gsub(character_class) do |char| + SEQUENCE_UPCASED_PERCENT_ENCODING_TABLE[char.ord] + end + if upcase_encoded.length > 0 + upcase_encoded_chars = upcase_encoded.bytes.map do |byte| + SEQUENCE_ENCODING_TABLE[byte] + end + component = component.gsub(/%(#{upcase_encoded_chars.join('|')})/, + &:upcase) + end + + return component + end + + class << self + alias_method :escape_component, :encode_component + end + + ## + # Unencodes any percent encoded characters within a URI component. + # This method may be used for unencoding either components or full URIs, + # however, it is recommended to use the unencode_component + # alias when unencoding components. + # + # @param [String, Addressable::URI, #to_str] uri + # The URI or component to unencode. + # + # @param [Class] return_type + # The type of object to return. + # This value may only be set to String or + # Addressable::URI. All other values are invalid. Defaults + # to String. + # + # @param [String] leave_encoded + # A string of characters to leave encoded. If a percent encoded character + # in this list is encountered then it will remain percent encoded. + # + # @return [String, Addressable::URI] + # The unencoded component or URI. + # The return type is determined by the return_type + # parameter. + def self.unencode(uri, return_type=String, leave_encoded='') + return nil if uri.nil? + + begin + uri = uri.to_str + rescue NoMethodError, TypeError + raise TypeError, "Can't convert #{uri.class} into String." + end if !uri.is_a? String + if ![String, ::Addressable::URI].include?(return_type) + raise TypeError, + "Expected Class (String or Addressable::URI), " + + "got #{return_type.inspect}" + end + + result = uri.gsub(/%[0-9a-f]{2}/i) do |sequence| + c = sequence[1..3].to_i(16).chr + c.force_encoding(sequence.encoding) + leave_encoded.include?(c) ? sequence : c + end + + result.force_encoding(Encoding::UTF_8) + if return_type == String + return result + elsif return_type == ::Addressable::URI + return ::Addressable::URI.parse(result) + end + end + + class << self + alias_method :unescape, :unencode + alias_method :unencode_component, :unencode + alias_method :unescape_component, :unencode + end + + + ## + # Normalizes the encoding of a URI component. + # + # @param [String, #to_str] component The URI component to encode. + # + # @param [String, Regexp] character_class + # The characters which are not percent encoded. If a String + # is passed, the String must be formatted as a regular + # expression character class. (Do not include the surrounding square + # brackets.) For example, "b-zB-Z0-9" would cause + # everything but the letters 'b' through 'z' and the numbers '0' + # through '9' to be percent encoded. If a Regexp is passed, + # the value /[^b-zB-Z0-9]/ would have the same effect. A + # set of useful String values may be found in the + # Addressable::URI::CharacterClasses module. The default + # value is the reserved plus unreserved character classes specified in + # RFC 3986. + # + # @param [String] leave_encoded + # When character_class is a String then + # leave_encoded is a string of characters that should remain + # percent encoded while normalizing the component; if they appear percent + # encoded in the original component, then they will be upcased ("%2f" + # normalized to "%2F") but otherwise left alone. + # + # @return [String] The normalized component. + # + # @example + # Addressable::URI.normalize_component("simpl%65/%65xampl%65", "b-zB-Z") + # => "simple%2Fex%61mple" + # Addressable::URI.normalize_component( + # "simpl%65/%65xampl%65", /[^b-zB-Z]/ + # ) + # => "simple%2Fex%61mple" + # Addressable::URI.normalize_component( + # "simpl%65/%65xampl%65", + # Addressable::URI::CharacterClasses::UNRESERVED + # ) + # => "simple%2Fexample" + # Addressable::URI.normalize_component( + # "one%20two%2fthree%26four", + # "0-9a-zA-Z &/", + # "/" + # ) + # => "one two%2Fthree&four" + def self.normalize_component(component, character_class= + CharacterClassesRegexps::RESERVED_AND_UNRESERVED, + leave_encoded='') + return nil if component.nil? + + begin + component = component.to_str + rescue NoMethodError, TypeError + raise TypeError, "Can't convert #{component.class} into String." + end if !component.is_a? String + + if ![String, Regexp].include?(character_class.class) + raise TypeError, + "Expected String or Regexp, got #{character_class.inspect}" + end + if character_class.kind_of?(String) + leave_re = if leave_encoded.length > 0 + character_class = "#{character_class}%" unless character_class.include?('%') + + bytes = leave_encoded.bytes + leave_encoded_pattern = bytes.map { |b| SEQUENCE_ENCODING_TABLE[b] }.join('|') + "|%(?!#{leave_encoded_pattern}|#{leave_encoded_pattern.upcase})" + end + + character_class = if leave_re + /[^#{character_class}]#{leave_re}/ + else + /[^#{character_class}]/ + end + end + # We can't perform regexps on invalid UTF sequences, but + # here we need to, so switch to ASCII. + component = component.dup + component.force_encoding(Encoding::ASCII_8BIT) + unencoded = self.unencode_component(component, String, leave_encoded) + begin + encoded = self.encode_component( + unencoded.unicode_normalize(:nfc), + character_class, + leave_encoded + ) + rescue ArgumentError + encoded = self.encode_component(unencoded) + end + encoded.force_encoding(Encoding::UTF_8) + return encoded + end + + ## + # Percent encodes any special characters in the URI. + # + # @param [String, Addressable::URI, #to_str] uri + # The URI to encode. + # + # @param [Class] return_type + # The type of object to return. + # This value may only be set to String or + # Addressable::URI. All other values are invalid. Defaults + # to String. + # + # @return [String, Addressable::URI] + # The encoded URI. + # The return type is determined by the return_type + # parameter. + def self.encode(uri, return_type=String) + return nil if uri.nil? + + begin + uri = uri.to_str + rescue NoMethodError, TypeError + raise TypeError, "Can't convert #{uri.class} into String." + end if !uri.is_a? String + + if ![String, ::Addressable::URI].include?(return_type) + raise TypeError, + "Expected Class (String or Addressable::URI), " + + "got #{return_type.inspect}" + end + uri_object = uri.kind_of?(self) ? uri : self.parse(uri) + encoded_uri = Addressable::URI.new( + :scheme => self.encode_component(uri_object.scheme, + Addressable::URI::CharacterClassesRegexps::SCHEME), + :authority => self.encode_component(uri_object.authority, + Addressable::URI::CharacterClassesRegexps::AUTHORITY), + :path => self.encode_component(uri_object.path, + Addressable::URI::CharacterClassesRegexps::PATH), + :query => self.encode_component(uri_object.query, + Addressable::URI::CharacterClassesRegexps::QUERY), + :fragment => self.encode_component(uri_object.fragment, + Addressable::URI::CharacterClassesRegexps::FRAGMENT) + ) + if return_type == String + return encoded_uri.to_s + elsif return_type == ::Addressable::URI + return encoded_uri + end + end + + class << self + alias_method :escape, :encode + end + + ## + # Normalizes the encoding of a URI. Characters within a hostname are + # not percent encoded to allow for internationalized domain names. + # + # @param [String, Addressable::URI, #to_str] uri + # The URI to encode. + # + # @param [Class] return_type + # The type of object to return. + # This value may only be set to String or + # Addressable::URI. All other values are invalid. Defaults + # to String. + # + # @return [String, Addressable::URI] + # The encoded URI. + # The return type is determined by the return_type + # parameter. + def self.normalized_encode(uri, return_type=String) + begin + uri = uri.to_str + rescue NoMethodError, TypeError + raise TypeError, "Can't convert #{uri.class} into String." + end if !uri.is_a? String + + if ![String, ::Addressable::URI].include?(return_type) + raise TypeError, + "Expected Class (String or Addressable::URI), " + + "got #{return_type.inspect}" + end + uri_object = uri.kind_of?(self) ? uri : self.parse(uri) + components = { + :scheme => self.unencode_component(uri_object.scheme), + :user => self.unencode_component(uri_object.user), + :password => self.unencode_component(uri_object.password), + :host => self.unencode_component(uri_object.host), + :port => (uri_object.port.nil? ? nil : uri_object.port.to_s), + :path => self.unencode_component(uri_object.path), + :query => self.unencode_component(uri_object.query), + :fragment => self.unencode_component(uri_object.fragment) + } + components.each do |key, value| + if value != nil + begin + components[key] = value.to_str.unicode_normalize(:nfc) + rescue ArgumentError + # Likely a malformed UTF-8 character, skip unicode normalization + components[key] = value.to_str + end + end + end + encoded_uri = Addressable::URI.new( + :scheme => self.encode_component(components[:scheme], + Addressable::URI::CharacterClassesRegexps::SCHEME), + :user => self.encode_component(components[:user], + Addressable::URI::CharacterClassesRegexps::UNRESERVED), + :password => self.encode_component(components[:password], + Addressable::URI::CharacterClassesRegexps::UNRESERVED), + :host => components[:host], + :port => components[:port], + :path => self.encode_component(components[:path], + Addressable::URI::CharacterClassesRegexps::PATH), + :query => self.encode_component(components[:query], + Addressable::URI::CharacterClassesRegexps::QUERY), + :fragment => self.encode_component(components[:fragment], + Addressable::URI::CharacterClassesRegexps::FRAGMENT) + ) + if return_type == String + return encoded_uri.to_s + elsif return_type == ::Addressable::URI + return encoded_uri + end + end + + ## + # Encodes a set of key/value pairs according to the rules for the + # application/x-www-form-urlencoded MIME type. + # + # @param [#to_hash, #to_ary] form_values + # The form values to encode. + # + # @param [TrueClass, FalseClass] sort + # Sort the key/value pairs prior to encoding. + # Defaults to false. + # + # @return [String] + # The encoded value. + def self.form_encode(form_values, sort=false) + if form_values.respond_to?(:to_hash) + form_values = form_values.to_hash.to_a + elsif form_values.respond_to?(:to_ary) + form_values = form_values.to_ary + else + raise TypeError, "Can't convert #{form_values.class} into Array." + end + + form_values = form_values.inject([]) do |accu, (key, value)| + if value.kind_of?(Array) + value.each do |v| + accu << [key.to_s, v.to_s] + end + else + accu << [key.to_s, value.to_s] + end + accu + end + + if sort + # Useful for OAuth and optimizing caching systems + form_values = form_values.sort + end + escaped_form_values = form_values.map do |(key, value)| + # Line breaks are CRLF pairs + [ + self.encode_component( + key.gsub(/(\r\n|\n|\r)/, "\r\n"), + CharacterClassesRegexps::UNRESERVED + ).gsub("%20", "+"), + self.encode_component( + value.gsub(/(\r\n|\n|\r)/, "\r\n"), + CharacterClassesRegexps::UNRESERVED + ).gsub("%20", "+") + ] + end + return escaped_form_values.map do |(key, value)| + "#{key}=#{value}" + end.join("&") + end + + ## + # Decodes a String according to the rules for the + # application/x-www-form-urlencoded MIME type. + # + # @param [String, #to_str] encoded_value + # The form values to decode. + # + # @return [Array] + # The decoded values. + # This is not a Hash because of the possibility for + # duplicate keys. + def self.form_unencode(encoded_value) + if !encoded_value.respond_to?(:to_str) + raise TypeError, "Can't convert #{encoded_value.class} into String." + end + encoded_value = encoded_value.to_str + split_values = encoded_value.split("&").map do |pair| + pair.split("=", 2) + end + return split_values.map do |(key, value)| + [ + key ? self.unencode_component( + key.gsub("+", "%20")).gsub(/(\r\n|\n|\r)/, "\n") : nil, + value ? (self.unencode_component( + value.gsub("+", "%20")).gsub(/(\r\n|\n|\r)/, "\n")) : nil + ] + end + end + + ## + # Creates a new uri object from component parts. + # + # @option [String, #to_str] scheme The scheme component. + # @option [String, #to_str] user The user component. + # @option [String, #to_str] password The password component. + # @option [String, #to_str] userinfo + # The userinfo component. If this is supplied, the user and password + # components must be omitted. + # @option [String, #to_str] host The host component. + # @option [String, #to_str] port The port component. + # @option [String, #to_str] authority + # The authority component. If this is supplied, the user, password, + # userinfo, host, and port components must be omitted. + # @option [String, #to_str] path The path component. + # @option [String, #to_str] query The query component. + # @option [String, #to_str] fragment The fragment component. + # + # @return [Addressable::URI] The constructed URI object. + def initialize(options={}) + if options.has_key?(:authority) + if (options.keys & [:userinfo, :user, :password, :host, :port]).any? + raise ArgumentError, + "Cannot specify both an authority and any of the components " + + "within the authority." + end + end + if options.has_key?(:userinfo) + if (options.keys & [:user, :password]).any? + raise ArgumentError, + "Cannot specify both a userinfo and either the user or password." + end + end + + reset_ivs + + defer_validation do + # Bunch of crazy logic required because of the composite components + # like userinfo and authority. + self.scheme = options[:scheme] if options[:scheme] + self.user = options[:user] if options[:user] + self.password = options[:password] if options[:password] + self.userinfo = options[:userinfo] if options[:userinfo] + self.host = options[:host] if options[:host] + self.port = options[:port] if options[:port] + self.authority = options[:authority] if options[:authority] + self.path = options[:path] if options[:path] + self.query = options[:query] if options[:query] + self.query_values = options[:query_values] if options[:query_values] + self.fragment = options[:fragment] if options[:fragment] + end + + to_s # force path validation + end + + ## + # Freeze URI, initializing instance variables. + # + # @return [Addressable::URI] The frozen URI object. + def freeze + self.normalized_scheme + self.normalized_user + self.normalized_password + self.normalized_userinfo + self.normalized_host + self.normalized_port + self.normalized_authority + self.normalized_site + self.normalized_path + self.normalized_query + self.normalized_fragment + self.hash + super + end + + ## + # The scheme component for this URI. + # + # @return [String] The scheme component. + attr_reader :scheme + + ## + # The scheme component for this URI, normalized. + # + # @return [String] The scheme component, normalized. + def normalized_scheme + return nil unless self.scheme + if @normalized_scheme == NONE + @normalized_scheme = if self.scheme =~ /^\s*ssh\+svn\s*$/i + "svn+ssh".dup + else + Addressable::URI.normalize_component( + self.scheme.strip.downcase, + Addressable::URI::NormalizeCharacterClasses::SCHEME + ) + end + end + # All normalized values should be UTF-8 + force_utf8_encoding_if_needed(@normalized_scheme) + @normalized_scheme + end + + ## + # Sets the scheme component for this URI. + # + # @param [String, #to_str] new_scheme The new scheme component. + def scheme=(new_scheme) + if new_scheme && !new_scheme.respond_to?(:to_str) + raise TypeError, "Can't convert #{new_scheme.class} into String." + elsif new_scheme + new_scheme = new_scheme.to_str + end + if new_scheme && new_scheme !~ /\A[a-z][a-z0-9\.\+\-]*\z/i + raise InvalidURIError, "Invalid scheme format: '#{new_scheme}'" + end + @scheme = new_scheme + @scheme = nil if @scheme.to_s.strip.empty? + + # Reset dependent values + @normalized_scheme = NONE + remove_composite_values + + # Ensure we haven't created an invalid URI + validate() + end + + ## + # The user component for this URI. + # + # @return [String] The user component. + attr_reader :user + + ## + # The user component for this URI, normalized. + # + # @return [String] The user component, normalized. + def normalized_user + return nil unless self.user + return @normalized_user unless @normalized_user == NONE + @normalized_user = begin + if normalized_scheme =~ /https?/ && self.user.strip.empty? && + (!self.password || self.password.strip.empty?) + nil + else + Addressable::URI.normalize_component( + self.user.strip, + Addressable::URI::NormalizeCharacterClasses::UNRESERVED + ) + end + end + # All normalized values should be UTF-8 + force_utf8_encoding_if_needed(@normalized_user) + @normalized_user + end + + ## + # Sets the user component for this URI. + # + # @param [String, #to_str] new_user The new user component. + def user=(new_user) + if new_user && !new_user.respond_to?(:to_str) + raise TypeError, "Can't convert #{new_user.class} into String." + end + @user = new_user ? new_user.to_str : nil + + # You can't have a nil user with a non-nil password + if password != nil + @user = EMPTY_STR unless user + end + + # Reset dependent values + @userinfo = nil + @normalized_userinfo = NONE + @authority = nil + @normalized_user = NONE + remove_composite_values + + # Ensure we haven't created an invalid URI + validate() + end + + ## + # The password component for this URI. + # + # @return [String] The password component. + attr_reader :password + + ## + # The password component for this URI, normalized. + # + # @return [String] The password component, normalized. + def normalized_password + return nil unless self.password + return @normalized_password unless @normalized_password == NONE + @normalized_password = begin + if self.normalized_scheme =~ /https?/ && self.password.strip.empty? && + (!self.user || self.user.strip.empty?) + nil + else + Addressable::URI.normalize_component( + self.password.strip, + Addressable::URI::NormalizeCharacterClasses::UNRESERVED + ) + end + end + # All normalized values should be UTF-8 + force_utf8_encoding_if_needed(@normalized_password) + @normalized_password + end + + ## + # Sets the password component for this URI. + # + # @param [String, #to_str] new_password The new password component. + def password=(new_password) + if new_password && !new_password.respond_to?(:to_str) + raise TypeError, "Can't convert #{new_password.class} into String." + end + @password = new_password ? new_password.to_str : nil + + # You can't have a nil user with a non-nil password + if @password != nil + self.user = EMPTY_STR if user.nil? + end + + # Reset dependent values + @userinfo = nil + @normalized_userinfo = NONE + @authority = nil + @normalized_password = NONE + remove_composite_values + + # Ensure we haven't created an invalid URI + validate() + end + + ## + # The userinfo component for this URI. + # Combines the user and password components. + # + # @return [String] The userinfo component. + def userinfo + current_user = self.user + current_password = self.password + (current_user || current_password) && @userinfo ||= begin + if current_user && current_password + "#{current_user}:#{current_password}" + elsif current_user && !current_password + "#{current_user}" + end + end + end + + ## + # The userinfo component for this URI, normalized. + # + # @return [String] The userinfo component, normalized. + def normalized_userinfo + return nil unless self.userinfo + return @normalized_userinfo unless @normalized_userinfo == NONE + @normalized_userinfo = begin + current_user = self.normalized_user + current_password = self.normalized_password + if !current_user && !current_password + nil + elsif current_user && current_password + "#{current_user}:#{current_password}".dup + elsif current_user && !current_password + "#{current_user}".dup + end + end + # All normalized values should be UTF-8 + force_utf8_encoding_if_needed(@normalized_userinfo) + @normalized_userinfo + end + + ## + # Sets the userinfo component for this URI. + # + # @param [String, #to_str] new_userinfo The new userinfo component. + def userinfo=(new_userinfo) + if new_userinfo && !new_userinfo.respond_to?(:to_str) + raise TypeError, "Can't convert #{new_userinfo.class} into String." + end + new_user, new_password = if new_userinfo + [ + new_userinfo.to_str.strip[/^(.*):/, 1], + new_userinfo.to_str.strip[/:(.*)$/, 1] + ] + else + [nil, nil] + end + + # Password assigned first to ensure validity in case of nil + self.password = new_password + self.user = new_user + + # Reset dependent values + @authority = nil + remove_composite_values + + # Ensure we haven't created an invalid URI + validate() + end + + ## + # The host component for this URI. + # + # @return [String] The host component. + attr_reader :host + + ## + # The host component for this URI, normalized. + # + # @return [String] The host component, normalized. + def normalized_host + return nil unless self.host + + @normalized_host ||= begin + if !self.host.strip.empty? + result = ::Addressable::IDNA.to_ascii( + URI.unencode_component(self.host.strip.downcase) + ) + if result =~ /[^\.]\.$/ + # Single trailing dots are unnecessary. + result = result[0...-1] + end + result = Addressable::URI.normalize_component( + result, + NormalizeCharacterClasses::HOST + ) + result + else + EMPTY_STR.dup + end + end + # All normalized values should be UTF-8 + force_utf8_encoding_if_needed(@normalized_host) + @normalized_host + end + + ## + # Sets the host component for this URI. + # + # @param [String, #to_str] new_host The new host component. + def host=(new_host) + if new_host && !new_host.respond_to?(:to_str) + raise TypeError, "Can't convert #{new_host.class} into String." + end + @host = new_host ? new_host.to_str : nil + + # Reset dependent values + @authority = nil + @normalized_host = nil + remove_composite_values + + # Ensure we haven't created an invalid URI + validate() + end + + ## + # This method is same as URI::Generic#host except + # brackets for IPv6 (and 'IPvFuture') addresses are removed. + # + # @see Addressable::URI#host + # + # @return [String] The hostname for this URI. + def hostname + v = self.host + /\A\[(.*)\]\z/ =~ v ? $1 : v + end + + ## + # This method is same as URI::Generic#host= except + # the argument can be a bare IPv6 address (or 'IPvFuture'). + # + # @see Addressable::URI#host= + # + # @param [String, #to_str] new_hostname The new hostname for this URI. + def hostname=(new_hostname) + if new_hostname && + (new_hostname.respond_to?(:ipv4?) || new_hostname.respond_to?(:ipv6?)) + new_hostname = new_hostname.to_s + elsif new_hostname && !new_hostname.respond_to?(:to_str) + raise TypeError, "Can't convert #{new_hostname.class} into String." + end + v = new_hostname ? new_hostname.to_str : nil + v = "[#{v}]" if /\A\[.*\]\z/ !~ v && /:/ =~ v + self.host = v + end + + ## + # Returns the top-level domain for this host. + # + # @example + # Addressable::URI.parse("http://www.example.co.uk").tld # => "co.uk" + def tld + PublicSuffix.parse(self.host, ignore_private: true).tld + end + + ## + # Sets the top-level domain for this URI. + # + # @param [String, #to_str] new_tld The new top-level domain. + def tld=(new_tld) + replaced_tld = host.sub(/#{tld}\z/, new_tld) + self.host = PublicSuffix::Domain.new(replaced_tld).to_s + end + + ## + # Returns the public suffix domain for this host. + # + # @example + # Addressable::URI.parse("http://www.example.co.uk").domain # => "example.co.uk" + def domain + PublicSuffix.domain(self.host, ignore_private: true) + end + + ## + # The authority component for this URI. + # Combines the user, password, host, and port components. + # + # @return [String] The authority component. + def authority + self.host && @authority ||= begin + authority = String.new + if self.userinfo != nil + authority << "#{self.userinfo}@" + end + authority << self.host + if self.port != nil + authority << ":#{self.port}" + end + authority + end + end + + ## + # The authority component for this URI, normalized. + # + # @return [String] The authority component, normalized. + def normalized_authority + return nil unless self.authority + @normalized_authority ||= begin + authority = String.new + if self.normalized_userinfo != nil + authority << "#{self.normalized_userinfo}@" + end + authority << self.normalized_host + if self.normalized_port != nil + authority << ":#{self.normalized_port}" + end + authority + end + # All normalized values should be UTF-8 + force_utf8_encoding_if_needed(@normalized_authority) + @normalized_authority + end + + ## + # Sets the authority component for this URI. + # + # @param [String, #to_str] new_authority The new authority component. + def authority=(new_authority) + if new_authority + if !new_authority.respond_to?(:to_str) + raise TypeError, "Can't convert #{new_authority.class} into String." + end + new_authority = new_authority.to_str + new_userinfo = new_authority[/^([^\[\]]*)@/, 1] + if new_userinfo + new_user = new_userinfo.strip[/^([^:]*):?/, 1] + new_password = new_userinfo.strip[/:(.*)$/, 1] + end + new_host = new_authority.sub( + /^([^\[\]]*)@/, EMPTY_STR + ).sub( + /:([^:@\[\]]*?)$/, EMPTY_STR + ) + new_port = + new_authority[/:([^:@\[\]]*?)$/, 1] + end + + # Password assigned first to ensure validity in case of nil + self.password = new_password + self.user = new_user + self.host = new_host + self.port = new_port + + # Reset dependent values + @userinfo = nil + @normalized_userinfo = NONE + remove_composite_values + + # Ensure we haven't created an invalid URI + validate() + end + + ## + # The origin for this URI, serialized to ASCII, as per + # RFC 6454, section 6.2. + # + # @return [String] The serialized origin. + def origin + if self.scheme && self.authority + if self.normalized_port + "#{self.normalized_scheme}://#{self.normalized_host}" + + ":#{self.normalized_port}" + else + "#{self.normalized_scheme}://#{self.normalized_host}" + end + else + "null" + end + end + + ## + # Sets the origin for this URI, serialized to ASCII, as per + # RFC 6454, section 6.2. This assignment will reset the `userinfo` + # component. + # + # @param [String, #to_str] new_origin The new origin component. + def origin=(new_origin) + if new_origin + if !new_origin.respond_to?(:to_str) + raise TypeError, "Can't convert #{new_origin.class} into String." + end + new_origin = new_origin.to_str + new_scheme = new_origin[/^([^:\/?#]+):\/\//, 1] + unless new_scheme + raise InvalidURIError, 'An origin cannot omit the scheme.' + end + new_host = new_origin[/:\/\/([^\/?#:]+)/, 1] + unless new_host + raise InvalidURIError, 'An origin cannot omit the host.' + end + new_port = new_origin[/:([^:@\[\]\/]*?)$/, 1] + end + + self.scheme = new_scheme + self.host = new_host + self.port = new_port + self.userinfo = nil + + # Reset dependent values + @userinfo = nil + @normalized_userinfo = NONE + @authority = nil + @normalized_authority = nil + remove_composite_values + + # Ensure we haven't created an invalid URI + validate() + end + + # Returns an array of known ip-based schemes. These schemes typically + # use a similar URI form: + # //:@:/ + def self.ip_based_schemes + return self.port_mapping.keys + end + + # Returns a hash of common IP-based schemes and their default port + # numbers. Adding new schemes to this hash, as necessary, will allow + # for better URI normalization. + def self.port_mapping + PORT_MAPPING + end + + ## + # The port component for this URI. + # This is the port number actually given in the URI. This does not + # infer port numbers from default values. + # + # @return [Integer] The port component. + attr_reader :port + + ## + # The port component for this URI, normalized. + # + # @return [Integer] The port component, normalized. + def normalized_port + return nil unless self.port + return @normalized_port unless @normalized_port == NONE + @normalized_port = begin + if URI.port_mapping[self.normalized_scheme] == self.port + nil + else + self.port + end + end + end + + ## + # Sets the port component for this URI. + # + # @param [String, Integer, #to_s] new_port The new port component. + def port=(new_port) + if new_port != nil && new_port.respond_to?(:to_str) + new_port = Addressable::URI.unencode_component(new_port.to_str) + end + + if new_port.respond_to?(:valid_encoding?) && !new_port.valid_encoding? + raise InvalidURIError, "Invalid encoding in port" + end + + if new_port != nil && !(new_port.to_s =~ /^\d+$/) + raise InvalidURIError, + "Invalid port number: #{new_port.inspect}" + end + + @port = new_port.to_s.to_i + @port = nil if @port == 0 + + # Reset dependent values + @authority = nil + @normalized_port = NONE + remove_composite_values + + # Ensure we haven't created an invalid URI + validate() + end + + ## + # The inferred port component for this URI. + # This method will normalize to the default port for the URI's scheme if + # the port isn't explicitly specified in the URI. + # + # @return [Integer] The inferred port component. + def inferred_port + if self.port.to_i == 0 + self.default_port + else + self.port.to_i + end + end + + ## + # The default port for this URI's scheme. + # This method will always returns the default port for the URI's scheme + # regardless of the presence of an explicit port in the URI. + # + # @return [Integer] The default port. + def default_port + URI.port_mapping[self.scheme.strip.downcase] if self.scheme + end + + ## + # The combination of components that represent a site. + # Combines the scheme, user, password, host, and port components. + # Primarily useful for HTTP and HTTPS. + # + # For example, "http://example.com/path?query" would have a + # site value of "http://example.com". + # + # @return [String] The components that identify a site. + def site + (self.scheme || self.authority) && @site ||= begin + site_string = "".dup + site_string << "#{self.scheme}:" if self.scheme != nil + site_string << "//#{self.authority}" if self.authority != nil + site_string + end + end + + ## + # The normalized combination of components that represent a site. + # Combines the scheme, user, password, host, and port components. + # Primarily useful for HTTP and HTTPS. + # + # For example, "http://example.com/path?query" would have a + # site value of "http://example.com". + # + # @return [String] The normalized components that identify a site. + def normalized_site + return nil unless self.site + @normalized_site ||= begin + site_string = "".dup + if self.normalized_scheme != nil + site_string << "#{self.normalized_scheme}:" + end + if self.normalized_authority != nil + site_string << "//#{self.normalized_authority}" + end + site_string + end + # All normalized values should be UTF-8 + force_utf8_encoding_if_needed(@normalized_site) + @normalized_site + end + + ## + # Sets the site value for this URI. + # + # @param [String, #to_str] new_site The new site value. + def site=(new_site) + if new_site + if !new_site.respond_to?(:to_str) + raise TypeError, "Can't convert #{new_site.class} into String." + end + new_site = new_site.to_str + # These two regular expressions derived from the primary parsing + # expression + self.scheme = new_site[/^(?:([^:\/?#]+):)?(?:\/\/(?:[^\/?#]*))?$/, 1] + self.authority = new_site[ + /^(?:(?:[^:\/?#]+):)?(?:\/\/([^\/?#]*))?$/, 1 + ] + else + self.scheme = nil + self.authority = nil + end + end + + ## + # The path component for this URI. + # + # @return [String] The path component. + attr_reader :path + + NORMPATH = /^(?!\/)[^\/:]*:.*$/ + ## + # The path component for this URI, normalized. + # + # @return [String] The path component, normalized. + def normalized_path + @normalized_path ||= begin + path = self.path.to_s + if self.scheme == nil && path =~ NORMPATH + # Relative paths with colons in the first segment are ambiguous. + path = path.sub(":", "%2F") + end + # String#split(delimiter, -1) uses the more strict splitting behavior + # found by default in Python. + result = path.strip.split(SLASH, -1).map do |segment| + Addressable::URI.normalize_component( + segment, + Addressable::URI::NormalizeCharacterClasses::PCHAR + ) + end.join(SLASH) + + result = URI.normalize_path(result) + if result.empty? && + ["http", "https", "ftp", "tftp"].include?(self.normalized_scheme) + result = SLASH.dup + end + result + end + # All normalized values should be UTF-8 + force_utf8_encoding_if_needed(@normalized_path) + @normalized_path + end + + ## + # Sets the path component for this URI. + # + # @param [String, #to_str] new_path The new path component. + def path=(new_path) + if new_path && !new_path.respond_to?(:to_str) + raise TypeError, "Can't convert #{new_path.class} into String." + end + @path = (new_path || EMPTY_STR).to_str + if !@path.empty? && @path[0..0] != SLASH && host != nil + @path = "/#{@path}" + end + + # Reset dependent values + @normalized_path = nil + remove_composite_values + + # Ensure we haven't created an invalid URI + validate() + end + + ## + # The basename, if any, of the file in the path component. + # + # @return [String] The path's basename. + def basename + # Path cannot be nil + return File.basename(self.path).sub(/;[^\/]*$/, EMPTY_STR) + end + + ## + # The extname, if any, of the file in the path component. + # Empty string if there is no extension. + # + # @return [String] The path's extname. + def extname + return nil unless self.path + return File.extname(self.basename) + end + + ## + # The query component for this URI. + # + # @return [String] The query component. + attr_reader :query + + ## + # The query component for this URI, normalized. + # + # @return [String] The query component, normalized. + def normalized_query(*flags) + return nil unless self.query + return @normalized_query unless @normalized_query == NONE + @normalized_query = begin + modified_query_class = Addressable::URI::CharacterClasses::QUERY.dup + # Make sure possible key-value pair delimiters are escaped. + modified_query_class.sub!("\\&", "").sub!("\\;", "") + pairs = (query || "").split("&", -1) + pairs.delete_if(&:empty?).uniq! if flags.include?(:compacted) + pairs.sort! if flags.include?(:sorted) + component = pairs.map do |pair| + Addressable::URI.normalize_component( + pair, + Addressable::URI::NormalizeCharacterClasses::QUERY, + "+" + ) + end.join("&") + component == "" ? nil : component + end + # All normalized values should be UTF-8 + force_utf8_encoding_if_needed(@normalized_query) + @normalized_query + end + + ## + # Sets the query component for this URI. + # + # @param [String, #to_str] new_query The new query component. + def query=(new_query) + if new_query && !new_query.respond_to?(:to_str) + raise TypeError, "Can't convert #{new_query.class} into String." + end + @query = new_query ? new_query.to_str : nil + + # Reset dependent values + @normalized_query = NONE + remove_composite_values + end + + ## + # Converts the query component to a Hash value. + # + # @param [Class] return_type The return type desired. Value must be either + # `Hash` or `Array`. + # + # @return [Hash, Array, nil] The query string parsed as a Hash or Array + # or nil if the query string is blank. + # + # @example + # Addressable::URI.parse("?one=1&two=2&three=3").query_values + # #=> {"one" => "1", "two" => "2", "three" => "3"} + # Addressable::URI.parse("?one=two&one=three").query_values(Array) + # #=> [["one", "two"], ["one", "three"]] + # Addressable::URI.parse("?one=two&one=three").query_values(Hash) + # #=> {"one" => "three"} + # Addressable::URI.parse("?").query_values + # #=> {} + # Addressable::URI.parse("").query_values + # #=> nil + def query_values(return_type=Hash) + empty_accumulator = Array == return_type ? [] : {} + if return_type != Hash && return_type != Array + raise ArgumentError, "Invalid return type. Must be Hash or Array." + end + return nil if self.query == nil + split_query = self.query.split("&").map do |pair| + pair.split("=", 2) if pair && !pair.empty? + end.compact + return split_query.inject(empty_accumulator.dup) do |accu, pair| + # I'd rather use key/value identifiers instead of array lookups, + # but in this case I really want to maintain the exact pair structure, + # so it's best to make all changes in-place. + pair[0] = URI.unencode_component(pair[0]) + if pair[1].respond_to?(:to_str) + value = pair[1].to_str + # I loathe the fact that I have to do this. Stupid HTML 4.01. + # Treating '+' as a space was just an unbelievably bad idea. + # There was nothing wrong with '%20'! + # If it ain't broke, don't fix it! + value = value.tr("+", " ") if ["http", "https", nil].include?(scheme) + pair[1] = URI.unencode_component(value) + end + if return_type == Hash + accu[pair[0]] = pair[1] + else + accu << pair + end + accu + end + end + + ## + # Sets the query component for this URI from a Hash object. + # An empty Hash or Array will result in an empty query string. + # + # @param [Hash, #to_hash, Array] new_query_values The new query values. + # + # @example + # uri.query_values = {:a => "a", :b => ["c", "d", "e"]} + # uri.query + # # => "a=a&b=c&b=d&b=e" + # uri.query_values = [['a', 'a'], ['b', 'c'], ['b', 'd'], ['b', 'e']] + # uri.query + # # => "a=a&b=c&b=d&b=e" + # uri.query_values = [['a', 'a'], ['b', ['c', 'd', 'e']]] + # uri.query + # # => "a=a&b=c&b=d&b=e" + # uri.query_values = [['flag'], ['key', 'value']] + # uri.query + # # => "flag&key=value" + def query_values=(new_query_values) + if new_query_values == nil + self.query = nil + return nil + end + + if !new_query_values.is_a?(Array) + if !new_query_values.respond_to?(:to_hash) + raise TypeError, + "Can't convert #{new_query_values.class} into Hash." + end + new_query_values = new_query_values.to_hash + new_query_values = new_query_values.map do |key, value| + key = key.to_s if key.kind_of?(Symbol) + [key, value] + end + # Useful default for OAuth and caching. + # Only to be used for non-Array inputs. Arrays should preserve order. + new_query_values.sort! + end + + # new_query_values have form [['key1', 'value1'], ['key2', 'value2']] + buffer = "".dup + new_query_values.each do |key, value| + encoded_key = URI.encode_component( + key, CharacterClassesRegexps::UNRESERVED + ) + if value == nil + buffer << "#{encoded_key}&" + elsif value.kind_of?(Array) + value.each do |sub_value| + encoded_value = URI.encode_component( + sub_value, CharacterClassesRegexps::UNRESERVED + ) + buffer << "#{encoded_key}=#{encoded_value}&" + end + else + encoded_value = URI.encode_component( + value, CharacterClassesRegexps::UNRESERVED + ) + buffer << "#{encoded_key}=#{encoded_value}&" + end + end + self.query = buffer.chop + end + + ## + # The HTTP request URI for this URI. This is the path and the + # query string. + # + # @return [String] The request URI required for an HTTP request. + def request_uri + return nil if self.absolute? && self.scheme !~ /^https?$/i + return ( + (!self.path.empty? ? self.path : SLASH) + + (self.query ? "?#{self.query}" : EMPTY_STR) + ) + end + + ## + # Sets the HTTP request URI for this URI. + # + # @param [String, #to_str] new_request_uri The new HTTP request URI. + def request_uri=(new_request_uri) + if !new_request_uri.respond_to?(:to_str) + raise TypeError, "Can't convert #{new_request_uri.class} into String." + end + if self.absolute? && self.scheme !~ /^https?$/i + raise InvalidURIError, + "Cannot set an HTTP request URI for a non-HTTP URI." + end + new_request_uri = new_request_uri.to_str + path_component = new_request_uri[/^([^\?]*)\??(?:.*)$/, 1] + query_component = new_request_uri[/^(?:[^\?]*)\?(.*)$/, 1] + path_component = path_component.to_s + path_component = (!path_component.empty? ? path_component : SLASH) + self.path = path_component + self.query = query_component + + # Reset dependent values + remove_composite_values + end + + ## + # The fragment component for this URI. + # + # @return [String] The fragment component. + attr_reader :fragment + + ## + # The fragment component for this URI, normalized. + # + # @return [String] The fragment component, normalized. + def normalized_fragment + return nil unless self.fragment + return @normalized_fragment unless @normalized_fragment == NONE + @normalized_fragment = begin + component = Addressable::URI.normalize_component( + self.fragment, + Addressable::URI::NormalizeCharacterClasses::FRAGMENT + ) + component == "" ? nil : component + end + # All normalized values should be UTF-8 + force_utf8_encoding_if_needed(@normalized_fragment) + @normalized_fragment + end + + ## + # Sets the fragment component for this URI. + # + # @param [String, #to_str] new_fragment The new fragment component. + def fragment=(new_fragment) + if new_fragment && !new_fragment.respond_to?(:to_str) + raise TypeError, "Can't convert #{new_fragment.class} into String." + end + @fragment = new_fragment ? new_fragment.to_str : nil + + # Reset dependent values + @normalized_fragment = NONE + remove_composite_values + + # Ensure we haven't created an invalid URI + validate() + end + + ## + # Determines if the scheme indicates an IP-based protocol. + # + # @return [TrueClass, FalseClass] + # true if the scheme indicates an IP-based protocol. + # false otherwise. + def ip_based? + if self.scheme + return URI.ip_based_schemes.include?( + self.scheme.strip.downcase) + end + return false + end + + ## + # Determines if the URI is relative. + # + # @return [TrueClass, FalseClass] + # true if the URI is relative. false + # otherwise. + def relative? + return self.scheme.nil? + end + + ## + # Determines if the URI is absolute. + # + # @return [TrueClass, FalseClass] + # true if the URI is absolute. false + # otherwise. + def absolute? + return !relative? + end + + ## + # Joins two URIs together. + # + # @param [String, Addressable::URI, #to_str] The URI to join with. + # + # @return [Addressable::URI] The joined URI. + def join(uri) + if !uri.respond_to?(:to_str) + raise TypeError, "Can't convert #{uri.class} into String." + end + if !uri.kind_of?(URI) + # Otherwise, convert to a String, then parse. + uri = URI.parse(uri.to_str) + end + if uri.to_s.empty? + return self.dup + end + + joined_scheme = nil + joined_user = nil + joined_password = nil + joined_host = nil + joined_port = nil + joined_path = nil + joined_query = nil + joined_fragment = nil + + # Section 5.2.2 of RFC 3986 + if uri.scheme != nil + joined_scheme = uri.scheme + joined_user = uri.user + joined_password = uri.password + joined_host = uri.host + joined_port = uri.port + joined_path = URI.normalize_path(uri.path) + joined_query = uri.query + else + if uri.authority != nil + joined_user = uri.user + joined_password = uri.password + joined_host = uri.host + joined_port = uri.port + joined_path = URI.normalize_path(uri.path) + joined_query = uri.query + else + if uri.path == nil || uri.path.empty? + joined_path = self.path + if uri.query != nil + joined_query = uri.query + else + joined_query = self.query + end + else + if uri.path[0..0] == SLASH + joined_path = URI.normalize_path(uri.path) + else + base_path = self.path.dup + base_path = EMPTY_STR if base_path == nil + base_path = URI.normalize_path(base_path) + + # Section 5.2.3 of RFC 3986 + # + # Removes the right-most path segment from the base path. + if base_path.include?(SLASH) + base_path.sub!(/\/[^\/]+$/, SLASH) + else + base_path = EMPTY_STR + end + + # If the base path is empty and an authority segment has been + # defined, use a base path of SLASH + if base_path.empty? && self.authority != nil + base_path = SLASH + end + + joined_path = URI.normalize_path(base_path + uri.path) + end + joined_query = uri.query + end + joined_user = self.user + joined_password = self.password + joined_host = self.host + joined_port = self.port + end + joined_scheme = self.scheme + end + joined_fragment = uri.fragment + + return self.class.new( + :scheme => joined_scheme, + :user => joined_user, + :password => joined_password, + :host => joined_host, + :port => joined_port, + :path => joined_path, + :query => joined_query, + :fragment => joined_fragment + ) + end + alias_method :+, :join + + ## + # Destructive form of join. + # + # @param [String, Addressable::URI, #to_str] The URI to join with. + # + # @return [Addressable::URI] The joined URI. + # + # @see Addressable::URI#join + def join!(uri) + replace_self(self.join(uri)) + end + + ## + # Merges a URI with a Hash of components. + # This method has different behavior from join. Any + # components present in the hash parameter will override the + # original components. The path component is not treated specially. + # + # @param [Hash, Addressable::URI, #to_hash] The components to merge with. + # + # @return [Addressable::URI] The merged URI. + # + # @see Hash#merge + def merge(hash) + unless hash.respond_to?(:to_hash) + raise TypeError, "Can't convert #{hash.class} into Hash." + end + hash = hash.to_hash + + if hash.has_key?(:authority) + if (hash.keys & [:userinfo, :user, :password, :host, :port]).any? + raise ArgumentError, + "Cannot specify both an authority and any of the components " + + "within the authority." + end + end + if hash.has_key?(:userinfo) + if (hash.keys & [:user, :password]).any? + raise ArgumentError, + "Cannot specify both a userinfo and either the user or password." + end + end + + uri = self.class.new + uri.defer_validation do + # Bunch of crazy logic required because of the composite components + # like userinfo and authority. + uri.scheme = + hash.has_key?(:scheme) ? hash[:scheme] : self.scheme + if hash.has_key?(:authority) + uri.authority = + hash.has_key?(:authority) ? hash[:authority] : self.authority + end + if hash.has_key?(:userinfo) + uri.userinfo = + hash.has_key?(:userinfo) ? hash[:userinfo] : self.userinfo + end + if !hash.has_key?(:userinfo) && !hash.has_key?(:authority) + uri.user = + hash.has_key?(:user) ? hash[:user] : self.user + uri.password = + hash.has_key?(:password) ? hash[:password] : self.password + end + if !hash.has_key?(:authority) + uri.host = + hash.has_key?(:host) ? hash[:host] : self.host + uri.port = + hash.has_key?(:port) ? hash[:port] : self.port + end + uri.path = + hash.has_key?(:path) ? hash[:path] : self.path + uri.query = + hash.has_key?(:query) ? hash[:query] : self.query + uri.fragment = + hash.has_key?(:fragment) ? hash[:fragment] : self.fragment + end + + return uri + end + + ## + # Destructive form of merge. + # + # @param [Hash, Addressable::URI, #to_hash] The components to merge with. + # + # @return [Addressable::URI] The merged URI. + # + # @see Addressable::URI#merge + def merge!(uri) + replace_self(self.merge(uri)) + end + + ## + # Returns the shortest normalized relative form of this URI that uses the + # supplied URI as a base for resolution. Returns an absolute URI if + # necessary. This is effectively the opposite of route_to. + # + # @param [String, Addressable::URI, #to_str] uri The URI to route from. + # + # @return [Addressable::URI] + # The normalized relative URI that is equivalent to the original URI. + def route_from(uri) + uri = URI.parse(uri).normalize + normalized_self = self.normalize + if normalized_self.relative? + raise ArgumentError, "Expected absolute URI, got: #{self.to_s}" + end + if uri.relative? + raise ArgumentError, "Expected absolute URI, got: #{uri.to_s}" + end + if normalized_self == uri + return Addressable::URI.parse("##{normalized_self.fragment}") + end + components = normalized_self.to_hash + if normalized_self.scheme == uri.scheme + components[:scheme] = nil + if normalized_self.authority == uri.authority + components[:user] = nil + components[:password] = nil + components[:host] = nil + components[:port] = nil + if normalized_self.path == uri.path + components[:path] = nil + if normalized_self.query == uri.query + components[:query] = nil + end + else + if uri.path != SLASH and components[:path] + self_splitted_path = split_path(components[:path]) + uri_splitted_path = split_path(uri.path) + self_dir = self_splitted_path.shift + uri_dir = uri_splitted_path.shift + while !self_splitted_path.empty? && !uri_splitted_path.empty? and self_dir == uri_dir + self_dir = self_splitted_path.shift + uri_dir = uri_splitted_path.shift + end + components[:path] = (uri_splitted_path.fill('..') + [self_dir] + self_splitted_path).join(SLASH) + end + end + end + end + # Avoid network-path references. + if components[:host] != nil + components[:scheme] = normalized_self.scheme + end + return Addressable::URI.new( + :scheme => components[:scheme], + :user => components[:user], + :password => components[:password], + :host => components[:host], + :port => components[:port], + :path => components[:path], + :query => components[:query], + :fragment => components[:fragment] + ) + end + + ## + # Returns the shortest normalized relative form of the supplied URI that + # uses this URI as a base for resolution. Returns an absolute URI if + # necessary. This is effectively the opposite of route_from. + # + # @param [String, Addressable::URI, #to_str] uri The URI to route to. + # + # @return [Addressable::URI] + # The normalized relative URI that is equivalent to the supplied URI. + def route_to(uri) + return URI.parse(uri).route_from(self) + end + + ## + # Returns a normalized URI object. + # + # NOTE: This method does not attempt to fully conform to specifications. + # It exists largely to correct other people's failures to read the + # specifications, and also to deal with caching issues since several + # different URIs may represent the same resource and should not be + # cached multiple times. + # + # @return [Addressable::URI] The normalized URI. + def normalize + # This is a special exception for the frequently misused feed + # URI scheme. + if normalized_scheme == "feed" + if self.to_s =~ /^feed:\/*http:\/*/ + return URI.parse( + self.to_s[/^feed:\/*(http:\/*.*)/, 1] + ).normalize + end + end + + return self.class.new( + :scheme => normalized_scheme, + :authority => normalized_authority, + :path => normalized_path, + :query => normalized_query, + :fragment => normalized_fragment + ) + end + + ## + # Destructively normalizes this URI object. + # + # @return [Addressable::URI] The normalized URI. + # + # @see Addressable::URI#normalize + def normalize! + replace_self(self.normalize) + end + + ## + # Creates a URI suitable for display to users. If semantic attacks are + # likely, the application should try to detect these and warn the user. + # See RFC 3986, + # section 7.6 for more information. + # + # @return [Addressable::URI] A URI suitable for display purposes. + def display_uri + display_uri = self.normalize + display_uri.host = ::Addressable::IDNA.to_unicode(display_uri.host) + return display_uri + end + + ## + # Returns true if the URI objects are equal. This method + # normalizes both URIs before doing the comparison, and allows comparison + # against Strings. + # + # @param [Object] uri The URI to compare. + # + # @return [TrueClass, FalseClass] + # true if the URIs are equivalent, false + # otherwise. + def ===(uri) + if uri.respond_to?(:normalize) + uri_string = uri.normalize.to_s + else + begin + uri_string = ::Addressable::URI.parse(uri).normalize.to_s + rescue InvalidURIError, TypeError + return false + end + end + return self.normalize.to_s == uri_string + end + + ## + # Returns true if the URI objects are equal. This method + # normalizes both URIs before doing the comparison. + # + # @param [Object] uri The URI to compare. + # + # @return [TrueClass, FalseClass] + # true if the URIs are equivalent, false + # otherwise. + def ==(uri) + return false unless uri.kind_of?(URI) + return self.normalize.to_s == uri.normalize.to_s + end + + ## + # Returns true if the URI objects are equal. This method + # does NOT normalize either URI before doing the comparison. + # + # @param [Object] uri The URI to compare. + # + # @return [TrueClass, FalseClass] + # true if the URIs are equivalent, false + # otherwise. + def eql?(uri) + return false unless uri.kind_of?(URI) + return self.to_s == uri.to_s + end + + ## + # A hash value that will make a URI equivalent to its normalized + # form. + # + # @return [Integer] A hash of the URI. + def hash + @hash ||= self.to_s.hash * -1 + end + + ## + # Clones the URI object. + # + # @return [Addressable::URI] The cloned URI. + def dup + duplicated_uri = self.class.new( + :scheme => self.scheme ? self.scheme.dup : nil, + :user => self.user ? self.user.dup : nil, + :password => self.password ? self.password.dup : nil, + :host => self.host ? self.host.dup : nil, + :port => self.port, + :path => self.path ? self.path.dup : nil, + :query => self.query ? self.query.dup : nil, + :fragment => self.fragment ? self.fragment.dup : nil + ) + return duplicated_uri + end + + ## + # Omits components from a URI. + # + # @param [Symbol] *components The components to be omitted. + # + # @return [Addressable::URI] The URI with components omitted. + # + # @example + # uri = Addressable::URI.parse("http://example.com/path?query") + # #=> # + # uri.omit(:scheme, :authority) + # #=> # + def omit(*components) + invalid_components = components - [ + :scheme, :user, :password, :userinfo, :host, :port, :authority, + :path, :query, :fragment + ] + unless invalid_components.empty? + raise ArgumentError, + "Invalid component names: #{invalid_components.inspect}." + end + duplicated_uri = self.dup + duplicated_uri.defer_validation do + components.each do |component| + duplicated_uri.send((component.to_s + "=").to_sym, nil) + end + duplicated_uri.user = duplicated_uri.normalized_user + end + duplicated_uri + end + + ## + # Destructive form of omit. + # + # @param [Symbol] *components The components to be omitted. + # + # @return [Addressable::URI] The URI with components omitted. + # + # @see Addressable::URI#omit + def omit!(*components) + replace_self(self.omit(*components)) + end + + ## + # Determines if the URI is an empty string. + # + # @return [TrueClass, FalseClass] + # Returns true if empty, false otherwise. + def empty? + return self.to_s.empty? + end + + ## + # Converts the URI to a String. + # + # @return [String] The URI's String representation. + def to_s + if self.scheme == nil && self.path != nil && !self.path.empty? && + self.path =~ NORMPATH + raise InvalidURIError, + "Cannot assemble URI string with ambiguous path: '#{self.path}'" + end + @uri_string ||= begin + uri_string = String.new + uri_string << "#{self.scheme}:" if self.scheme != nil + uri_string << "//#{self.authority}" if self.authority != nil + uri_string << self.path.to_s + uri_string << "?#{self.query}" if self.query != nil + uri_string << "##{self.fragment}" if self.fragment != nil + uri_string.force_encoding(Encoding::UTF_8) + uri_string + end + end + + ## + # URI's are glorified Strings. Allow implicit conversion. + alias_method :to_str, :to_s + + ## + # Returns a Hash of the URI components. + # + # @return [Hash] The URI as a Hash of components. + def to_hash + return { + :scheme => self.scheme, + :user => self.user, + :password => self.password, + :host => self.host, + :port => self.port, + :path => self.path, + :query => self.query, + :fragment => self.fragment + } + end + + ## + # Returns a String representation of the URI object's state. + # + # @return [String] The URI object's state, as a String. + def inspect + sprintf("#<%s:%#0x URI:%s>", self.class.to_s, self.object_id, self.to_s) + end + + ## + # This method allows you to make several changes to a URI simultaneously, + # which separately would cause validation errors, but in conjunction, + # are valid. The URI will be revalidated as soon as the entire block has + # been executed. + # + # @param [Proc] block + # A set of operations to perform on a given URI. + def defer_validation + raise LocalJumpError, "No block given." unless block_given? + @validation_deferred = true + yield + @validation_deferred = false + validate + ensure + @validation_deferred = false + end + + def encode_with(coder) + instance_variables.each do |ivar| + value = instance_variable_get(ivar) + if value != NONE + key = ivar.to_s.slice(1..-1) + coder[key] = value + end + end + nil + end + + def init_with(coder) + reset_ivs + coder.map.each do |key, value| + instance_variable_set("@#{key}", value) + end + nil + end + + protected + SELF_REF = '.' + PARENT = '..' + + RULE_2A = /\/\.\/|\/\.$/ + RULE_2B_2C = /\/([^\/]*)\/\.\.\/|\/([^\/]*)\/\.\.$/ + RULE_2D = /^\.\.?\/?/ + RULE_PREFIXED_PARENT = /^\/\.\.?\/|^(\/\.\.?)+\/?$/ + + ## + # Resolves paths to their simplest form. + # + # @param [String] path The path to normalize. + # + # @return [String] The normalized path. + def self.normalize_path(path) + # Section 5.2.4 of RFC 3986 + + return if path.nil? + normalized_path = path.dup + loop do + mod ||= normalized_path.gsub!(RULE_2A, SLASH) + + pair = normalized_path.match(RULE_2B_2C) + if pair + parent = pair[1] + current = pair[2] + else + parent = nil + current = nil + end + + regexp = "/#{Regexp.escape(parent.to_s)}/\\.\\./|" + regexp += "(/#{Regexp.escape(current.to_s)}/\\.\\.$)" + + if pair && ((parent != SELF_REF && parent != PARENT) || + (current != SELF_REF && current != PARENT)) + mod ||= normalized_path.gsub!(Regexp.new(regexp), SLASH) + end + + mod ||= normalized_path.gsub!(RULE_2D, EMPTY_STR) + # Non-standard, removes prefixed dotted segments from path. + mod ||= normalized_path.gsub!(RULE_PREFIXED_PARENT, SLASH) + break if mod.nil? + end + + normalized_path + end + + ## + # Ensures that the URI is valid. + def validate + return if !!@validation_deferred + if self.scheme != nil && self.ip_based? && + (self.host == nil || self.host.empty?) && + (self.path == nil || self.path.empty?) + raise InvalidURIError, + "Absolute URI missing hierarchical segment: '#{self.to_s}'" + end + if self.host == nil + if self.port != nil || + self.user != nil || + self.password != nil + raise InvalidURIError, "Hostname not supplied: '#{self.to_s}'" + end + end + if self.path != nil && !self.path.empty? && self.path[0..0] != SLASH && + self.authority != nil + raise InvalidURIError, + "Cannot have a relative path with an authority set: '#{self.to_s}'" + end + if self.path != nil && !self.path.empty? && + self.path[0..1] == SLASH + SLASH && self.authority == nil + raise InvalidURIError, + "Cannot have a path with two leading slashes " + + "without an authority set: '#{self.to_s}'" + end + unreserved = CharacterClasses::UNRESERVED + sub_delims = CharacterClasses::SUB_DELIMS + if !self.host.nil? && (self.host =~ /[<>{}\/\\\?\#\@"[[:space:]]]/ || + (self.host[/^\[(.*)\]$/, 1] != nil && self.host[/^\[(.*)\]$/, 1] !~ + Regexp.new("^[#{unreserved}#{sub_delims}:]*$"))) + raise InvalidURIError, "Invalid character in host: '#{self.host.to_s}'" + end + return nil + end + + ## + # Replaces the internal state of self with the specified URI's state. + # Used in destructive operations to avoid massive code repetition. + # + # @param [Addressable::URI] uri The URI to replace self with. + # + # @return [Addressable::URI] self. + def replace_self(uri) + # Reset dependent values + reset_ivs + + @scheme = uri.scheme + @user = uri.user + @password = uri.password + @host = uri.host + @port = uri.port + @path = uri.path + @query = uri.query + @fragment = uri.fragment + return self + end + + ## + # Splits path string with "/" (slash). + # It is considered that there is empty string after last slash when + # path ends with slash. + # + # @param [String] path The path to split. + # + # @return [Array] An array of parts of path. + def split_path(path) + splitted = path.split(SLASH) + splitted << EMPTY_STR if path.end_with? SLASH + splitted + end + + ## + # Resets composite values for the entire URI + # + # @api private + def remove_composite_values + @uri_string = nil + @hash = nil + end + + ## + # Converts the string to be UTF-8 if it is not already UTF-8 + # + # @api private + def force_utf8_encoding_if_needed(str) + if str && str.encoding != Encoding::UTF_8 + str.force_encoding(Encoding::UTF_8) + end + end + + private + + ## + # Resets instance variables + # + # @api private + def reset_ivs + @scheme = nil + @user = nil + @normalized_scheme = NONE + @normalized_user = NONE + @uri_string = nil + @hash = nil + @userinfo = nil + @normalized_userinfo = NONE + @authority = nil + @password = nil + @normalized_authority = nil + @port = nil + @normalized_password = NONE + @host = nil + @normalized_host = nil + @normalized_port = NONE + @path = EMPTY_STR + @normalized_path = nil + @normalized_query = NONE + @fragment = nil + @normalized_fragment = NONE + @query = nil + end + + NONE = Module.new.freeze + + private_constant :NONE + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/lib/addressable/version.rb b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/lib/addressable/version.rb new file mode 100644 index 0000000..0813a57 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/lib/addressable/version.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +#-- +# Copyright (C) Bob Aman +# +# 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. +#++ + + +# Used to prevent the class/module from being loaded more than once +if !defined?(Addressable::VERSION) + module Addressable + module VERSION + MAJOR = 2 + MINOR = 8 + TINY = 8 + + STRING = [MAJOR, MINOR, TINY].join('.') + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/spec/addressable/idna_spec.rb b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/spec/addressable/idna_spec.rb new file mode 100644 index 0000000..428c9ec --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/spec/addressable/idna_spec.rb @@ -0,0 +1,302 @@ +# frozen_string_literal: true + +# Copyright (C) Bob Aman +# +# 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. + + +require "spec_helper" + +# Have to use RubyGems to load the idn gem. +require "rubygems" + +require "addressable/idna" + +shared_examples_for "converting from unicode to ASCII" do + it "should convert 'www.google.com' correctly" do + expect(Addressable::IDNA.to_ascii("www.google.com")).to eq("www.google.com") + end + + long = 'AcinusFallumTrompetumNullunCreditumVisumEstAtCuadLongumEtCefallum.com' + it "should convert '#{long}' correctly" do + expect(Addressable::IDNA.to_ascii(long)).to eq(long) + end + + it "should convert 'www.詹姆斯.com' correctly" do + expect(Addressable::IDNA.to_ascii( + "www.詹姆斯.com" + )).to eq("www.xn--8ws00zhy3a.com") + end + + it "also accepts unicode strings encoded as ascii-8bit" do + expect(Addressable::IDNA.to_ascii( + "www.詹姆斯.com".b + )).to eq("www.xn--8ws00zhy3a.com") + end + + it "should convert 'www.Iñtërnâtiônàlizætiøn.com' correctly" do + "www.Iñtërnâtiônàlizætiøn.com" + expect(Addressable::IDNA.to_ascii( + "www.I\xC3\xB1t\xC3\xABrn\xC3\xA2ti\xC3\xB4" + + "n\xC3\xA0liz\xC3\xA6ti\xC3\xB8n.com" + )).to eq("www.xn--itrntinliztin-vdb0a5exd8ewcye.com") + end + + it "should convert 'www.Iñtërnâtiônàlizætiøn.com' correctly" do + expect(Addressable::IDNA.to_ascii( + "www.In\xCC\x83te\xCC\x88rna\xCC\x82tio\xCC\x82n" + + "a\xCC\x80liz\xC3\xA6ti\xC3\xB8n.com" + )).to eq("www.xn--itrntinliztin-vdb0a5exd8ewcye.com") + end + + it "should convert " + + "'www.ほんとうにながいわけのわからないどめいんめいのらべるまだながくしないとたりない.w3.mag.keio.ac.jp' " + + "correctly" do + expect(Addressable::IDNA.to_ascii( + "www.\343\201\273\343\202\223\343\201\250\343\201\206\343\201\253\343" + + "\201\252\343\201\214\343\201\204\343\202\217\343\201\221\343\201\256" + + "\343\202\217\343\201\213\343\202\211\343\201\252\343\201\204\343\201" + + "\251\343\202\201\343\201\204\343\202\223\343\202\201\343\201\204\343" + + "\201\256\343\202\211\343\201\271\343\202\213\343\201\276\343\201\240" + + "\343\201\252\343\201\214\343\201\217\343\201\227\343\201\252\343\201" + + "\204\343\201\250\343\201\237\343\202\212\343\201\252\343\201\204." + + "w3.mag.keio.ac.jp" + )).to eq( + "www.xn--n8jaaaaai5bhf7as8fsfk3jnknefdde3" + + "fg11amb5gzdb4wi9bya3kc6lra.w3.mag.keio.ac.jp" + ) + end + + it "should convert " + + "'www.ほんとうにながいわけのわからないどめいんめいのらべるまだながくしないとたりない.w3.mag.keio.ac.jp' " + + "correctly" do + expect(Addressable::IDNA.to_ascii( + "www.\343\201\273\343\202\223\343\201\250\343\201\206\343\201\253\343" + + "\201\252\343\201\213\343\202\231\343\201\204\343\202\217\343\201\221" + + "\343\201\256\343\202\217\343\201\213\343\202\211\343\201\252\343\201" + + "\204\343\201\250\343\202\231\343\202\201\343\201\204\343\202\223\343" + + "\202\201\343\201\204\343\201\256\343\202\211\343\201\270\343\202\231" + + "\343\202\213\343\201\276\343\201\237\343\202\231\343\201\252\343\201" + + "\213\343\202\231\343\201\217\343\201\227\343\201\252\343\201\204\343" + + "\201\250\343\201\237\343\202\212\343\201\252\343\201\204." + + "w3.mag.keio.ac.jp" + )).to eq( + "www.xn--n8jaaaaai5bhf7as8fsfk3jnknefdde3" + + "fg11amb5gzdb4wi9bya3kc6lra.w3.mag.keio.ac.jp" + ) + end + + it "should convert '点心和烤鸭.w3.mag.keio.ac.jp' correctly" do + expect(Addressable::IDNA.to_ascii( + "点心和烤鸭.w3.mag.keio.ac.jp" + )).to eq("xn--0trv4xfvn8el34t.w3.mag.keio.ac.jp") + end + + it "should convert '가각갂갃간갅갆갇갈갉힢힣.com' correctly" do + expect(Addressable::IDNA.to_ascii( + "가각갂갃간갅갆갇갈갉힢힣.com" + )).to eq("xn--o39acdefghijk5883jma.com") + end + + it "should convert " + + "'\347\242\274\346\250\231\346\272\226\350" + + "\220\254\345\234\213\347\242\274.com' correctly" do + expect(Addressable::IDNA.to_ascii( + "\347\242\274\346\250\231\346\272\226\350" + + "\220\254\345\234\213\347\242\274.com" + )).to eq("xn--9cs565brid46mda086o.com") + end + + it "should convert 'リ宠퐱〹.com' correctly" do + expect(Addressable::IDNA.to_ascii( + "\357\276\230\345\256\240\355\220\261\343\200\271.com" + )).to eq("xn--eek174hoxfpr4k.com") + end + + it "should convert 'リ宠퐱卄.com' correctly" do + expect(Addressable::IDNA.to_ascii( + "\343\203\252\345\256\240\355\220\261\345\215\204.com" + )).to eq("xn--eek174hoxfpr4k.com") + end + + it "should convert 'ᆵ' correctly" do + expect(Addressable::IDNA.to_ascii( + "\341\206\265" + )).to eq("xn--4ud") + end + + it "should convert 'ᆵ' correctly" do + expect(Addressable::IDNA.to_ascii( + "\357\276\257" + )).to eq("xn--4ud") + end + + it "should convert '🌹🌹🌹.ws' correctly" do + expect(Addressable::IDNA.to_ascii( + "\360\237\214\271\360\237\214\271\360\237\214\271.ws" + )).to eq("xn--2h8haa.ws") + end + + it "should handle two adjacent '.'s correctly" do + expect(Addressable::IDNA.to_ascii( + "example..host" + )).to eq("example..host") + end +end + +shared_examples_for "converting from ASCII to unicode" do + long = 'AcinusFallumTrompetumNullunCreditumVisumEstAtCuadLongumEtCefallum.com' + it "should convert '#{long}' correctly" do + expect(Addressable::IDNA.to_unicode(long)).to eq(long) + end + + it "should return the identity conversion when punycode decode fails" do + expect(Addressable::IDNA.to_unicode("xn--zckp1cyg1.sblo.jp")).to eq( + "xn--zckp1cyg1.sblo.jp") + end + + it "should return the identity conversion when the ACE prefix has no suffix" do + expect(Addressable::IDNA.to_unicode("xn--...-")).to eq("xn--...-") + end + + it "should convert 'www.google.com' correctly" do + expect(Addressable::IDNA.to_unicode("www.google.com")).to eq( + "www.google.com") + end + + it "should convert 'www.詹姆斯.com' correctly" do + expect(Addressable::IDNA.to_unicode( + "www.xn--8ws00zhy3a.com" + )).to eq("www.詹姆斯.com") + end + + it "should convert '詹姆斯.com' correctly" do + expect(Addressable::IDNA.to_unicode( + "xn--8ws00zhy3a.com" + )).to eq("詹姆斯.com") + end + + it "should convert 'www.iñtërnâtiônàlizætiøn.com' correctly" do + expect(Addressable::IDNA.to_unicode( + "www.xn--itrntinliztin-vdb0a5exd8ewcye.com" + )).to eq("www.iñtërnâtiônàlizætiøn.com") + end + + it "should convert 'iñtërnâtiônàlizætiøn.com' correctly" do + expect(Addressable::IDNA.to_unicode( + "xn--itrntinliztin-vdb0a5exd8ewcye.com" + )).to eq("iñtërnâtiônàlizætiøn.com") + end + + it "should convert " + + "'www.ほんとうにながいわけのわからないどめいんめいのらべるまだながくしないとたりない.w3.mag.keio.ac.jp' " + + "correctly" do + expect(Addressable::IDNA.to_unicode( + "www.xn--n8jaaaaai5bhf7as8fsfk3jnknefdde3" + + "fg11amb5gzdb4wi9bya3kc6lra.w3.mag.keio.ac.jp" + )).to eq( + "www.ほんとうにながいわけのわからないどめいんめいのらべるまだながくしないとたりない.w3.mag.keio.ac.jp" + ) + end + + it "should convert '点心和烤鸭.w3.mag.keio.ac.jp' correctly" do + expect(Addressable::IDNA.to_unicode( + "xn--0trv4xfvn8el34t.w3.mag.keio.ac.jp" + )).to eq("点心和烤鸭.w3.mag.keio.ac.jp") + end + + it "should convert '가각갂갃간갅갆갇갈갉힢힣.com' correctly" do + expect(Addressable::IDNA.to_unicode( + "xn--o39acdefghijk5883jma.com" + )).to eq("가각갂갃간갅갆갇갈갉힢힣.com") + end + + it "should convert " + + "'\347\242\274\346\250\231\346\272\226\350" + + "\220\254\345\234\213\347\242\274.com' correctly" do + expect(Addressable::IDNA.to_unicode( + "xn--9cs565brid46mda086o.com" + )).to eq( + "\347\242\274\346\250\231\346\272\226\350" + + "\220\254\345\234\213\347\242\274.com" + ) + end + + it "should convert 'リ宠퐱卄.com' correctly" do + expect(Addressable::IDNA.to_unicode( + "xn--eek174hoxfpr4k.com" + )).to eq("\343\203\252\345\256\240\355\220\261\345\215\204.com") + end + + it "should convert 'ᆵ' correctly" do + expect(Addressable::IDNA.to_unicode( + "xn--4ud" + )).to eq("\341\206\265") + end + + it "should convert '🌹🌹🌹.ws' correctly" do + expect(Addressable::IDNA.to_unicode( + "xn--2h8haa.ws" + )).to eq("\360\237\214\271\360\237\214\271\360\237\214\271.ws") + end + + it "should handle two adjacent '.'s correctly" do + expect(Addressable::IDNA.to_unicode( + "example..host" + )).to eq("example..host") + end +end + +describe Addressable::IDNA, "when using the pure-Ruby implementation" do + before do + Addressable.send(:remove_const, :IDNA) + load "addressable/idna/pure.rb" + end + + it_should_behave_like "converting from unicode to ASCII" + it_should_behave_like "converting from ASCII to unicode" + + begin + require "fiber" + + it "should not blow up inside fibers" do + f = Fiber.new do + Addressable.send(:remove_const, :IDNA) + load "addressable/idna/pure.rb" + end + f.resume + end + rescue LoadError + # Fibers aren't supported in this version of Ruby, skip this test. + warn('Fibers unsupported.') + end +end + +begin + require "idn" + + describe Addressable::IDNA, "when using the native-code implementation" do + before do + Addressable.send(:remove_const, :IDNA) + load "addressable/idna/native.rb" + end + + it_should_behave_like "converting from unicode to ASCII" + it_should_behave_like "converting from ASCII to unicode" + end +rescue LoadError => error + raise error if ENV["CI"] && TestHelper.native_supported? + + # Cannot test the native implementation without libidn support. + warn('Could not load native IDN implementation.') +end diff --git a/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/spec/addressable/net_http_compat_spec.rb b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/spec/addressable/net_http_compat_spec.rb new file mode 100644 index 0000000..d07a43e --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/spec/addressable/net_http_compat_spec.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +# Copyright (C) Bob Aman +# +# 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. + + +require "spec_helper" + +require "addressable/uri" +require "net/http" + +describe Net::HTTP do + it "should be compatible with Addressable" do + response_body = + Net::HTTP.get(Addressable::URI.parse('http://www.google.com/')) + expect(response_body).not_to be_nil + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/spec/addressable/security_spec.rb b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/spec/addressable/security_spec.rb new file mode 100644 index 0000000..3bf90a2 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/spec/addressable/security_spec.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +# Copyright (C) Bob Aman +# +# 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. + + +require "spec_helper" + +require "addressable/uri" + +describe Addressable::URI, "when created with a URI known to cause crashes " + + "in certain browsers" do + it "should parse correctly" do + uri = Addressable::URI.parse('%%30%30') + expect(uri.path).to eq('%%30%30') + expect(uri.normalize.path).to eq('%2500') + end + + it "should parse correctly as a full URI" do + uri = Addressable::URI.parse('http://www.example.com/%%30%30') + expect(uri.path).to eq('/%%30%30') + expect(uri.normalize.path).to eq('/%2500') + end +end + +describe Addressable::URI, "when created with a URI known to cause crashes " + + "in certain browsers" do + it "should parse correctly" do + uri = Addressable::URI.parse('لُصّبُلُلصّبُررً ॣ ॣh ॣ ॣ 冗') + expect(uri.path).to eq('لُصّبُلُلصّبُررً ॣ ॣh ॣ ॣ 冗') + expect(uri.normalize.path).to eq( + '%D9%84%D9%8F%D8%B5%D9%91%D8%A8%D9%8F%D9%84%D9%8F%D9%84%D8%B5%D9%91' + + '%D8%A8%D9%8F%D8%B1%D8%B1%D9%8B%20%E0%A5%A3%20%E0%A5%A3h%20%E0%A5' + + '%A3%20%E0%A5%A3%20%E5%86%97' + ) + end + + it "should parse correctly as a full URI" do + uri = Addressable::URI.parse('http://www.example.com/لُصّبُلُلصّبُررً ॣ ॣh ॣ ॣ 冗') + expect(uri.path).to eq('/لُصّبُلُلصّبُررً ॣ ॣh ॣ ॣ 冗') + expect(uri.normalize.path).to eq( + '/%D9%84%D9%8F%D8%B5%D9%91%D8%A8%D9%8F%D9%84%D9%8F%D9%84%D8%B5%D9%91' + + '%D8%A8%D9%8F%D8%B1%D8%B1%D9%8B%20%E0%A5%A3%20%E0%A5%A3h%20%E0%A5' + + '%A3%20%E0%A5%A3%20%E5%86%97' + ) + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/spec/addressable/template_spec.rb b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/spec/addressable/template_spec.rb new file mode 100644 index 0000000..24616c2 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/spec/addressable/template_spec.rb @@ -0,0 +1,1264 @@ +# frozen_string_literal: true + +# Copyright (C) Bob Aman +# +# 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. + + +require "spec_helper" + +require "bigdecimal" +require "timeout" +require "addressable/template" + +shared_examples_for 'expands' do |tests| + tests.each do |template, expansion| + exp = expansion.is_a?(Array) ? expansion.first : expansion + it "#{template} to #{exp}" do + tmpl = Addressable::Template.new(template).expand(subject) + expect(tmpl.to_str).to eq(expansion) + end + end +end + +describe "eql?" do + let(:template) { Addressable::Template.new('https://www.example.com/{foo}') } + it 'is equal when the pattern matches' do + other_template = Addressable::Template.new('https://www.example.com/{foo}') + expect(template).to be_eql(other_template) + expect(other_template).to be_eql(template) + end + it 'is not equal when the pattern differs' do + other_template = Addressable::Template.new('https://www.example.com/{bar}') + expect(template).to_not be_eql(other_template) + expect(other_template).to_not be_eql(template) + end + it 'is not equal to non-templates' do + uri = 'https://www.example.com/foo/bar' + addressable_template = Addressable::Template.new uri + addressable_uri = Addressable::URI.parse uri + expect(addressable_template).to_not be_eql(addressable_uri) + expect(addressable_uri).to_not be_eql(addressable_template) + end +end + +describe "==" do + let(:template) { Addressable::Template.new('https://www.example.com/{foo}') } + it 'is equal when the pattern matches' do + other_template = Addressable::Template.new('https://www.example.com/{foo}') + expect(template).to eq other_template + expect(other_template).to eq template + end + it 'is not equal when the pattern differs' do + other_template = Addressable::Template.new('https://www.example.com/{bar}') + expect(template).not_to eq other_template + expect(other_template).not_to eq template + end + it 'is not equal to non-templates' do + uri = 'https://www.example.com/foo/bar' + addressable_template = Addressable::Template.new uri + addressable_uri = Addressable::URI.parse uri + expect(addressable_template).not_to eq addressable_uri + expect(addressable_uri).not_to eq addressable_template + end +end + +describe "#to_regexp" do + it "does not match the first line of multiline strings" do + uri = "https://www.example.com/bar" + template = Addressable::Template.new(uri) + expect(template.match(uri)).not_to be_nil + expect(template.match("#{uri}\ngarbage")).to be_nil + end +end + +describe "Type conversion" do + subject { + { + :var => true, + :hello => 1234, + :nothing => nil, + :sym => :symbolic, + :decimal => BigDecimal('1') + } + } + + it_behaves_like 'expands', { + '{var}' => 'true', + '{hello}' => '1234', + '{nothing}' => '', + '{sym}' => 'symbolic', + '{decimal}' => RUBY_VERSION < '2.4.0' ? '0.1E1' : '0.1e1' + } +end + +describe "Level 1:" do + subject { + {:var => "value", :hello => "Hello World!"} + } + it_behaves_like 'expands', { + '{var}' => 'value', + '{hello}' => 'Hello%20World%21' + } +end + +describe "Level 2" do + subject { + { + :var => "value", + :hello => "Hello World!", + :path => "/foo/bar" + } + } + context "Operator +:" do + it_behaves_like 'expands', { + '{+var}' => 'value', + '{+hello}' => 'Hello%20World!', + '{+path}/here' => '/foo/bar/here', + 'here?ref={+path}' => 'here?ref=/foo/bar' + } + end + context "Operator #:" do + it_behaves_like 'expands', { + 'X{#var}' => 'X#value', + 'X{#hello}' => 'X#Hello%20World!' + } + end +end + +describe "Level 3" do + subject { + { + :var => "value", + :hello => "Hello World!", + :empty => "", + :path => "/foo/bar", + :x => "1024", + :y => "768" + } + } + context "Operator nil (multiple vars):" do + it_behaves_like 'expands', { + 'map?{x,y}' => 'map?1024,768', + '{x,hello,y}' => '1024,Hello%20World%21,768' + } + end + context "Operator + (multiple vars):" do + it_behaves_like 'expands', { + '{+x,hello,y}' => '1024,Hello%20World!,768', + '{+path,x}/here' => '/foo/bar,1024/here' + } + end + context "Operator # (multiple vars):" do + it_behaves_like 'expands', { + '{#x,hello,y}' => '#1024,Hello%20World!,768', + '{#path,x}/here' => '#/foo/bar,1024/here' + } + end + context "Operator ." do + it_behaves_like 'expands', { + 'X{.var}' => 'X.value', + 'X{.x,y}' => 'X.1024.768' + } + end + context "Operator /" do + it_behaves_like 'expands', { + '{/var}' => '/value', + '{/var,x}/here' => '/value/1024/here' + } + end + context "Operator ;" do + it_behaves_like 'expands', { + '{;x,y}' => ';x=1024;y=768', + '{;x,y,empty}' => ';x=1024;y=768;empty' + } + end + context "Operator ?" do + it_behaves_like 'expands', { + '{?x,y}' => '?x=1024&y=768', + '{?x,y,empty}' => '?x=1024&y=768&empty=' + } + end + context "Operator &" do + it_behaves_like 'expands', { + '?fixed=yes{&x}' => '?fixed=yes&x=1024', + '{&x,y,empty}' => '&x=1024&y=768&empty=' + } + end +end + +describe "Level 4" do + subject { + { + :var => "value", + :hello => "Hello World!", + :path => "/foo/bar", + :semi => ";", + :list => %w(red green blue), + :keys => {"semi" => ';', "dot" => '.', :comma => ','} + } + } + context "Expansion with value modifiers" do + it_behaves_like 'expands', { + '{var:3}' => 'val', + '{var:30}' => 'value', + '{list}' => 'red,green,blue', + '{list*}' => 'red,green,blue', + '{keys}' => 'semi,%3B,dot,.,comma,%2C', + '{keys*}' => 'semi=%3B,dot=.,comma=%2C', + } + end + context "Operator + with value modifiers" do + it_behaves_like 'expands', { + '{+path:6}/here' => '/foo/b/here', + '{+list}' => 'red,green,blue', + '{+list*}' => 'red,green,blue', + '{+keys}' => 'semi,;,dot,.,comma,,', + '{+keys*}' => 'semi=;,dot=.,comma=,', + } + end + context "Operator # with value modifiers" do + it_behaves_like 'expands', { + '{#path:6}/here' => '#/foo/b/here', + '{#list}' => '#red,green,blue', + '{#list*}' => '#red,green,blue', + '{#keys}' => '#semi,;,dot,.,comma,,', + '{#keys*}' => '#semi=;,dot=.,comma=,', + } + end + context "Operator . with value modifiers" do + it_behaves_like 'expands', { + 'X{.var:3}' => 'X.val', + 'X{.list}' => 'X.red,green,blue', + 'X{.list*}' => 'X.red.green.blue', + 'X{.keys}' => 'X.semi,%3B,dot,.,comma,%2C', + 'X{.keys*}' => 'X.semi=%3B.dot=..comma=%2C', + } + end + context "Operator / with value modifiers" do + it_behaves_like 'expands', { + '{/var:1,var}' => '/v/value', + '{/list}' => '/red,green,blue', + '{/list*}' => '/red/green/blue', + '{/list*,path:4}' => '/red/green/blue/%2Ffoo', + '{/keys}' => '/semi,%3B,dot,.,comma,%2C', + '{/keys*}' => '/semi=%3B/dot=./comma=%2C', + } + end + context "Operator ; with value modifiers" do + it_behaves_like 'expands', { + '{;hello:5}' => ';hello=Hello', + '{;list}' => ';list=red,green,blue', + '{;list*}' => ';list=red;list=green;list=blue', + '{;keys}' => ';keys=semi,%3B,dot,.,comma,%2C', + '{;keys*}' => ';semi=%3B;dot=.;comma=%2C', + } + end + context "Operator ? with value modifiers" do + it_behaves_like 'expands', { + '{?var:3}' => '?var=val', + '{?list}' => '?list=red,green,blue', + '{?list*}' => '?list=red&list=green&list=blue', + '{?keys}' => '?keys=semi,%3B,dot,.,comma,%2C', + '{?keys*}' => '?semi=%3B&dot=.&comma=%2C', + } + end + context "Operator & with value modifiers" do + it_behaves_like 'expands', { + '{&var:3}' => '&var=val', + '{&list}' => '&list=red,green,blue', + '{&list*}' => '&list=red&list=green&list=blue', + '{&keys}' => '&keys=semi,%3B,dot,.,comma,%2C', + '{&keys*}' => '&semi=%3B&dot=.&comma=%2C', + } + end +end +describe "Modifiers" do + subject { + { + :var => "value", + :semi => ";", + :year => [1965, 2000, 2012], + :dom => %w(example com) + } + } + context "length" do + it_behaves_like 'expands', { + '{var:3}' => 'val', + '{var:30}' => 'value', + '{var}' => 'value', + '{semi}' => '%3B', + '{semi:2}' => '%3B' + } + end + context "explode" do + it_behaves_like 'expands', { + 'find{?year*}' => 'find?year=1965&year=2000&year=2012', + 'www{.dom*}' => 'www.example.com', + } + end +end +describe "Expansion" do + subject { + { + :count => ["one", "two", "three"], + :dom => ["example", "com"], + :dub => "me/too", + :hello => "Hello World!", + :half => "50%", + :var => "value", + :who => "fred", + :base => "http://example.com/home/", + :path => "/foo/bar", + :list => ["red", "green", "blue"], + :keys => {"semi" => ";","dot" => ".",:comma => ","}, + :v => "6", + :x => "1024", + :y => "768", + :empty => "", + :empty_keys => {}, + :undef => nil + } + } + context "concatenation" do + it_behaves_like 'expands', { + '{count}' => 'one,two,three', + '{count*}' => 'one,two,three', + '{/count}' => '/one,two,three', + '{/count*}' => '/one/two/three', + '{;count}' => ';count=one,two,three', + '{;count*}' => ';count=one;count=two;count=three', + '{?count}' => '?count=one,two,three', + '{?count*}' => '?count=one&count=two&count=three', + '{&count*}' => '&count=one&count=two&count=three' + } + end + context "simple expansion" do + it_behaves_like 'expands', { + '{var}' => 'value', + '{hello}' => 'Hello%20World%21', + '{half}' => '50%25', + 'O{empty}X' => 'OX', + 'O{undef}X' => 'OX', + '{x,y}' => '1024,768', + '{x,hello,y}' => '1024,Hello%20World%21,768', + '?{x,empty}' => '?1024,', + '?{x,undef}' => '?1024', + '?{undef,y}' => '?768', + '{var:3}' => 'val', + '{var:30}' => 'value', + '{list}' => 'red,green,blue', + '{list*}' => 'red,green,blue', + '{keys}' => 'semi,%3B,dot,.,comma,%2C', + '{keys*}' => 'semi=%3B,dot=.,comma=%2C', + } + end + context "reserved expansion (+)" do + it_behaves_like 'expands', { + '{+var}' => 'value', + '{+hello}' => 'Hello%20World!', + '{+half}' => '50%25', + '{base}index' => 'http%3A%2F%2Fexample.com%2Fhome%2Findex', + '{+base}index' => 'http://example.com/home/index', + 'O{+empty}X' => 'OX', + 'O{+undef}X' => 'OX', + '{+path}/here' => '/foo/bar/here', + 'here?ref={+path}' => 'here?ref=/foo/bar', + 'up{+path}{var}/here' => 'up/foo/barvalue/here', + '{+x,hello,y}' => '1024,Hello%20World!,768', + '{+path,x}/here' => '/foo/bar,1024/here', + '{+path:6}/here' => '/foo/b/here', + '{+list}' => 'red,green,blue', + '{+list*}' => 'red,green,blue', + '{+keys}' => 'semi,;,dot,.,comma,,', + '{+keys*}' => 'semi=;,dot=.,comma=,', + } + end + context "fragment expansion (#)" do + it_behaves_like 'expands', { + '{#var}' => '#value', + '{#hello}' => '#Hello%20World!', + '{#half}' => '#50%25', + 'foo{#empty}' => 'foo#', + 'foo{#undef}' => 'foo', + '{#x,hello,y}' => '#1024,Hello%20World!,768', + '{#path,x}/here' => '#/foo/bar,1024/here', + '{#path:6}/here' => '#/foo/b/here', + '{#list}' => '#red,green,blue', + '{#list*}' => '#red,green,blue', + '{#keys}' => '#semi,;,dot,.,comma,,', + '{#keys*}' => '#semi=;,dot=.,comma=,', + } + end + context "label expansion (.)" do + it_behaves_like 'expands', { + '{.who}' => '.fred', + '{.who,who}' => '.fred.fred', + '{.half,who}' => '.50%25.fred', + 'www{.dom*}' => 'www.example.com', + 'X{.var}' => 'X.value', + 'X{.empty}' => 'X.', + 'X{.undef}' => 'X', + 'X{.var:3}' => 'X.val', + 'X{.list}' => 'X.red,green,blue', + 'X{.list*}' => 'X.red.green.blue', + 'X{.keys}' => 'X.semi,%3B,dot,.,comma,%2C', + 'X{.keys*}' => 'X.semi=%3B.dot=..comma=%2C', + 'X{.empty_keys}' => 'X', + 'X{.empty_keys*}' => 'X' + } + end + context "path expansion (/)" do + it_behaves_like 'expands', { + '{/who}' => '/fred', + '{/who,who}' => '/fred/fred', + '{/half,who}' => '/50%25/fred', + '{/who,dub}' => '/fred/me%2Ftoo', + '{/var}' => '/value', + '{/var,empty}' => '/value/', + '{/var,undef}' => '/value', + '{/var,x}/here' => '/value/1024/here', + '{/var:1,var}' => '/v/value', + '{/list}' => '/red,green,blue', + '{/list*}' => '/red/green/blue', + '{/list*,path:4}' => '/red/green/blue/%2Ffoo', + '{/keys}' => '/semi,%3B,dot,.,comma,%2C', + '{/keys*}' => '/semi=%3B/dot=./comma=%2C', + } + end + context "path-style expansion (;)" do + it_behaves_like 'expands', { + '{;who}' => ';who=fred', + '{;half}' => ';half=50%25', + '{;empty}' => ';empty', + '{;v,empty,who}' => ';v=6;empty;who=fred', + '{;v,bar,who}' => ';v=6;who=fred', + '{;x,y}' => ';x=1024;y=768', + '{;x,y,empty}' => ';x=1024;y=768;empty', + '{;x,y,undef}' => ';x=1024;y=768', + '{;hello:5}' => ';hello=Hello', + '{;list}' => ';list=red,green,blue', + '{;list*}' => ';list=red;list=green;list=blue', + '{;keys}' => ';keys=semi,%3B,dot,.,comma,%2C', + '{;keys*}' => ';semi=%3B;dot=.;comma=%2C', + } + end + context "form query expansion (?)" do + it_behaves_like 'expands', { + '{?who}' => '?who=fred', + '{?half}' => '?half=50%25', + '{?x,y}' => '?x=1024&y=768', + '{?x,y,empty}' => '?x=1024&y=768&empty=', + '{?x,y,undef}' => '?x=1024&y=768', + '{?var:3}' => '?var=val', + '{?list}' => '?list=red,green,blue', + '{?list*}' => '?list=red&list=green&list=blue', + '{?keys}' => '?keys=semi,%3B,dot,.,comma,%2C', + '{?keys*}' => '?semi=%3B&dot=.&comma=%2C', + } + end + context "form query expansion (&)" do + it_behaves_like 'expands', { + '{&who}' => '&who=fred', + '{&half}' => '&half=50%25', + '?fixed=yes{&x}' => '?fixed=yes&x=1024', + '{&x,y,empty}' => '&x=1024&y=768&empty=', + '{&x,y,undef}' => '&x=1024&y=768', + '{&var:3}' => '&var=val', + '{&list}' => '&list=red,green,blue', + '{&list*}' => '&list=red&list=green&list=blue', + '{&keys}' => '&keys=semi,%3B,dot,.,comma,%2C', + '{&keys*}' => '&semi=%3B&dot=.&comma=%2C', + } + end + context "non-string key in match data" do + subject {Addressable::Template.new("http://example.com/{one}")} + + it "raises TypeError" do + expect { subject.expand(Object.new => "1") }.to raise_error TypeError + end + end +end + +class ExampleTwoProcessor + def self.restore(name, value) + return value.gsub(/-/, " ") if name == "query" + return value + end + + def self.match(name) + return ".*?" if name == "first" + return ".*" + end + def self.validate(name, value) + return !!(value =~ /^[\w ]+$/) if name == "query" + return true + end + + def self.transform(name, value) + return value.gsub(/ /, "+") if name == "query" + return value + end +end + +class DumbProcessor + def self.match(name) + return ".*?" if name == "first" + end +end + +describe Addressable::Template do + describe 'initialize' do + context 'with a non-string' do + it 'raises a TypeError' do + expect { Addressable::Template.new(nil) }.to raise_error(TypeError) + end + end + end + + describe 'freeze' do + subject { Addressable::Template.new("http://example.com/{first}/{+second}/") } + it 'freezes the template' do + expect(subject.freeze).to be_frozen + end + end + + describe "Matching" do + let(:uri){ + Addressable::URI.parse( + "http://example.com/search/an-example-search-query/" + ) + } + let(:uri2){ + Addressable::URI.parse("http://example.com/a/b/c/") + } + let(:uri3){ + Addressable::URI.parse("http://example.com/;a=1;b=2;c=3;first=foo") + } + let(:uri4){ + Addressable::URI.parse("http://example.com/?a=1&b=2&c=3&first=foo") + } + let(:uri5){ + "http://example.com/foo" + } + context "first uri with ExampleTwoProcessor" do + subject { + Addressable::Template.new( + "http://example.com/search/{query}/" + ).match(uri, ExampleTwoProcessor) + } + its(:variables){ should == ["query"] } + its(:captures){ should == ["an example search query"] } + end + + context "second uri with ExampleTwoProcessor" do + subject { + Addressable::Template.new( + "http://example.com/{first}/{+second}/" + ).match(uri2, ExampleTwoProcessor) + } + its(:variables){ should == ["first", "second"] } + its(:captures){ should == ["a", "b/c"] } + end + + context "second uri with DumbProcessor" do + subject { + Addressable::Template.new( + "http://example.com/{first}/{+second}/" + ).match(uri2, DumbProcessor) + } + its(:variables){ should == ["first", "second"] } + its(:captures){ should == ["a", "b/c"] } + end + + context "second uri" do + subject { + Addressable::Template.new( + "http://example.com/{first}{/second*}/" + ).match(uri2) + } + its(:variables){ should == ["first", "second"] } + its(:captures){ should == ["a", ["b","c"]] } + end + context "third uri" do + subject { + Addressable::Template.new( + "http://example.com/{;hash*,first}" + ).match(uri3) + } + its(:variables){ should == ["hash", "first"] } + its(:captures){ should == [ + {"a" => "1", "b" => "2", "c" => "3", "first" => "foo"}, nil] } + end + # Note that this expansion is impossible to revert deterministically - the + # * operator means first could have been a key of hash or a separate key. + # Semantically, a separate key is more likely, but both are possible. + context "fourth uri" do + subject { + Addressable::Template.new( + "http://example.com/{?hash*,first}" + ).match(uri4) + } + its(:variables){ should == ["hash", "first"] } + its(:captures){ should == [ + {"a" => "1", "b" => "2", "c" => "3", "first"=> "foo"}, nil] } + end + context "fifth uri" do + subject { + Addressable::Template.new( + "http://example.com/{path}{?hash*,first}" + ).match(uri5) + } + its(:variables){ should == ["path", "hash", "first"] } + its(:captures){ should == ["foo", nil, nil] } + end + end + + describe 'match' do + subject { Addressable::Template.new('http://example.com/first/second/') } + context 'when the URI is the same as the template' do + it 'returns the match data itself with an empty mapping' do + uri = Addressable::URI.parse('http://example.com/first/second/') + match_data = subject.match(uri) + expect(match_data).to be_an Addressable::Template::MatchData + expect(match_data.uri).to eq(uri) + expect(match_data.template).to eq(subject) + expect(match_data.mapping).to be_empty + expect(match_data.inspect).to be_an String + end + end + end + + describe "extract" do + let(:template) { + Addressable::Template.new( + "http://{host}{/segments*}/{?one,two,bogus}{#fragment}" + ) + } + let(:uri){ "http://example.com/a/b/c/?one=1&two=2#foo" } + let(:uri2){ "http://example.com/a/b/c/#foo" } + it "should be able to extract with queries" do + expect(template.extract(uri)).to eq({ + "host" => "example.com", + "segments" => %w(a b c), + "one" => "1", + "bogus" => nil, + "two" => "2", + "fragment" => "foo" + }) + end + it "should be able to extract without queries" do + expect(template.extract(uri2)).to eq({ + "host" => "example.com", + "segments" => %w(a b c), + "one" => nil, + "bogus" => nil, + "two" => nil, + "fragment" => "foo" + }) + end + + context "issue #137" do + subject { Addressable::Template.new('/path{?page,per_page}') } + + it "can match empty" do + data = subject.extract("/path") + expect(data["page"]).to eq(nil) + expect(data["per_page"]).to eq(nil) + expect(data.keys.sort).to eq(['page', 'per_page']) + end + + it "can match first var" do + data = subject.extract("/path?page=1") + expect(data["page"]).to eq("1") + expect(data["per_page"]).to eq(nil) + expect(data.keys.sort).to eq(['page', 'per_page']) + end + + it "can match second var" do + data = subject.extract("/path?per_page=1") + expect(data["page"]).to eq(nil) + expect(data["per_page"]).to eq("1") + expect(data.keys.sort).to eq(['page', 'per_page']) + end + + it "can match both vars" do + data = subject.extract("/path?page=2&per_page=1") + expect(data["page"]).to eq("2") + expect(data["per_page"]).to eq("1") + expect(data.keys.sort).to eq(['page', 'per_page']) + end + end + end + + describe "Partial expand with symbols" do + context "partial_expand with two simple values" do + subject { + Addressable::Template.new("http://example.com/{one}/{two}/") + } + it "builds a new pattern" do + expect(subject.partial_expand(:one => "1").pattern).to eq( + "http://example.com/1/{two}/" + ) + end + end + context "partial_expand query with missing param in middle" do + subject { + Addressable::Template.new("http://example.com/{?one,two,three}/") + } + it "builds a new pattern" do + expect(subject.partial_expand(:one => "1", :three => "3").pattern).to eq( + "http://example.com/?one=1{&two}&three=3/" + ) + end + end + context "partial_expand form style query with missing param at beginning" do + subject { + Addressable::Template.new("http://example.com/{?one,two}/") + } + it "builds a new pattern" do + expect(subject.partial_expand(:two => "2").pattern).to eq( + "http://example.com/?two=2{&one}/" + ) + end + end + context "issue #307 - partial_expand form query with nil params" do + subject do + Addressable::Template.new("http://example.com/{?one,two,three}/") + end + it "builds a new pattern with two=nil" do + expect(subject.partial_expand(two: nil).pattern).to eq( + "http://example.com/{?one}{&three}/" + ) + end + it "builds a new pattern with one=nil and two=nil" do + expect(subject.partial_expand(one: nil, two: nil).pattern).to eq( + "http://example.com/{?three}/" + ) + end + it "builds a new pattern with one=1 and two=nil" do + expect(subject.partial_expand(one: 1, two: nil).pattern).to eq( + "http://example.com/?one=1{&three}/" + ) + end + it "builds a new pattern with one=nil and two=2" do + expect(subject.partial_expand(one: nil, two: 2).pattern).to eq( + "http://example.com/?two=2{&three}/" + ) + end + it "builds a new pattern with one=nil" do + expect(subject.partial_expand(one: nil).pattern).to eq( + "http://example.com/{?two}{&three}/" + ) + end + end + context "partial_expand with query string" do + subject { + Addressable::Template.new("http://example.com/{?two,one}/") + } + it "builds a new pattern" do + expect(subject.partial_expand(:one => "1").pattern).to eq( + "http://example.com/?one=1{&two}/" + ) + end + end + context "partial_expand with path operator" do + subject { + Addressable::Template.new("http://example.com{/one,two}/") + } + it "builds a new pattern" do + expect(subject.partial_expand(:one => "1").pattern).to eq( + "http://example.com/1{/two}/" + ) + end + end + context "partial expand with unicode values" do + subject do + Addressable::Template.new("http://example.com/{resource}/{query}/") + end + it "normalizes unicode by default" do + template = subject.partial_expand("query" => "Cafe\u0301") + expect(template.pattern).to eq( + "http://example.com/{resource}/Caf%C3%A9/" + ) + end + + it "normalizes as unicode even with wrong encoding specified" do + template = subject.partial_expand("query" => "Cafe\u0301".b) + expect(template.pattern).to eq( + "http://example.com/{resource}/Caf%C3%A9/" + ) + end + + it "raises on invalid unicode input" do + expect { + subject.partial_expand("query" => "M\xE9thode".b) + }.to raise_error(ArgumentError, "invalid byte sequence in UTF-8") + end + + it "does not normalize unicode when byte semantics requested" do + template = subject.partial_expand({"query" => "Cafe\u0301"}, nil, false) + expect(template.pattern).to eq( + "http://example.com/{resource}/Cafe%CC%81/" + ) + end + end + end + describe "Partial expand with strings" do + context "partial_expand with two simple values" do + subject { + Addressable::Template.new("http://example.com/{one}/{two}/") + } + it "builds a new pattern" do + expect(subject.partial_expand("one" => "1").pattern).to eq( + "http://example.com/1/{two}/" + ) + end + end + context "partial_expand query with missing param in middle" do + subject { + Addressable::Template.new("http://example.com/{?one,two,three}/") + } + it "builds a new pattern" do + expect(subject.partial_expand("one" => "1", "three" => "3").pattern).to eq( + "http://example.com/?one=1{&two}&three=3/" + ) + end + end + context "partial_expand with query string" do + subject { + Addressable::Template.new("http://example.com/{?two,one}/") + } + it "builds a new pattern" do + expect(subject.partial_expand("one" => "1").pattern).to eq( + "http://example.com/?one=1{&two}/" + ) + end + end + context "partial_expand with path operator" do + subject { + Addressable::Template.new("http://example.com{/one,two}/") + } + it "builds a new pattern" do + expect(subject.partial_expand("one" => "1").pattern).to eq( + "http://example.com/1{/two}/" + ) + end + end + end + describe "Expand" do + context "expand with unicode values" do + subject do + Addressable::Template.new("http://example.com/search/{query}/") + end + it "normalizes unicode by default" do + uri = subject.expand("query" => "Cafe\u0301").to_str + expect(uri).to eq("http://example.com/search/Caf%C3%A9/") + end + + it "normalizes as unicode even with wrong encoding specified" do + uri = subject.expand("query" => "Cafe\u0301".b).to_str + expect(uri).to eq("http://example.com/search/Caf%C3%A9/") + end + + it "raises on invalid unicode input" do + expect { + subject.expand("query" => "M\xE9thode".b).to_str + }.to raise_error(ArgumentError, "invalid byte sequence in UTF-8") + end + + it "does not normalize unicode when byte semantics requested" do + uri = subject.expand({ "query" => "Cafe\u0301" }, nil, false).to_str + expect(uri).to eq("http://example.com/search/Cafe%CC%81/") + end + end + context "expand with a processor" do + subject { + Addressable::Template.new("http://example.com/search/{query}/") + } + it "processes spaces" do + expect(subject.expand({"query" => "an example search query"}, + ExampleTwoProcessor).to_str).to eq( + "http://example.com/search/an+example+search+query/" + ) + end + it "validates" do + expect{ + subject.expand({"query" => "Bogus!"}, + ExampleTwoProcessor).to_str + }.to raise_error(Addressable::Template::InvalidTemplateValueError) + end + end + context "partial_expand query with missing param in middle" do + subject { + Addressable::Template.new("http://example.com/{?one,two,three}/") + } + it "builds a new pattern" do + expect(subject.partial_expand("one" => "1", "three" => "3").pattern).to eq( + "http://example.com/?one=1{&two}&three=3/" + ) + end + end + context "partial_expand with query string" do + subject { + Addressable::Template.new("http://example.com/{?two,one}/") + } + it "builds a new pattern" do + expect(subject.partial_expand("one" => "1").pattern).to eq( + "http://example.com/?one=1{&two}/" + ) + end + end + context "partial_expand with path operator" do + subject { + Addressable::Template.new("http://example.com{/one,two}/") + } + it "builds a new pattern" do + expect(subject.partial_expand("one" => "1").pattern).to eq( + "http://example.com/1{/two}/" + ) + end + end + end + context "Matching with operators" do + describe "Level 1:" do + subject { Addressable::Template.new("foo{foo}/{bar}baz") } + it "can match" do + data = subject.match("foofoo/bananabaz") + expect(data.mapping["foo"]).to eq("foo") + expect(data.mapping["bar"]).to eq("banana") + end + it "can fail" do + expect(subject.match("bar/foo")).to be_nil + expect(subject.match("foobaz")).to be_nil + end + it "can match empty" do + data = subject.match("foo/baz") + expect(data.mapping["foo"]).to eq(nil) + expect(data.mapping["bar"]).to eq(nil) + end + it "lists vars" do + expect(subject.variables).to eq(["foo", "bar"]) + end + end + + describe "Level 2:" do + subject { Addressable::Template.new("foo{+foo}{#bar}baz") } + it "can match" do + data = subject.match("foo/test/banana#bazbaz") + expect(data.mapping["foo"]).to eq("/test/banana") + expect(data.mapping["bar"]).to eq("baz") + end + it "can match empty level 2 #" do + data = subject.match("foo/test/bananabaz") + expect(data.mapping["foo"]).to eq("/test/banana") + expect(data.mapping["bar"]).to eq(nil) + data = subject.match("foo/test/banana#baz") + expect(data.mapping["foo"]).to eq("/test/banana") + expect(data.mapping["bar"]).to eq("") + end + it "can match empty level 2 +" do + data = subject.match("foobaz") + expect(data.mapping["foo"]).to eq(nil) + expect(data.mapping["bar"]).to eq(nil) + data = subject.match("foo#barbaz") + expect(data.mapping["foo"]).to eq(nil) + expect(data.mapping["bar"]).to eq("bar") + end + it "lists vars" do + expect(subject.variables).to eq(["foo", "bar"]) + end + end + + describe "Level 3:" do + context "no operator" do + subject { Addressable::Template.new("foo{foo,bar}baz") } + it "can match" do + data = subject.match("foofoo,barbaz") + expect(data.mapping["foo"]).to eq("foo") + expect(data.mapping["bar"]).to eq("bar") + end + it "lists vars" do + expect(subject.variables).to eq(["foo", "bar"]) + end + end + context "+ operator" do + subject { Addressable::Template.new("foo{+foo,bar}baz") } + it "can match" do + data = subject.match("foofoo/bar,barbaz") + expect(data.mapping["bar"]).to eq("foo/bar,bar") + expect(data.mapping["foo"]).to eq("") + end + it "lists vars" do + expect(subject.variables).to eq(["foo", "bar"]) + end + end + context ". operator" do + subject { Addressable::Template.new("foo{.foo,bar}baz") } + it "can match" do + data = subject.match("foo.foo.barbaz") + expect(data.mapping["foo"]).to eq("foo") + expect(data.mapping["bar"]).to eq("bar") + end + it "lists vars" do + expect(subject.variables).to eq(["foo", "bar"]) + end + end + context "/ operator" do + subject { Addressable::Template.new("foo{/foo,bar}baz") } + it "can match" do + data = subject.match("foo/foo/barbaz") + expect(data.mapping["foo"]).to eq("foo") + expect(data.mapping["bar"]).to eq("bar") + end + it "lists vars" do + expect(subject.variables).to eq(["foo", "bar"]) + end + end + context "; operator" do + subject { Addressable::Template.new("foo{;foo,bar,baz}baz") } + it "can match" do + data = subject.match("foo;foo=bar%20baz;bar=foo;bazbaz") + expect(data.mapping["foo"]).to eq("bar baz") + expect(data.mapping["bar"]).to eq("foo") + expect(data.mapping["baz"]).to eq("") + end + it "lists vars" do + expect(subject.variables).to eq(%w(foo bar baz)) + end + end + context "? operator" do + context "test" do + subject { Addressable::Template.new("foo{?foo,bar}baz") } + it "can match" do + data = subject.match("foo?foo=bar%20baz&bar=foobaz") + expect(data.mapping["foo"]).to eq("bar baz") + expect(data.mapping["bar"]).to eq("foo") + end + it "lists vars" do + expect(subject.variables).to eq(%w(foo bar)) + end + end + + context "issue #137" do + subject { Addressable::Template.new('/path{?page,per_page}') } + + it "can match empty" do + data = subject.match("/path") + expect(data.mapping["page"]).to eq(nil) + expect(data.mapping["per_page"]).to eq(nil) + expect(data.mapping.keys.sort).to eq(['page', 'per_page']) + end + + it "can match first var" do + data = subject.match("/path?page=1") + expect(data.mapping["page"]).to eq("1") + expect(data.mapping["per_page"]).to eq(nil) + expect(data.mapping.keys.sort).to eq(['page', 'per_page']) + end + + it "can match second var" do + data = subject.match("/path?per_page=1") + expect(data.mapping["page"]).to eq(nil) + expect(data.mapping["per_page"]).to eq("1") + expect(data.mapping.keys.sort).to eq(['page', 'per_page']) + end + + it "can match both vars" do + data = subject.match("/path?page=2&per_page=1") + expect(data.mapping["page"]).to eq("2") + expect(data.mapping["per_page"]).to eq("1") + expect(data.mapping.keys.sort).to eq(['page', 'per_page']) + end + end + + context "issue #71" do + subject { Addressable::Template.new("http://cyberscore.dev/api/users{?username}") } + it "can match" do + data = subject.match("http://cyberscore.dev/api/users?username=foobaz") + expect(data.mapping["username"]).to eq("foobaz") + end + it "lists vars" do + expect(subject.variables).to eq(%w(username)) + expect(subject.keys).to eq(%w(username)) + end + end + end + context "& operator" do + subject { Addressable::Template.new("foo{&foo,bar}baz") } + it "can match" do + data = subject.match("foo&foo=bar%20baz&bar=foobaz") + expect(data.mapping["foo"]).to eq("bar baz") + expect(data.mapping["bar"]).to eq("foo") + end + it "lists vars" do + expect(subject.variables).to eq(%w(foo bar)) + end + end + end + end + + context "support regexes:" do + context "EXPRESSION" do + subject { Addressable::Template::EXPRESSION } + it "should be able to match an expression" do + expect(subject).to match("{foo}") + expect(subject).to match("{foo,9}") + expect(subject).to match("{foo.bar,baz}") + expect(subject).to match("{+foo.bar,baz}") + expect(subject).to match("{foo,foo%20bar}") + expect(subject).to match("{#foo:20,baz*}") + expect(subject).to match("stuff{#foo:20,baz*}things") + end + it "should fail on non vars" do + expect(subject).not_to match("!{foo") + expect(subject).not_to match("{foo.bar.}") + expect(subject).not_to match("!{}") + end + end + context "VARNAME" do + subject { Addressable::Template::VARNAME } + it "should be able to match a variable" do + expect(subject).to match("foo") + expect(subject).to match("9") + expect(subject).to match("foo.bar") + expect(subject).to match("foo_bar") + expect(subject).to match("foo_bar.baz") + expect(subject).to match("foo%20bar") + expect(subject).to match("foo%20bar.baz") + end + it "should fail on non vars" do + expect(subject).not_to match("!foo") + expect(subject).not_to match("foo.bar.") + expect(subject).not_to match("foo%2%00bar") + expect(subject).not_to match("foo_ba%r") + expect(subject).not_to match("foo_bar*") + expect(subject).not_to match("foo_bar:20") + end + + it 'should parse in a reasonable time' do + expect do + Timeout.timeout(0.1) do + expect(subject).not_to match("0"*25 + "!") + end + end.not_to raise_error + end + end + context "VARIABLE_LIST" do + subject { Addressable::Template::VARIABLE_LIST } + it "should be able to match a variable list" do + expect(subject).to match("foo,bar") + expect(subject).to match("foo") + expect(subject).to match("foo,bar*,baz") + expect(subject).to match("foo.bar,bar_baz*,baz:12") + end + it "should fail on non vars" do + expect(subject).not_to match(",foo,bar*,baz") + expect(subject).not_to match("foo,*bar,baz") + expect(subject).not_to match("foo,,bar*,baz") + end + end + context "VARSPEC" do + subject { Addressable::Template::VARSPEC } + it "should be able to match a variable with modifier" do + expect(subject).to match("9:8") + expect(subject).to match("foo.bar*") + expect(subject).to match("foo_bar:12") + expect(subject).to match("foo_bar.baz*") + expect(subject).to match("foo%20bar:12") + expect(subject).to match("foo%20bar.baz*") + end + it "should fail on non vars" do + expect(subject).not_to match("!foo") + expect(subject).not_to match("*foo") + expect(subject).not_to match("fo*o") + expect(subject).not_to match("fo:o") + expect(subject).not_to match("foo:") + end + end + end +end + +describe Addressable::Template::MatchData do + let(:template) { Addressable::Template.new('{foo}/{bar}') } + subject(:its) { template.match('ab/cd') } + its(:uri) { should == Addressable::URI.parse('ab/cd') } + its(:template) { should == template } + its(:mapping) { should == { 'foo' => 'ab', 'bar' => 'cd' } } + its(:variables) { should == ['foo', 'bar'] } + its(:keys) { should == ['foo', 'bar'] } + its(:names) { should == ['foo', 'bar'] } + its(:values) { should == ['ab', 'cd'] } + its(:captures) { should == ['ab', 'cd'] } + its(:to_a) { should == ['ab/cd', 'ab', 'cd'] } + its(:to_s) { should == 'ab/cd' } + its(:string) { should == its.to_s } + its(:pre_match) { should == "" } + its(:post_match) { should == "" } + + describe 'values_at' do + it 'returns an array with the values' do + expect(its.values_at(0, 2)).to eq(['ab/cd', 'cd']) + end + it 'allows mixing integer an string keys' do + expect(its.values_at('foo', 1)).to eq(['ab', 'ab']) + end + it 'accepts unknown keys' do + expect(its.values_at('baz', 'foo')).to eq([nil, 'ab']) + end + end + + describe '[]' do + context 'string key' do + it 'returns the corresponding capture' do + expect(its['foo']).to eq('ab') + expect(its['bar']).to eq('cd') + end + it 'returns nil for unknown keys' do + expect(its['baz']).to be_nil + end + end + context 'symbol key' do + it 'returns the corresponding capture' do + expect(its[:foo]).to eq('ab') + expect(its[:bar]).to eq('cd') + end + it 'returns nil for unknown keys' do + expect(its[:baz]).to be_nil + end + end + context 'integer key' do + it 'returns the full URI for index 0' do + expect(its[0]).to eq('ab/cd') + end + it 'returns the corresponding capture' do + expect(its[1]).to eq('ab') + expect(its[2]).to eq('cd') + end + it 'returns nil for unknown keys' do + expect(its[3]).to be_nil + end + end + context 'other key' do + it 'raises an exception' do + expect { its[Object.new] }.to raise_error(TypeError) + end + end + context 'with length' do + it 'returns an array starting at index with given length' do + expect(its[0, 2]).to eq(['ab/cd', 'ab']) + expect(its[2, 1]).to eq(['cd']) + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/spec/addressable/uri_spec.rb b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/spec/addressable/uri_spec.rb new file mode 100644 index 0000000..68ee303 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/spec/addressable/uri_spec.rb @@ -0,0 +1,6854 @@ +# frozen_string_literal: true + +# Copyright (C) Bob Aman +# +# 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. + + +require "spec_helper" + +require "addressable/uri" +require "uri" +require "ipaddr" +require "yaml" + +if !"".respond_to?("force_encoding") + class String + def force_encoding(encoding) + @encoding = encoding + end + + def encoding + @encoding ||= Encoding::ASCII_8BIT + end + end + + class Encoding + def initialize(name) + @name = name + end + + def to_s + return @name + end + + UTF_8 = Encoding.new("UTF-8") + ASCII_8BIT = Encoding.new("US-ASCII") + end +end + +module Fake + module URI + class HTTP + def initialize(uri) + @uri = uri + end + + def to_s + return @uri.to_s + end + + alias :to_str :to_s + end + end +end + +describe Addressable::URI, "when created with a non-numeric port number" do + it "should raise an error" do + expect do + Addressable::URI.new(:port => "bogus") + end.to raise_error(Addressable::URI::InvalidURIError) + end +end + +describe Addressable::URI, "when created with a invalid encoded port number" do + it "should raise an error" do + expect do + Addressable::URI.new(:port => "%eb") + end.to raise_error(Addressable::URI::InvalidURIError) + end +end + +describe Addressable::URI, "when created with a non-string scheme" do + it "should raise an error" do + expect do + Addressable::URI.new(:scheme => :bogus) + end.to raise_error(TypeError) + end +end + +describe Addressable::URI, "when created with a non-string user" do + it "should raise an error" do + expect do + Addressable::URI.new(:user => :bogus) + end.to raise_error(TypeError) + end +end + +describe Addressable::URI, "when created with a non-string password" do + it "should raise an error" do + expect do + Addressable::URI.new(:password => :bogus) + end.to raise_error(TypeError) + end +end + +describe Addressable::URI, "when created with a non-string userinfo" do + it "should raise an error" do + expect do + Addressable::URI.new(:userinfo => :bogus) + end.to raise_error(TypeError) + end +end + +describe Addressable::URI, "when created with a non-string host" do + it "should raise an error" do + expect do + Addressable::URI.new(:host => :bogus) + end.to raise_error(TypeError) + end +end + +describe Addressable::URI, "when created with a non-string authority" do + it "should raise an error" do + expect do + Addressable::URI.new(:authority => :bogus) + end.to raise_error(TypeError) + end +end + +describe Addressable::URI, "when created with a non-string path" do + it "should raise an error" do + expect do + Addressable::URI.new(:path => :bogus) + end.to raise_error(TypeError) + end +end + +describe Addressable::URI, "when created with a non-string query" do + it "should raise an error" do + expect do + Addressable::URI.new(:query => :bogus) + end.to raise_error(TypeError) + end +end + +describe Addressable::URI, "when created with a non-string fragment" do + it "should raise an error" do + expect do + Addressable::URI.new(:fragment => :bogus) + end.to raise_error(TypeError) + end +end + +describe Addressable::URI, "when created with a scheme but no hierarchical " + + "segment" do + it "should raise an error" do + expect do + Addressable::URI.parse("http:") + end.to raise_error(Addressable::URI::InvalidURIError) + end +end + +describe Addressable::URI, "quote handling" do + describe 'in host name' do + it "should raise an error for single quote" do + expect do + Addressable::URI.parse("http://local\"host/") + end.to raise_error(Addressable::URI::InvalidURIError) + end + end +end + +describe Addressable::URI, "newline normalization" do + it "should not accept newlines in scheme" do + expect do + Addressable::URI.parse("ht%0atp://localhost/") + end.to raise_error(Addressable::URI::InvalidURIError) + end + + it "should not unescape newline in path" do + uri = Addressable::URI.parse("http://localhost/%0a").normalize + expect(uri.to_s).to eq("http://localhost/%0A") + end + + it "should not unescape newline in hostname" do + uri = Addressable::URI.parse("http://local%0ahost/").normalize + expect(uri.to_s).to eq("http://local%0Ahost/") + end + + it "should not unescape newline in username" do + uri = Addressable::URI.parse("http://foo%0abar@localhost/").normalize + expect(uri.to_s).to eq("http://foo%0Abar@localhost/") + end + + it "should not unescape newline in username" do + uri = Addressable::URI.parse("http://example:foo%0abar@example/").normalize + expect(uri.to_s).to eq("http://example:foo%0Abar@example/") + end + + it "should not accept newline in hostname" do + uri = Addressable::URI.parse("http://localhost/") + expect do + uri.host = "local\nhost" + end.to raise_error(Addressable::URI::InvalidURIError) + end +end + +describe Addressable::URI, "when created with ambiguous path" do + it "should raise an error" do + expect do + Addressable::URI.parse("::http") + end.to raise_error(Addressable::URI::InvalidURIError) + end +end + +describe Addressable::URI, "when created with an invalid host" do + it "should raise an error" do + expect do + Addressable::URI.new(:host => "") + end.to raise_error(Addressable::URI::InvalidURIError) + end +end + +describe Addressable::URI, "when created with a host consisting of " + + "sub-delims characters" do + it "should not raise an error" do + expect do + Addressable::URI.new( + :host => Addressable::URI::CharacterClasses::SUB_DELIMS.gsub(/\\/, '') + ) + end.not_to raise_error + end +end + +describe Addressable::URI, "when created with a host consisting of " + + "unreserved characters" do + it "should not raise an error" do + expect do + Addressable::URI.new( + :host => Addressable::URI::CharacterClasses::UNRESERVED.gsub(/\\/, '') + ) + end.not_to raise_error + end +end + +describe Addressable::URI, "when created from nil components" do + before do + @uri = Addressable::URI.new + end + + it "should have a nil site value" do + expect(@uri.site).to eq(nil) + end + + it "should have an empty path" do + expect(@uri.path).to eq("") + end + + it "should be an empty uri" do + expect(@uri.to_s).to eq("") + end + + it "should have a nil default port" do + expect(@uri.default_port).to eq(nil) + end + + it "should be empty" do + expect(@uri).to be_empty + end + + it "should raise an error if the scheme is set to whitespace" do + expect do + @uri.scheme = "\t \n" + end.to raise_error(Addressable::URI::InvalidURIError, /'\t \n'/) + end + + it "should raise an error if the scheme is set to all digits" do + expect do + @uri.scheme = "123" + end.to raise_error(Addressable::URI::InvalidURIError, /'123'/) + end + + it "should raise an error if the scheme begins with a digit" do + expect do + @uri.scheme = "1scheme" + end.to raise_error(Addressable::URI::InvalidURIError, /'1scheme'/) + end + + it "should raise an error if the scheme begins with a plus" do + expect do + @uri.scheme = "+scheme" + end.to raise_error(Addressable::URI::InvalidURIError, /'\+scheme'/) + end + + it "should raise an error if the scheme begins with a dot" do + expect do + @uri.scheme = ".scheme" + end.to raise_error(Addressable::URI::InvalidURIError, /'\.scheme'/) + end + + it "should raise an error if the scheme begins with a dash" do + expect do + @uri.scheme = "-scheme" + end.to raise_error(Addressable::URI::InvalidURIError, /'-scheme'/) + end + + it "should raise an error if the scheme contains an illegal character" do + expect do + @uri.scheme = "scheme!" + end.to raise_error(Addressable::URI::InvalidURIError, /'scheme!'/) + end + + it "should raise an error if the scheme contains whitespace" do + expect do + @uri.scheme = "sch eme" + end.to raise_error(Addressable::URI::InvalidURIError, /'sch eme'/) + end + + it "should raise an error if the scheme contains a newline" do + expect do + @uri.scheme = "sch\neme" + end.to raise_error(Addressable::URI::InvalidURIError) + end + + it "should raise an error if set into an invalid state" do + expect do + @uri.user = "user" + end.to raise_error(Addressable::URI::InvalidURIError) + end + + it "should raise an error if set into an invalid state" do + expect do + @uri.password = "pass" + end.to raise_error(Addressable::URI::InvalidURIError) + end + + it "should raise an error if set into an invalid state" do + expect do + @uri.scheme = "http" + @uri.fragment = "fragment" + end.to raise_error(Addressable::URI::InvalidURIError) + end + + it "should raise an error if set into an invalid state" do + expect do + @uri.fragment = "fragment" + @uri.scheme = "http" + end.to raise_error(Addressable::URI::InvalidURIError) + end +end + +describe Addressable::URI, "when initialized from individual components" do + before do + @uri = Addressable::URI.new( + :scheme => "http", + :user => "user", + :password => "password", + :host => "example.com", + :port => 8080, + :path => "/path", + :query => "query=value", + :fragment => "fragment" + ) + end + + it "returns 'http' for #scheme" do + expect(@uri.scheme).to eq("http") + end + + it "returns 'http' for #normalized_scheme" do + expect(@uri.normalized_scheme).to eq("http") + end + + it "returns 'user' for #user" do + expect(@uri.user).to eq("user") + end + + it "returns 'user' for #normalized_user" do + expect(@uri.normalized_user).to eq("user") + end + + it "returns 'password' for #password" do + expect(@uri.password).to eq("password") + end + + it "returns 'password' for #normalized_password" do + expect(@uri.normalized_password).to eq("password") + end + + it "returns 'user:password' for #userinfo" do + expect(@uri.userinfo).to eq("user:password") + end + + it "returns 'user:password' for #normalized_userinfo" do + expect(@uri.normalized_userinfo).to eq("user:password") + end + + it "returns 'example.com' for #host" do + expect(@uri.host).to eq("example.com") + end + + it "returns 'example.com' for #normalized_host" do + expect(@uri.normalized_host).to eq("example.com") + end + + it "returns 'com' for #tld" do + expect(@uri.tld).to eq("com") + end + + it "returns 'user:password@example.com:8080' for #authority" do + expect(@uri.authority).to eq("user:password@example.com:8080") + end + + it "returns 'user:password@example.com:8080' for #normalized_authority" do + expect(@uri.normalized_authority).to eq("user:password@example.com:8080") + end + + it "returns 8080 for #port" do + expect(@uri.port).to eq(8080) + end + + it "returns 8080 for #normalized_port" do + expect(@uri.normalized_port).to eq(8080) + end + + it "returns 80 for #default_port" do + expect(@uri.default_port).to eq(80) + end + + it "returns 'http://user:password@example.com:8080' for #site" do + expect(@uri.site).to eq("http://user:password@example.com:8080") + end + + it "returns 'http://user:password@example.com:8080' for #normalized_site" do + expect(@uri.normalized_site).to eq("http://user:password@example.com:8080") + end + + it "returns '/path' for #path" do + expect(@uri.path).to eq("/path") + end + + it "returns '/path' for #normalized_path" do + expect(@uri.normalized_path).to eq("/path") + end + + it "returns 'query=value' for #query" do + expect(@uri.query).to eq("query=value") + end + + it "returns 'query=value' for #normalized_query" do + expect(@uri.normalized_query).to eq("query=value") + end + + it "returns 'fragment' for #fragment" do + expect(@uri.fragment).to eq("fragment") + end + + it "returns 'fragment' for #normalized_fragment" do + expect(@uri.normalized_fragment).to eq("fragment") + end + + it "returns #hash" do + expect(@uri.hash).not_to be nil + end + + it "returns #to_s" do + expect(@uri.to_s).to eq( + "http://user:password@example.com:8080/path?query=value#fragment" + ) + end + + it "should not be empty" do + expect(@uri).not_to be_empty + end + + it "should not be frozen" do + expect(@uri).not_to be_frozen + end + + it "should allow destructive operations" do + expect { @uri.normalize! }.not_to raise_error + end +end + +describe Addressable::URI, "when initialized from " + + "frozen individual components" do + before do + @uri = Addressable::URI.new( + :scheme => "http".freeze, + :user => "user".freeze, + :password => "password".freeze, + :host => "example.com".freeze, + :port => "8080".freeze, + :path => "/path".freeze, + :query => "query=value".freeze, + :fragment => "fragment".freeze + ) + end + + it "returns 'http' for #scheme" do + expect(@uri.scheme).to eq("http") + end + + it "returns 'http' for #normalized_scheme" do + expect(@uri.normalized_scheme).to eq("http") + end + + it "returns 'user' for #user" do + expect(@uri.user).to eq("user") + end + + it "returns 'user' for #normalized_user" do + expect(@uri.normalized_user).to eq("user") + end + + it "returns 'password' for #password" do + expect(@uri.password).to eq("password") + end + + it "returns 'password' for #normalized_password" do + expect(@uri.normalized_password).to eq("password") + end + + it "returns 'user:password' for #userinfo" do + expect(@uri.userinfo).to eq("user:password") + end + + it "returns 'user:password' for #normalized_userinfo" do + expect(@uri.normalized_userinfo).to eq("user:password") + end + + it "returns 'example.com' for #host" do + expect(@uri.host).to eq("example.com") + end + + it "returns 'example.com' for #normalized_host" do + expect(@uri.normalized_host).to eq("example.com") + end + + it "returns 'user:password@example.com:8080' for #authority" do + expect(@uri.authority).to eq("user:password@example.com:8080") + end + + it "returns 'user:password@example.com:8080' for #normalized_authority" do + expect(@uri.normalized_authority).to eq("user:password@example.com:8080") + end + + it "returns 8080 for #port" do + expect(@uri.port).to eq(8080) + end + + it "returns 8080 for #normalized_port" do + expect(@uri.normalized_port).to eq(8080) + end + + it "returns 80 for #default_port" do + expect(@uri.default_port).to eq(80) + end + + it "returns 'http://user:password@example.com:8080' for #site" do + expect(@uri.site).to eq("http://user:password@example.com:8080") + end + + it "returns 'http://user:password@example.com:8080' for #normalized_site" do + expect(@uri.normalized_site).to eq("http://user:password@example.com:8080") + end + + it "returns '/path' for #path" do + expect(@uri.path).to eq("/path") + end + + it "returns '/path' for #normalized_path" do + expect(@uri.normalized_path).to eq("/path") + end + + it "returns 'query=value' for #query" do + expect(@uri.query).to eq("query=value") + end + + it "returns 'query=value' for #normalized_query" do + expect(@uri.normalized_query).to eq("query=value") + end + + it "returns 'fragment' for #fragment" do + expect(@uri.fragment).to eq("fragment") + end + + it "returns 'fragment' for #normalized_fragment" do + expect(@uri.normalized_fragment).to eq("fragment") + end + + it "returns #hash" do + expect(@uri.hash).not_to be nil + end + + it "returns #to_s" do + expect(@uri.to_s).to eq( + "http://user:password@example.com:8080/path?query=value#fragment" + ) + end + + it "should not be empty" do + expect(@uri).not_to be_empty + end + + it "should not be frozen" do + expect(@uri).not_to be_frozen + end + + it "should allow destructive operations" do + expect { @uri.normalize! }.not_to raise_error + end +end + +describe Addressable::URI, "when parsed from a frozen string" do + before do + @uri = Addressable::URI.parse( + "http://user:password@example.com:8080/path?query=value#fragment".freeze + ) + end + + it "returns 'http' for #scheme" do + expect(@uri.scheme).to eq("http") + end + + it "returns 'http' for #normalized_scheme" do + expect(@uri.normalized_scheme).to eq("http") + end + + it "returns 'user' for #user" do + expect(@uri.user).to eq("user") + end + + it "returns 'user' for #normalized_user" do + expect(@uri.normalized_user).to eq("user") + end + + it "returns 'password' for #password" do + expect(@uri.password).to eq("password") + end + + it "returns 'password' for #normalized_password" do + expect(@uri.normalized_password).to eq("password") + end + + it "returns 'user:password' for #userinfo" do + expect(@uri.userinfo).to eq("user:password") + end + + it "returns 'user:password' for #normalized_userinfo" do + expect(@uri.normalized_userinfo).to eq("user:password") + end + + it "returns 'example.com' for #host" do + expect(@uri.host).to eq("example.com") + end + + it "returns 'example.com' for #normalized_host" do + expect(@uri.normalized_host).to eq("example.com") + end + + it "returns 'user:password@example.com:8080' for #authority" do + expect(@uri.authority).to eq("user:password@example.com:8080") + end + + it "returns 'user:password@example.com:8080' for #normalized_authority" do + expect(@uri.normalized_authority).to eq("user:password@example.com:8080") + end + + it "returns 8080 for #port" do + expect(@uri.port).to eq(8080) + end + + it "returns 8080 for #normalized_port" do + expect(@uri.normalized_port).to eq(8080) + end + + it "returns 80 for #default_port" do + expect(@uri.default_port).to eq(80) + end + + it "returns 'http://user:password@example.com:8080' for #site" do + expect(@uri.site).to eq("http://user:password@example.com:8080") + end + + it "returns 'http://user:password@example.com:8080' for #normalized_site" do + expect(@uri.normalized_site).to eq("http://user:password@example.com:8080") + end + + it "returns '/path' for #path" do + expect(@uri.path).to eq("/path") + end + + it "returns '/path' for #normalized_path" do + expect(@uri.normalized_path).to eq("/path") + end + + it "returns 'query=value' for #query" do + expect(@uri.query).to eq("query=value") + end + + it "returns 'query=value' for #normalized_query" do + expect(@uri.normalized_query).to eq("query=value") + end + + it "returns 'fragment' for #fragment" do + expect(@uri.fragment).to eq("fragment") + end + + it "returns 'fragment' for #normalized_fragment" do + expect(@uri.normalized_fragment).to eq("fragment") + end + + it "returns #hash" do + expect(@uri.hash).not_to be nil + end + + it "returns #to_s" do + expect(@uri.to_s).to eq( + "http://user:password@example.com:8080/path?query=value#fragment" + ) + end + + it "should not be empty" do + expect(@uri).not_to be_empty + end + + it "should not be frozen" do + expect(@uri).not_to be_frozen + end + + it "should allow destructive operations" do + expect { @uri.normalize! }.not_to raise_error + end +end + +describe Addressable::URI, "when frozen" do + before do + @uri = Addressable::URI.new.freeze + end + + it "returns nil for #scheme" do + expect(@uri.scheme).to eq(nil) + end + + it "returns nil for #normalized_scheme" do + expect(@uri.normalized_scheme).to eq(nil) + end + + it "returns nil for #user" do + expect(@uri.user).to eq(nil) + end + + it "returns nil for #normalized_user" do + expect(@uri.normalized_user).to eq(nil) + end + + it "returns nil for #password" do + expect(@uri.password).to eq(nil) + end + + it "returns nil for #normalized_password" do + expect(@uri.normalized_password).to eq(nil) + end + + it "returns nil for #userinfo" do + expect(@uri.userinfo).to eq(nil) + end + + it "returns nil for #normalized_userinfo" do + expect(@uri.normalized_userinfo).to eq(nil) + end + + it "returns nil for #host" do + expect(@uri.host).to eq(nil) + end + + it "returns nil for #normalized_host" do + expect(@uri.normalized_host).to eq(nil) + end + + it "returns nil for #authority" do + expect(@uri.authority).to eq(nil) + end + + it "returns nil for #normalized_authority" do + expect(@uri.normalized_authority).to eq(nil) + end + + it "returns nil for #port" do + expect(@uri.port).to eq(nil) + end + + it "returns nil for #normalized_port" do + expect(@uri.normalized_port).to eq(nil) + end + + it "returns nil for #default_port" do + expect(@uri.default_port).to eq(nil) + end + + it "returns nil for #site" do + expect(@uri.site).to eq(nil) + end + + it "returns nil for #normalized_site" do + expect(@uri.normalized_site).to eq(nil) + end + + it "returns '' for #path" do + expect(@uri.path).to eq('') + end + + it "returns '' for #normalized_path" do + expect(@uri.normalized_path).to eq('') + end + + it "returns nil for #query" do + expect(@uri.query).to eq(nil) + end + + it "returns nil for #normalized_query" do + expect(@uri.normalized_query).to eq(nil) + end + + it "returns nil for #fragment" do + expect(@uri.fragment).to eq(nil) + end + + it "returns nil for #normalized_fragment" do + expect(@uri.normalized_fragment).to eq(nil) + end + + it "returns #hash" do + expect(@uri.hash).not_to be nil + end + + it "returns #to_s" do + expect(@uri.to_s).to eq('') + end + + it "should be empty" do + expect(@uri).to be_empty + end + + it "should be frozen" do + expect(@uri).to be_frozen + end + + it "should not be frozen after duping" do + expect(@uri.dup).not_to be_frozen + end + + it "should not allow destructive operations" do + expect { @uri.normalize! }.to raise_error { |error| + expect(error.message).to match(/can't modify frozen/) + expect(error).to satisfy { |e| RuntimeError === e || TypeError === e } + } + end +end + +describe Addressable::URI, "when frozen" do + before do + @uri = Addressable::URI.parse( + "HTTP://example.com.:%38%30/%70a%74%68?a=%31#1%323" + ).freeze + end + + it "returns 'HTTP' for #scheme" do + expect(@uri.scheme).to eq("HTTP") + end + + it "returns 'http' for #normalized_scheme" do + expect(@uri.normalized_scheme).to eq("http") + expect(@uri.normalize.scheme).to eq("http") + end + + it "returns nil for #user" do + expect(@uri.user).to eq(nil) + end + + it "returns nil for #normalized_user" do + expect(@uri.normalized_user).to eq(nil) + end + + it "returns nil for #password" do + expect(@uri.password).to eq(nil) + end + + it "returns nil for #normalized_password" do + expect(@uri.normalized_password).to eq(nil) + end + + it "returns nil for #userinfo" do + expect(@uri.userinfo).to eq(nil) + end + + it "returns nil for #normalized_userinfo" do + expect(@uri.normalized_userinfo).to eq(nil) + end + + it "returns 'example.com.' for #host" do + expect(@uri.host).to eq("example.com.") + end + + it "returns nil for #normalized_host" do + expect(@uri.normalized_host).to eq("example.com") + expect(@uri.normalize.host).to eq("example.com") + end + + it "returns 'example.com.:80' for #authority" do + expect(@uri.authority).to eq("example.com.:80") + end + + it "returns 'example.com:80' for #normalized_authority" do + expect(@uri.normalized_authority).to eq("example.com") + expect(@uri.normalize.authority).to eq("example.com") + end + + it "returns 80 for #port" do + expect(@uri.port).to eq(80) + end + + it "returns nil for #normalized_port" do + expect(@uri.normalized_port).to eq(nil) + expect(@uri.normalize.port).to eq(nil) + end + + it "returns 80 for #default_port" do + expect(@uri.default_port).to eq(80) + end + + it "returns 'HTTP://example.com.:80' for #site" do + expect(@uri.site).to eq("HTTP://example.com.:80") + end + + it "returns 'http://example.com' for #normalized_site" do + expect(@uri.normalized_site).to eq("http://example.com") + expect(@uri.normalize.site).to eq("http://example.com") + end + + it "returns '/%70a%74%68' for #path" do + expect(@uri.path).to eq("/%70a%74%68") + end + + it "returns '/path' for #normalized_path" do + expect(@uri.normalized_path).to eq("/path") + expect(@uri.normalize.path).to eq("/path") + end + + it "returns 'a=%31' for #query" do + expect(@uri.query).to eq("a=%31") + end + + it "returns 'a=1' for #normalized_query" do + expect(@uri.normalized_query).to eq("a=1") + expect(@uri.normalize.query).to eq("a=1") + end + + it "returns '/%70a%74%68?a=%31' for #request_uri" do + expect(@uri.request_uri).to eq("/%70a%74%68?a=%31") + end + + it "returns '1%323' for #fragment" do + expect(@uri.fragment).to eq("1%323") + end + + it "returns '123' for #normalized_fragment" do + expect(@uri.normalized_fragment).to eq("123") + expect(@uri.normalize.fragment).to eq("123") + end + + it "returns #hash" do + expect(@uri.hash).not_to be nil + end + + it "returns #to_s" do + expect(@uri.to_s).to eq('HTTP://example.com.:80/%70a%74%68?a=%31#1%323') + expect(@uri.normalize.to_s).to eq('http://example.com/path?a=1#123') + end + + it "should not be empty" do + expect(@uri).not_to be_empty + end + + it "should be frozen" do + expect(@uri).to be_frozen + end + + it "should not be frozen after duping" do + expect(@uri.dup).not_to be_frozen + end + + it "should not allow destructive operations" do + expect { @uri.normalize! }.to raise_error { |error| + expect(error.message).to match(/can't modify frozen/) + expect(error).to satisfy { |e| RuntimeError === e || TypeError === e } + } + end +end + +describe Addressable::URI, "when normalized and then deeply frozen" do + before do + @uri = Addressable::URI.parse( + "http://user:password@example.com:8080/path?query=value#fragment" + ).normalize! + + @uri.instance_variables.each do |var| + @uri.instance_variable_set(var, @uri.instance_variable_get(var).freeze) + end + + @uri.freeze + end + + it "#normalized_scheme should not error" do + expect { @uri.normalized_scheme }.not_to raise_error + end + + it "#normalized_user should not error" do + expect { @uri.normalized_user }.not_to raise_error + end + + it "#normalized_password should not error" do + expect { @uri.normalized_password }.not_to raise_error + end + + it "#normalized_userinfo should not error" do + expect { @uri.normalized_userinfo }.not_to raise_error + end + + it "#normalized_host should not error" do + expect { @uri.normalized_host }.not_to raise_error + end + + it "#normalized_authority should not error" do + expect { @uri.normalized_authority }.not_to raise_error + end + + it "#normalized_port should not error" do + expect { @uri.normalized_port }.not_to raise_error + end + + it "#normalized_site should not error" do + expect { @uri.normalized_site }.not_to raise_error + end + + it "#normalized_path should not error" do + expect { @uri.normalized_path }.not_to raise_error + end + + it "#normalized_query should not error" do + expect { @uri.normalized_query }.not_to raise_error + end + + it "#normalized_fragment should not error" do + expect { @uri.normalized_fragment }.not_to raise_error + end + + it "should be frozen" do + expect(@uri).to be_frozen + end + + it "should not allow destructive operations" do + expect { @uri.normalize! }.to raise_error(RuntimeError) + end +end + +describe Addressable::URI, "when created from string components" do + before do + @uri = Addressable::URI.new( + :scheme => "http", :host => "example.com" + ) + end + + it "should have a site value of 'http://example.com'" do + expect(@uri.site).to eq("http://example.com") + end + + it "should be equal to the equivalent parsed URI" do + expect(@uri).to eq(Addressable::URI.parse("http://example.com")) + end + + it "should raise an error if invalid components omitted" do + expect do + @uri.omit(:bogus) + end.to raise_error(ArgumentError) + expect do + @uri.omit(:scheme, :bogus, :path) + end.to raise_error(ArgumentError) + end +end + +describe Addressable::URI, "when created with a nil host but " + + "non-nil authority components" do + it "should raise an error" do + expect do + Addressable::URI.new(:user => "user", :password => "pass", :port => 80) + end.to raise_error(Addressable::URI::InvalidURIError) + end +end + +describe Addressable::URI, "when created with both an authority and a user" do + it "should raise an error" do + expect do + Addressable::URI.new( + :user => "user", :authority => "user@example.com:80" + ) + end.to raise_error(ArgumentError) + end +end + +describe Addressable::URI, "when created with an authority and no port" do + before do + @uri = Addressable::URI.new(:authority => "user@example.com") + end + + it "should not infer a port" do + expect(@uri.port).to eq(nil) + expect(@uri.default_port).to eq(nil) + expect(@uri.inferred_port).to eq(nil) + end + + it "should have a site value of '//user@example.com'" do + expect(@uri.site).to eq("//user@example.com") + end + + it "should have a 'null' origin" do + expect(@uri.origin).to eq('null') + end +end + +describe Addressable::URI, "when created with a host with trailing dots" do + before do + @uri = Addressable::URI.new(:authority => "example...") + end + + it "should have a stable normalized form" do + expect(@uri.normalize.normalize.normalize.host).to eq( + @uri.normalize.host + ) + end +end + +describe Addressable::URI, "when created with a host with a backslash" do + it "should raise an error" do + expect do + Addressable::URI.new(:authority => "example\\example") + end.to raise_error(Addressable::URI::InvalidURIError) + end +end + +describe Addressable::URI, "when created with a host with a slash" do + it "should raise an error" do + expect do + Addressable::URI.new(:authority => "example/example") + end.to raise_error(Addressable::URI::InvalidURIError) + end +end + +describe Addressable::URI, "when created with a host with a space" do + it "should raise an error" do + expect do + Addressable::URI.new(:authority => "example example") + end.to raise_error(Addressable::URI::InvalidURIError) + end +end + +describe Addressable::URI, "when created with both a userinfo and a user" do + it "should raise an error" do + expect do + Addressable::URI.new(:user => "user", :userinfo => "user:pass") + end.to raise_error(ArgumentError) + end +end + +describe Addressable::URI, "when created with a path that hasn't been " + + "prefixed with a '/' but a host specified" do + before do + @uri = Addressable::URI.new( + :scheme => "http", :host => "example.com", :path => "path" + ) + end + + it "should prefix a '/' to the path" do + expect(@uri).to eq(Addressable::URI.parse("http://example.com/path")) + end + + it "should have a site value of 'http://example.com'" do + expect(@uri.site).to eq("http://example.com") + end + + it "should have an origin of 'http://example.com" do + expect(@uri.origin).to eq('http://example.com') + end +end + +describe Addressable::URI, "when created with a path that hasn't been " + + "prefixed with a '/' but no host specified" do + before do + @uri = Addressable::URI.new( + :scheme => "http", :path => "path" + ) + end + + it "should not prefix a '/' to the path" do + expect(@uri).to eq(Addressable::URI.parse("http:path")) + end + + it "should have a site value of 'http:'" do + expect(@uri.site).to eq("http:") + end + + it "should have a 'null' origin" do + expect(@uri.origin).to eq('null') + end +end + +describe Addressable::URI, "when parsed from an Addressable::URI object" do + it "should not have unexpected side-effects" do + original_uri = Addressable::URI.parse("http://example.com/") + new_uri = Addressable::URI.parse(original_uri) + new_uri.host = 'www.example.com' + expect(new_uri.host).to eq('www.example.com') + expect(new_uri.to_s).to eq('http://www.example.com/') + expect(original_uri.host).to eq('example.com') + expect(original_uri.to_s).to eq('http://example.com/') + end + + it "should not have unexpected side-effects" do + original_uri = Addressable::URI.parse("http://example.com/") + new_uri = Addressable::URI.heuristic_parse(original_uri) + new_uri.host = 'www.example.com' + expect(new_uri.host).to eq('www.example.com') + expect(new_uri.to_s).to eq('http://www.example.com/') + expect(original_uri.host).to eq('example.com') + expect(original_uri.to_s).to eq('http://example.com/') + end + + it "should not have unexpected side-effects" do + original_uri = Addressable::URI.parse("http://example.com/") + new_uri = Addressable::URI.parse(original_uri) + new_uri.origin = 'https://www.example.com:8080' + expect(new_uri.host).to eq('www.example.com') + expect(new_uri.to_s).to eq('https://www.example.com:8080/') + expect(original_uri.host).to eq('example.com') + expect(original_uri.to_s).to eq('http://example.com/') + end + + it "should not have unexpected side-effects" do + original_uri = Addressable::URI.parse("http://example.com/") + new_uri = Addressable::URI.heuristic_parse(original_uri) + new_uri.origin = 'https://www.example.com:8080' + expect(new_uri.host).to eq('www.example.com') + expect(new_uri.to_s).to eq('https://www.example.com:8080/') + expect(original_uri.host).to eq('example.com') + expect(original_uri.to_s).to eq('http://example.com/') + end +end + +describe Addressable::URI, "when parsed from something that looks " + + "like a URI object" do + it "should parse without error" do + uri = Addressable::URI.parse(Fake::URI::HTTP.new("http://example.com/")) + expect do + Addressable::URI.parse(uri) + end.not_to raise_error + end +end + +describe Addressable::URI, "when parsed from a standard library URI object" do + it "should parse without error" do + uri = Addressable::URI.parse(URI.parse("http://example.com/")) + expect do + Addressable::URI.parse(uri) + end.not_to raise_error + end +end + +describe Addressable::URI, "when parsed from ''" do + before do + @uri = Addressable::URI.parse("") + end + + it "should have no scheme" do + expect(@uri.scheme).to eq(nil) + end + + it "should not be considered to be ip-based" do + expect(@uri).not_to be_ip_based + end + + it "should have a path of ''" do + expect(@uri.path).to eq("") + end + + it "should have a request URI of '/'" do + expect(@uri.request_uri).to eq("/") + end + + it "should be considered relative" do + expect(@uri).to be_relative + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end + + it "should have a 'null' origin" do + expect(@uri.origin).to eq('null') + end +end + +# Section 1.1.2 of RFC 3986 +describe Addressable::URI, "when parsed from " + + "'ftp://ftp.is.co.za/rfc/rfc1808.txt'" do + before do + @uri = Addressable::URI.parse("ftp://ftp.is.co.za/rfc/rfc1808.txt") + end + + it "should use the 'ftp' scheme" do + expect(@uri.scheme).to eq("ftp") + end + + it "should be considered to be ip-based" do + expect(@uri).to be_ip_based + end + + it "should have a host of 'ftp.is.co.za'" do + expect(@uri.host).to eq("ftp.is.co.za") + end + + it "should have inferred_port of 21" do + expect(@uri.inferred_port).to eq(21) + end + + it "should have a path of '/rfc/rfc1808.txt'" do + expect(@uri.path).to eq("/rfc/rfc1808.txt") + end + + it "should not have a request URI" do + expect(@uri.request_uri).to eq(nil) + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end + + it "should have an origin of 'ftp://ftp.is.co.za'" do + expect(@uri.origin).to eq('ftp://ftp.is.co.za') + end +end + +# Section 1.1.2 of RFC 3986 +describe Addressable::URI, "when parsed from " + + "'http://www.ietf.org/rfc/rfc2396.txt'" do + before do + @uri = Addressable::URI.parse("http://www.ietf.org/rfc/rfc2396.txt") + end + + it "should use the 'http' scheme" do + expect(@uri.scheme).to eq("http") + end + + it "should be considered to be ip-based" do + expect(@uri).to be_ip_based + end + + it "should have a host of 'www.ietf.org'" do + expect(@uri.host).to eq("www.ietf.org") + end + + it "should have inferred_port of 80" do + expect(@uri.inferred_port).to eq(80) + end + + it "should have a path of '/rfc/rfc2396.txt'" do + expect(@uri.path).to eq("/rfc/rfc2396.txt") + end + + it "should have a request URI of '/rfc/rfc2396.txt'" do + expect(@uri.request_uri).to eq("/rfc/rfc2396.txt") + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end + + it "should correctly omit components" do + expect(@uri.omit(:scheme).to_s).to eq("//www.ietf.org/rfc/rfc2396.txt") + expect(@uri.omit(:path).to_s).to eq("http://www.ietf.org") + end + + it "should correctly omit components destructively" do + @uri.omit!(:scheme) + expect(@uri.to_s).to eq("//www.ietf.org/rfc/rfc2396.txt") + end + + it "should have an origin of 'http://www.ietf.org'" do + expect(@uri.origin).to eq('http://www.ietf.org') + end +end + +# Section 1.1.2 of RFC 3986 +describe Addressable::URI, "when parsed from " + + "'ldap://[2001:db8::7]/c=GB?objectClass?one'" do + before do + @uri = Addressable::URI.parse("ldap://[2001:db8::7]/c=GB?objectClass?one") + end + + it "should use the 'ldap' scheme" do + expect(@uri.scheme).to eq("ldap") + end + + it "should be considered to be ip-based" do + expect(@uri).to be_ip_based + end + + it "should have a host of '[2001:db8::7]'" do + expect(@uri.host).to eq("[2001:db8::7]") + end + + it "should have inferred_port of 389" do + expect(@uri.inferred_port).to eq(389) + end + + it "should have a path of '/c=GB'" do + expect(@uri.path).to eq("/c=GB") + end + + it "should not have a request URI" do + expect(@uri.request_uri).to eq(nil) + end + + it "should not allow request URI assignment" do + expect do + @uri.request_uri = "/" + end.to raise_error(Addressable::URI::InvalidURIError) + end + + it "should have a query of 'objectClass?one'" do + expect(@uri.query).to eq("objectClass?one") + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end + + it "should correctly omit components" do + expect(@uri.omit(:scheme, :authority).to_s).to eq("/c=GB?objectClass?one") + expect(@uri.omit(:path).to_s).to eq("ldap://[2001:db8::7]?objectClass?one") + end + + it "should correctly omit components destructively" do + @uri.omit!(:scheme, :authority) + expect(@uri.to_s).to eq("/c=GB?objectClass?one") + end + + it "should raise an error if omission would create an invalid URI" do + expect do + @uri.omit(:authority, :path) + end.to raise_error(Addressable::URI::InvalidURIError) + end + + it "should have an origin of 'ldap://[2001:db8::7]'" do + expect(@uri.origin).to eq('ldap://[2001:db8::7]') + end +end + +# Section 1.1.2 of RFC 3986 +describe Addressable::URI, "when parsed from " + + "'mailto:John.Doe@example.com'" do + before do + @uri = Addressable::URI.parse("mailto:John.Doe@example.com") + end + + it "should use the 'mailto' scheme" do + expect(@uri.scheme).to eq("mailto") + end + + it "should not be considered to be ip-based" do + expect(@uri).not_to be_ip_based + end + + it "should not have an inferred_port" do + expect(@uri.inferred_port).to eq(nil) + end + + it "should have a path of 'John.Doe@example.com'" do + expect(@uri.path).to eq("John.Doe@example.com") + end + + it "should not have a request URI" do + expect(@uri.request_uri).to eq(nil) + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end + + it "should have a 'null' origin" do + expect(@uri.origin).to eq('null') + end +end + +# Section 2 of RFC 6068 +describe Addressable::URI, "when parsed from " + + "'mailto:?to=addr1@an.example,addr2@an.example'" do + before do + @uri = Addressable::URI.parse( + "mailto:?to=addr1@an.example,addr2@an.example" + ) + end + + it "should use the 'mailto' scheme" do + expect(@uri.scheme).to eq("mailto") + end + + it "should not be considered to be ip-based" do + expect(@uri).not_to be_ip_based + end + + it "should not have an inferred_port" do + expect(@uri.inferred_port).to eq(nil) + end + + it "should have a path of ''" do + expect(@uri.path).to eq("") + end + + it "should not have a request URI" do + expect(@uri.request_uri).to eq(nil) + end + + it "should have the To: field value parameterized" do + expect(@uri.query_values(Hash)["to"]).to eq( + "addr1@an.example,addr2@an.example" + ) + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end + + it "should have a 'null' origin" do + expect(@uri.origin).to eq('null') + end +end + +# Section 1.1.2 of RFC 3986 +describe Addressable::URI, "when parsed from " + + "'news:comp.infosystems.www.servers.unix'" do + before do + @uri = Addressable::URI.parse("news:comp.infosystems.www.servers.unix") + end + + it "should use the 'news' scheme" do + expect(@uri.scheme).to eq("news") + end + + it "should not have an inferred_port" do + expect(@uri.inferred_port).to eq(nil) + end + + it "should not be considered to be ip-based" do + expect(@uri).not_to be_ip_based + end + + it "should have a path of 'comp.infosystems.www.servers.unix'" do + expect(@uri.path).to eq("comp.infosystems.www.servers.unix") + end + + it "should not have a request URI" do + expect(@uri.request_uri).to eq(nil) + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end + + it "should have a 'null' origin" do + expect(@uri.origin).to eq('null') + end +end + +# Section 1.1.2 of RFC 3986 +describe Addressable::URI, "when parsed from " + + "'tel:+1-816-555-1212'" do + before do + @uri = Addressable::URI.parse("tel:+1-816-555-1212") + end + + it "should use the 'tel' scheme" do + expect(@uri.scheme).to eq("tel") + end + + it "should not be considered to be ip-based" do + expect(@uri).not_to be_ip_based + end + + it "should not have an inferred_port" do + expect(@uri.inferred_port).to eq(nil) + end + + it "should have a path of '+1-816-555-1212'" do + expect(@uri.path).to eq("+1-816-555-1212") + end + + it "should not have a request URI" do + expect(@uri.request_uri).to eq(nil) + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end + + it "should have a 'null' origin" do + expect(@uri.origin).to eq('null') + end +end + +# Section 1.1.2 of RFC 3986 +describe Addressable::URI, "when parsed from " + + "'telnet://192.0.2.16:80/'" do + before do + @uri = Addressable::URI.parse("telnet://192.0.2.16:80/") + end + + it "should use the 'telnet' scheme" do + expect(@uri.scheme).to eq("telnet") + end + + it "should have a host of '192.0.2.16'" do + expect(@uri.host).to eq("192.0.2.16") + end + + it "should have a port of 80" do + expect(@uri.port).to eq(80) + end + + it "should have a inferred_port of 80" do + expect(@uri.inferred_port).to eq(80) + end + + it "should have a default_port of 23" do + expect(@uri.default_port).to eq(23) + end + + it "should be considered to be ip-based" do + expect(@uri).to be_ip_based + end + + it "should have a path of '/'" do + expect(@uri.path).to eq("/") + end + + it "should not have a request URI" do + expect(@uri.request_uri).to eq(nil) + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end + + it "should have an origin of 'telnet://192.0.2.16:80'" do + expect(@uri.origin).to eq('telnet://192.0.2.16:80') + end +end + +# Section 1.1.2 of RFC 3986 +describe Addressable::URI, "when parsed from " + + "'urn:oasis:names:specification:docbook:dtd:xml:4.1.2'" do + before do + @uri = Addressable::URI.parse( + "urn:oasis:names:specification:docbook:dtd:xml:4.1.2") + end + + it "should use the 'urn' scheme" do + expect(@uri.scheme).to eq("urn") + end + + it "should not have an inferred_port" do + expect(@uri.inferred_port).to eq(nil) + end + + it "should not be considered to be ip-based" do + expect(@uri).not_to be_ip_based + end + + it "should have a path of " + + "'oasis:names:specification:docbook:dtd:xml:4.1.2'" do + expect(@uri.path).to eq("oasis:names:specification:docbook:dtd:xml:4.1.2") + end + + it "should not have a request URI" do + expect(@uri.request_uri).to eq(nil) + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end + + it "should have a 'null' origin" do + expect(@uri.origin).to eq('null') + end +end + +describe Addressable::URI, "when heuristically parsed from " + + "'192.0.2.16:8000/path'" do + before do + @uri = Addressable::URI.heuristic_parse("192.0.2.16:8000/path") + end + + it "should use the 'http' scheme" do + expect(@uri.scheme).to eq("http") + end + + it "should have a host of '192.0.2.16'" do + expect(@uri.host).to eq("192.0.2.16") + end + + it "should have a port of '8000'" do + expect(@uri.port).to eq(8000) + end + + it "should be considered to be ip-based" do + expect(@uri).to be_ip_based + end + + it "should have a path of '/path'" do + expect(@uri.path).to eq("/path") + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end + + it "should have an origin of 'http://192.0.2.16:8000'" do + expect(@uri.origin).to eq('http://192.0.2.16:8000') + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com'" do + before do + @uri = Addressable::URI.parse("http://example.com") + end + + it "when inspected, should have the correct URI" do + expect(@uri.inspect).to include("http://example.com") + end + + it "when inspected, should have the correct class name" do + expect(@uri.inspect).to include("Addressable::URI") + end + + it "when inspected, should have the correct object id" do + expect(@uri.inspect).to include("%#0x" % @uri.object_id) + end + + context "when Addressable::URI has been sub-classed" do + class CustomURIClass < Addressable::URI + end + + before do + @uri = CustomURIClass.parse("http://example.com") + end + + it "when inspected, should have the sub-classes name" do + expect(@uri.inspect).to include("CustomURIClass") + end + end + + it "should use the 'http' scheme" do + expect(@uri.scheme).to eq("http") + end + + it "should be considered to be ip-based" do + expect(@uri).to be_ip_based + end + + it "should have an authority segment of 'example.com'" do + expect(@uri.authority).to eq("example.com") + end + + it "should have a host of 'example.com'" do + expect(@uri.host).to eq("example.com") + end + + it "should be considered ip-based" do + expect(@uri).to be_ip_based + end + + it "should have no username" do + expect(@uri.user).to eq(nil) + end + + it "should have no password" do + expect(@uri.password).to eq(nil) + end + + it "should use port 80" do + expect(@uri.inferred_port).to eq(80) + end + + it "should not have a specified port" do + expect(@uri.port).to eq(nil) + end + + it "should have an empty path" do + expect(@uri.path).to eq("") + end + + it "should have no query string" do + expect(@uri.query).to eq(nil) + expect(@uri.query_values).to eq(nil) + end + + it "should have a request URI of '/'" do + expect(@uri.request_uri).to eq("/") + end + + it "should have no fragment" do + expect(@uri.fragment).to eq(nil) + end + + it "should be considered absolute" do + expect(@uri).to be_absolute + end + + it "should not be considered relative" do + expect(@uri).not_to be_relative + end + + it "should not be exactly equal to 42" do + expect(@uri.eql?(42)).to eq(false) + end + + it "should not be equal to 42" do + expect(@uri == 42).to eq(false) + end + + it "should not be roughly equal to 42" do + expect(@uri === 42).to eq(false) + end + + it "should be exactly equal to http://example.com" do + expect(@uri.eql?(Addressable::URI.parse("http://example.com"))).to eq(true) + end + + it "should be roughly equal to http://example.com/" do + expect(@uri === Addressable::URI.parse("http://example.com/")).to eq(true) + end + + it "should be roughly equal to the string 'http://example.com/'" do + expect(@uri === "http://example.com/").to eq(true) + end + + it "should not be roughly equal to the string " + + "'http://example.com:bogus/'" do + expect do + expect(@uri === "http://example.com:bogus/").to eq(false) + end.not_to raise_error + end + + it "should result in itself when joined with itself" do + expect(@uri.join(@uri).to_s).to eq("http://example.com") + expect(@uri.join!(@uri).to_s).to eq("http://example.com") + end + + it "should be equivalent to http://EXAMPLE.com" do + expect(@uri).to eq(Addressable::URI.parse("http://EXAMPLE.com")) + end + + it "should be equivalent to http://EXAMPLE.com:80/" do + expect(@uri).to eq(Addressable::URI.parse("http://EXAMPLE.com:80/")) + end + + it "should have the same hash as http://example.com" do + expect(@uri.hash).to eq(Addressable::URI.parse("http://example.com").hash) + end + + it "should have the same hash as http://EXAMPLE.com after assignment" do + @uri.origin = "http://EXAMPLE.com" + expect(@uri.hash).to eq(Addressable::URI.parse("http://EXAMPLE.com").hash) + end + + it "should have a different hash from http://EXAMPLE.com" do + expect(@uri.hash).not_to eq(Addressable::URI.parse("http://EXAMPLE.com").hash) + end + + it "should not allow origin assignment without scheme" do + expect do + @uri.origin = "example.com" + end.to raise_error(Addressable::URI::InvalidURIError) + end + + it "should not allow origin assignment without host" do + expect do + @uri.origin = "http://" + end.to raise_error(Addressable::URI::InvalidURIError) + end + + it "should not allow origin assignment with bogus type" do + expect do + @uri.origin = :bogus + end.to raise_error(TypeError) + end + + # Section 6.2.3 of RFC 3986 + it "should be equivalent to http://example.com/" do + expect(@uri).to eq(Addressable::URI.parse("http://example.com/")) + end + + # Section 6.2.3 of RFC 3986 + it "should be equivalent to http://example.com:/" do + expect(@uri).to eq(Addressable::URI.parse("http://example.com:/")) + end + + # Section 6.2.3 of RFC 3986 + it "should be equivalent to http://example.com:80/" do + expect(@uri).to eq(Addressable::URI.parse("http://example.com:80/")) + end + + # Section 6.2.2.1 of RFC 3986 + it "should be equivalent to http://EXAMPLE.COM/" do + expect(@uri).to eq(Addressable::URI.parse("http://EXAMPLE.COM/")) + end + + it "should have a route of '/path/' to 'http://example.com/path/'" do + expect(@uri.route_to("http://example.com/path/")).to eq( + Addressable::URI.parse("/path/") + ) + end + + it "should have a route of '..' from 'http://example.com/path/'" do + expect(@uri.route_from("http://example.com/path/")).to eq( + Addressable::URI.parse("..") + ) + end + + it "should have a route of '#' to 'http://example.com/'" do + expect(@uri.route_to("http://example.com/")).to eq( + Addressable::URI.parse("#") + ) + end + + it "should have a route of 'http://elsewhere.com/' to " + + "'http://elsewhere.com/'" do + expect(@uri.route_to("http://elsewhere.com/")).to eq( + Addressable::URI.parse("http://elsewhere.com/") + ) + end + + it "when joined with 'relative/path' should be " + + "'http://example.com/relative/path'" do + expect(@uri.join('relative/path')).to eq( + Addressable::URI.parse("http://example.com/relative/path") + ) + end + + it "when joined with a bogus object a TypeError should be raised" do + expect do + @uri.join(42) + end.to raise_error(TypeError) + end + + it "should have the correct username after assignment" do + @uri.user = "newuser" + expect(@uri.user).to eq("newuser") + expect(@uri.password).to eq(nil) + expect(@uri.to_s).to eq("http://newuser@example.com") + end + + it "should have the correct username after assignment" do + @uri.user = "user@123!" + expect(@uri.user).to eq("user@123!") + expect(@uri.normalized_user).to eq("user%40123%21") + expect(@uri.password).to eq(nil) + expect(@uri.normalize.to_s).to eq("http://user%40123%21@example.com/") + end + + it "should have the correct password after assignment" do + @uri.password = "newpass" + expect(@uri.password).to eq("newpass") + expect(@uri.user).to eq("") + expect(@uri.to_s).to eq("http://:newpass@example.com") + end + + it "should have the correct password after assignment" do + @uri.password = "#secret@123!" + expect(@uri.password).to eq("#secret@123!") + expect(@uri.normalized_password).to eq("%23secret%40123%21") + expect(@uri.user).to eq("") + expect(@uri.normalize.to_s).to eq("http://:%23secret%40123%21@example.com/") + expect(@uri.omit(:password).to_s).to eq("http://example.com") + end + + it "should have the correct user/pass after repeated assignment" do + @uri.user = nil + expect(@uri.user).to eq(nil) + @uri.password = "newpass" + expect(@uri.password).to eq("newpass") + # Username cannot be nil if the password is set + expect(@uri.user).to eq("") + expect(@uri.to_s).to eq("http://:newpass@example.com") + @uri.user = "newuser" + expect(@uri.user).to eq("newuser") + @uri.password = nil + expect(@uri.password).to eq(nil) + expect(@uri.to_s).to eq("http://newuser@example.com") + @uri.user = "newuser" + expect(@uri.user).to eq("newuser") + @uri.password = "" + expect(@uri.password).to eq("") + expect(@uri.to_s).to eq("http://newuser:@example.com") + @uri.password = "newpass" + expect(@uri.password).to eq("newpass") + @uri.user = nil + # Username cannot be nil if the password is set + expect(@uri.user).to eq("") + expect(@uri.to_s).to eq("http://:newpass@example.com") + end + + it "should have the correct user/pass after userinfo assignment" do + @uri.user = "newuser" + expect(@uri.user).to eq("newuser") + @uri.password = "newpass" + expect(@uri.password).to eq("newpass") + @uri.userinfo = nil + expect(@uri.userinfo).to eq(nil) + expect(@uri.user).to eq(nil) + expect(@uri.password).to eq(nil) + end + + it "should correctly convert to a hash" do + expect(@uri.to_hash).to eq({ + :scheme => "http", + :user => nil, + :password => nil, + :host => "example.com", + :port => nil, + :path => "", + :query => nil, + :fragment => nil + }) + end + + it "should be identical to its duplicate" do + expect(@uri).to eq(@uri.dup) + end + + it "should have an origin of 'http://example.com'" do + expect(@uri.origin).to eq('http://example.com') + end +end + +# Section 5.1.2 of RFC 2616 +describe Addressable::URI, "when parsed from " + + "'HTTP://www.w3.org/pub/WWW/TheProject.html'" do + before do + @uri = Addressable::URI.parse("HTTP://www.w3.org/pub/WWW/TheProject.html") + end + + it "should have the correct request URI" do + expect(@uri.request_uri).to eq("/pub/WWW/TheProject.html") + end + + it "should have the correct request URI after assignment" do + @uri.request_uri = "/pub/WWW/TheProject.html?" + expect(@uri.request_uri).to eq("/pub/WWW/TheProject.html?") + expect(@uri.path).to eq("/pub/WWW/TheProject.html") + expect(@uri.query).to eq("") + end + + it "should have the correct request URI after assignment" do + @uri.request_uri = "/some/where/else.html" + expect(@uri.request_uri).to eq("/some/where/else.html") + expect(@uri.path).to eq("/some/where/else.html") + expect(@uri.query).to eq(nil) + end + + it "should have the correct request URI after assignment" do + @uri.request_uri = "/some/where/else.html?query?string" + expect(@uri.request_uri).to eq("/some/where/else.html?query?string") + expect(@uri.path).to eq("/some/where/else.html") + expect(@uri.query).to eq("query?string") + end + + it "should have the correct request URI after assignment" do + @uri.request_uri = "?x=y" + expect(@uri.request_uri).to eq("/?x=y") + expect(@uri.path).to eq("/") + expect(@uri.query).to eq("x=y") + end + + it "should raise an error if the site value is set to something bogus" do + expect do + @uri.site = 42 + end.to raise_error(TypeError) + end + + it "should raise an error if the request URI is set to something bogus" do + expect do + @uri.request_uri = 42 + end.to raise_error(TypeError) + end + + it "should correctly convert to a hash" do + expect(@uri.to_hash).to eq({ + :scheme => "HTTP", + :user => nil, + :password => nil, + :host => "www.w3.org", + :port => nil, + :path => "/pub/WWW/TheProject.html", + :query => nil, + :fragment => nil + }) + end + + it "should have an origin of 'http://www.w3.org'" do + expect(@uri.origin).to eq('http://www.w3.org') + end +end + +describe Addressable::URI, "when parsing IPv6 addresses" do + it "should not raise an error for " + + "'http://[3ffe:1900:4545:3:200:f8ff:fe21:67cf]/'" do + Addressable::URI.parse("http://[3ffe:1900:4545:3:200:f8ff:fe21:67cf]/") + end + + it "should not raise an error for " + + "'http://[fe80:0:0:0:200:f8ff:fe21:67cf]/'" do + Addressable::URI.parse("http://[fe80:0:0:0:200:f8ff:fe21:67cf]/") + end + + it "should not raise an error for " + + "'http://[fe80::200:f8ff:fe21:67cf]/'" do + Addressable::URI.parse("http://[fe80::200:f8ff:fe21:67cf]/") + end + + it "should not raise an error for " + + "'http://[::1]/'" do + Addressable::URI.parse("http://[::1]/") + end + + it "should not raise an error for " + + "'http://[fe80::1]/'" do + Addressable::URI.parse("http://[fe80::1]/") + end + + it "should raise an error for " + + "'http://[]/'" do + expect do + Addressable::URI.parse("http://[]/") + end.to raise_error(Addressable::URI::InvalidURIError) + end +end + +describe Addressable::URI, "when parsing IPv6 address" do + subject { Addressable::URI.parse("http://[3ffe:1900:4545:3:200:f8ff:fe21:67cf]/") } + its(:host) { should == '[3ffe:1900:4545:3:200:f8ff:fe21:67cf]' } + its(:hostname) { should == '3ffe:1900:4545:3:200:f8ff:fe21:67cf' } +end + +describe Addressable::URI, "when assigning IPv6 address" do + it "should allow to set bare IPv6 address as hostname" do + uri = Addressable::URI.parse("http://[::1]/") + uri.hostname = '3ffe:1900:4545:3:200:f8ff:fe21:67cf' + expect(uri.to_s).to eq('http://[3ffe:1900:4545:3:200:f8ff:fe21:67cf]/') + end + + it "should allow to set bare IPv6 address as hostname with IPAddr object" do + uri = Addressable::URI.parse("http://[::1]/") + uri.hostname = IPAddr.new('3ffe:1900:4545:3:200:f8ff:fe21:67cf') + expect(uri.to_s).to eq('http://[3ffe:1900:4545:3:200:f8ff:fe21:67cf]/') + end + + it "should not allow to set bare IPv6 address as host" do + uri = Addressable::URI.parse("http://[::1]/") + skip "not checked" + expect do + uri.host = '3ffe:1900:4545:3:200:f8ff:fe21:67cf' + end.to raise_error(Addressable::URI::InvalidURIError) + end +end + +describe Addressable::URI, "when parsing IPvFuture addresses" do + it "should not raise an error for " + + "'http://[v9.3ffe:1900:4545:3:200:f8ff:fe21:67cf]/'" do + Addressable::URI.parse("http://[v9.3ffe:1900:4545:3:200:f8ff:fe21:67cf]/") + end + + it "should not raise an error for " + + "'http://[vff.fe80:0:0:0:200:f8ff:fe21:67cf]/'" do + Addressable::URI.parse("http://[vff.fe80:0:0:0:200:f8ff:fe21:67cf]/") + end + + it "should not raise an error for " + + "'http://[v12.fe80::200:f8ff:fe21:67cf]/'" do + Addressable::URI.parse("http://[v12.fe80::200:f8ff:fe21:67cf]/") + end + + it "should not raise an error for " + + "'http://[va0.::1]/'" do + Addressable::URI.parse("http://[va0.::1]/") + end + + it "should not raise an error for " + + "'http://[v255.fe80::1]/'" do + Addressable::URI.parse("http://[v255.fe80::1]/") + end + + it "should raise an error for " + + "'http://[v0.]/'" do + expect do + Addressable::URI.parse("http://[v0.]/") + end.to raise_error(Addressable::URI::InvalidURIError) + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/'" do + before do + @uri = Addressable::URI.parse("http://example.com/") + end + + # Based on http://intertwingly.net/blog/2004/07/31/URI-Equivalence + it "should be equivalent to http://example.com" do + expect(@uri).to eq(Addressable::URI.parse("http://example.com")) + end + + # Based on http://intertwingly.net/blog/2004/07/31/URI-Equivalence + it "should be equivalent to HTTP://example.com/" do + expect(@uri).to eq(Addressable::URI.parse("HTTP://example.com/")) + end + + # Based on http://intertwingly.net/blog/2004/07/31/URI-Equivalence + it "should be equivalent to http://example.com:/" do + expect(@uri).to eq(Addressable::URI.parse("http://example.com:/")) + end + + # Based on http://intertwingly.net/blog/2004/07/31/URI-Equivalence + it "should be equivalent to http://example.com:80/" do + expect(@uri).to eq(Addressable::URI.parse("http://example.com:80/")) + end + + # Based on http://intertwingly.net/blog/2004/07/31/URI-Equivalence + it "should be equivalent to http://Example.com/" do + expect(@uri).to eq(Addressable::URI.parse("http://Example.com/")) + end + + it "should have the correct username after assignment" do + @uri.user = nil + expect(@uri.user).to eq(nil) + expect(@uri.password).to eq(nil) + expect(@uri.to_s).to eq("http://example.com/") + end + + it "should have the correct password after assignment" do + @uri.password = nil + expect(@uri.password).to eq(nil) + expect(@uri.user).to eq(nil) + expect(@uri.to_s).to eq("http://example.com/") + end + + it "should have a request URI of '/'" do + expect(@uri.request_uri).to eq("/") + end + + it "should correctly convert to a hash" do + expect(@uri.to_hash).to eq({ + :scheme => "http", + :user => nil, + :password => nil, + :host => "example.com", + :port => nil, + :path => "/", + :query => nil, + :fragment => nil + }) + end + + it "should be identical to its duplicate" do + expect(@uri).to eq(@uri.dup) + end + + it "should have the same hash as its duplicate" do + expect(@uri.hash).to eq(@uri.dup.hash) + end + + it "should have a different hash from its equivalent String value" do + expect(@uri.hash).not_to eq(@uri.to_s.hash) + end + + it "should have the same hash as an equal URI" do + expect(@uri.hash).to eq(Addressable::URI.parse("http://example.com/").hash) + end + + it "should be equivalent to http://EXAMPLE.com" do + expect(@uri).to eq(Addressable::URI.parse("http://EXAMPLE.com")) + end + + it "should be equivalent to http://EXAMPLE.com:80/" do + expect(@uri).to eq(Addressable::URI.parse("http://EXAMPLE.com:80/")) + end + + it "should have the same hash as http://example.com/" do + expect(@uri.hash).to eq(Addressable::URI.parse("http://example.com/").hash) + end + + it "should have the same hash as http://example.com after assignment" do + @uri.path = "" + expect(@uri.hash).to eq(Addressable::URI.parse("http://example.com").hash) + end + + it "should have the same hash as http://example.com/? after assignment" do + @uri.query = "" + expect(@uri.hash).to eq(Addressable::URI.parse("http://example.com/?").hash) + end + + it "should have the same hash as http://example.com/? after assignment" do + @uri.query_values = {} + expect(@uri.hash).to eq(Addressable::URI.parse("http://example.com/?").hash) + end + + it "should have the same hash as http://example.com/# after assignment" do + @uri.fragment = "" + expect(@uri.hash).to eq(Addressable::URI.parse("http://example.com/#").hash) + end + + it "should have a different hash from http://example.com" do + expect(@uri.hash).not_to eq(Addressable::URI.parse("http://example.com").hash) + end + + it "should have an origin of 'http://example.com'" do + expect(@uri.origin).to eq('http://example.com') + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com?#'" do + before do + @uri = Addressable::URI.parse("http://example.com?#") + end + + it "should correctly convert to a hash" do + expect(@uri.to_hash).to eq({ + :scheme => "http", + :user => nil, + :password => nil, + :host => "example.com", + :port => nil, + :path => "", + :query => "", + :fragment => "" + }) + end + + it "should have a request URI of '/?'" do + expect(@uri.request_uri).to eq("/?") + end + + it "should normalize to 'http://example.com/'" do + expect(@uri.normalize.to_s).to eq("http://example.com/") + end + + it "should have an origin of 'http://example.com'" do + expect(@uri.origin).to eq("http://example.com") + end +end + +describe Addressable::URI, "when parsed from " + + "'http://@example.com/'" do + before do + @uri = Addressable::URI.parse("http://@example.com/") + end + + it "should be equivalent to http://example.com" do + expect(@uri).to eq(Addressable::URI.parse("http://example.com")) + end + + it "should correctly convert to a hash" do + expect(@uri.to_hash).to eq({ + :scheme => "http", + :user => "", + :password => nil, + :host => "example.com", + :port => nil, + :path => "/", + :query => nil, + :fragment => nil + }) + end + + it "should be identical to its duplicate" do + expect(@uri).to eq(@uri.dup) + end + + it "should have an origin of 'http://example.com'" do + expect(@uri.origin).to eq('http://example.com') + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com./'" do + before do + @uri = Addressable::URI.parse("http://example.com./") + end + + it "should be equivalent to http://example.com" do + expect(@uri).to eq(Addressable::URI.parse("http://example.com")) + end + + it "should not be considered to be in normal form" do + expect(@uri.normalize).not_to be_eql(@uri) + end + + it "should be identical to its duplicate" do + expect(@uri).to eq(@uri.dup) + end + + it "should have an origin of 'http://example.com'" do + expect(@uri.origin).to eq('http://example.com') + end +end + +describe Addressable::URI, "when parsed from " + + "'http://:@example.com/'" do + before do + @uri = Addressable::URI.parse("http://:@example.com/") + end + + it "should be equivalent to http://example.com" do + expect(@uri).to eq(Addressable::URI.parse("http://example.com")) + end + + it "should correctly convert to a hash" do + expect(@uri.to_hash).to eq({ + :scheme => "http", + :user => "", + :password => "", + :host => "example.com", + :port => nil, + :path => "/", + :query => nil, + :fragment => nil + }) + end + + it "should be identical to its duplicate" do + expect(@uri).to eq(@uri.dup) + end + + it "should have an origin of 'http://example.com'" do + expect(@uri.origin).to eq('http://example.com') + end +end + +describe Addressable::URI, "when parsed from " + + "'HTTP://EXAMPLE.COM/'" do + before do + @uri = Addressable::URI.parse("HTTP://EXAMPLE.COM/") + end + + it "should be equivalent to http://example.com" do + expect(@uri).to eq(Addressable::URI.parse("http://example.com")) + end + + it "should correctly convert to a hash" do + expect(@uri.to_hash).to eq({ + :scheme => "HTTP", + :user => nil, + :password => nil, + :host => "EXAMPLE.COM", + :port => nil, + :path => "/", + :query => nil, + :fragment => nil + }) + end + + it "should be identical to its duplicate" do + expect(@uri).to eq(@uri.dup) + end + + it "should have an origin of 'http://example.com'" do + expect(@uri.origin).to eq('http://example.com') + end + + it "should have a tld of 'com'" do + expect(@uri.tld).to eq('com') + end +end + +describe Addressable::URI, "when parsed from " + + "'http://www.example.co.uk/'" do + before do + @uri = Addressable::URI.parse("http://www.example.co.uk/") + end + + it "should have an origin of 'http://www.example.co.uk'" do + expect(@uri.origin).to eq('http://www.example.co.uk') + end + + it "should have a tld of 'co.uk'" do + expect(@uri.tld).to eq('co.uk') + end + + it "should have a domain of 'example.co.uk'" do + expect(@uri.domain).to eq('example.co.uk') + end +end + +describe Addressable::URI, "when parsed from " + + "'http://sub_domain.blogspot.com/'" do + before do + @uri = Addressable::URI.parse("http://sub_domain.blogspot.com/") + end + + it "should have an origin of 'http://sub_domain.blogspot.com'" do + expect(@uri.origin).to eq('http://sub_domain.blogspot.com') + end + + it "should have a tld of 'com'" do + expect(@uri.tld).to eq('com') + end + + it "should have a domain of 'blogspot.com'" do + expect(@uri.domain).to eq('blogspot.com') + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/~smith/'" do + before do + @uri = Addressable::URI.parse("http://example.com/~smith/") + end + + # Based on http://intertwingly.net/blog/2004/07/31/URI-Equivalence + it "should be equivalent to http://example.com/%7Esmith/" do + expect(@uri).to eq(Addressable::URI.parse("http://example.com/%7Esmith/")) + end + + # Based on http://intertwingly.net/blog/2004/07/31/URI-Equivalence + it "should be equivalent to http://example.com/%7esmith/" do + expect(@uri).to eq(Addressable::URI.parse("http://example.com/%7esmith/")) + end + + it "should be identical to its duplicate" do + expect(@uri).to eq(@uri.dup) + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/%E8'" do + before do + @uri = Addressable::URI.parse("http://example.com/%E8") + end + + it "should not raise an exception when normalized" do + expect do + @uri.normalize + end.not_to raise_error + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end + + it "should not change if encoded with the normalizing algorithm" do + expect(Addressable::URI.normalized_encode(@uri).to_s).to eq( + "http://example.com/%E8" + ) + expect(Addressable::URI.normalized_encode(@uri, Addressable::URI).to_s).to be === + "http://example.com/%E8" + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/path%2Fsegment/'" do + before do + @uri = Addressable::URI.parse("http://example.com/path%2Fsegment/") + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end + + it "should be equal to 'http://example.com/path%2Fsegment/'" do + expect(@uri.normalize).to be_eql( + Addressable::URI.parse("http://example.com/path%2Fsegment/") + ) + end + + it "should not be equal to 'http://example.com/path/segment/'" do + expect(@uri).not_to eq( + Addressable::URI.parse("http://example.com/path/segment/") + ) + end + + it "should not be equal to 'http://example.com/path/segment/'" do + expect(@uri.normalize).not_to be_eql( + Addressable::URI.parse("http://example.com/path/segment/") + ) + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/?%F6'" do + before do + @uri = Addressable::URI.parse("http://example.com/?%F6") + end + + it "should not raise an exception when normalized" do + expect do + @uri.normalize + end.not_to raise_error + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end + + it "should not change if encoded with the normalizing algorithm" do + expect(Addressable::URI.normalized_encode(@uri).to_s).to eq( + "http://example.com/?%F6" + ) + expect(Addressable::URI.normalized_encode(@uri, Addressable::URI).to_s).to be === + "http://example.com/?%F6" + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/#%F6'" do + before do + @uri = Addressable::URI.parse("http://example.com/#%F6") + end + + it "should not raise an exception when normalized" do + expect do + @uri.normalize + end.not_to raise_error + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end + + it "should not change if encoded with the normalizing algorithm" do + expect(Addressable::URI.normalized_encode(@uri).to_s).to eq( + "http://example.com/#%F6" + ) + expect(Addressable::URI.normalized_encode(@uri, Addressable::URI).to_s).to be === + "http://example.com/#%F6" + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/%C3%87'" do + before do + @uri = Addressable::URI.parse("http://example.com/%C3%87") + end + + # Based on http://intertwingly.net/blog/2004/07/31/URI-Equivalence + it "should be equivalent to 'http://example.com/C%CC%A7'" do + expect(@uri).to eq(Addressable::URI.parse("http://example.com/C%CC%A7")) + end + + it "should not change if encoded with the normalizing algorithm" do + expect(Addressable::URI.normalized_encode(@uri).to_s).to eq( + "http://example.com/%C3%87" + ) + expect(Addressable::URI.normalized_encode(@uri, Addressable::URI).to_s).to be === + "http://example.com/%C3%87" + end + + it "should raise an error if encoding with an unexpected return type" do + expect do + Addressable::URI.normalized_encode(@uri, Integer) + end.to raise_error(TypeError) + end + + it "if percent encoded should be 'http://example.com/C%25CC%25A7'" do + expect(Addressable::URI.encode(@uri).to_s).to eq( + "http://example.com/%25C3%2587" + ) + end + + it "if percent encoded should be 'http://example.com/C%25CC%25A7'" do + expect(Addressable::URI.encode(@uri, Addressable::URI)).to eq( + Addressable::URI.parse("http://example.com/%25C3%2587") + ) + end + + it "should raise an error if encoding with an unexpected return type" do + expect do + Addressable::URI.encode(@uri, Integer) + end.to raise_error(TypeError) + end + + it "should be identical to its duplicate" do + expect(@uri).to eq(@uri.dup) + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/?q=string'" do + before do + @uri = Addressable::URI.parse("http://example.com/?q=string") + end + + it "should use the 'http' scheme" do + expect(@uri.scheme).to eq("http") + end + + it "should have an authority segment of 'example.com'" do + expect(@uri.authority).to eq("example.com") + end + + it "should have a host of 'example.com'" do + expect(@uri.host).to eq("example.com") + end + + it "should have no username" do + expect(@uri.user).to eq(nil) + end + + it "should have no password" do + expect(@uri.password).to eq(nil) + end + + it "should use port 80" do + expect(@uri.inferred_port).to eq(80) + end + + it "should have a path of '/'" do + expect(@uri.path).to eq("/") + end + + it "should have a query string of 'q=string'" do + expect(@uri.query).to eq("q=string") + end + + it "should have no fragment" do + expect(@uri.fragment).to eq(nil) + end + + it "should be considered absolute" do + expect(@uri).to be_absolute + end + + it "should not be considered relative" do + expect(@uri).not_to be_relative + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end + + it "should be identical to its duplicate" do + expect(@uri).to eq(@uri.dup) + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com:80/'" do + before do + @uri = Addressable::URI.parse("http://example.com:80/") + end + + it "should use the 'http' scheme" do + expect(@uri.scheme).to eq("http") + end + + it "should have an authority segment of 'example.com:80'" do + expect(@uri.authority).to eq("example.com:80") + end + + it "should have a host of 'example.com'" do + expect(@uri.host).to eq("example.com") + end + + it "should have no username" do + expect(@uri.user).to eq(nil) + end + + it "should have no password" do + expect(@uri.password).to eq(nil) + end + + it "should use port 80" do + expect(@uri.inferred_port).to eq(80) + end + + it "should have explicit port 80" do + expect(@uri.port).to eq(80) + end + + it "should have a path of '/'" do + expect(@uri.path).to eq("/") + end + + it "should have no query string" do + expect(@uri.query).to eq(nil) + end + + it "should have no fragment" do + expect(@uri.fragment).to eq(nil) + end + + it "should be considered absolute" do + expect(@uri).to be_absolute + end + + it "should not be considered relative" do + expect(@uri).not_to be_relative + end + + it "should be exactly equal to http://example.com:80/" do + expect(@uri.eql?(Addressable::URI.parse("http://example.com:80/"))).to eq(true) + end + + it "should be roughly equal to http://example.com/" do + expect(@uri === Addressable::URI.parse("http://example.com/")).to eq(true) + end + + it "should be roughly equal to the string 'http://example.com/'" do + expect(@uri === "http://example.com/").to eq(true) + end + + it "should not be roughly equal to the string " + + "'http://example.com:bogus/'" do + expect do + expect(@uri === "http://example.com:bogus/").to eq(false) + end.not_to raise_error + end + + it "should result in itself when joined with itself" do + expect(@uri.join(@uri).to_s).to eq("http://example.com:80/") + expect(@uri.join!(@uri).to_s).to eq("http://example.com:80/") + end + + # Section 6.2.3 of RFC 3986 + it "should be equal to http://example.com/" do + expect(@uri).to eq(Addressable::URI.parse("http://example.com/")) + end + + # Section 6.2.3 of RFC 3986 + it "should be equal to http://example.com:/" do + expect(@uri).to eq(Addressable::URI.parse("http://example.com:/")) + end + + # Section 6.2.3 of RFC 3986 + it "should be equal to http://example.com:80/" do + expect(@uri).to eq(Addressable::URI.parse("http://example.com:80/")) + end + + # Section 6.2.2.1 of RFC 3986 + it "should be equal to http://EXAMPLE.COM/" do + expect(@uri).to eq(Addressable::URI.parse("http://EXAMPLE.COM/")) + end + + it "should correctly convert to a hash" do + expect(@uri.to_hash).to eq({ + :scheme => "http", + :user => nil, + :password => nil, + :host => "example.com", + :port => 80, + :path => "/", + :query => nil, + :fragment => nil + }) + end + + it "should be identical to its duplicate" do + expect(@uri).to eq(@uri.dup) + end + + it "should have an origin of 'http://example.com'" do + expect(@uri.origin).to eq('http://example.com') + end + + it "should not change if encoded with the normalizing algorithm" do + expect(Addressable::URI.normalized_encode(@uri).to_s).to eq( + "http://example.com:80/" + ) + expect(Addressable::URI.normalized_encode(@uri, Addressable::URI).to_s).to be === + "http://example.com:80/" + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com:8080/'" do + before do + @uri = Addressable::URI.parse("http://example.com:8080/") + end + + it "should use the 'http' scheme" do + expect(@uri.scheme).to eq("http") + end + + it "should have an authority segment of 'example.com:8080'" do + expect(@uri.authority).to eq("example.com:8080") + end + + it "should have a host of 'example.com'" do + expect(@uri.host).to eq("example.com") + end + + it "should have no username" do + expect(@uri.user).to eq(nil) + end + + it "should have no password" do + expect(@uri.password).to eq(nil) + end + + it "should use port 8080" do + expect(@uri.inferred_port).to eq(8080) + end + + it "should have explicit port 8080" do + expect(@uri.port).to eq(8080) + end + + it "should have default port 80" do + expect(@uri.default_port).to eq(80) + end + + it "should have a path of '/'" do + expect(@uri.path).to eq("/") + end + + it "should have no query string" do + expect(@uri.query).to eq(nil) + end + + it "should have no fragment" do + expect(@uri.fragment).to eq(nil) + end + + it "should be considered absolute" do + expect(@uri).to be_absolute + end + + it "should not be considered relative" do + expect(@uri).not_to be_relative + end + + it "should be exactly equal to http://example.com:8080/" do + expect(@uri.eql?(Addressable::URI.parse( + "http://example.com:8080/"))).to eq(true) + end + + it "should have a route of 'http://example.com:8080/' from " + + "'http://example.com/path/to/'" do + expect(@uri.route_from("http://example.com/path/to/")).to eq( + Addressable::URI.parse("http://example.com:8080/") + ) + end + + it "should have a route of 'http://example.com:8080/' from " + + "'http://example.com:80/path/to/'" do + expect(@uri.route_from("http://example.com:80/path/to/")).to eq( + Addressable::URI.parse("http://example.com:8080/") + ) + end + + it "should have a route of '../../' from " + + "'http://example.com:8080/path/to/'" do + expect(@uri.route_from("http://example.com:8080/path/to/")).to eq( + Addressable::URI.parse("../../") + ) + end + + it "should have a route of 'http://example.com:8080/' from " + + "'http://user:pass@example.com/path/to/'" do + expect(@uri.route_from("http://user:pass@example.com/path/to/")).to eq( + Addressable::URI.parse("http://example.com:8080/") + ) + end + + it "should correctly convert to a hash" do + expect(@uri.to_hash).to eq({ + :scheme => "http", + :user => nil, + :password => nil, + :host => "example.com", + :port => 8080, + :path => "/", + :query => nil, + :fragment => nil + }) + end + + it "should be identical to its duplicate" do + expect(@uri).to eq(@uri.dup) + end + + it "should have an origin of 'http://example.com:8080'" do + expect(@uri.origin).to eq('http://example.com:8080') + end + + it "should not change if encoded with the normalizing algorithm" do + expect(Addressable::URI.normalized_encode(@uri).to_s).to eq( + "http://example.com:8080/" + ) + expect(Addressable::URI.normalized_encode(@uri, Addressable::URI).to_s).to be === + "http://example.com:8080/" + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com:%38%30/'" do + before do + @uri = Addressable::URI.parse("http://example.com:%38%30/") + end + + it "should have the correct port" do + expect(@uri.port).to eq(80) + end + + it "should not be considered to be in normal form" do + expect(@uri.normalize).not_to be_eql(@uri) + end + + it "should normalize to 'http://example.com/'" do + expect(@uri.normalize.to_s).to eq("http://example.com/") + end + + it "should have an origin of 'http://example.com'" do + expect(@uri.origin).to eq('http://example.com') + end +end + +describe Addressable::URI, "when parsed with empty port" do + subject(:uri) do + Addressable::URI.parse("//example.com:") + end + + it "should not infer a port" do + expect(uri.port).to be(nil) + end + + it "should have a site value of '//example.com'" do + expect(uri.site).to eq("//example.com") + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/%2E/'" do + before do + @uri = Addressable::URI.parse("http://example.com/%2E/") + end + + it "should be considered to be in normal form" do + skip( + 'path segment normalization should happen before ' + + 'percent escaping normalization' + ) + @uri.normalize.should be_eql(@uri) + end + + it "should normalize to 'http://example.com/%2E/'" do + skip( + 'path segment normalization should happen before ' + + 'percent escaping normalization' + ) + expect(@uri.normalize).to eq("http://example.com/%2E/") + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/..'" do + before do + @uri = Addressable::URI.parse("http://example.com/..") + end + + it "should have the correct port" do + expect(@uri.inferred_port).to eq(80) + end + + it "should not be considered to be in normal form" do + expect(@uri.normalize).not_to be_eql(@uri) + end + + it "should normalize to 'http://example.com/'" do + expect(@uri.normalize.to_s).to eq("http://example.com/") + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/../..'" do + before do + @uri = Addressable::URI.parse("http://example.com/../..") + end + + it "should have the correct port" do + expect(@uri.inferred_port).to eq(80) + end + + it "should not be considered to be in normal form" do + expect(@uri.normalize).not_to be_eql(@uri) + end + + it "should normalize to 'http://example.com/'" do + expect(@uri.normalize.to_s).to eq("http://example.com/") + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/path(/..'" do + before do + @uri = Addressable::URI.parse("http://example.com/path(/..") + end + + it "should have the correct port" do + expect(@uri.inferred_port).to eq(80) + end + + it "should not be considered to be in normal form" do + expect(@uri.normalize).not_to be_eql(@uri) + end + + it "should normalize to 'http://example.com/'" do + expect(@uri.normalize.to_s).to eq("http://example.com/") + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/(path)/..'" do + before do + @uri = Addressable::URI.parse("http://example.com/(path)/..") + end + + it "should have the correct port" do + expect(@uri.inferred_port).to eq(80) + end + + it "should not be considered to be in normal form" do + expect(@uri.normalize).not_to be_eql(@uri) + end + + it "should normalize to 'http://example.com/'" do + expect(@uri.normalize.to_s).to eq("http://example.com/") + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/path(/../'" do + before do + @uri = Addressable::URI.parse("http://example.com/path(/../") + end + + it "should have the correct port" do + expect(@uri.inferred_port).to eq(80) + end + + it "should not be considered to be in normal form" do + expect(@uri.normalize).not_to be_eql(@uri) + end + + it "should normalize to 'http://example.com/'" do + expect(@uri.normalize.to_s).to eq("http://example.com/") + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/(path)/../'" do + before do + @uri = Addressable::URI.parse("http://example.com/(path)/../") + end + + it "should have the correct port" do + expect(@uri.inferred_port).to eq(80) + end + + it "should not be considered to be in normal form" do + expect(@uri.normalize).not_to be_eql(@uri) + end + + it "should normalize to 'http://example.com/'" do + expect(@uri.normalize.to_s).to eq("http://example.com/") + end +end + +describe Addressable::URI, "when parsed from " + + "'/..//example.com'" do + before do + @uri = Addressable::URI.parse("/..//example.com") + end + + it "should become invalid when normalized" do + expect do + @uri.normalize + end.to raise_error(Addressable::URI::InvalidURIError, /authority/) + end + + it "should have a path of '/..//example.com'" do + expect(@uri.path).to eq("/..//example.com") + end +end + +describe Addressable::URI, "when parsed from '/a/b/c/./../../g'" do + before do + @uri = Addressable::URI.parse("/a/b/c/./../../g") + end + + it "should not be considered to be in normal form" do + expect(@uri.normalize).not_to be_eql(@uri) + end + + # Section 5.2.4 of RFC 3986 + it "should normalize to '/a/g'" do + expect(@uri.normalize.to_s).to eq("/a/g") + end +end + +describe Addressable::URI, "when parsed from 'mid/content=5/../6'" do + before do + @uri = Addressable::URI.parse("mid/content=5/../6") + end + + it "should not be considered to be in normal form" do + expect(@uri.normalize).not_to be_eql(@uri) + end + + # Section 5.2.4 of RFC 3986 + it "should normalize to 'mid/6'" do + expect(@uri.normalize.to_s).to eq("mid/6") + end +end + +describe Addressable::URI, "when parsed from " + + "'http://www.example.com///../'" do + before do + @uri = Addressable::URI.parse('http://www.example.com///../') + end + + it "should not be considered to be in normal form" do + expect(@uri.normalize).not_to be_eql(@uri) + end + + it "should normalize to 'http://www.example.com//'" do + expect(@uri.normalize.to_s).to eq("http://www.example.com//") + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/path/to/resource/'" do + before do + @uri = Addressable::URI.parse("http://example.com/path/to/resource/") + end + + it "should use the 'http' scheme" do + expect(@uri.scheme).to eq("http") + end + + it "should have an authority segment of 'example.com'" do + expect(@uri.authority).to eq("example.com") + end + + it "should have a host of 'example.com'" do + expect(@uri.host).to eq("example.com") + end + + it "should have no username" do + expect(@uri.user).to eq(nil) + end + + it "should have no password" do + expect(@uri.password).to eq(nil) + end + + it "should use port 80" do + expect(@uri.inferred_port).to eq(80) + end + + it "should have a path of '/path/to/resource/'" do + expect(@uri.path).to eq("/path/to/resource/") + end + + it "should have no query string" do + expect(@uri.query).to eq(nil) + end + + it "should have no fragment" do + expect(@uri.fragment).to eq(nil) + end + + it "should be considered absolute" do + expect(@uri).to be_absolute + end + + it "should not be considered relative" do + expect(@uri).not_to be_relative + end + + it "should be exactly equal to http://example.com:8080/" do + expect(@uri.eql?(Addressable::URI.parse( + "http://example.com/path/to/resource/"))).to eq(true) + end + + it "should have a route of 'resource/' from " + + "'http://example.com/path/to/'" do + expect(@uri.route_from("http://example.com/path/to/")).to eq( + Addressable::URI.parse("resource/") + ) + end + + it "should have a route of '../' from " + + "'http://example.com/path/to/resource/sub'" do + expect(@uri.route_from("http://example.com/path/to/resource/sub")).to eq( + Addressable::URI.parse("../") + ) + end + + + it "should have a route of 'resource/' from " + + "'http://example.com/path/to/another'" do + expect(@uri.route_from("http://example.com/path/to/another")).to eq( + Addressable::URI.parse("resource/") + ) + end + + it "should have a route of 'resource/' from " + + "'http://example.com/path/to/res'" do + expect(@uri.route_from("http://example.com/path/to/res")).to eq( + Addressable::URI.parse("resource/") + ) + end + + it "should have a route of 'resource/' from " + + "'http://example.com:80/path/to/'" do + expect(@uri.route_from("http://example.com:80/path/to/")).to eq( + Addressable::URI.parse("resource/") + ) + end + + it "should have a route of 'http://example.com/path/to/' from " + + "'http://example.com:8080/path/to/'" do + expect(@uri.route_from("http://example.com:8080/path/to/")).to eq( + Addressable::URI.parse("http://example.com/path/to/resource/") + ) + end + + it "should have a route of 'http://example.com/path/to/' from " + + "'http://user:pass@example.com/path/to/'" do + expect(@uri.route_from("http://user:pass@example.com/path/to/")).to eq( + Addressable::URI.parse("http://example.com/path/to/resource/") + ) + end + + it "should have a route of '../../path/to/resource/' from " + + "'http://example.com/to/resource/'" do + expect(@uri.route_from("http://example.com/to/resource/")).to eq( + Addressable::URI.parse("../../path/to/resource/") + ) + end + + it "should correctly convert to a hash" do + expect(@uri.to_hash).to eq({ + :scheme => "http", + :user => nil, + :password => nil, + :host => "example.com", + :port => nil, + :path => "/path/to/resource/", + :query => nil, + :fragment => nil + }) + end + + it "should be identical to its duplicate" do + expect(@uri).to eq(@uri.dup) + end +end + +describe Addressable::URI, "when parsed from " + + "'relative/path/to/resource'" do + before do + @uri = Addressable::URI.parse("relative/path/to/resource") + end + + it "should not have a scheme" do + expect(@uri.scheme).to eq(nil) + end + + it "should not be considered ip-based" do + expect(@uri).not_to be_ip_based + end + + it "should not have an authority segment" do + expect(@uri.authority).to eq(nil) + end + + it "should not have a host" do + expect(@uri.host).to eq(nil) + end + + it "should have no username" do + expect(@uri.user).to eq(nil) + end + + it "should have no password" do + expect(@uri.password).to eq(nil) + end + + it "should not have a port" do + expect(@uri.port).to eq(nil) + end + + it "should have a path of 'relative/path/to/resource'" do + expect(@uri.path).to eq("relative/path/to/resource") + end + + it "should have no query string" do + expect(@uri.query).to eq(nil) + end + + it "should have no fragment" do + expect(@uri.fragment).to eq(nil) + end + + it "should not be considered absolute" do + expect(@uri).not_to be_absolute + end + + it "should be considered relative" do + expect(@uri).to be_relative + end + + it "should raise an error if routing is attempted" do + expect do + @uri.route_to("http://example.com/") + end.to raise_error(ArgumentError, /relative\/path\/to\/resource/) + expect do + @uri.route_from("http://example.com/") + end.to raise_error(ArgumentError, /relative\/path\/to\/resource/) + end + + it "when joined with 'another/relative/path' should be " + + "'relative/path/to/another/relative/path'" do + expect(@uri.join('another/relative/path')).to eq( + Addressable::URI.parse("relative/path/to/another/relative/path") + ) + end + + it "should be identical to its duplicate" do + expect(@uri).to eq(@uri.dup) + end +end + +describe Addressable::URI, "when parsed from " + + "'relative_path_with_no_slashes'" do + before do + @uri = Addressable::URI.parse("relative_path_with_no_slashes") + end + + it "should not have a scheme" do + expect(@uri.scheme).to eq(nil) + end + + it "should not be considered ip-based" do + expect(@uri).not_to be_ip_based + end + + it "should not have an authority segment" do + expect(@uri.authority).to eq(nil) + end + + it "should not have a host" do + expect(@uri.host).to eq(nil) + end + + it "should have no username" do + expect(@uri.user).to eq(nil) + end + + it "should have no password" do + expect(@uri.password).to eq(nil) + end + + it "should not have a port" do + expect(@uri.port).to eq(nil) + end + + it "should have a path of 'relative_path_with_no_slashes'" do + expect(@uri.path).to eq("relative_path_with_no_slashes") + end + + it "should have no query string" do + expect(@uri.query).to eq(nil) + end + + it "should have no fragment" do + expect(@uri.fragment).to eq(nil) + end + + it "should not be considered absolute" do + expect(@uri).not_to be_absolute + end + + it "should be considered relative" do + expect(@uri).to be_relative + end + + it "when joined with 'another_relative_path' should be " + + "'another_relative_path'" do + expect(@uri.join('another_relative_path')).to eq( + Addressable::URI.parse("another_relative_path") + ) + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/file.txt'" do + before do + @uri = Addressable::URI.parse("http://example.com/file.txt") + end + + it "should have a scheme of 'http'" do + expect(@uri.scheme).to eq("http") + end + + it "should have an authority segment of 'example.com'" do + expect(@uri.authority).to eq("example.com") + end + + it "should have a host of 'example.com'" do + expect(@uri.host).to eq("example.com") + end + + it "should have no username" do + expect(@uri.user).to eq(nil) + end + + it "should have no password" do + expect(@uri.password).to eq(nil) + end + + it "should use port 80" do + expect(@uri.inferred_port).to eq(80) + end + + it "should have a path of '/file.txt'" do + expect(@uri.path).to eq("/file.txt") + end + + it "should have a basename of 'file.txt'" do + expect(@uri.basename).to eq("file.txt") + end + + it "should have an extname of '.txt'" do + expect(@uri.extname).to eq(".txt") + end + + it "should have no query string" do + expect(@uri.query).to eq(nil) + end + + it "should have no fragment" do + expect(@uri.fragment).to eq(nil) + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/file.txt;parameter'" do + before do + @uri = Addressable::URI.parse("http://example.com/file.txt;parameter") + end + + it "should have a scheme of 'http'" do + expect(@uri.scheme).to eq("http") + end + + it "should have an authority segment of 'example.com'" do + expect(@uri.authority).to eq("example.com") + end + + it "should have a host of 'example.com'" do + expect(@uri.host).to eq("example.com") + end + + it "should have no username" do + expect(@uri.user).to eq(nil) + end + + it "should have no password" do + expect(@uri.password).to eq(nil) + end + + it "should use port 80" do + expect(@uri.inferred_port).to eq(80) + end + + it "should have a path of '/file.txt;parameter'" do + expect(@uri.path).to eq("/file.txt;parameter") + end + + it "should have a basename of 'file.txt'" do + expect(@uri.basename).to eq("file.txt") + end + + it "should have an extname of '.txt'" do + expect(@uri.extname).to eq(".txt") + end + + it "should have no query string" do + expect(@uri.query).to eq(nil) + end + + it "should have no fragment" do + expect(@uri.fragment).to eq(nil) + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/file.txt;x=y'" do + before do + @uri = Addressable::URI.parse("http://example.com/file.txt;x=y") + end + + it "should have a scheme of 'http'" do + expect(@uri.scheme).to eq("http") + end + + it "should have a scheme of 'http'" do + expect(@uri.scheme).to eq("http") + end + + it "should have an authority segment of 'example.com'" do + expect(@uri.authority).to eq("example.com") + end + + it "should have a host of 'example.com'" do + expect(@uri.host).to eq("example.com") + end + + it "should have no username" do + expect(@uri.user).to eq(nil) + end + + it "should have no password" do + expect(@uri.password).to eq(nil) + end + + it "should use port 80" do + expect(@uri.inferred_port).to eq(80) + end + + it "should have a path of '/file.txt;x=y'" do + expect(@uri.path).to eq("/file.txt;x=y") + end + + it "should have an extname of '.txt'" do + expect(@uri.extname).to eq(".txt") + end + + it "should have no query string" do + expect(@uri.query).to eq(nil) + end + + it "should have no fragment" do + expect(@uri.fragment).to eq(nil) + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end +end + +describe Addressable::URI, "when parsed from " + + "'svn+ssh://developername@rubyforge.org/var/svn/project'" do + before do + @uri = Addressable::URI.parse( + "svn+ssh://developername@rubyforge.org/var/svn/project" + ) + end + + it "should have a scheme of 'svn+ssh'" do + expect(@uri.scheme).to eq("svn+ssh") + end + + it "should be considered to be ip-based" do + expect(@uri).to be_ip_based + end + + it "should have a path of '/var/svn/project'" do + expect(@uri.path).to eq("/var/svn/project") + end + + it "should have a username of 'developername'" do + expect(@uri.user).to eq("developername") + end + + it "should have no password" do + expect(@uri.password).to eq(nil) + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end +end + +describe Addressable::URI, "when parsed from " + + "'ssh+svn://developername@RUBYFORGE.ORG/var/svn/project'" do + before do + @uri = Addressable::URI.parse( + "ssh+svn://developername@RUBYFORGE.ORG/var/svn/project" + ) + end + + it "should have a scheme of 'ssh+svn'" do + expect(@uri.scheme).to eq("ssh+svn") + end + + it "should have a normalized scheme of 'svn+ssh'" do + expect(@uri.normalized_scheme).to eq("svn+ssh") + end + + it "should have a normalized site of 'svn+ssh'" do + expect(@uri.normalized_site).to eq("svn+ssh://developername@rubyforge.org") + end + + it "should not be considered to be ip-based" do + expect(@uri).not_to be_ip_based + end + + it "should have a path of '/var/svn/project'" do + expect(@uri.path).to eq("/var/svn/project") + end + + it "should have a username of 'developername'" do + expect(@uri.user).to eq("developername") + end + + it "should have no password" do + expect(@uri.password).to eq(nil) + end + + it "should not be considered to be in normal form" do + expect(@uri.normalize).not_to be_eql(@uri) + end +end + +describe Addressable::URI, "when parsed from " + + "'mailto:user@example.com'" do + before do + @uri = Addressable::URI.parse("mailto:user@example.com") + end + + it "should have a scheme of 'mailto'" do + expect(@uri.scheme).to eq("mailto") + end + + it "should not be considered to be ip-based" do + expect(@uri).not_to be_ip_based + end + + it "should have a path of 'user@example.com'" do + expect(@uri.path).to eq("user@example.com") + end + + it "should have no user" do + expect(@uri.user).to eq(nil) + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end +end + +describe Addressable::URI, "when parsed from " + + "'tag:example.com,2006-08-18:/path/to/something'" do + before do + @uri = Addressable::URI.parse( + "tag:example.com,2006-08-18:/path/to/something") + end + + it "should have a scheme of 'tag'" do + expect(@uri.scheme).to eq("tag") + end + + it "should be considered to be ip-based" do + expect(@uri).not_to be_ip_based + end + + it "should have a path of " + + "'example.com,2006-08-18:/path/to/something'" do + expect(@uri.path).to eq("example.com,2006-08-18:/path/to/something") + end + + it "should have no user" do + expect(@uri.user).to eq(nil) + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end + + it "should have a 'null' origin" do + expect(@uri.origin).to eq('null') + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/x;y/'" do + before do + @uri = Addressable::URI.parse("http://example.com/x;y/") + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/?x=1&y=2'" do + before do + @uri = Addressable::URI.parse("http://example.com/?x=1&y=2") + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end +end + +describe Addressable::URI, "when parsed from " + + "'view-source:http://example.com/'" do + before do + @uri = Addressable::URI.parse("view-source:http://example.com/") + end + + it "should have a scheme of 'view-source'" do + expect(@uri.scheme).to eq("view-source") + end + + it "should have a path of 'http://example.com/'" do + expect(@uri.path).to eq("http://example.com/") + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end + + it "should have a 'null' origin" do + expect(@uri.origin).to eq('null') + end +end + +describe Addressable::URI, "when parsed from " + + "'http://user:pass@example.com/path/to/resource?query=x#fragment'" do + before do + @uri = Addressable::URI.parse( + "http://user:pass@example.com/path/to/resource?query=x#fragment") + end + + it "should use the 'http' scheme" do + expect(@uri.scheme).to eq("http") + end + + it "should have an authority segment of 'user:pass@example.com'" do + expect(@uri.authority).to eq("user:pass@example.com") + end + + it "should have a username of 'user'" do + expect(@uri.user).to eq("user") + end + + it "should have a password of 'pass'" do + expect(@uri.password).to eq("pass") + end + + it "should have a host of 'example.com'" do + expect(@uri.host).to eq("example.com") + end + + it "should use port 80" do + expect(@uri.inferred_port).to eq(80) + end + + it "should have a path of '/path/to/resource'" do + expect(@uri.path).to eq("/path/to/resource") + end + + it "should have a query string of 'query=x'" do + expect(@uri.query).to eq("query=x") + end + + it "should have a fragment of 'fragment'" do + expect(@uri.fragment).to eq("fragment") + end + + it "should be considered to be in normal form" do + expect(@uri.normalize).to be_eql(@uri) + end + + it "should have a route of '../../' to " + + "'http://user:pass@example.com/path/'" do + expect(@uri.route_to("http://user:pass@example.com/path/")).to eq( + Addressable::URI.parse("../../") + ) + end + + it "should have a route of 'to/resource?query=x#fragment' " + + "from 'http://user:pass@example.com/path/'" do + expect(@uri.route_from("http://user:pass@example.com/path/")).to eq( + Addressable::URI.parse("to/resource?query=x#fragment") + ) + end + + it "should have a route of '?query=x#fragment' " + + "from 'http://user:pass@example.com/path/to/resource'" do + expect(@uri.route_from("http://user:pass@example.com/path/to/resource")).to eq( + Addressable::URI.parse("?query=x#fragment") + ) + end + + it "should have a route of '#fragment' " + + "from 'http://user:pass@example.com/path/to/resource?query=x'" do + expect(@uri.route_from( + "http://user:pass@example.com/path/to/resource?query=x")).to eq( + Addressable::URI.parse("#fragment") + ) + end + + it "should have a route of '#fragment' from " + + "'http://user:pass@example.com/path/to/resource?query=x#fragment'" do + expect(@uri.route_from( + "http://user:pass@example.com/path/to/resource?query=x#fragment" + )).to eq(Addressable::URI.parse("#fragment")) + end + + it "should have a route of 'http://elsewhere.com/' to " + + "'http://elsewhere.com/'" do + expect(@uri.route_to("http://elsewhere.com/")).to eq( + Addressable::URI.parse("http://elsewhere.com/") + ) + end + + it "should have a route of " + + "'http://user:pass@example.com/path/to/resource?query=x#fragment' " + + "from 'http://example.com/path/to/'" do + expect(@uri.route_from("http://elsewhere.com/path/to/")).to eq( + Addressable::URI.parse( + "http://user:pass@example.com/path/to/resource?query=x#fragment") + ) + end + + it "should have the correct scheme after assignment" do + @uri.scheme = "ftp" + expect(@uri.scheme).to eq("ftp") + expect(@uri.to_s).to eq( + "ftp://user:pass@example.com/path/to/resource?query=x#fragment" + ) + expect(@uri.to_str).to eq( + "ftp://user:pass@example.com/path/to/resource?query=x#fragment" + ) + end + + it "should have the correct site segment after assignment" do + @uri.site = "https://newuser:newpass@example.com:443" + expect(@uri.scheme).to eq("https") + expect(@uri.authority).to eq("newuser:newpass@example.com:443") + expect(@uri.user).to eq("newuser") + expect(@uri.password).to eq("newpass") + expect(@uri.userinfo).to eq("newuser:newpass") + expect(@uri.normalized_userinfo).to eq("newuser:newpass") + expect(@uri.host).to eq("example.com") + expect(@uri.port).to eq(443) + expect(@uri.inferred_port).to eq(443) + expect(@uri.to_s).to eq( + "https://newuser:newpass@example.com:443" + + "/path/to/resource?query=x#fragment" + ) + end + + it "should have the correct authority segment after assignment" do + @uri.authority = "newuser:newpass@example.com:80" + expect(@uri.authority).to eq("newuser:newpass@example.com:80") + expect(@uri.user).to eq("newuser") + expect(@uri.password).to eq("newpass") + expect(@uri.userinfo).to eq("newuser:newpass") + expect(@uri.normalized_userinfo).to eq("newuser:newpass") + expect(@uri.host).to eq("example.com") + expect(@uri.port).to eq(80) + expect(@uri.inferred_port).to eq(80) + expect(@uri.to_s).to eq( + "http://newuser:newpass@example.com:80" + + "/path/to/resource?query=x#fragment" + ) + end + + it "should have the correct userinfo segment after assignment" do + @uri.userinfo = "newuser:newpass" + expect(@uri.userinfo).to eq("newuser:newpass") + expect(@uri.authority).to eq("newuser:newpass@example.com") + expect(@uri.user).to eq("newuser") + expect(@uri.password).to eq("newpass") + expect(@uri.host).to eq("example.com") + expect(@uri.port).to eq(nil) + expect(@uri.inferred_port).to eq(80) + expect(@uri.to_s).to eq( + "http://newuser:newpass@example.com" + + "/path/to/resource?query=x#fragment" + ) + end + + it "should have the correct username after assignment" do + @uri.user = "newuser" + expect(@uri.user).to eq("newuser") + expect(@uri.authority).to eq("newuser:pass@example.com") + end + + it "should have the correct password after assignment" do + @uri.password = "newpass" + expect(@uri.password).to eq("newpass") + expect(@uri.authority).to eq("user:newpass@example.com") + end + + it "should have the correct host after assignment" do + @uri.host = "newexample.com" + expect(@uri.host).to eq("newexample.com") + expect(@uri.authority).to eq("user:pass@newexample.com") + end + + it "should have the correct host after assignment" do + @uri.hostname = "newexample.com" + expect(@uri.host).to eq("newexample.com") + expect(@uri.hostname).to eq("newexample.com") + expect(@uri.authority).to eq("user:pass@newexample.com") + end + + it "should raise an error if assigning a bogus object to the hostname" do + expect do + @uri.hostname = Object.new + end.to raise_error(TypeError) + end + + it "should have the correct port after assignment" do + @uri.port = 8080 + expect(@uri.port).to eq(8080) + expect(@uri.authority).to eq("user:pass@example.com:8080") + end + + it "should have the correct origin after assignment" do + @uri.origin = "http://newexample.com" + expect(@uri.host).to eq("newexample.com") + expect(@uri.authority).to eq("newexample.com") + end + + it "should have the correct path after assignment" do + @uri.path = "/newpath/to/resource" + expect(@uri.path).to eq("/newpath/to/resource") + expect(@uri.to_s).to eq( + "http://user:pass@example.com/newpath/to/resource?query=x#fragment" + ) + end + + it "should have the correct scheme and authority after nil assignment" do + @uri.site = nil + expect(@uri.scheme).to eq(nil) + expect(@uri.authority).to eq(nil) + expect(@uri.to_s).to eq("/path/to/resource?query=x#fragment") + end + + it "should have the correct scheme and authority after assignment" do + @uri.site = "file://" + expect(@uri.scheme).to eq("file") + expect(@uri.authority).to eq("") + expect(@uri.to_s).to eq("file:///path/to/resource?query=x#fragment") + end + + it "should have the correct path after nil assignment" do + @uri.path = nil + expect(@uri.path).to eq("") + expect(@uri.to_s).to eq( + "http://user:pass@example.com?query=x#fragment" + ) + end + + it "should have the correct query string after assignment" do + @uri.query = "newquery=x" + expect(@uri.query).to eq("newquery=x") + expect(@uri.to_s).to eq( + "http://user:pass@example.com/path/to/resource?newquery=x#fragment" + ) + @uri.query = nil + expect(@uri.query).to eq(nil) + expect(@uri.to_s).to eq( + "http://user:pass@example.com/path/to/resource#fragment" + ) + end + + it "should have the correct query string after hash assignment" do + @uri.query_values = {"?uestion mark" => "=sign", "hello" => "g\xC3\xBCnther"} + expect(@uri.query.split("&")).to include("%3Fuestion%20mark=%3Dsign") + expect(@uri.query.split("&")).to include("hello=g%C3%BCnther") + expect(@uri.query_values).to eq({ + "?uestion mark" => "=sign", "hello" => "g\xC3\xBCnther" + }) + end + + it "should have the correct query string after flag hash assignment" do + @uri.query_values = {'flag?1' => nil, 'fl=ag2' => nil, 'flag3' => nil} + expect(@uri.query.split("&")).to include("flag%3F1") + expect(@uri.query.split("&")).to include("fl%3Dag2") + expect(@uri.query.split("&")).to include("flag3") + expect(@uri.query_values(Array).sort).to eq([["fl=ag2"], ["flag3"], ["flag?1"]]) + expect(@uri.query_values(Hash)).to eq({ + 'flag?1' => nil, 'fl=ag2' => nil, 'flag3' => nil + }) + end + + it "should raise an error if query values are set to a bogus type" do + expect do + @uri.query_values = "bogus" + end.to raise_error(TypeError) + end + + it "should have the correct fragment after assignment" do + @uri.fragment = "newfragment" + expect(@uri.fragment).to eq("newfragment") + expect(@uri.to_s).to eq( + "http://user:pass@example.com/path/to/resource?query=x#newfragment" + ) + + @uri.fragment = nil + expect(@uri.fragment).to eq(nil) + expect(@uri.to_s).to eq( + "http://user:pass@example.com/path/to/resource?query=x" + ) + end + + it "should have the correct values after a merge" do + expect(@uri.merge(:fragment => "newfragment").to_s).to eq( + "http://user:pass@example.com/path/to/resource?query=x#newfragment" + ) + end + + it "should have the correct values after a merge" do + expect(@uri.merge(:fragment => nil).to_s).to eq( + "http://user:pass@example.com/path/to/resource?query=x" + ) + end + + it "should have the correct values after a merge" do + expect(@uri.merge(:userinfo => "newuser:newpass").to_s).to eq( + "http://newuser:newpass@example.com/path/to/resource?query=x#fragment" + ) + end + + it "should have the correct values after a merge" do + expect(@uri.merge(:userinfo => nil).to_s).to eq( + "http://example.com/path/to/resource?query=x#fragment" + ) + end + + it "should have the correct values after a merge" do + expect(@uri.merge(:path => "newpath").to_s).to eq( + "http://user:pass@example.com/newpath?query=x#fragment" + ) + end + + it "should have the correct values after a merge" do + expect(@uri.merge(:port => "42", :path => "newpath", :query => "").to_s).to eq( + "http://user:pass@example.com:42/newpath?#fragment" + ) + end + + it "should have the correct values after a merge" do + expect(@uri.merge(:authority => "foo:bar@baz:42").to_s).to eq( + "http://foo:bar@baz:42/path/to/resource?query=x#fragment" + ) + # Ensure the operation was not destructive + expect(@uri.to_s).to eq( + "http://user:pass@example.com/path/to/resource?query=x#fragment" + ) + end + + it "should have the correct values after a destructive merge" do + @uri.merge!(:authority => "foo:bar@baz:42") + # Ensure the operation was destructive + expect(@uri.to_s).to eq( + "http://foo:bar@baz:42/path/to/resource?query=x#fragment" + ) + end + + it "should fail to merge with bogus values" do + expect do + @uri.merge(:port => "bogus") + end.to raise_error(Addressable::URI::InvalidURIError) + end + + it "should fail to merge with bogus values" do + expect do + @uri.merge(:authority => "bar@baz:bogus") + end.to raise_error(Addressable::URI::InvalidURIError) + end + + it "should fail to merge with bogus parameters" do + expect do + @uri.merge(42) + end.to raise_error(TypeError) + end + + it "should fail to merge with bogus parameters" do + expect do + @uri.merge("http://example.com/") + end.to raise_error(TypeError) + end + + it "should fail to merge with both authority and subcomponents" do + expect do + @uri.merge(:authority => "foo:bar@baz:42", :port => "42") + end.to raise_error(ArgumentError) + end + + it "should fail to merge with both userinfo and subcomponents" do + expect do + @uri.merge(:userinfo => "foo:bar", :user => "foo") + end.to raise_error(ArgumentError) + end + + it "should be identical to its duplicate" do + expect(@uri).to eq(@uri.dup) + end + + it "should have an origin of 'http://example.com'" do + expect(@uri.origin).to eq('http://example.com') + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/search?q=Q%26A'" do + + before do + @uri = Addressable::URI.parse("http://example.com/search?q=Q%26A") + end + + it "should have a query of 'q=Q%26A'" do + expect(@uri.query).to eq("q=Q%26A") + end + + it "should have query_values of {'q' => 'Q&A'}" do + expect(@uri.query_values).to eq({ 'q' => 'Q&A' }) + end + + it "should normalize to the original uri " + + "(with the ampersand properly percent-encoded)" do + expect(@uri.normalize.to_s).to eq("http://example.com/search?q=Q%26A") + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/?&x=b'" do + before do + @uri = Addressable::URI.parse("http://example.com/?&x=b") + end + + it "should have a query of '&x=b'" do + expect(@uri.query).to eq("&x=b") + end + + it "should have query_values of {'x' => 'b'}" do + expect(@uri.query_values).to eq({'x' => 'b'}) + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/?q='one;two'&x=1'" do + before do + @uri = Addressable::URI.parse("http://example.com/?q='one;two'&x=1") + end + + it "should have a query of 'q='one;two'&x=1'" do + expect(@uri.query).to eq("q='one;two'&x=1") + end + + it "should have query_values of {\"q\" => \"'one;two'\", \"x\" => \"1\"}" do + expect(@uri.query_values).to eq({"q" => "'one;two'", "x" => "1"}) + end + + it "should escape the ';' character when normalizing to avoid ambiguity " + + "with the W3C HTML 4.01 specification" do + # HTML 4.01 Section B.2.2 + expect(@uri.normalize.query).to eq("q='one%3Btwo'&x=1") + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/?&&x=b'" do + before do + @uri = Addressable::URI.parse("http://example.com/?&&x=b") + end + + it "should have a query of '&&x=b'" do + expect(@uri.query).to eq("&&x=b") + end + + it "should have query_values of {'x' => 'b'}" do + expect(@uri.query_values).to eq({'x' => 'b'}) + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/?q=a&&x=b'" do + before do + @uri = Addressable::URI.parse("http://example.com/?q=a&&x=b") + end + + it "should have a query of 'q=a&&x=b'" do + expect(@uri.query).to eq("q=a&&x=b") + end + + it "should have query_values of {'q' => 'a, 'x' => 'b'}" do + expect(@uri.query_values).to eq({'q' => 'a', 'x' => 'b'}) + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/?q&&x=b'" do + before do + @uri = Addressable::URI.parse("http://example.com/?q&&x=b") + end + + it "should have a query of 'q&&x=b'" do + expect(@uri.query).to eq("q&&x=b") + end + + it "should have query_values of {'q' => true, 'x' => 'b'}" do + expect(@uri.query_values).to eq({'q' => nil, 'x' => 'b'}) + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/?q=a+b'" do + before do + @uri = Addressable::URI.parse("http://example.com/?q=a+b") + end + + it "should have a query of 'q=a+b'" do + expect(@uri.query).to eq("q=a+b") + end + + it "should have query_values of {'q' => 'a b'}" do + expect(@uri.query_values).to eq({'q' => 'a b'}) + end + + it "should have a normalized query of 'q=a+b'" do + expect(@uri.normalized_query).to eq("q=a+b") + end +end + +describe Addressable::URI, "when parsed from 'https://example.com/?q=a+b'" do + before do + @uri = Addressable::URI.parse("https://example.com/?q=a+b") + end + + it "should have query_values of {'q' => 'a b'}" do + expect(@uri.query_values).to eq("q" => "a b") + end +end + +describe Addressable::URI, "when parsed from 'example.com?q=a+b'" do + before do + @uri = Addressable::URI.parse("example.com?q=a+b") + end + + it "should have query_values of {'q' => 'a b'}" do + expect(@uri.query_values).to eq("q" => "a b") + end +end + +describe Addressable::URI, "when parsed from 'mailto:?q=a+b'" do + before do + @uri = Addressable::URI.parse("mailto:?q=a+b") + end + + it "should have query_values of {'q' => 'a+b'}" do + expect(@uri.query_values).to eq("q" => "a+b") + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/?q=a%2bb'" do + before do + @uri = Addressable::URI.parse("http://example.com/?q=a%2bb") + end + + it "should have a query of 'q=a+b'" do + expect(@uri.query).to eq("q=a%2bb") + end + + it "should have query_values of {'q' => 'a+b'}" do + expect(@uri.query_values).to eq({'q' => 'a+b'}) + end + + it "should have a normalized query of 'q=a%2Bb'" do + expect(@uri.normalized_query).to eq("q=a%2Bb") + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/?v=%7E&w=%&x=%25&y=%2B&z=C%CC%A7'" do + before do + @uri = Addressable::URI.parse("http://example.com/?v=%7E&w=%&x=%25&y=%2B&z=C%CC%A7") + end + + it "should have a normalized query of 'v=~&w=%25&x=%25&y=%2B&z=%C3%87'" do + expect(@uri.normalized_query).to eq("v=~&w=%25&x=%25&y=%2B&z=%C3%87") + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/?v=%7E&w=%&x=%25&y=+&z=C%CC%A7'" do + before do + @uri = Addressable::URI.parse("http://example.com/?v=%7E&w=%&x=%25&y=+&z=C%CC%A7") + end + + it "should have a normalized query of 'v=~&w=%25&x=%25&y=+&z=%C3%87'" do + expect(@uri.normalized_query).to eq("v=~&w=%25&x=%25&y=+&z=%C3%87") + end +end + +describe Addressable::URI, "when parsed from 'http://example/?b=1&a=2&c=3'" do + before do + @uri = Addressable::URI.parse("http://example/?b=1&a=2&c=3") + end + + it "should have a sorted normalized query of 'a=2&b=1&c=3'" do + expect(@uri.normalized_query(:sorted)).to eq("a=2&b=1&c=3") + end +end + +describe Addressable::URI, "when parsed from 'http://example/?&a&&c&'" do + before do + @uri = Addressable::URI.parse("http://example/?&a&&c&") + end + + it "should have a compacted normalized query of 'a&c'" do + expect(@uri.normalized_query(:compacted)).to eq("a&c") + end +end + +describe Addressable::URI, "when parsed from 'http://example.com/?a=1&a=1'" do + before do + @uri = Addressable::URI.parse("http://example.com/?a=1&a=1") + end + + it "should have a compacted normalized query of 'a=1'" do + expect(@uri.normalized_query(:compacted)).to eq("a=1") + end +end + +describe Addressable::URI, "when parsed from 'http://example.com/?a=1&a=2'" do + before do + @uri = Addressable::URI.parse("http://example.com/?a=1&a=2") + end + + it "should have a compacted normalized query of 'a=1&a=2'" do + expect(@uri.normalized_query(:compacted)).to eq("a=1&a=2") + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/sound%2bvision'" do + before do + @uri = Addressable::URI.parse("http://example.com/sound%2bvision") + end + + it "should have a normalized path of '/sound+vision'" do + expect(@uri.normalized_path).to eq('/sound+vision') + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/?q='" do + before do + @uri = Addressable::URI.parse("http://example.com/?q=") + end + + it "should have a query of 'q='" do + expect(@uri.query).to eq("q=") + end + + it "should have query_values of {'q' => ''}" do + expect(@uri.query_values).to eq({'q' => ''}) + end +end + +describe Addressable::URI, "when parsed from " + + "'http://user@example.com'" do + before do + @uri = Addressable::URI.parse("http://user@example.com") + end + + it "should use the 'http' scheme" do + expect(@uri.scheme).to eq("http") + end + + it "should have a username of 'user'" do + expect(@uri.user).to eq("user") + end + + it "should have no password" do + expect(@uri.password).to eq(nil) + end + + it "should have a userinfo of 'user'" do + expect(@uri.userinfo).to eq("user") + end + + it "should have a normalized userinfo of 'user'" do + expect(@uri.normalized_userinfo).to eq("user") + end + + it "should have a host of 'example.com'" do + expect(@uri.host).to eq("example.com") + end + + it "should have default_port 80" do + expect(@uri.default_port).to eq(80) + end + + it "should use port 80" do + expect(@uri.inferred_port).to eq(80) + end + + it "should have the correct username after assignment" do + @uri.user = "newuser" + expect(@uri.user).to eq("newuser") + expect(@uri.password).to eq(nil) + expect(@uri.to_s).to eq("http://newuser@example.com") + end + + it "should have the correct password after assignment" do + @uri.password = "newpass" + expect(@uri.password).to eq("newpass") + expect(@uri.to_s).to eq("http://user:newpass@example.com") + end + + it "should have the correct userinfo segment after assignment" do + @uri.userinfo = "newuser:newpass" + expect(@uri.userinfo).to eq("newuser:newpass") + expect(@uri.user).to eq("newuser") + expect(@uri.password).to eq("newpass") + expect(@uri.host).to eq("example.com") + expect(@uri.port).to eq(nil) + expect(@uri.inferred_port).to eq(80) + expect(@uri.to_s).to eq("http://newuser:newpass@example.com") + end + + it "should have the correct userinfo segment after nil assignment" do + @uri.userinfo = nil + expect(@uri.userinfo).to eq(nil) + expect(@uri.user).to eq(nil) + expect(@uri.password).to eq(nil) + expect(@uri.host).to eq("example.com") + expect(@uri.port).to eq(nil) + expect(@uri.inferred_port).to eq(80) + expect(@uri.to_s).to eq("http://example.com") + end + + it "should have the correct authority segment after assignment" do + @uri.authority = "newuser@example.com" + expect(@uri.authority).to eq("newuser@example.com") + expect(@uri.user).to eq("newuser") + expect(@uri.password).to eq(nil) + expect(@uri.host).to eq("example.com") + expect(@uri.port).to eq(nil) + expect(@uri.inferred_port).to eq(80) + expect(@uri.to_s).to eq("http://newuser@example.com") + end + + it "should raise an error after nil assignment of authority segment" do + expect do + # This would create an invalid URI + @uri.authority = nil + end.to raise_error(Addressable::URI::InvalidURIError) + end +end + +describe Addressable::URI, "when parsed from " + + "'http://user:@example.com'" do + before do + @uri = Addressable::URI.parse("http://user:@example.com") + end + + it "should use the 'http' scheme" do + expect(@uri.scheme).to eq("http") + end + + it "should have a username of 'user'" do + expect(@uri.user).to eq("user") + end + + it "should have a password of ''" do + expect(@uri.password).to eq("") + end + + it "should have a normalized userinfo of 'user:'" do + expect(@uri.normalized_userinfo).to eq("user:") + end + + it "should have a host of 'example.com'" do + expect(@uri.host).to eq("example.com") + end + + it "should use port 80" do + expect(@uri.inferred_port).to eq(80) + end + + it "should have the correct username after assignment" do + @uri.user = "newuser" + expect(@uri.user).to eq("newuser") + expect(@uri.password).to eq("") + expect(@uri.to_s).to eq("http://newuser:@example.com") + end + + it "should have the correct password after assignment" do + @uri.password = "newpass" + expect(@uri.password).to eq("newpass") + expect(@uri.to_s).to eq("http://user:newpass@example.com") + end + + it "should have the correct authority segment after assignment" do + @uri.authority = "newuser:@example.com" + expect(@uri.authority).to eq("newuser:@example.com") + expect(@uri.user).to eq("newuser") + expect(@uri.password).to eq("") + expect(@uri.host).to eq("example.com") + expect(@uri.port).to eq(nil) + expect(@uri.inferred_port).to eq(80) + expect(@uri.to_s).to eq("http://newuser:@example.com") + end +end + +describe Addressable::URI, "when parsed from " + + "'http://:pass@example.com'" do + before do + @uri = Addressable::URI.parse("http://:pass@example.com") + end + + it "should use the 'http' scheme" do + expect(@uri.scheme).to eq("http") + end + + it "should have a username of ''" do + expect(@uri.user).to eq("") + end + + it "should have a password of 'pass'" do + expect(@uri.password).to eq("pass") + end + + it "should have a userinfo of ':pass'" do + expect(@uri.userinfo).to eq(":pass") + end + + it "should have a normalized userinfo of ':pass'" do + expect(@uri.normalized_userinfo).to eq(":pass") + end + + it "should have a host of 'example.com'" do + expect(@uri.host).to eq("example.com") + end + + it "should use port 80" do + expect(@uri.inferred_port).to eq(80) + end + + it "should have the correct username after assignment" do + @uri.user = "newuser" + expect(@uri.user).to eq("newuser") + expect(@uri.password).to eq("pass") + expect(@uri.to_s).to eq("http://newuser:pass@example.com") + end + + it "should have the correct password after assignment" do + @uri.password = "newpass" + expect(@uri.password).to eq("newpass") + expect(@uri.user).to eq("") + expect(@uri.to_s).to eq("http://:newpass@example.com") + end + + it "should have the correct authority segment after assignment" do + @uri.authority = ":newpass@example.com" + expect(@uri.authority).to eq(":newpass@example.com") + expect(@uri.user).to eq("") + expect(@uri.password).to eq("newpass") + expect(@uri.host).to eq("example.com") + expect(@uri.port).to eq(nil) + expect(@uri.inferred_port).to eq(80) + expect(@uri.to_s).to eq("http://:newpass@example.com") + end +end + +describe Addressable::URI, "when parsed from " + + "'http://:@example.com'" do + before do + @uri = Addressable::URI.parse("http://:@example.com") + end + + it "should use the 'http' scheme" do + expect(@uri.scheme).to eq("http") + end + + it "should have a username of ''" do + expect(@uri.user).to eq("") + end + + it "should have a password of ''" do + expect(@uri.password).to eq("") + end + + it "should have a normalized userinfo of nil" do + expect(@uri.normalized_userinfo).to eq(nil) + end + + it "should have a host of 'example.com'" do + expect(@uri.host).to eq("example.com") + end + + it "should use port 80" do + expect(@uri.inferred_port).to eq(80) + end + + it "should have the correct username after assignment" do + @uri.user = "newuser" + expect(@uri.user).to eq("newuser") + expect(@uri.password).to eq("") + expect(@uri.to_s).to eq("http://newuser:@example.com") + end + + it "should have the correct password after assignment" do + @uri.password = "newpass" + expect(@uri.password).to eq("newpass") + expect(@uri.user).to eq("") + expect(@uri.to_s).to eq("http://:newpass@example.com") + end + + it "should have the correct authority segment after assignment" do + @uri.authority = ":@newexample.com" + expect(@uri.authority).to eq(":@newexample.com") + expect(@uri.user).to eq("") + expect(@uri.password).to eq("") + expect(@uri.host).to eq("newexample.com") + expect(@uri.port).to eq(nil) + expect(@uri.inferred_port).to eq(80) + expect(@uri.to_s).to eq("http://:@newexample.com") + end +end + +describe Addressable::URI, "when parsed from " + + "'#example'" do + before do + @uri = Addressable::URI.parse("#example") + end + + it "should be considered relative" do + expect(@uri).to be_relative + end + + it "should have a host of nil" do + expect(@uri.host).to eq(nil) + end + + it "should have a site of nil" do + expect(@uri.site).to eq(nil) + end + + it "should have a normalized_site of nil" do + expect(@uri.normalized_site).to eq(nil) + end + + it "should have a path of ''" do + expect(@uri.path).to eq("") + end + + it "should have a query string of nil" do + expect(@uri.query).to eq(nil) + end + + it "should have a fragment of 'example'" do + expect(@uri.fragment).to eq("example") + end +end + +describe Addressable::URI, "when parsed from " + + "the network-path reference '//example.com/'" do + before do + @uri = Addressable::URI.parse("//example.com/") + end + + it "should be considered relative" do + expect(@uri).to be_relative + end + + it "should have a host of 'example.com'" do + expect(@uri.host).to eq("example.com") + end + + it "should have a path of '/'" do + expect(@uri.path).to eq("/") + end + + it "should raise an error if routing is attempted" do + expect do + @uri.route_to("http://example.com/") + end.to raise_error(ArgumentError, /\/\/example.com\//) + expect do + @uri.route_from("http://example.com/") + end.to raise_error(ArgumentError, /\/\/example.com\//) + end + + it "should have a 'null' origin" do + expect(@uri.origin).to eq('null') + end +end + +describe Addressable::URI, "when parsed from " + + "'feed://http://example.com/'" do + before do + @uri = Addressable::URI.parse("feed://http://example.com/") + end + + it "should have a host of 'http'" do + expect(@uri.host).to eq("http") + end + + it "should have a path of '//example.com/'" do + expect(@uri.path).to eq("//example.com/") + end +end + +describe Addressable::URI, "when parsed from " + + "'feed:http://example.com/'" do + before do + @uri = Addressable::URI.parse("feed:http://example.com/") + end + + it "should have a path of 'http://example.com/'" do + expect(@uri.path).to eq("http://example.com/") + end + + it "should normalize to 'http://example.com/'" do + expect(@uri.normalize.to_s).to eq("http://example.com/") + expect(@uri.normalize!.to_s).to eq("http://example.com/") + end + + it "should have a 'null' origin" do + expect(@uri.origin).to eq('null') + end +end + +describe Addressable::URI, "when parsed from " + + "'example://a/b/c/%7Bfoo%7D'" do + before do + @uri = Addressable::URI.parse("example://a/b/c/%7Bfoo%7D") + end + + # Section 6.2.2 of RFC 3986 + it "should be equivalent to eXAMPLE://a/./b/../b/%63/%7bfoo%7d" do + expect(@uri).to eq( + Addressable::URI.parse("eXAMPLE://a/./b/../b/%63/%7bfoo%7d") + ) + end + + it "should have an origin of 'example://a'" do + expect(@uri.origin).to eq('example://a') + end +end + +describe Addressable::URI, "when parsed from " + + "'http://example.com/indirect/path/./to/../resource/'" do + before do + @uri = Addressable::URI.parse( + "http://example.com/indirect/path/./to/../resource/") + end + + it "should use the 'http' scheme" do + expect(@uri.scheme).to eq("http") + end + + it "should have a host of 'example.com'" do + expect(@uri.host).to eq("example.com") + end + + it "should use port 80" do + expect(@uri.inferred_port).to eq(80) + end + + it "should have a path of '/indirect/path/./to/../resource/'" do + expect(@uri.path).to eq("/indirect/path/./to/../resource/") + end + + # Section 6.2.2.3 of RFC 3986 + it "should have a normalized path of '/indirect/path/resource/'" do + expect(@uri.normalize.path).to eq("/indirect/path/resource/") + expect(@uri.normalize!.path).to eq("/indirect/path/resource/") + end +end + +describe Addressable::URI, "when parsed from " + + "'http://under_score.example.com/'" do + it "should not cause an error" do + expect do + Addressable::URI.parse("http://under_score.example.com/") + end.not_to raise_error + end +end + +describe Addressable::URI, "when parsed from " + + "'./this:that'" do + before do + @uri = Addressable::URI.parse("./this:that") + end + + it "should be considered relative" do + expect(@uri).to be_relative + end + + it "should have no scheme" do + expect(@uri.scheme).to eq(nil) + end + + it "should have a 'null' origin" do + expect(@uri.origin).to eq('null') + end +end + +describe Addressable::URI, "when parsed from " + + "'this:that'" do + before do + @uri = Addressable::URI.parse("this:that") + end + + it "should be considered absolute" do + expect(@uri).to be_absolute + end + + it "should have a scheme of 'this'" do + expect(@uri.scheme).to eq("this") + end + + it "should have a 'null' origin" do + expect(@uri.origin).to eq('null') + end +end + +describe Addressable::URI, "when parsed from '?'" do + before do + @uri = Addressable::URI.parse("?") + end + + it "should normalize to ''" do + expect(@uri.normalize.to_s).to eq("") + end + + it "should have the correct return type" do + expect(@uri.query_values).to eq({}) + expect(@uri.query_values(Hash)).to eq({}) + expect(@uri.query_values(Array)).to eq([]) + end + + it "should have a 'null' origin" do + expect(@uri.origin).to eq('null') + end +end + +describe Addressable::URI, "when parsed from '?one=1&two=2&three=3'" do + before do + @uri = Addressable::URI.parse("?one=1&two=2&three=3") + end + + it "should have the correct query values" do + expect(@uri.query_values).to eq({"one" => "1", "two" => "2", "three" => "3"}) + end + + it "should raise an error for invalid return type values" do + expect do + @uri.query_values(Integer) + end.to raise_error(ArgumentError) + end + + it "should have the correct array query values" do + expect(@uri.query_values(Array)).to eq([ + ["one", "1"], ["two", "2"], ["three", "3"] + ]) + end + + it "should have a 'null' origin" do + expect(@uri.origin).to eq('null') + end +end + +describe Addressable::URI, "when parsed from '?one=1=uno&two=2=dos'" do + before do + @uri = Addressable::URI.parse("?one=1=uno&two=2=dos") + end + + it "should have the correct query values" do + expect(@uri.query_values).to eq({"one" => "1=uno", "two" => "2=dos"}) + end + + it "should have the correct array query values" do + expect(@uri.query_values(Array)).to eq([ + ["one", "1=uno"], ["two", "2=dos"] + ]) + end +end + +describe Addressable::URI, "when parsed from '?one[two][three]=four'" do + before do + @uri = Addressable::URI.parse("?one[two][three]=four") + end + + it "should have the correct query values" do + expect(@uri.query_values).to eq({"one[two][three]" => "four"}) + end + + it "should have the correct array query values" do + expect(@uri.query_values(Array)).to eq([ + ["one[two][three]", "four"] + ]) + end +end + +describe Addressable::URI, "when parsed from '?one.two.three=four'" do + before do + @uri = Addressable::URI.parse("?one.two.three=four") + end + + it "should have the correct query values" do + expect(@uri.query_values).to eq({ + "one.two.three" => "four" + }) + end + + it "should have the correct array query values" do + expect(@uri.query_values(Array)).to eq([ + ["one.two.three", "four"] + ]) + end +end + +describe Addressable::URI, "when parsed from " + + "'?one[two][three]=four&one[two][five]=six'" do + before do + @uri = Addressable::URI.parse("?one[two][three]=four&one[two][five]=six") + end + + it "should have the correct query values" do + expect(@uri.query_values).to eq({ + "one[two][three]" => "four", "one[two][five]" => "six" + }) + end + + it "should have the correct array query values" do + expect(@uri.query_values(Array)).to eq([ + ["one[two][three]", "four"], ["one[two][five]", "six"] + ]) + end +end + +describe Addressable::URI, "when parsed from " + + "'?one.two.three=four&one.two.five=six'" do + before do + @uri = Addressable::URI.parse("?one.two.three=four&one.two.five=six") + end + + it "should have the correct query values" do + expect(@uri.query_values).to eq({ + "one.two.three" => "four", "one.two.five" => "six" + }) + end + + it "should have the correct array query values" do + expect(@uri.query_values(Array)).to eq([ + ["one.two.three", "four"], ["one.two.five", "six"] + ]) + end +end + +describe Addressable::URI, "when parsed from " + + "'?one=two&one=three'" do + before do + @uri = Addressable::URI.parse( + "?one=two&one=three&one=four" + ) + end + + it "should have correct array query values" do + expect(@uri.query_values(Array)).to eq( + [['one', 'two'], ['one', 'three'], ['one', 'four']] + ) + end + + it "should have correct hash query values" do + skip("This is probably more desirable behavior.") + expect(@uri.query_values(Hash)).to eq( + {'one' => ['two', 'three', 'four']} + ) + end + + it "should handle assignment with keys of mixed type" do + @uri.query_values = @uri.query_values(Hash).merge({:one => 'three'}) + expect(@uri.query_values(Hash)).to eq({'one' => 'three'}) + end +end + +describe Addressable::URI, "when parsed from " + + "'?one[two][three][]=four&one[two][three][]=five'" do + before do + @uri = Addressable::URI.parse( + "?one[two][three][]=four&one[two][three][]=five" + ) + end + + it "should have correct query values" do + expect(@uri.query_values(Hash)).to eq({"one[two][three][]" => "five"}) + end + + it "should have correct array query values" do + expect(@uri.query_values(Array)).to eq([ + ["one[two][three][]", "four"], ["one[two][three][]", "five"] + ]) + end +end + +describe Addressable::URI, "when parsed from " + + "'?one[two][three][0]=four&one[two][three][1]=five'" do + before do + @uri = Addressable::URI.parse( + "?one[two][three][0]=four&one[two][three][1]=five" + ) + end + + it "should have the correct query values" do + expect(@uri.query_values).to eq({ + "one[two][three][0]" => "four", "one[two][three][1]" => "five" + }) + end +end + +describe Addressable::URI, "when parsed from " + + "'?one[two][three][1]=four&one[two][three][0]=five'" do + before do + @uri = Addressable::URI.parse( + "?one[two][three][1]=four&one[two][three][0]=five" + ) + end + + it "should have the correct query values" do + expect(@uri.query_values).to eq({ + "one[two][three][1]" => "four", "one[two][three][0]" => "five" + }) + end +end + +describe Addressable::URI, "when parsed from " + + "'?one[two][three][2]=four&one[two][three][1]=five'" do + before do + @uri = Addressable::URI.parse( + "?one[two][three][2]=four&one[two][three][1]=five" + ) + end + + it "should have the correct query values" do + expect(@uri.query_values).to eq({ + "one[two][three][2]" => "four", "one[two][three][1]" => "five" + }) + end +end + +describe Addressable::URI, "when parsed from " + + "'http://www.詹姆斯.com/'" do + before do + @uri = Addressable::URI.parse("http://www.詹姆斯.com/") + end + + it "should be equivalent to 'http://www.xn--8ws00zhy3a.com/'" do + expect(@uri).to eq( + Addressable::URI.parse("http://www.xn--8ws00zhy3a.com/") + ) + end + + it "should not have domain name encoded during normalization" do + expect(Addressable::URI.normalized_encode(@uri.to_s)).to eq( + "http://www.詹姆斯.com/" + ) + end + + it "should have an origin of 'http://www.xn--8ws00zhy3a.com'" do + expect(@uri.origin).to eq('http://www.xn--8ws00zhy3a.com') + end +end + +describe Addressable::URI, "when parsed from " + + "'http://www.詹姆斯.com/ some spaces /'" do + before do + @uri = Addressable::URI.parse("http://www.詹姆斯.com/ some spaces /") + end + + it "should be equivalent to " + + "'http://www.xn--8ws00zhy3a.com/%20some%20spaces%20/'" do + expect(@uri).to eq( + Addressable::URI.parse( + "http://www.xn--8ws00zhy3a.com/%20some%20spaces%20/") + ) + end + + it "should not have domain name encoded during normalization" do + expect(Addressable::URI.normalized_encode(@uri.to_s)).to eq( + "http://www.詹姆斯.com/%20some%20spaces%20/" + ) + end + + it "should have an origin of 'http://www.xn--8ws00zhy3a.com'" do + expect(@uri.origin).to eq('http://www.xn--8ws00zhy3a.com') + end +end + +describe Addressable::URI, "when parsed from " + + "'http://www.xn--8ws00zhy3a.com/'" do + before do + @uri = Addressable::URI.parse("http://www.xn--8ws00zhy3a.com/") + end + + it "should be displayed as http://www.詹姆斯.com/" do + expect(@uri.display_uri.to_s).to eq("http://www.詹姆斯.com/") + end + + it "should properly force the encoding" do + display_string = @uri.display_uri.to_str + expect(display_string).to eq("http://www.詹姆斯.com/") + if display_string.respond_to?(:encoding) + expect(display_string.encoding.to_s).to eq(Encoding::UTF_8.to_s) + end + end + + it "should have an origin of 'http://www.xn--8ws00zhy3a.com'" do + expect(@uri.origin).to eq('http://www.xn--8ws00zhy3a.com') + end +end + +describe Addressable::URI, "when parsed from " + + "'http://www.詹姆斯.com/atomtests/iri/詹.html'" do + before do + @uri = Addressable::URI.parse("http://www.詹姆斯.com/atomtests/iri/詹.html") + end + + it "should normalize to " + + "http://www.xn--8ws00zhy3a.com/atomtests/iri/%E8%A9%B9.html" do + expect(@uri.normalize.to_s).to eq( + "http://www.xn--8ws00zhy3a.com/atomtests/iri/%E8%A9%B9.html" + ) + expect(@uri.normalize!.to_s).to eq( + "http://www.xn--8ws00zhy3a.com/atomtests/iri/%E8%A9%B9.html" + ) + end +end + +describe Addressable::URI, "when parsed from a percent-encoded IRI" do + before do + @uri = Addressable::URI.parse( + "http://www.%E3%81%BB%E3%82%93%E3%81%A8%E3%81%86%E3%81%AB%E3%81%AA" + + "%E3%81%8C%E3%81%84%E3%82%8F%E3%81%91%E3%81%AE%E3%82%8F%E3%81%8B%E3" + + "%82%89%E3%81%AA%E3%81%84%E3%81%A9%E3%82%81%E3%81%84%E3%82%93%E3%82" + + "%81%E3%81%84%E3%81%AE%E3%82%89%E3%81%B9%E3%82%8B%E3%81%BE%E3%81%A0" + + "%E3%81%AA%E3%81%8C%E3%81%8F%E3%81%97%E3%81%AA%E3%81%84%E3%81%A8%E3" + + "%81%9F%E3%82%8A%E3%81%AA%E3%81%84.w3.mag.keio.ac.jp" + ) + end + + it "should normalize to something sane" do + expect(@uri.normalize.to_s).to eq( + "http://www.xn--n8jaaaaai5bhf7as8fsfk3jnknefdde3f" + + "g11amb5gzdb4wi9bya3kc6lra.w3.mag.keio.ac.jp/" + ) + expect(@uri.normalize!.to_s).to eq( + "http://www.xn--n8jaaaaai5bhf7as8fsfk3jnknefdde3f" + + "g11amb5gzdb4wi9bya3kc6lra.w3.mag.keio.ac.jp/" + ) + end + + it "should have the correct origin" do + expect(@uri.origin).to eq( + "http://www.xn--n8jaaaaai5bhf7as8fsfk3jnknefdde3f" + + "g11amb5gzdb4wi9bya3kc6lra.w3.mag.keio.ac.jp" + ) + end +end + +describe Addressable::URI, "with a base uri of 'http://a/b/c/d;p?q'" do + before do + @uri = Addressable::URI.parse("http://a/b/c/d;p?q") + end + + # Section 5.4.1 of RFC 3986 + it "when joined with 'g:h' should resolve to g:h" do + expect((@uri + "g:h").to_s).to eq("g:h") + expect(Addressable::URI.join(@uri, "g:h").to_s).to eq("g:h") + end + + # Section 5.4.1 of RFC 3986 + it "when joined with 'g' should resolve to http://a/b/c/g" do + expect((@uri + "g").to_s).to eq("http://a/b/c/g") + expect(Addressable::URI.join(@uri.to_s, "g").to_s).to eq("http://a/b/c/g") + end + + # Section 5.4.1 of RFC 3986 + it "when joined with './g' should resolve to http://a/b/c/g" do + expect((@uri + "./g").to_s).to eq("http://a/b/c/g") + expect(Addressable::URI.join(@uri.to_s, "./g").to_s).to eq("http://a/b/c/g") + end + + # Section 5.4.1 of RFC 3986 + it "when joined with 'g/' should resolve to http://a/b/c/g/" do + expect((@uri + "g/").to_s).to eq("http://a/b/c/g/") + expect(Addressable::URI.join(@uri.to_s, "g/").to_s).to eq("http://a/b/c/g/") + end + + # Section 5.4.1 of RFC 3986 + it "when joined with '/g' should resolve to http://a/g" do + expect((@uri + "/g").to_s).to eq("http://a/g") + expect(Addressable::URI.join(@uri.to_s, "/g").to_s).to eq("http://a/g") + end + + # Section 5.4.1 of RFC 3986 + it "when joined with '//g' should resolve to http://g" do + expect((@uri + "//g").to_s).to eq("http://g") + expect(Addressable::URI.join(@uri.to_s, "//g").to_s).to eq("http://g") + end + + # Section 5.4.1 of RFC 3986 + it "when joined with '?y' should resolve to http://a/b/c/d;p?y" do + expect((@uri + "?y").to_s).to eq("http://a/b/c/d;p?y") + expect(Addressable::URI.join(@uri.to_s, "?y").to_s).to eq("http://a/b/c/d;p?y") + end + + # Section 5.4.1 of RFC 3986 + it "when joined with 'g?y' should resolve to http://a/b/c/g?y" do + expect((@uri + "g?y").to_s).to eq("http://a/b/c/g?y") + expect(Addressable::URI.join(@uri.to_s, "g?y").to_s).to eq("http://a/b/c/g?y") + end + + # Section 5.4.1 of RFC 3986 + it "when joined with '#s' should resolve to http://a/b/c/d;p?q#s" do + expect((@uri + "#s").to_s).to eq("http://a/b/c/d;p?q#s") + expect(Addressable::URI.join(@uri.to_s, "#s").to_s).to eq( + "http://a/b/c/d;p?q#s" + ) + end + + # Section 5.4.1 of RFC 3986 + it "when joined with 'g#s' should resolve to http://a/b/c/g#s" do + expect((@uri + "g#s").to_s).to eq("http://a/b/c/g#s") + expect(Addressable::URI.join(@uri.to_s, "g#s").to_s).to eq("http://a/b/c/g#s") + end + + # Section 5.4.1 of RFC 3986 + it "when joined with 'g?y#s' should resolve to http://a/b/c/g?y#s" do + expect((@uri + "g?y#s").to_s).to eq("http://a/b/c/g?y#s") + expect(Addressable::URI.join( + @uri.to_s, "g?y#s").to_s).to eq("http://a/b/c/g?y#s") + end + + # Section 5.4.1 of RFC 3986 + it "when joined with ';x' should resolve to http://a/b/c/;x" do + expect((@uri + ";x").to_s).to eq("http://a/b/c/;x") + expect(Addressable::URI.join(@uri.to_s, ";x").to_s).to eq("http://a/b/c/;x") + end + + # Section 5.4.1 of RFC 3986 + it "when joined with 'g;x' should resolve to http://a/b/c/g;x" do + expect((@uri + "g;x").to_s).to eq("http://a/b/c/g;x") + expect(Addressable::URI.join(@uri.to_s, "g;x").to_s).to eq("http://a/b/c/g;x") + end + + # Section 5.4.1 of RFC 3986 + it "when joined with 'g;x?y#s' should resolve to http://a/b/c/g;x?y#s" do + expect((@uri + "g;x?y#s").to_s).to eq("http://a/b/c/g;x?y#s") + expect(Addressable::URI.join( + @uri.to_s, "g;x?y#s").to_s).to eq("http://a/b/c/g;x?y#s") + end + + # Section 5.4.1 of RFC 3986 + it "when joined with '' should resolve to http://a/b/c/d;p?q" do + expect((@uri + "").to_s).to eq("http://a/b/c/d;p?q") + expect(Addressable::URI.join(@uri.to_s, "").to_s).to eq("http://a/b/c/d;p?q") + end + + # Section 5.4.1 of RFC 3986 + it "when joined with '.' should resolve to http://a/b/c/" do + expect((@uri + ".").to_s).to eq("http://a/b/c/") + expect(Addressable::URI.join(@uri.to_s, ".").to_s).to eq("http://a/b/c/") + end + + # Section 5.4.1 of RFC 3986 + it "when joined with './' should resolve to http://a/b/c/" do + expect((@uri + "./").to_s).to eq("http://a/b/c/") + expect(Addressable::URI.join(@uri.to_s, "./").to_s).to eq("http://a/b/c/") + end + + # Section 5.4.1 of RFC 3986 + it "when joined with '..' should resolve to http://a/b/" do + expect((@uri + "..").to_s).to eq("http://a/b/") + expect(Addressable::URI.join(@uri.to_s, "..").to_s).to eq("http://a/b/") + end + + # Section 5.4.1 of RFC 3986 + it "when joined with '../' should resolve to http://a/b/" do + expect((@uri + "../").to_s).to eq("http://a/b/") + expect(Addressable::URI.join(@uri.to_s, "../").to_s).to eq("http://a/b/") + end + + # Section 5.4.1 of RFC 3986 + it "when joined with '../g' should resolve to http://a/b/g" do + expect((@uri + "../g").to_s).to eq("http://a/b/g") + expect(Addressable::URI.join(@uri.to_s, "../g").to_s).to eq("http://a/b/g") + end + + # Section 5.4.1 of RFC 3986 + it "when joined with '../..' should resolve to http://a/" do + expect((@uri + "../..").to_s).to eq("http://a/") + expect(Addressable::URI.join(@uri.to_s, "../..").to_s).to eq("http://a/") + end + + # Section 5.4.1 of RFC 3986 + it "when joined with '../../' should resolve to http://a/" do + expect((@uri + "../../").to_s).to eq("http://a/") + expect(Addressable::URI.join(@uri.to_s, "../../").to_s).to eq("http://a/") + end + + # Section 5.4.1 of RFC 3986 + it "when joined with '../../g' should resolve to http://a/g" do + expect((@uri + "../../g").to_s).to eq("http://a/g") + expect(Addressable::URI.join(@uri.to_s, "../../g").to_s).to eq("http://a/g") + end + + # Section 5.4.2 of RFC 3986 + it "when joined with '../../../g' should resolve to http://a/g" do + expect((@uri + "../../../g").to_s).to eq("http://a/g") + expect(Addressable::URI.join(@uri.to_s, "../../../g").to_s).to eq("http://a/g") + end + + it "when joined with '../.././../g' should resolve to http://a/g" do + expect((@uri + "../.././../g").to_s).to eq("http://a/g") + expect(Addressable::URI.join(@uri.to_s, "../.././../g").to_s).to eq( + "http://a/g" + ) + end + + # Section 5.4.2 of RFC 3986 + it "when joined with '../../../../g' should resolve to http://a/g" do + expect((@uri + "../../../../g").to_s).to eq("http://a/g") + expect(Addressable::URI.join( + @uri.to_s, "../../../../g").to_s).to eq("http://a/g") + end + + # Section 5.4.2 of RFC 3986 + it "when joined with '/./g' should resolve to http://a/g" do + expect((@uri + "/./g").to_s).to eq("http://a/g") + expect(Addressable::URI.join(@uri.to_s, "/./g").to_s).to eq("http://a/g") + end + + # Section 5.4.2 of RFC 3986 + it "when joined with '/../g' should resolve to http://a/g" do + expect((@uri + "/../g").to_s).to eq("http://a/g") + expect(Addressable::URI.join(@uri.to_s, "/../g").to_s).to eq("http://a/g") + end + + # Section 5.4.2 of RFC 3986 + it "when joined with 'g.' should resolve to http://a/b/c/g." do + expect((@uri + "g.").to_s).to eq("http://a/b/c/g.") + expect(Addressable::URI.join(@uri.to_s, "g.").to_s).to eq("http://a/b/c/g.") + end + + # Section 5.4.2 of RFC 3986 + it "when joined with '.g' should resolve to http://a/b/c/.g" do + expect((@uri + ".g").to_s).to eq("http://a/b/c/.g") + expect(Addressable::URI.join(@uri.to_s, ".g").to_s).to eq("http://a/b/c/.g") + end + + # Section 5.4.2 of RFC 3986 + it "when joined with 'g..' should resolve to http://a/b/c/g.." do + expect((@uri + "g..").to_s).to eq("http://a/b/c/g..") + expect(Addressable::URI.join(@uri.to_s, "g..").to_s).to eq("http://a/b/c/g..") + end + + # Section 5.4.2 of RFC 3986 + it "when joined with '..g' should resolve to http://a/b/c/..g" do + expect((@uri + "..g").to_s).to eq("http://a/b/c/..g") + expect(Addressable::URI.join(@uri.to_s, "..g").to_s).to eq("http://a/b/c/..g") + end + + # Section 5.4.2 of RFC 3986 + it "when joined with './../g' should resolve to http://a/b/g" do + expect((@uri + "./../g").to_s).to eq("http://a/b/g") + expect(Addressable::URI.join(@uri.to_s, "./../g").to_s).to eq("http://a/b/g") + end + + # Section 5.4.2 of RFC 3986 + it "when joined with './g/.' should resolve to http://a/b/c/g/" do + expect((@uri + "./g/.").to_s).to eq("http://a/b/c/g/") + expect(Addressable::URI.join(@uri.to_s, "./g/.").to_s).to eq("http://a/b/c/g/") + end + + # Section 5.4.2 of RFC 3986 + it "when joined with 'g/./h' should resolve to http://a/b/c/g/h" do + expect((@uri + "g/./h").to_s).to eq("http://a/b/c/g/h") + expect(Addressable::URI.join(@uri.to_s, "g/./h").to_s).to eq("http://a/b/c/g/h") + end + + # Section 5.4.2 of RFC 3986 + it "when joined with 'g/../h' should resolve to http://a/b/c/h" do + expect((@uri + "g/../h").to_s).to eq("http://a/b/c/h") + expect(Addressable::URI.join(@uri.to_s, "g/../h").to_s).to eq("http://a/b/c/h") + end + + # Section 5.4.2 of RFC 3986 + it "when joined with 'g;x=1/./y' " + + "should resolve to http://a/b/c/g;x=1/y" do + expect((@uri + "g;x=1/./y").to_s).to eq("http://a/b/c/g;x=1/y") + expect(Addressable::URI.join( + @uri.to_s, "g;x=1/./y").to_s).to eq("http://a/b/c/g;x=1/y") + end + + # Section 5.4.2 of RFC 3986 + it "when joined with 'g;x=1/../y' should resolve to http://a/b/c/y" do + expect((@uri + "g;x=1/../y").to_s).to eq("http://a/b/c/y") + expect(Addressable::URI.join( + @uri.to_s, "g;x=1/../y").to_s).to eq("http://a/b/c/y") + end + + # Section 5.4.2 of RFC 3986 + it "when joined with 'g?y/./x' " + + "should resolve to http://a/b/c/g?y/./x" do + expect((@uri + "g?y/./x").to_s).to eq("http://a/b/c/g?y/./x") + expect(Addressable::URI.join( + @uri.to_s, "g?y/./x").to_s).to eq("http://a/b/c/g?y/./x") + end + + # Section 5.4.2 of RFC 3986 + it "when joined with 'g?y/../x' " + + "should resolve to http://a/b/c/g?y/../x" do + expect((@uri + "g?y/../x").to_s).to eq("http://a/b/c/g?y/../x") + expect(Addressable::URI.join( + @uri.to_s, "g?y/../x").to_s).to eq("http://a/b/c/g?y/../x") + end + + # Section 5.4.2 of RFC 3986 + it "when joined with 'g#s/./x' " + + "should resolve to http://a/b/c/g#s/./x" do + expect((@uri + "g#s/./x").to_s).to eq("http://a/b/c/g#s/./x") + expect(Addressable::URI.join( + @uri.to_s, "g#s/./x").to_s).to eq("http://a/b/c/g#s/./x") + end + + # Section 5.4.2 of RFC 3986 + it "when joined with 'g#s/../x' " + + "should resolve to http://a/b/c/g#s/../x" do + expect((@uri + "g#s/../x").to_s).to eq("http://a/b/c/g#s/../x") + expect(Addressable::URI.join( + @uri.to_s, "g#s/../x").to_s).to eq("http://a/b/c/g#s/../x") + end + + # Section 5.4.2 of RFC 3986 + it "when joined with 'http:g' should resolve to http:g" do + expect((@uri + "http:g").to_s).to eq("http:g") + expect(Addressable::URI.join(@uri.to_s, "http:g").to_s).to eq("http:g") + end + + # Edge case to be sure + it "when joined with '//example.com/' should " + + "resolve to http://example.com/" do + expect((@uri + "//example.com/").to_s).to eq("http://example.com/") + expect(Addressable::URI.join( + @uri.to_s, "//example.com/").to_s).to eq("http://example.com/") + end + + it "when joined with a bogus object a TypeError should be raised" do + expect do + Addressable::URI.join(@uri, 42) + end.to raise_error(TypeError) + end +end + +describe Addressable::URI, "when converting the path " + + "'relative/path/to/something'" do + before do + @path = 'relative/path/to/something' + end + + it "should convert to " + + "\'relative/path/to/something\'" do + @uri = Addressable::URI.convert_path(@path) + expect(@uri.to_str).to eq("relative/path/to/something") + end + + it "should join with an absolute file path correctly" do + @base = Addressable::URI.convert_path("/absolute/path/") + @uri = Addressable::URI.convert_path(@path) + expect((@base + @uri).to_str).to eq( + "file:///absolute/path/relative/path/to/something" + ) + end +end + +describe Addressable::URI, "when converting a bogus path" do + it "should raise a TypeError" do + expect do + Addressable::URI.convert_path(42) + end.to raise_error(TypeError) + end +end + +describe Addressable::URI, "when given a UNIX root directory" do + before do + @path = "/" + end + + it "should convert to \'file:///\'" do + @uri = Addressable::URI.convert_path(@path) + expect(@uri.to_str).to eq("file:///") + end + + it "should have an origin of 'file://'" do + @uri = Addressable::URI.convert_path(@path) + expect(@uri.origin).to eq('file://') + end +end + +describe Addressable::URI, "when given a Windows root directory" do + before do + @path = "C:\\" + end + + it "should convert to \'file:///c:/\'" do + @uri = Addressable::URI.convert_path(@path) + expect(@uri.to_str).to eq("file:///c:/") + end + + it "should have an origin of 'file://'" do + @uri = Addressable::URI.convert_path(@path) + expect(@uri.origin).to eq('file://') + end +end + +describe Addressable::URI, "when given the path '/one/two/'" do + before do + @path = '/one/two/' + end + + it "should convert to " + + "\'file:///one/two/\'" do + @uri = Addressable::URI.convert_path(@path) + expect(@uri.to_str).to eq("file:///one/two/") + end + + it "should have an origin of 'file://'" do + @uri = Addressable::URI.convert_path(@path) + expect(@uri.origin).to eq('file://') + end +end + +describe Addressable::URI, "when given the tld " do + it "'uk' should have a tld of 'uk'" do + uri = Addressable::URI.parse("http://example.com") + uri.tld = "uk" + + expect(uri.tld).to eq("uk") + end + + context "which " do + let (:uri) { Addressable::URI.parse("http://www.comrade.net/path/to/source/") } + + it "contains a subdomain" do + uri.tld = "co.uk" + + expect(uri.to_s).to eq("http://www.comrade.co.uk/path/to/source/") + end + + it "is part of the domain" do + uri.tld = "com" + + expect(uri.to_s).to eq("http://www.comrade.com/path/to/source/") + end + end +end + +describe Addressable::URI, "when given the path " + + "'c:\\windows\\My Documents 100%20\\foo.txt'" do + before do + @path = "c:\\windows\\My Documents 100%20\\foo.txt" + end + + it "should convert to " + + "\'file:///c:/windows/My%20Documents%20100%20/foo.txt\'" do + @uri = Addressable::URI.convert_path(@path) + expect(@uri.to_str).to eq("file:///c:/windows/My%20Documents%20100%20/foo.txt") + end + + it "should have an origin of 'file://'" do + @uri = Addressable::URI.convert_path(@path) + expect(@uri.origin).to eq('file://') + end +end + +describe Addressable::URI, "when given the path " + + "'file://c:\\windows\\My Documents 100%20\\foo.txt'" do + before do + @path = "file://c:\\windows\\My Documents 100%20\\foo.txt" + end + + it "should convert to " + + "\'file:///c:/windows/My%20Documents%20100%20/foo.txt\'" do + @uri = Addressable::URI.convert_path(@path) + expect(@uri.to_str).to eq("file:///c:/windows/My%20Documents%20100%20/foo.txt") + end + + it "should have an origin of 'file://'" do + @uri = Addressable::URI.convert_path(@path) + expect(@uri.origin).to eq('file://') + end +end + +describe Addressable::URI, "when given the path " + + "'file:c:\\windows\\My Documents 100%20\\foo.txt'" do + before do + @path = "file:c:\\windows\\My Documents 100%20\\foo.txt" + end + + it "should convert to " + + "\'file:///c:/windows/My%20Documents%20100%20/foo.txt\'" do + @uri = Addressable::URI.convert_path(@path) + expect(@uri.to_str).to eq("file:///c:/windows/My%20Documents%20100%20/foo.txt") + end + + it "should have an origin of 'file://'" do + @uri = Addressable::URI.convert_path(@path) + expect(@uri.origin).to eq('file://') + end +end + +describe Addressable::URI, "when given the path " + + "'file:/c:\\windows\\My Documents 100%20\\foo.txt'" do + before do + @path = "file:/c:\\windows\\My Documents 100%20\\foo.txt" + end + + it "should convert to " + + "\'file:///c:/windows/My%20Documents%20100%20/foo.txt\'" do + @uri = Addressable::URI.convert_path(@path) + expect(@uri.to_str).to eq("file:///c:/windows/My%20Documents%20100%20/foo.txt") + end + + it "should have an origin of 'file://'" do + @uri = Addressable::URI.convert_path(@path) + expect(@uri.origin).to eq('file://') + end +end + +describe Addressable::URI, "when given the path " + + "'file:///c|/windows/My%20Documents%20100%20/foo.txt'" do + before do + @path = "file:///c|/windows/My%20Documents%20100%20/foo.txt" + end + + it "should convert to " + + "\'file:///c:/windows/My%20Documents%20100%20/foo.txt\'" do + @uri = Addressable::URI.convert_path(@path) + expect(@uri.to_str).to eq("file:///c:/windows/My%20Documents%20100%20/foo.txt") + end + + it "should have an origin of 'file://'" do + @uri = Addressable::URI.convert_path(@path) + expect(@uri.origin).to eq('file://') + end +end + +describe Addressable::URI, "when given an http protocol URI" do + before do + @path = "http://example.com/" + end + + it "should not do any conversion at all" do + @uri = Addressable::URI.convert_path(@path) + expect(@uri.to_str).to eq("http://example.com/") + end +end + +class SuperString + def initialize(string) + @string = string.to_s + end + + def to_str + return @string + end +end + +describe Addressable::URI, "when parsing a non-String object" do + it "should correctly parse anything with a 'to_str' method" do + Addressable::URI.parse(SuperString.new(42)) + end + + it "should raise a TypeError for objects than cannot be converted" do + expect do + Addressable::URI.parse(42) + end.to raise_error(TypeError) + end + + it "should correctly parse heuristically anything with a 'to_str' method" do + Addressable::URI.heuristic_parse(SuperString.new(42)) + end + + it "should raise a TypeError for objects than cannot be converted" do + expect do + Addressable::URI.heuristic_parse(42) + end.to raise_error(TypeError) + end +end + +describe Addressable::URI, "when form encoding a hash" do + it "should result in correct percent encoded sequence" do + expect(Addressable::URI.form_encode( + [["&one", "/1"], ["=two", "?2"], [":three", "#3"]] + )).to eq("%26one=%2F1&%3Dtwo=%3F2&%3Athree=%233") + end + + it "should result in correct percent encoded sequence" do + expect(Addressable::URI.form_encode( + {"q" => "one two three"} + )).to eq("q=one+two+three") + end + + it "should result in correct percent encoded sequence" do + expect(Addressable::URI.form_encode( + {"key" => nil} + )).to eq("key=") + end + + it "should result in correct percent encoded sequence" do + expect(Addressable::URI.form_encode( + {"q" => ["one", "two", "three"]} + )).to eq("q=one&q=two&q=three") + end + + it "should result in correctly encoded newlines" do + expect(Addressable::URI.form_encode( + {"text" => "one\ntwo\rthree\r\nfour\n\r"} + )).to eq("text=one%0D%0Atwo%0D%0Athree%0D%0Afour%0D%0A%0D%0A") + end + + it "should result in a sorted percent encoded sequence" do + expect(Addressable::URI.form_encode( + [["a", "1"], ["dup", "3"], ["dup", "2"]], true + )).to eq("a=1&dup=2&dup=3") + end +end + +describe Addressable::URI, "when form encoding a non-Array object" do + it "should raise a TypeError for objects than cannot be converted" do + expect do + Addressable::URI.form_encode(42) + end.to raise_error(TypeError) + end +end + +# See https://tools.ietf.org/html/rfc6749#appendix-B +describe Addressable::URI, "when form encoding the example value from OAuth 2" do + it "should result in correct values" do + expect(Addressable::URI.form_encode( + {"value" => " %&+£€"} + )).to eq("value=+%25%26%2B%C2%A3%E2%82%AC") + end +end + +# See https://tools.ietf.org/html/rfc6749#appendix-B +describe Addressable::URI, "when form unencoding the example value from OAuth 2" do + it "should result in correct values" do + expect(Addressable::URI.form_unencode( + "value=+%25%26%2B%C2%A3%E2%82%AC" + )).to eq([["value", " %&+£€"]]) + end +end + +describe Addressable::URI, "when form unencoding a string" do + it "should result in correct values" do + expect(Addressable::URI.form_unencode( + "%26one=%2F1&%3Dtwo=%3F2&%3Athree=%233" + )).to eq([["&one", "/1"], ["=two", "?2"], [":three", "#3"]]) + end + + it "should result in correct values" do + expect(Addressable::URI.form_unencode( + "q=one+two+three" + )).to eq([["q", "one two three"]]) + end + + it "should result in correct values" do + expect(Addressable::URI.form_unencode( + "text=one%0D%0Atwo%0D%0Athree%0D%0Afour%0D%0A%0D%0A" + )).to eq([["text", "one\ntwo\nthree\nfour\n\n"]]) + end + + it "should result in correct values" do + expect(Addressable::URI.form_unencode( + "a=1&dup=2&dup=3" + )).to eq([["a", "1"], ["dup", "2"], ["dup", "3"]]) + end + + it "should result in correct values" do + expect(Addressable::URI.form_unencode( + "key" + )).to eq([["key", nil]]) + end + + it "should result in correct values" do + expect(Addressable::URI.form_unencode("GivenName=Ren%C3%A9")).to eq( + [["GivenName", "René"]] + ) + end +end + +describe Addressable::URI, "when form unencoding a non-String object" do + it "should correctly parse anything with a 'to_str' method" do + Addressable::URI.form_unencode(SuperString.new(42)) + end + + it "should raise a TypeError for objects than cannot be converted" do + expect do + Addressable::URI.form_unencode(42) + end.to raise_error(TypeError) + end +end + +describe Addressable::URI, "when normalizing a non-String object" do + it "should correctly parse anything with a 'to_str' method" do + Addressable::URI.normalize_component(SuperString.new(42)) + end + + it "should raise a TypeError for objects than cannot be converted" do + expect do + Addressable::URI.normalize_component(42) + end.to raise_error(TypeError) + end + + it "should raise a TypeError for objects than cannot be converted" do + expect do + Addressable::URI.normalize_component("component", 42) + end.to raise_error(TypeError) + end +end + +describe Addressable::URI, "when normalizing a path with an encoded slash" do + it "should result in correct percent encoded sequence" do + expect(Addressable::URI.parse("/path%2Fsegment/").normalize.path).to eq( + "/path%2Fsegment/" + ) + end +end + +describe Addressable::URI, "when normalizing a path with special unicode" do + it "does not stop at or ignore null bytes" do + expect(Addressable::URI.parse("/path%00segment/").normalize.path).to eq( + "/path%00segment/" + ) + end + + it "does apply NFC unicode normalization" do + expect(Addressable::URI.parse("/%E2%84%A6").normalize.path).to eq( + "/%CE%A9" + ) + end + + it "does not apply NFKC unicode normalization" do + expect(Addressable::URI.parse("/%C2%AF%C2%A0").normalize.path).to eq( + "/%C2%AF%C2%A0" + ) + end +end + +describe Addressable::URI, "when normalizing a partially encoded string" do + it "should result in correct percent encoded sequence" do + expect(Addressable::URI.normalize_component( + "partially % encoded%21" + )).to eq("partially%20%25%20encoded!") + end + + it "should result in correct percent encoded sequence" do + expect(Addressable::URI.normalize_component( + "partially %25 encoded!" + )).to eq("partially%20%25%20encoded!") + end +end + +describe Addressable::URI, "when normalizing a unicode sequence" do + it "should result in correct percent encoded sequence" do + expect(Addressable::URI.normalize_component( + "/C%CC%A7" + )).to eq("/%C3%87") + end + + it "should result in correct percent encoded sequence" do + expect(Addressable::URI.normalize_component( + "/%C3%87" + )).to eq("/%C3%87") + end +end + +describe Addressable::URI, "when normalizing a multibyte string" do + it "should result in correct percent encoded sequence" do + expect(Addressable::URI.normalize_component("günther")).to eq( + "g%C3%BCnther" + ) + end + + it "should result in correct percent encoded sequence" do + expect(Addressable::URI.normalize_component("g%C3%BCnther")).to eq( + "g%C3%BCnther" + ) + end +end + +describe Addressable::URI, "when normalizing a string but leaving some characters encoded" do + it "should result in correct percent encoded sequence" do + expect(Addressable::URI.normalize_component("%58X%59Y%5AZ", "0-9a-zXY", "Y")).to eq( + "XX%59Y%5A%5A" + ) + end + + it "should not modify the character class" do + character_class = "0-9a-zXY" + + character_class_copy = character_class.dup + + Addressable::URI.normalize_component("%58X%59Y%5AZ", character_class, "Y") + + expect(character_class).to eq(character_class_copy) + end +end + +describe Addressable::URI, "when encoding IP literals" do + it "should work for IPv4" do + input = "http://127.0.0.1/" + expect(Addressable::URI.encode(input)).to eq(input) + end + + it "should work for IPv6" do + input = "http://[fe80::200:f8ff:fe21:67cf]/" + expect(Addressable::URI.encode(input)).to eq(input) + end +end + +describe Addressable::URI, "when encoding a string with existing encodings to upcase" do + it "should result in correct percent encoded sequence" do + expect(Addressable::URI.encode_component("JK%4c", "0-9A-IKM-Za-z%", "L")).to eq("%4AK%4C") + end +end + +describe Addressable::URI, "when encoding a multibyte string" do + it "should result in correct percent encoded sequence" do + expect(Addressable::URI.encode_component("günther")).to eq("g%C3%BCnther") + end + + it "should result in correct percent encoded sequence" do + expect(Addressable::URI.encode_component( + "günther", /[^a-zA-Z0-9\:\/\?\#\[\]\@\!\$\&\'\(\)\*\+\,\;\=\-\.\_\~]/ + )).to eq("g%C3%BCnther") + end +end + +describe Addressable::URI, "when form encoding a multibyte string" do + it "should result in correct percent encoded sequence" do + expect(Addressable::URI.form_encode({"GivenName" => "René"})).to eq( + "GivenName=Ren%C3%A9" + ) + end +end + +describe Addressable::URI, "when encoding a string with ASCII chars 0-15" do + it "should result in correct percent encoded sequence" do + expect(Addressable::URI.encode_component("one\ntwo")).to eq("one%0Atwo") + end + + it "should result in correct percent encoded sequence" do + expect(Addressable::URI.encode_component( + "one\ntwo", /[^a-zA-Z0-9\:\/\?\#\[\]\@\!\$\&\'\(\)\*\+\,\;\=\-\.\_\~]/ + )).to eq("one%0Atwo") + end +end + +describe Addressable::URI, "when unencoding a multibyte string" do + it "should result in correct percent encoded sequence" do + expect(Addressable::URI.unencode_component("g%C3%BCnther")).to eq("günther") + end + + it "should consistently use UTF-8 internally" do + expect(Addressable::URI.unencode_component("ski=%BA%DAɫ")).to eq("ski=\xBA\xDAɫ") + end + + it "should not fail with UTF-8 incompatible string" do + url = "/M%E9/\xE9?p=\xFC".b + expect(Addressable::URI.unencode_component(url)).to eq("/M\xE9/\xE9?p=\xFC") + end + + it "should result in correct percent encoded sequence as a URI" do + expect(Addressable::URI.unencode( + "/path?g%C3%BCnther", ::Addressable::URI + )).to eq(Addressable::URI.new( + :path => "/path", :query => "günther" + )) + end +end + +describe Addressable::URI, "when partially unencoding a string" do + it "should unencode all characters by default" do + expect(Addressable::URI.unencode('%%25~%7e+%2b', String)).to eq('%%~~++') + end + + it "should unencode characters not in leave_encoded" do + expect(Addressable::URI.unencode('%%25~%7e+%2b', String, '~')).to eq('%%~%7e++') + end + + it "should leave characters in leave_encoded alone" do + expect(Addressable::URI.unencode('%%25~%7e+%2b', String, '%~+')).to eq('%%25~%7e+%2b') + end +end + +describe Addressable::URI, "when unencoding a bogus object" do + it "should raise a TypeError" do + expect do + Addressable::URI.unencode_component(42) + end.to raise_error(TypeError) + end + + it "should raise a TypeError" do + expect do + Addressable::URI.unencode("/path?g%C3%BCnther", Integer) + end.to raise_error(TypeError) + end +end + +describe Addressable::URI, "when encoding a bogus object" do + it "should raise a TypeError" do + expect do + Addressable::URI.encode(Object.new) + end.to raise_error(TypeError) + end + + it "should raise a TypeError" do + expect do + Addressable::URI.normalized_encode(Object.new) + end.to raise_error(TypeError) + end + + it "should raise a TypeError" do + expect do + Addressable::URI.encode_component("günther", Object.new) + end.to raise_error(TypeError) + end + + it "should raise a TypeError" do + expect do + Addressable::URI.encode_component(Object.new) + end.to raise_error(TypeError) + end +end + +describe Addressable::URI, "when given the input " + + "'http://example.com/'" do + before do + @input = "http://example.com/" + end + + it "should heuristically parse to 'http://example.com/'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.to_s).to eq("http://example.com/") + end + + it "should not raise error when frozen" do + expect do + Addressable::URI.heuristic_parse(@input).freeze.to_s + end.not_to raise_error + end +end + +describe Addressable::URI, "when given the input " + + "'https://example.com/'" do + before do + @input = "https://example.com/" + end + + it "should heuristically parse to 'https://example.com/'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.to_s).to eq("https://example.com/") + end +end + +describe Addressable::URI, "when given the input " + + "'http:example.com/'" do + before do + @input = "http:example.com/" + end + + it "should heuristically parse to 'http://example.com/'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.to_s).to eq("http://example.com/") + end + + it "should heuristically parse to 'http://example.com/' " + + "even with a scheme hint of 'ftp'" do + @uri = Addressable::URI.heuristic_parse(@input, {:scheme => 'ftp'}) + expect(@uri.to_s).to eq("http://example.com/") + end +end + +describe Addressable::URI, "when given the input " + + "'https:example.com/'" do + before do + @input = "https:example.com/" + end + + it "should heuristically parse to 'https://example.com/'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.to_s).to eq("https://example.com/") + end + + it "should heuristically parse to 'https://example.com/' " + + "even with a scheme hint of 'ftp'" do + @uri = Addressable::URI.heuristic_parse(@input, {:scheme => 'ftp'}) + expect(@uri.to_s).to eq("https://example.com/") + end +end + +describe Addressable::URI, "when given the input " + + "'http://example.com/example.com/'" do + before do + @input = "http://example.com/example.com/" + end + + it "should heuristically parse to 'http://example.com/example.com/'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.to_s).to eq("http://example.com/example.com/") + end +end + +describe Addressable::URI, "when given the input " + + "'http://prefix\\.example.com/'" do + before do + @input = "http://prefix\\.example.com/" + end + + it "should heuristically parse to 'http://prefix/.example.com/'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.authority).to eq("prefix") + expect(@uri.to_s).to eq("http://prefix/.example.com/") + end + + it "should heuristically parse to 'http://prefix/.example.com/' " + + "even with a scheme hint of 'ftp'" do + @uri = Addressable::URI.heuristic_parse(@input, {:scheme => 'ftp'}) + expect(@uri.to_s).to eq("http://prefix/.example.com/") + end +end + +describe Addressable::URI, "when given the input " + + "'http://p:\\/'" do + before do + @input = "http://p:\\/" + end + + it "should heuristically parse to 'http://p//'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.authority).to eq("p") + expect(@uri.to_s).to eq("http://p//") + end + + it "should heuristically parse to 'http://p//' " + + "even with a scheme hint of 'ftp'" do + @uri = Addressable::URI.heuristic_parse(@input, {:scheme => 'ftp'}) + expect(@uri.to_s).to eq("http://p//") + end +end + +describe Addressable::URI, "when given the input " + + "'http://p://'" do + before do + @input = "http://p://" + end + + it "should heuristically parse to 'http://p//'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.authority).to eq("p") + expect(@uri.to_s).to eq("http://p//") + end + + it "should heuristically parse to 'http://p//' " + + "even with a scheme hint of 'ftp'" do + @uri = Addressable::URI.heuristic_parse(@input, {:scheme => 'ftp'}) + expect(@uri.to_s).to eq("http://p//") + end +end + +describe Addressable::URI, "when given the input " + + "'http://p://p'" do + before do + @input = "http://p://p" + end + + it "should heuristically parse to 'http://p//p'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.authority).to eq("p") + expect(@uri.to_s).to eq("http://p//p") + end + + it "should heuristically parse to 'http://p//p' " + + "even with a scheme hint of 'ftp'" do + @uri = Addressable::URI.heuristic_parse(@input, {:scheme => 'ftp'}) + expect(@uri.to_s).to eq("http://p//p") + end +end + +describe Addressable::URI, "when given the input " + + "'http://prefix .example.com/'" do + before do + @input = "http://prefix .example.com/" + end + + # Justification here being that no browser actually tries to resolve this. + # They all treat this as a web search. + it "should heuristically parse to 'http://prefix%20.example.com/'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.authority).to eq("prefix%20.example.com") + expect(@uri.to_s).to eq("http://prefix%20.example.com/") + end + + it "should heuristically parse to 'http://prefix%20.example.com/' " + + "even with a scheme hint of 'ftp'" do + @uri = Addressable::URI.heuristic_parse(@input, {:scheme => 'ftp'}) + expect(@uri.to_s).to eq("http://prefix%20.example.com/") + end +end + +describe Addressable::URI, "when given the input " + + "' http://www.example.com/ '" do + before do + @input = " http://www.example.com/ " + end + + it "should heuristically parse to 'http://prefix%20.example.com/'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.scheme).to eq("http") + expect(@uri.path).to eq("/") + expect(@uri.to_s).to eq("http://www.example.com/") + end +end + +describe Addressable::URI, "when given the input " + + "'http://prefix%2F.example.com/'" do + before do + @input = "http://prefix%2F.example.com/" + end + + it "should heuristically parse to 'http://prefix%2F.example.com/'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.authority).to eq("prefix%2F.example.com") + expect(@uri.to_s).to eq("http://prefix%2F.example.com/") + end + + it "should heuristically parse to 'http://prefix%2F.example.com/' " + + "even with a scheme hint of 'ftp'" do + @uri = Addressable::URI.heuristic_parse(@input, {:scheme => 'ftp'}) + expect(@uri.to_s).to eq("http://prefix%2F.example.com/") + end +end + +describe Addressable::URI, "when given the input " + + "'/path/to/resource'" do + before do + @input = "/path/to/resource" + end + + it "should heuristically parse to '/path/to/resource'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.to_s).to eq("/path/to/resource") + end +end + +describe Addressable::URI, "when given the input " + + "'relative/path/to/resource'" do + before do + @input = "relative/path/to/resource" + end + + it "should heuristically parse to 'relative/path/to/resource'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.to_s).to eq("relative/path/to/resource") + end +end + +describe Addressable::URI, "when given the input " + + "'example.com'" do + before do + @input = "example.com" + end + + it "should heuristically parse to 'http://example.com'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.to_s).to eq("http://example.com") + end +end + +describe Addressable::URI, "when given the input " + + "'example.com' and a scheme hint of 'ftp'" do + before do + @input = "example.com" + @hints = {:scheme => 'ftp'} + end + + it "should heuristically parse to 'http://example.com'" do + @uri = Addressable::URI.heuristic_parse(@input, @hints) + expect(@uri.to_s).to eq("ftp://example.com") + end +end + +describe Addressable::URI, "when given the input " + + "'example.com:21' and a scheme hint of 'ftp'" do + before do + @input = "example.com:21" + @hints = {:scheme => 'ftp'} + end + + it "should heuristically parse to 'http://example.com:21'" do + @uri = Addressable::URI.heuristic_parse(@input, @hints) + expect(@uri.to_s).to eq("ftp://example.com:21") + end +end + +describe Addressable::URI, "when given the input " + + "'example.com/path/to/resource'" do + before do + @input = "example.com/path/to/resource" + end + + it "should heuristically parse to 'http://example.com/path/to/resource'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.to_s).to eq("http://example.com/path/to/resource") + end +end + +describe Addressable::URI, "when given the input " + + "'http:///example.com'" do + before do + @input = "http:///example.com" + end + + it "should heuristically parse to 'http://example.com'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.to_s).to eq("http://example.com") + end +end + +describe Addressable::URI, "when given the input which "\ + "start with digits and has specified port" do + before do + @input = "7777.example.org:8089" + end + + it "should heuristically parse to 'http://7777.example.org:8089'" do + uri = Addressable::URI.heuristic_parse(@input) + expect(uri.to_s).to eq("http://7777.example.org:8089") + end +end + +describe Addressable::URI, "when given the input " + + "'feed:///example.com'" do + before do + @input = "feed:///example.com" + end + + it "should heuristically parse to 'feed://example.com'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.to_s).to eq("feed://example.com") + end +end + +describe Addressable::URI, "when given the input " + + "'file://localhost/path/to/resource/'" do + before do + @input = "file://localhost/path/to/resource/" + end + + it "should heuristically parse to 'file:///path/to/resource/'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.to_s).to eq("file:///path/to/resource/") + end +end + +describe Addressable::URI, "when given the input " + + "'file://path/to/resource/'" do + before do + @input = "file://path/to/resource/" + end + + it "should heuristically parse to 'file:///path/to/resource/'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.to_s).to eq("file:///path/to/resource/") + end +end + +describe Addressable::URI, "when given the input " + + "'file://///path/to/resource/'" do + before do + @input = "file:///////path/to/resource/" + end + + it "should heuristically parse to 'file:////path/to/resource/'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.to_s).to eq("file:////path/to/resource/") + end +end + +describe Addressable::URI, "when given the input " + + "'feed://http://example.com'" do + before do + @input = "feed://http://example.com" + end + + it "should heuristically parse to 'feed:http://example.com'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.to_s).to eq("feed:http://example.com") + end +end + +describe Addressable::URI, "when given the input " + + "::URI.parse('http://example.com')" do + before do + @input = ::URI.parse('http://example.com') + end + + it "should heuristically parse to 'http://example.com'" do + @uri = Addressable::URI.heuristic_parse(@input) + expect(@uri.to_s).to eq("http://example.com") + end +end + +describe Addressable::URI, "when given the input: 'user@domain.com'" do + before do + @input = "user@domain.com" + end + + context "for heuristic parse" do + it "should remain 'mailto:user@domain.com'" do + uri = Addressable::URI.heuristic_parse("mailto:#{@input}") + expect(uri.to_s).to eq("mailto:user@domain.com") + end + + it "should have a scheme of 'mailto'" do + uri = Addressable::URI.heuristic_parse(@input) + expect(uri.to_s).to eq("mailto:user@domain.com") + expect(uri.scheme).to eq("mailto") + end + + it "should remain 'acct:user@domain.com'" do + uri = Addressable::URI.heuristic_parse("acct:#{@input}") + expect(uri.to_s).to eq("acct:user@domain.com") + end + + context "HTTP" do + before do + @uri = Addressable::URI.heuristic_parse("http://#{@input}/") + end + + it "should remain 'http://user@domain.com/'" do + expect(@uri.to_s).to eq("http://user@domain.com/") + end + + it "should have the username 'user' for HTTP basic authentication" do + expect(@uri.user).to eq("user") + end + end + end +end + +describe Addressable::URI, "when assigning query values" do + before do + @uri = Addressable::URI.new + end + + it "should correctly assign {:a => 'a', :b => ['c', 'd', 'e']}" do + @uri.query_values = {:a => "a", :b => ["c", "d", "e"]} + expect(@uri.query).to eq("a=a&b=c&b=d&b=e") + end + + it "should raise an error attempting to assign {'a' => {'b' => ['c']}}" do + expect do + @uri.query_values = { 'a' => {'b' => ['c'] } } + end.to raise_error(TypeError) + end + + it "should raise an error attempting to assign " + + "{:b => '2', :a => {:c => '1'}}" do + expect do + @uri.query_values = {:b => '2', :a => {:c => '1'}} + end.to raise_error(TypeError) + end + + it "should raise an error attempting to assign " + + "{:a => 'a', :b => [{:c => 'c', :d => 'd'}, " + + "{:e => 'e', :f => 'f'}]}" do + expect do + @uri.query_values = { + :a => "a", :b => [{:c => "c", :d => "d"}, {:e => "e", :f => "f"}] + } + end.to raise_error(TypeError) + end + + it "should raise an error attempting to assign " + + "{:a => 'a', :b => [{:c => true, :d => 'd'}, " + + "{:e => 'e', :f => 'f'}]}" do + expect do + @uri.query_values = { + :a => 'a', :b => [{:c => true, :d => 'd'}, {:e => 'e', :f => 'f'}] + } + end.to raise_error(TypeError) + end + + it "should raise an error attempting to assign " + + "{:a => 'a', :b => {:c => true, :d => 'd'}}" do + expect do + @uri.query_values = { + :a => 'a', :b => {:c => true, :d => 'd'} + } + end.to raise_error(TypeError) + end + + it "should raise an error attempting to assign " + + "{:a => 'a', :b => {:c => true, :d => 'd'}}" do + expect do + @uri.query_values = { + :a => 'a', :b => {:c => true, :d => 'd'} + } + end.to raise_error(TypeError) + end + + it "should correctly assign {:a => 1, :b => 1.5}" do + @uri.query_values = { :a => 1, :b => 1.5 } + expect(@uri.query).to eq("a=1&b=1.5") + end + + it "should raise an error attempting to assign " + + "{:z => 1, :f => [2, {999.1 => [3,'4']}, ['h', 'i']], " + + ":a => {:b => ['c', 'd'], :e => true, :y => 0.5}}" do + expect do + @uri.query_values = { + :z => 1, + :f => [ 2, {999.1 => [3,'4']}, ['h', 'i'] ], + :a => { :b => ['c', 'd'], :e => true, :y => 0.5 } + } + end.to raise_error(TypeError) + end + + it "should correctly assign {}" do + @uri.query_values = {} + expect(@uri.query).to eq('') + end + + it "should correctly assign nil" do + @uri.query_values = nil + expect(@uri.query).to eq(nil) + end + + it "should correctly sort {'ab' => 'c', :ab => 'a', :a => 'x'}" do + @uri.query_values = {'ab' => 'c', :ab => 'a', :a => 'x'} + expect(@uri.query).to eq("a=x&ab=a&ab=c") + end + + it "should correctly assign " + + "[['b', 'c'], ['b', 'a'], ['a', 'a']]" do + # Order can be guaranteed in this format, so preserve it. + @uri.query_values = [['b', 'c'], ['b', 'a'], ['a', 'a']] + expect(@uri.query).to eq("b=c&b=a&a=a") + end + + it "should preserve query string order" do + query_string = (('a'..'z').to_a.reverse.map { |e| "#{e}=#{e}" }).join("&") + @uri.query = query_string + original_uri = @uri.to_s + @uri.query_values = @uri.query_values(Array) + expect(@uri.to_s).to eq(original_uri) + end + + describe 'when a hash with mixed types is assigned to query_values' do + it 'should not raise an error' do + skip 'Issue #94' + expect { subject.query_values = { "page" => "1", :page => 2 } }.to_not raise_error + end + end +end + +describe Addressable::URI, "when assigning path values" do + before do + @uri = Addressable::URI.new + end + + it "should correctly assign paths containing colons" do + @uri.path = "acct:bob@sporkmonger.com" + expect(@uri.path).to eq("acct:bob@sporkmonger.com") + expect(@uri.normalize.to_str).to eq("acct%2Fbob@sporkmonger.com") + expect { @uri.to_s }.to raise_error( + Addressable::URI::InvalidURIError + ) + end + + it "should correctly assign paths containing colons" do + @uri.path = "/acct:bob@sporkmonger.com" + @uri.authority = "example.com" + expect(@uri.normalize.to_str).to eq("//example.com/acct:bob@sporkmonger.com") + end + + it "should correctly assign paths containing colons" do + @uri.path = "acct:bob@sporkmonger.com" + @uri.scheme = "something" + expect(@uri.normalize.to_str).to eq("something:acct:bob@sporkmonger.com") + end + + it "should not allow relative paths to be assigned on absolute URIs" do + expect do + @uri.scheme = "http" + @uri.host = "example.com" + @uri.path = "acct:bob@sporkmonger.com" + end.to raise_error(Addressable::URI::InvalidURIError) + end + + it "should not allow relative paths to be assigned on absolute URIs" do + expect do + @uri.path = "acct:bob@sporkmonger.com" + @uri.scheme = "http" + @uri.host = "example.com" + end.to raise_error(Addressable::URI::InvalidURIError) + end + + it "should not allow relative paths to be assigned on absolute URIs" do + expect do + @uri.path = "uuid:0b3ecf60-3f93-11df-a9c3-001f5bfffe12" + @uri.scheme = "urn" + end.not_to raise_error + end +end + +describe Addressable::URI, "when initializing a subclass of Addressable::URI" do + before do + @uri = Class.new(Addressable::URI).new + end + + it "should have the same class after being parsed" do + expect(@uri.class).to eq(Addressable::URI.parse(@uri).class) + end + + it "should have the same class as its duplicate" do + expect(@uri.class).to eq(@uri.dup.class) + end + + it "should have the same class after being normalized" do + expect(@uri.class).to eq(@uri.normalize.class) + end + + it "should have the same class after being merged" do + expect(@uri.class).to eq(@uri.merge(:path => 'path').class) + end + + it "should have the same class after being joined" do + expect(@uri.class).to eq(@uri.join('path').class) + end +end + +describe Addressable::URI, "support serialization roundtrip" do + before do + @uri = Addressable::URI.new( + :scheme => "http", + :user => "user", + :password => "password", + :host => "example.com", + :port => 80, + :path => "/path", + :query => "query=value", + :fragment => "fragment" + ) + end + + it "is in a working state after being serialized with Marshal" do + @uri = Addressable::URI.parse("http://example.com") + cloned_uri = Marshal.load(Marshal.dump(@uri)) + expect(cloned_uri.normalized_scheme).to be == @uri.normalized_scheme + end + + it "is in a working state after being serialized with YAML" do + @uri = Addressable::URI.parse("http://example.com") + cloned_uri = if YAML.respond_to?(:unsafe_load) + YAML.unsafe_load(YAML.dump(@uri)) + else + YAML.load(YAML.dump(@uri)) + end + expect(cloned_uri.normalized_scheme).to be == @uri.normalized_scheme + end +end + +describe Addressable::URI, "when initialized in a non-main `Ractor`" do + it "should have the same value as if used in the main `Ractor`" do + pending("Ruby 3.0+ for `Ractor` support") unless defined?(Ractor) + value_method = RUBY_VERSION >= "3.5.0" ? :value : :take # see https://github.com/ruby/ruby/pull/13445 + main = Addressable::URI.parse("http://example.com") + expect( + Ractor.new { Addressable::URI.parse("http://example.com") }.public_send(value_method) + ).to eq(main) + end +end + +describe Addressable::URI, "when deferring validation" do + subject(:deferred) { uri.instance_variable_get(:@validation_deferred) } + + let(:uri) { Addressable::URI.parse("http://example.com") } + + it "defers validation within the block" do + uri.defer_validation do + expect(deferred).to be true + end + end + + it "always resets deferral afterward" do + expect { uri.defer_validation { raise "boom" } }.to raise_error("boom") + expect(deferred).to be false + end + + it "returns nil" do + res = uri.defer_validation {} + expect(res).to be nil + end +end + +describe Addressable::URI, "YAML safe loading" do + it "doesn't serialize anonymous objects" do + url = Addressable::URI.parse("http://example.com/") + expect(YAML.dump(url)).to_not include("!ruby/object {}") + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/spec/spec_helper.rb b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/spec/spec_helper.rb new file mode 100644 index 0000000..bd8e395 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/spec/spec_helper.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +require 'bundler/setup' +require 'rspec/its' + +begin + require 'coveralls' + Coveralls.wear! do + add_filter "spec/" + add_filter "vendor/" + end +rescue LoadError + warn "warning: coveralls gem not found; skipping Coveralls" + require 'simplecov' + SimpleCov.start do + add_filter "spec/" + add_filter "vendor/" + end +end if Gem.loaded_specs.key?("simplecov") + +class TestHelper + def self.native_supported? + mri = RUBY_ENGINE == "ruby" + windows = RUBY_PLATFORM.include?("mingw") + + mri && !windows + end +end + +RSpec.configure do |config| + config.warnings = true + config.filter_run_when_matching :focus +end diff --git a/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/tasks/clobber.rake b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/tasks/clobber.rake new file mode 100644 index 0000000..a9e32b3 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/tasks/clobber.rake @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +desc "Remove all build products" +task "clobber" diff --git a/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/tasks/gem.rake b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/tasks/gem.rake new file mode 100644 index 0000000..fe0824a --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/tasks/gem.rake @@ -0,0 +1,100 @@ +# frozen_string_literal: true + +require "rubygems/package_task" + +namespace :gem do + GEM_SPEC = Gem::Specification.new do |s| + s.name = PKG_NAME + s.version = PKG_VERSION + s.summary = PKG_SUMMARY + s.description = PKG_DESCRIPTION + + s.files = PKG_FILES.to_a + + s.extra_rdoc_files = %w( README.md ) + s.rdoc_options.concat ["--main", "README.md"] + + if !s.respond_to?(:add_development_dependency) + puts "Cannot build Gem with this version of RubyGems." + exit(1) + end + + s.required_ruby_version = ">= 2.2" + + s.add_runtime_dependency "public_suffix", ">= 2.0.2", "< 8.0" + s.add_development_dependency "bundler", ">= 1.0", "< 3.0" + + s.require_path = "lib" + + s.author = "Bob Aman" + s.email = "bob@sporkmonger.com" + s.homepage = "https://github.com/sporkmonger/addressable" + s.license = "Apache-2.0" + s.metadata = { + "changelog_uri" => "https://github.com/sporkmonger/addressable/blob/main/CHANGELOG.md#v#{PKG_VERSION}" + } + end + + Gem::PackageTask.new(GEM_SPEC) do |p| + p.gem_spec = GEM_SPEC + p.need_tar = true + p.need_zip = true + end + + desc "Generates .gemspec file" + task :gemspec do + # ensure correct date in gemspec + # https://github.com/ruby/rubygems/pull/8568 + # https://github.com/ruby/rubygems/issues/8679 + ENV["SOURCE_DATE_EPOCH"] ||= Time.now.to_i.to_s + + spec_string = GEM_SPEC.to_ruby + File.open("#{GEM_SPEC.name}.gemspec", "w") do |file| + file.write spec_string + end + end + + desc "Show information about the gem" + task :debug do + puts GEM_SPEC.to_ruby + end + + desc "Install the gem" + task :install => ["clobber", "gem:package"] do + sh "gem install --local ./pkg/#{GEM_SPEC.full_name}.gem" + end + + desc "Uninstall the gem" + task :uninstall do + installed_list = Gem.source_index.find_name(PKG_NAME) + if installed_list && + (installed_list.collect { |s| s.version.to_s}.include?(PKG_VERSION)) + sh( + "gem uninstall --version '#{PKG_VERSION}' " + + "--ignore-dependencies --executables #{PKG_NAME}" + ) + end + end + + desc "Reinstall the gem" + task :reinstall => [:uninstall, :install] + + desc "Package for release" + task :release => ["gem:package", "gem:gemspec"] do |t| + v = ENV["VERSION"] or abort "Must supply VERSION=x.y.z" + abort "Versions don't match #{v} vs #{PROJ.version}" if v != PKG_VERSION + pkg = "pkg/#{GEM_SPEC.full_name}" + + changelog = File.open("CHANGELOG.md") { |file| file.read } + + puts "Releasing #{PKG_NAME} v. #{PKG_VERSION}" + Rake::Task["git:tag:create"].invoke + end +end + +desc "Alias to gem:package" +task "gem" => "gem:package" + +task "gem:release" => "gem:gemspec" + +task "clobber" => ["gem:clobber_package"] diff --git a/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/tasks/git.rake b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/tasks/git.rake new file mode 100644 index 0000000..1238c8d --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/tasks/git.rake @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +namespace :git do + namespace :tag do + desc "List tags from the Git repository" + task :list do + tags = `git tag -l` + tags.gsub!("\r", "") + tags = tags.split("\n").sort {|a, b| b <=> a } + puts tags.join("\n") + end + + desc "Create a new tag in the Git repository" + task :create do + changelog = File.open("CHANGELOG.md", "r") { |file| file.read } + puts "-" * 80 + puts changelog + puts "-" * 80 + puts + + v = ENV["VERSION"] or abort "Must supply VERSION=x.y.z" + abort "Versions don't match #{v} vs #{PKG_VERSION}" if v != PKG_VERSION + + git_status = `git status` + if git_status !~ /^nothing to commit/ + abort "Working directory isn't clean." + end + + tag = "#{PKG_NAME}-#{PKG_VERSION}" + msg = "Release #{PKG_NAME}-#{PKG_VERSION}" + + existing_tags = `git tag -l #{PKG_NAME}-*`.split('\n') + if existing_tags.include?(tag) + warn("Tag already exists, deleting...") + unless system "git tag -d #{tag}" + abort "Tag deletion failed." + end + end + puts "Creating git tag '#{tag}'..." + unless system "git tag -a -m \"#{msg}\" #{tag}" + abort "Tag creation failed." + end + end + end +end + +task "gem:release" => "git:tag:create" diff --git a/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/tasks/metrics.rake b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/tasks/metrics.rake new file mode 100644 index 0000000..107cc24 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/tasks/metrics.rake @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +namespace :metrics do + task :lines do + lines, codelines, total_lines, total_codelines = 0, 0, 0, 0 + for file_name in FileList["lib/**/*.rb"] + f = File.open(file_name) + while line = f.gets + lines += 1 + next if line =~ /^\s*$/ + next if line =~ /^\s*#/ + codelines += 1 + end + puts "L: #{sprintf("%4d", lines)}, " + + "LOC #{sprintf("%4d", codelines)} | #{file_name}" + total_lines += lines + total_codelines += codelines + + lines, codelines = 0, 0 + end + + puts "Total: Lines #{total_lines}, LOC #{total_codelines}" + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/tasks/profile.rake b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/tasks/profile.rake new file mode 100644 index 0000000..60d7a66 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/tasks/profile.rake @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +namespace :profile do + desc "Profile Template match memory allocations" + task :template_match_memory do + require "memory_profiler" + require "addressable/template" + + start_at = Time.now.to_f + template = Addressable::Template.new("http://example.com/{?one,two,three}") + report = MemoryProfiler.report do + 30_000.times do + template.match( + "http://example.com/?one=one&two=floo&three=me" + ) + end + end + end_at = Time.now.to_f + print_options = { scale_bytes: true, normalize_paths: true } + puts "\n\n" + + if ENV["CI"] + report.pretty_print(**print_options) + else + t_allocated = report.scale_bytes(report.total_allocated_memsize) + t_retained = report.scale_bytes(report.total_retained_memsize) + + puts "Total allocated: #{t_allocated} (#{report.total_allocated} objects)" + puts "Total retained: #{t_retained} (#{report.total_retained} objects)" + puts "Took #{end_at - start_at} seconds" + + FileUtils.mkdir_p("tmp") + report.pretty_print(to_file: "tmp/memprof.txt", **print_options) + end + end + + desc "Profile URI parse memory allocations" + task :memory do + require "memory_profiler" + require "addressable/uri" + if ENV["IDNA_MODE"] == "pure" + Addressable.send(:remove_const, :IDNA) + load "addressable/idna/pure.rb" + end + + start_at = Time.now.to_f + report = MemoryProfiler.report do + 30_000.times do + Addressable::URI.parse( + "http://google.com/stuff/../?with_lots=of¶ms=asdff#!stuff" + ).normalize + end + end + end_at = Time.now.to_f + print_options = { scale_bytes: true, normalize_paths: true } + puts "\n\n" + + if ENV["CI"] + report.pretty_print(**print_options) + else + t_allocated = report.scale_bytes(report.total_allocated_memsize) + t_retained = report.scale_bytes(report.total_retained_memsize) + + puts "Total allocated: #{t_allocated} (#{report.total_allocated} objects)" + puts "Total retained: #{t_retained} (#{report.total_retained} objects)" + puts "Took #{end_at - start_at} seconds" + + FileUtils.mkdir_p("tmp") + report.pretty_print(to_file: "tmp/memprof.txt", **print_options) + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/tasks/rspec.rake b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/tasks/rspec.rake new file mode 100644 index 0000000..e3d9f01 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/tasks/rspec.rake @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +require "rspec/core/rake_task" + +namespace :spec do + RSpec::Core::RakeTask.new(:simplecov) do |t| + t.pattern = FileList['spec/**/*_spec.rb'] + t.rspec_opts = %w[--color --format documentation] unless ENV["CI"] + end + + namespace :simplecov do + desc "Browse the code coverage report." + task :browse => "spec:simplecov" do + require "launchy" + Launchy.open("coverage/index.html") + end + end +end + +desc "Alias to spec:simplecov" +task "spec" => "spec:simplecov" + +task "clobber" => ["spec:clobber_simplecov"] diff --git a/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/tasks/yard.rake b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/tasks/yard.rake new file mode 100644 index 0000000..515f960 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/addressable-2.8.8/tasks/yard.rake @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require "rake" + +begin + require "yard" + require "yard/rake/yardoc_task" + + namespace :doc do + desc "Generate Yardoc documentation" + YARD::Rake::YardocTask.new do |yardoc| + yardoc.name = "yard" + yardoc.options = ["--verbose", "--markup", "markdown"] + yardoc.files = FileList[ + "lib/**/*.rb", "ext/**/*.c", + "README.md", "CHANGELOG.md", "LICENSE.txt" + ].exclude(/idna/) + end + end + + task "clobber" => ["doc:clobber_yard"] + + desc "Alias to doc:yard" + task "doc" => "doc:yard" +rescue LoadError + # If yard isn't available, it's not the end of the world + desc "Alias to doc:rdoc" + task "doc" => "doc:rdoc" +end diff --git a/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/LICENSE b/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/LICENSE new file mode 100644 index 0000000..a1f19ff --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/LICENSE @@ -0,0 +1,56 @@ +Ruby is copyrighted free software by Yukihiro Matsumoto . +You can redistribute it and/or modify it under either the terms of the +2-clause BSDL (see the file BSDL), or the conditions below: + + 1. You may make and give away verbatim copies of the source form of the + software without restriction, provided that you duplicate all of the + original copyright notices and associated disclaimers. + + 2. You may modify your copy of the software in any way, provided that + you do at least ONE of the following: + + a) place your modifications in the Public Domain or otherwise + make them Freely Available, such as by posting said + modifications to Usenet or an equivalent medium, or by allowing + the author to include your modifications in the software. + + b) use the modified software only within your corporation or + organization. + + c) give non-standard binaries non-standard names, with + instructions on where to get the original software distribution. + + d) make other distribution arrangements with the author. + + 3. You may distribute the software in object code or binary form, + provided that you do at least ONE of the following: + + a) distribute the binaries and library files of the software, + together with instructions (in the manual page or equivalent) + on where to get the original distribution. + + b) accompany the distribution with the machine-readable source of + the software. + + c) give non-standard binaries non-standard names, with + instructions on where to get the original software distribution. + + d) make other distribution arrangements with the author. + + 4. You may modify and include the part of the software into any other + software (possibly commercial). But some files in the distribution + are not written by the author, so that they are not under these terms. + + For the list of those files and their copying conditions, see the + file LEGAL. + + 5. The scripts and library files supplied as input to or produced as + output from the software do not automatically fall under the + copyright of the software, but belong to whomever generated them, + and may be sold commercially, and may be aggregated with this + software. + + 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE. diff --git a/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/bigdecimal.gemspec b/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/bigdecimal.gemspec new file mode 100644 index 0000000..b6ef8fd --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/bigdecimal.gemspec @@ -0,0 +1,57 @@ +# 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[ + LICENSE + 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") + + s.metadata["changelog_uri"] = s.homepage + "/blob/master/CHANGES.md" +end diff --git a/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/ext/bigdecimal/Makefile b/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/ext/bigdecimal/Makefile new file mode 100644 index 0000000..1617dba --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/ext/bigdecimal/Makefile @@ -0,0 +1,270 @@ + +SHELL = /bin/sh + +# V=0 quiet, V=1 verbose. other values don't work. +V = 0 +V0 = $(V:0=) +Q1 = $(V:1=) +Q = $(Q1:0=@) +ECHO1 = $(V:1=@ :) +ECHO = $(ECHO1:0=@ echo) +NULLCMD = : + +#### Start of system configuration section. #### + +srcdir = . +topdir = /opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0 +hdrdir = $(topdir) +arch_hdrdir = /opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/x86_64-linux +PATH_SEPARATOR = : +VPATH = $(srcdir):$(arch_hdrdir)/ruby:$(hdrdir)/ruby +prefix = $(DESTDIR)/opt/hostedtoolcache/Ruby/3.3.10/x64 +rubysitearchprefix = $(rubylibprefix)/$(sitearch) +rubyarchprefix = $(rubylibprefix)/$(arch) +rubylibprefix = $(libdir)/$(RUBY_BASE_NAME) +exec_prefix = $(prefix) +vendorarchhdrdir = $(vendorhdrdir)/$(sitearch) +sitearchhdrdir = $(sitehdrdir)/$(sitearch) +rubyarchhdrdir = $(rubyhdrdir)/$(arch) +vendorhdrdir = $(rubyhdrdir)/vendor_ruby +sitehdrdir = $(rubyhdrdir)/site_ruby +rubyhdrdir = $(includedir)/$(RUBY_VERSION_NAME) +vendorarchdir = $(vendorlibdir)/$(sitearch) +vendorlibdir = $(vendordir)/$(ruby_version) +vendordir = $(rubylibprefix)/vendor_ruby +sitearchdir = $(sitelibdir)/$(sitearch) +sitelibdir = $(sitedir)/$(ruby_version) +sitedir = $(rubylibprefix)/site_ruby +rubyarchdir = $(rubylibdir)/$(arch) +rubylibdir = $(rubylibprefix)/$(ruby_version) +sitearchincludedir = $(includedir)/$(sitearch) +archincludedir = $(includedir)/$(arch) +sitearchlibdir = $(libdir)/$(sitearch) +archlibdir = $(libdir)/$(arch) +ridir = $(datarootdir)/$(RI_BASE_NAME) +mandir = $(datarootdir)/man +localedir = $(datarootdir)/locale +libdir = $(exec_prefix)/lib +psdir = $(docdir) +pdfdir = $(docdir) +dvidir = $(docdir) +htmldir = $(docdir) +infodir = $(datarootdir)/info +docdir = $(datarootdir)/doc/$(PACKAGE) +oldincludedir = $(DESTDIR)/usr/include +includedir = $(prefix)/include +runstatedir = $(localstatedir)/run +localstatedir = $(prefix)/var +sharedstatedir = $(prefix)/com +sysconfdir = $(prefix)/etc +datadir = $(datarootdir) +datarootdir = $(prefix)/share +libexecdir = $(exec_prefix)/libexec +sbindir = $(exec_prefix)/sbin +bindir = $(exec_prefix)/bin +archdir = $(rubyarchdir) + + +CC_WRAPPER = +CC = gcc +CXX = g++ +LIBRUBY = $(LIBRUBY_SO) +LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a +LIBRUBYARG_SHARED = -Wl,-rpath,$(libdir) -L$(libdir) -l$(RUBY_SO_NAME) +LIBRUBYARG_STATIC = -Wl,-rpath,$(libdir) -L$(libdir) -l$(RUBY_SO_NAME)-static $(MAINLIBS) +empty = +OUTFLAG = -o $(empty) +COUTFLAG = -o $(empty) +CSRCFLAG = $(empty) + +RUBY_EXTCONF_H = +cflags = $(optflags) $(debugflags) $(warnflags) +cxxflags = +optflags = -O3 -fno-fast-math +debugflags = -ggdb3 +warnflags = -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef +cppflags = +CCDLFLAGS = -fPIC +CFLAGS = $(CCDLFLAGS) $(cflags) -fPIC $(ARCH_FLAG) +INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir)/ruby/backward -I$(hdrdir) -I$(srcdir) +DEFS = +CPPFLAGS = -DHAVE_BUILTIN___BUILTIN_CLZ -DHAVE_BUILTIN___BUILTIN_CLZL -DHAVE_BUILTIN___BUILTIN_CLZLL -DHAVE_FLOAT_H -DHAVE_MATH_H -DHAVE_STDBOOL_H -DHAVE_STDLIB_H -DHAVE_X86INTRIN_H -DHAVE_RUBY_ATOMIC_H -DHAVE_RUBY_INTERNAL_HAS_BUILTIN_H -DHAVE_RUBY_INTERNAL_STATIC_ASSERT_H -DHAVE_RB_COMPLEX_REAL -DHAVE_RB_COMPLEX_IMAG -DHAVE_RB_OPTS_EXCEPTION_P -DHAVE_RB_CATEGORY_WARN -DHAVE_CONST_RB_WARN_CATEGORY_DEPRECATED -DENABLE_PATH_CHECK=0 $(DEFS) $(cppflags) +CXXFLAGS = $(CCDLFLAGS) $(ARCH_FLAG) +ldflags = -L. -fstack-protector-strong -rdynamic -Wl,-export-dynamic -Wl,--no-as-needed +dldflags = -Wl,--compress-debug-sections=zlib +ARCH_FLAG = +DLDFLAGS = $(ldflags) $(dldflags) $(ARCH_FLAG) +LDSHARED = $(CC) -shared +LDSHAREDXX = $(CXX) -shared +AR = gcc-ar +EXEEXT = + +RUBY_INSTALL_NAME = $(RUBY_BASE_NAME) +RUBY_SO_NAME = ruby +RUBYW_INSTALL_NAME = +RUBY_VERSION_NAME = $(RUBY_BASE_NAME)-$(ruby_version) +RUBYW_BASE_NAME = rubyw +RUBY_BASE_NAME = ruby + +arch = x86_64-linux +sitearch = $(arch) +ruby_version = 3.3.0 +ruby = $(bindir)/$(RUBY_BASE_NAME) +RUBY = $(ruby) +BUILTRUBY = $(bindir)/$(RUBY_BASE_NAME) +ruby_headers = $(hdrdir)/ruby.h $(hdrdir)/ruby/backward.h $(hdrdir)/ruby/ruby.h $(hdrdir)/ruby/defines.h $(hdrdir)/ruby/missing.h $(hdrdir)/ruby/intern.h $(hdrdir)/ruby/st.h $(hdrdir)/ruby/subst.h $(arch_hdrdir)/ruby/config.h + +RM = rm -f +RM_RF = rm -fr +RMDIRS = rmdir --ignore-fail-on-non-empty -p +MAKEDIRS = /usr/bin/mkdir -p +INSTALL = /usr/bin/install -c +INSTALL_PROG = $(INSTALL) -m 0755 +INSTALL_DATA = $(INSTALL) -m 644 +COPY = cp +TOUCH = exit > + +#### End of system configuration section. #### + +preload = +libpath = . $(libdir) +LIBPATH = -L. -L$(libdir) -Wl,-rpath,$(libdir) +DEFFILE = + +CLEANFILES = mkmf.log +DISTCLEANFILES = +DISTCLEANDIRS = + +extout = +extout_prefix = +target_prefix = +LOCAL_LIBS = +LIBS = $(LIBRUBYARG_SHARED) -lm -lpthread -lc +ORIG_SRCS = bigdecimal.c missing.c +SRCS = $(ORIG_SRCS) +OBJS = bigdecimal.o missing.o +HDRS = $(srcdir)/bigdecimal.h $(srcdir)/bits.h $(srcdir)/feature.h $(srcdir)/missing.h $(srcdir)/static_assert.h +LOCAL_HDRS = +TARGET = bigdecimal +TARGET_NAME = bigdecimal +TARGET_ENTRY = Init_$(TARGET_NAME) +DLLIB = $(TARGET).so +EXTSTATIC = +STATIC_LIB = + +TIMESTAMP_DIR = . +BINDIR = $(bindir) +RUBYCOMMONDIR = $(sitedir)$(target_prefix) +RUBYLIBDIR = $(sitelibdir)$(target_prefix) +RUBYARCHDIR = $(sitearchdir)$(target_prefix) +HDRDIR = $(sitehdrdir)$(target_prefix) +ARCHHDRDIR = $(sitearchhdrdir)$(target_prefix) +TARGET_SO_DIR = +TARGET_SO = $(TARGET_SO_DIR)$(DLLIB) +CLEANLIBS = $(TARGET_SO) false +CLEANOBJS = $(OBJS) *.bak +TARGET_SO_DIR_TIMESTAMP = $(TIMESTAMP_DIR)/.sitearchdir.time +BIGDECIMAL_RB = $(srcdir)/../../lib/bigdecimal.rb + +all: $(DLLIB) +static: $(STATIC_LIB) +.PHONY: all install static install-so install-rb +.PHONY: clean clean-so clean-static clean-rb + +clean-static:: +clean-rb-default:: +clean-rb:: +clean-so:: +clean: clean-so clean-static clean-rb-default clean-rb + -$(Q)$(RM_RF) $(CLEANLIBS) $(CLEANOBJS) $(CLEANFILES) .*.time + +distclean-rb-default:: +distclean-rb:: +distclean-so:: +distclean-static:: +distclean: clean distclean-so distclean-static distclean-rb-default distclean-rb + -$(Q)$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log + -$(Q)$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES) + -$(Q)$(RMDIRS) $(DISTCLEANDIRS) 2> /dev/null || true + +realclean: distclean +install: install-so install-rb + +install-so: $(DLLIB) $(TARGET_SO_DIR_TIMESTAMP) + $(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR) +clean-static:: + -$(Q)$(RM) $(STATIC_LIB) +install-rb: pre-install-rb do-install-rb install-rb-default +install-rb-default: pre-install-rb-default do-install-rb-default +pre-install-rb: Makefile +pre-install-rb-default: Makefile +do-install-rb: +do-install-rb-default: +pre-install-rb-default: + @$(NULLCMD) +$(TARGET_SO_DIR_TIMESTAMP): + $(Q) $(MAKEDIRS) $(@D) $(RUBYARCHDIR) + $(Q) $(TOUCH) $@ + +site-install: site-install-so site-install-rb +site-install-so: install-so +site-install-rb: install-rb + +.SUFFIXES: .c .m .cc .mm .cxx .cpp .o .S + +.cc.o: + $(ECHO) compiling $(<) + $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$< + +.cc.S: + $(ECHO) translating $(<) + $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$< + +.mm.o: + $(ECHO) compiling $(<) + $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$< + +.mm.S: + $(ECHO) translating $(<) + $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$< + +.cxx.o: + $(ECHO) compiling $(<) + $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$< + +.cxx.S: + $(ECHO) translating $(<) + $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$< + +.cpp.o: + $(ECHO) compiling $(<) + $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$< + +.cpp.S: + $(ECHO) translating $(<) + $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$< + +.c.o: + $(ECHO) compiling $(<) + $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$< + +.c.S: + $(ECHO) translating $(<) + $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$< + +.m.o: + $(ECHO) compiling $(<) + $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$< + +.m.S: + $(ECHO) translating $(<) + $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$< + +$(TARGET_SO): $(OBJS) Makefile + $(ECHO) linking shared-object $(DLLIB) + -$(Q)$(RM) $(@) + $(Q) $(LDSHARED) -o $@ $(OBJS) $(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS) + + + +$(OBJS): $(HDRS) $(ruby_headers) diff --git a/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/ext/bigdecimal/bigdecimal.c b/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/ext/bigdecimal/bigdecimal.c new file mode 100644 index 0000000..78449fb --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/ext/bigdecimal/bigdecimal.c @@ -0,0 +1,6206 @@ +/* + * + * 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 + +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_IEEEFP_H +#include +#endif + +#include "bits.h" +#include "static_assert.h" + +#define BIGDECIMAL_VERSION "4.0.1" + +/* #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) +#define ADD_OVERFLOW_SIGNED_VALUE_P(a, b) ADD_OVERFLOW_SIGNED_INTEGER_P(a, b, SIGNED_VALUE_MIN, SIGNED_VALUE_MAX) + +/* max_value = 0.9999_9999_9999E[exponent], exponent <= SIGNED_VALUE_MAX */ +#define VP_EXPONENT_MAX (SIGNED_VALUE_MAX / BASE_FIG) +/* min_value = 0.0001_0000_0000E[exponent], exponent-(BASE_FIG-1) >= SIGNED_VALUE_MIN */ +#define VP_EXPONENT_MIN ((SIGNED_VALUE_MIN + BASE_FIG - 1) / BASE_FIG) +#define EXPONENT_MAX (VP_EXPONENT_MAX * BASE_FIG) +#define EXPONENT_MIN (VP_EXPONENT_MIN * BASE_FIG - (BASE_FIG - 1)) + +VALUE rb_cBigDecimal; + +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]; + +typedef struct { + VALUE bigdecimal; + Real *real; +} BDVALUE; + +typedef struct { + VALUE bigdecimal_or_nil; + Real *real_or_null; +} NULLABLE_BDVALUE; + +static inline BDVALUE +bdvalue_nonnullable(NULLABLE_BDVALUE v) +{ + assert(v.real_or_null != NULL); + return (BDVALUE) { v.bigdecimal_or_nil, v.real_or_null }; +} + +static inline NULLABLE_BDVALUE +bdvalue_nullable(BDVALUE v) +{ + return (NULLABLE_BDVALUE) { v.bigdecimal, v.real }; +} + +#define BASE_FIG BIGDECIMAL_COMPONENT_FIGURES +#define BASE BIGDECIMAL_BASE + +#define HALF_BASE (BASE/2) +#define BASE1 (BASE/10) + +#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 */ + +/* VpMult VpDivd helpers */ +#define VPMULT_RESULT_PREC(a, b) (a->Prec + b->Prec) +/* To calculate VpDivd with n-digits precision, quotient needs n+2*BASE_FIG-1 digits space */ +/* In the worst precision case 0001_1111_1111 / 9999 = 0000_0001_1112, there are 2*BASE_FIG-1 leading zeros */ +#define VPDIVD_QUO_DIGITS(required_digits) ((required_digits) + 2 * BASE_FIG - 1) +/* Required r.MaxPrec for calculating VpDivd(c, r, a, b) */ +#define VPDIVD_REM_PREC(a, b, c) Max(a->Prec, b->Prec + c->MaxPrec - 1) + +static NULLABLE_BDVALUE +CreateFromString(const char *str, VALUE klass, bool strict_p, bool raise_exception); + +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 inline Real * +rbd_allocate_struct_decimal_digits(size_t const decimal_digits) +{ + return rbd_allocate_struct(roomof(decimal_digits, BASE_FIG)); +} + +static void +rbd_free_struct(Real *real) +{ + if (real != NULL) { + check_allocation_count_nonzero(); + ruby_xfree(real); + atomic_allocation_count_dec_nounderflow(); + } +} + +MAYBE_UNUSED(static inline Real * rbd_allocate_struct_zero(int sign, size_t const digits)); +#define NewZero rbd_allocate_struct_zero +static inline Real * +rbd_allocate_struct_zero(int sign, size_t const digits) +{ + Real *real = rbd_allocate_struct_decimal_digits(digits); + VpSetZero(real, sign); + return real; +} + +/* + * ================== 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 int AddExponent(Real *a, SIGNED_VALUE n); +static VALUE CheckGetValue(BDVALUE v); +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 VALUE BigDecimal_addsub_with_coerce(VALUE self, VALUE r, size_t prec, int operation); +static VALUE BigDecimal_mult_with_coerce(VALUE self, VALUE r, size_t prec); + +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 +}; + +// TypedData_Wrap_Struct may fail if there is no memory, or GC.add_stress_to_class(BigDecimal) is set. +// We need to first allocate empty struct, allocate Real struct, and then set the data pointer. +typedef struct { VALUE _obj; } NULL_WRAPPED_VALUE; +static NULL_WRAPPED_VALUE +BigDecimal_alloc_empty_struct(VALUE klass) +{ + return (NULL_WRAPPED_VALUE) { TypedData_Wrap_Struct(klass, &BigDecimal_data_type, NULL) }; +} + +static VALUE +BigDecimal_wrap_struct(NULL_WRAPPED_VALUE v, Real *real) +{ + VALUE obj = v._obj; + assert(RTYPEDDATA_DATA(obj) == NULL); + RTYPEDDATA_DATA(obj) = real; + RB_OBJ_FREEZE(obj); + return obj; +} + +MAYBE_UNUSED(static inline BDVALUE rbd_allocate_struct_zero_wrap(int sign, size_t const digits)); +#define NewZeroWrap rbd_allocate_struct_zero_wrap +static BDVALUE +rbd_allocate_struct_zero_wrap(int sign, size_t const digits) +{ + NULL_WRAPPED_VALUE null_wrapped = BigDecimal_alloc_empty_struct(rb_cBigDecimal); + Real *real = rbd_allocate_struct_zero(sign, digits); + return (BDVALUE) { BigDecimal_wrap_struct(null_wrapped, real), real }; +} + +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); +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, int raise_exception); +static VALUE rb_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception); + +static NULLABLE_BDVALUE +GetBDValueWithPrecInternal(VALUE v, size_t prec, int must) +{ + switch(TYPE(v)) { + case T_FLOAT: + v = rb_float_convert_to_BigDecimal(v, 0, true); + break; + + case T_RATIONAL: + v = rb_rational_convert_to_BigDecimal(v, prec, true); + break; + + case T_DATA: + if (!is_kind_of_BigDecimal(v)) { + goto SomeOneMayDoIt; + } + break; + + case T_FIXNUM: + case T_BIGNUM: { + v = rb_inum_convert_to_BigDecimal(v); + break; + } + +#ifdef ENABLE_NUMERIC_STRING + case T_STRING: { + const char *c_str = StringValueCStr(v); + v = rb_cstr_convert_to_BigDecimal(c_str, must); + break; + } +#endif /* ENABLE_NUMERIC_STRING */ + + default: + goto SomeOneMayDoIt; + } + + Real *vp; + TypedData_Get_Struct(v, Real, &BigDecimal_data_type, vp); + return (NULLABLE_BDVALUE) { v, vp }; + +SomeOneMayDoIt: + if (must) { + cannot_be_coerced_into_BigDecimal(rb_eTypeError, v); + } + return (NULLABLE_BDVALUE) { Qnil, NULL }; /* NULL means to coerce */ +} + +static inline NULLABLE_BDVALUE +GetBDValueWithPrec(VALUE v, size_t prec) +{ + return GetBDValueWithPrecInternal(v, prec, 0); +} + + +static inline BDVALUE +GetBDValueWithPrecMust(VALUE v, size_t prec) +{ + return bdvalue_nonnullable(GetBDValueWithPrecInternal(v, prec, 1)); +} + +// self must be a receiver of BigDecimal instance method or a gc guarded BigDecimal object. +static inline Real* +GetSelfVpValue(VALUE self) +{ + return GetBDValueWithPrecMust(self, 0).real; +} + +static inline BDVALUE +GetBDValueMust(VALUE v) +{ + return GetBDValueWithPrecMust(v, 0); +} + +/* 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(BIGDECIMAL_DOUBLE_FIGURES); +} + +static void +VpCountPrecisionAndScale(Real *p, ssize_t *out_precision, ssize_t *out_scale) +{ + if (out_precision == NULL && out_scale == NULL) + return; + 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; + } +} + +static void +BigDecimal_count_precision_and_scale(VALUE self, ssize_t *out_precision, ssize_t *out_scale) +{ + BDVALUE v = GetBDValueMust(self); + VpCountPrecisionAndScale(v.real, out_precision, out_scale); + RB_GC_GUARD(v.bigdecimal); +} + +/* + * 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 # => 0 + * BigDecimal("1.1").scale # => 1 + * BigDecimal("3.1415").scale # => 4 + * BigDecimal("-1e20").scale # => 0 + * BigDecimal("1e-20").scale # => 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) +{ + BDVALUE v = GetBDValueMust(self); + if (VpIsZero(v.real) || !VpIsDef(v.real)) { + return INT2FIX(0); + } + + ssize_t n = v.real->Prec; /* The length of frac without trailing zeros. */ + for (n = v.real->Prec; n > 0 && v.real->frac[n-1] == 0; --n); + if (n == 0) return INT2FIX(0); + + DECDIG x; + int nlz = BASE_FIG; + for (x = v.real->frac[0]; x > 0; x /= 10) --nlz; + + int ntz = 0; + for (x = v.real->frac[n-1]; x > 0 && x % 10 == 0; x /= 10) ++ntz; + + RB_GC_GUARD(v.bigdecimal); + 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) +{ + BDVALUE v = GetBDValueMust(self); + st_index_t hash = (st_index_t)v.real->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(v.real->frac, sizeof(DECDIG)*v.real->Prec); + hash += v.real->exponent; + } + RB_GC_GUARD(v.bigdecimal); + 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) +{ + BDVALUE v; + char *psz; + VALUE dummy; + volatile VALUE dump; + size_t len; + + rb_scan_args(argc, argv, "01", &dummy); + v = GetBDValueMust(self); + dump = rb_str_new(0, VpNumOfChars(v.real, "E")+50); + psz = RSTRING_PTR(dump); + snprintf(psz, RSTRING_LEN(dump), "%"PRIuSIZE":", v.real->Prec*VpBaseFig()); + len = strlen(psz); + VpToString(v.real, psz+len, RSTRING_LEN(dump)-len, 0, 0); + rb_str_resize(dump, strlen(psz)); + + RB_GC_GUARD(v.bigdecimal); + return dump; +} + +/* + * Internal method used to provide marshalling support. See the Marshal module. + */ +static VALUE +BigDecimal_load(VALUE self, VALUE str) +{ + BDVALUE v; + unsigned char *pch; + unsigned char ch; + + pch = (unsigned char *)StringValueCStr(str); + /* First skip max prec. Don't trust the value. */ + while((*pch) != (unsigned char)'\0' && (ch = *pch++) != (unsigned char)':') { + if(!ISDIGIT(ch)) { + rb_raise(rb_eTypeError, "load failed: invalid character in the marshaled string"); + } + } + v = bdvalue_nonnullable(CreateFromString((char *)pch, self, true, true)); + return CheckGetValue(v); +} + +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. + * + * NaNs + * + * Mode \BigDecimal::EXCEPTION_NaN controls behavior + * when a \BigDecimal NaN is created. + * + * Settings: + * + * - +false+ (default): Returns BigDecimal('NaN'). + * - +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 + * + * Infinities + * + * Mode \BigDecimal::EXCEPTION_INFINITY controls behavior + * when a \BigDecimal Infinity or -Infinity is created. + * Settings: + * + * - +false+ (default): Returns BigDecimal('Infinity') + * or BigDecimal('-Infinity'). + * - +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 + * + * Underflow + * + * Mode \BigDecimal::EXCEPTION_UNDERFLOW controls behavior + * when a \BigDecimal underflow occurs. + * Settings: + * + * - +false+ (default): Returns BigDecimal('0') + * or BigDecimal('-Infinity'). + * - +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 + * + * Overflow + * + * Mode \BigDecimal::EXCEPTION_OVERFLOW controls behavior + * when a \BigDecimal overflow occurs. + * Settings: + * + * - +false+ (default): Returns BigDecimal('Infinity') + * or BigDecimal('-Infinity'). + * - +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 + * + * Zero Division + * + * Mode \BigDecimal::EXCEPTION_ZERODIVIDE controls behavior + * when a zero-division occurs. + * Settings: + * + * - +false+ (default): Returns BigDecimal('Infinity') + * or BigDecimal('-Infinity'). + * - +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 + * + * All Exceptions + * + * Mode \BigDecimal::EXCEPTION_ALL controls all of the above: + * + * BigDecimal.mode(BigDecimal::EXCEPTION_ALL, false) # => 0 + * BigDecimal.mode(BigDecimal::EXCEPTION_ALL, true) # => 23 + * + * Rounding + * + * 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) +{ + if (!VpIsDef(a) || !VpIsDef(b)) return (size_t)-1L; + ssize_t min_a = a->exponent - a->Prec; + ssize_t min_b = b->exponent - b->Prec; + return Max(a->exponent, b->exponent) - Min(min_a, min_b); +} + +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 NULLABLE_BDVALUE +CreateFromString(const char *str, VALUE klass, bool strict_p, bool raise_exception) +{ + NULL_WRAPPED_VALUE null_wrapped = BigDecimal_alloc_empty_struct(klass); + Real *pv = VpAlloc(str, strict_p, raise_exception); + if (!pv) return (NULLABLE_BDVALUE) { Qnil, NULL }; + return (NULLABLE_BDVALUE) { BigDecimal_wrap_struct(null_wrapped, pv), pv }; +} + +static Real * +VpCopy(Real *pv, Real const* const x) +{ + assert(x != NULL); + + pv = (Real *)ruby_xrealloc(pv, rbd_struct_size(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 = GetSelfVpValue(self); + 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 = GetSelfVpValue(self); + 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 = GetSelfVpValue(self); + 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_fix(VALUE self); +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) +{ + BDVALUE v; + VALUE ret; + + v = GetBDValueMust(self); + BigDecimal_check_num(v.real); + + if (v.real->exponent <= 0) return INT2FIX(0); + if (v.real->exponent == 1) { + ret = LONG2NUM((long)(VpGetSign(v.real) * (DECDIG_DBL_SIGNED)v.real->frac[0])); + } + else { + VALUE fix = (ssize_t)v.real->Prec > v.real->exponent ? BigDecimal_fix(self) : self; + VALUE digits = RARRAY_AREF(BigDecimal_split(fix), 1); + ssize_t dpower = VpExponent10(v.real) - (ssize_t)RSTRING_LEN(digits); + ret = rb_funcall(digits, rb_intern("to_i"), 0); + + if (BIGDECIMAL_NEGATIVE_P(v.real)) { + ret = rb_funcall(ret, '*', 1, INT2FIX(-1)); + } + if (dpower) { + VALUE pow10 = rb_funcall(INT2FIX(10), rb_intern("**"), 1, SSIZET2NUM(dpower)); + // In Ruby < 3.4, int**int may return Float::INFINITY + if (RB_TYPE_P(pow10, T_FLOAT)) rb_raise(rb_eFloatDomainError, "Infinity"); + + ret = rb_funcall(ret, '*', 1, pow10); + } + } + + RB_GC_GUARD(v.bigdecimal); + 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) +{ + double d; + SIGNED_VALUE e; + char *buf; + volatile VALUE str; + BDVALUE v = GetBDValueMust(self); + bool negative = BIGDECIMAL_NEGATIVE_P(v.real); + + if (VpVtoD(&d, &e, v.real) != 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-DBL_DIG)) + goto underflow; + + str = rb_str_new(0, VpNumOfChars(v.real, "E")); + buf = RSTRING_PTR(str); + VpToString(v.real, buf, RSTRING_LEN(str), 0, 0); + + RB_GC_GUARD(v.bigdecimal); + + 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 (negative) + return rb_float_new(VpGetDoubleNegInf()); + else + return rb_float_new(VpGetDoublePosInf()); + +underflow: + VpException(VP_EXCEPTION_UNDERFLOW, "BigDecimal to Float conversion", 0); + if (negative) + 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) +{ + BDVALUE v; + ssize_t sign, power, denomi_power; + VALUE a, digits, numerator; + + v = GetBDValueMust(self); + BigDecimal_check_num(v.real); + sign = VpGetSign(v.real); + power = VpExponent10(v.real); + RB_GC_GUARD(v.bigdecimal); + + 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)))); + } +} + +static size_t +GetCoercePrec(Real *a, size_t prec) +{ + if (prec == 0) prec = a->Prec * BASE_FIG; + if (prec < 2 * BIGDECIMAL_DOUBLE_FIGURES) prec = 2 * BIGDECIMAL_DOUBLE_FIGURES; + return prec; +} + +/* 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) +{ + Real* pv = DATA_PTR(self); + BDVALUE b = GetBDValueWithPrecMust(other, GetCoercePrec(pv, 0)); + return rb_assoc_new(CheckGetValue(b), self); +} + +/* + * call-seq: + * +big_decimal -> self + * + * Returns +self+: + * + * +BigDecimal(5) # => 0.5e1 + * +BigDecimal(-5) # => -0.5e1 + * + */ + +static VALUE +BigDecimal_uplus(VALUE self) +{ + return self; +} + +static bool +is_coerceable_to_BigDecimal(VALUE r) +{ + return is_kind_of_BigDecimal(r) || + RB_INTEGER_TYPE_P(r) || + RB_TYPE_P(r, T_FLOAT) || + RB_TYPE_P(r, T_RATIONAL); +} + + /* + * 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) +{ + if (!is_coerceable_to_BigDecimal(r)) return DoSomeOne(self, r, '+'); + return BigDecimal_addsub_with_coerce(self, r, 0, +1); +} + +static VALUE +BigDecimal_addsub_with_coerce(VALUE self, VALUE r, size_t prec, int operation) +{ + BDVALUE a, b, c; + size_t mx; + + a = GetBDValueMust(self); + b = GetBDValueWithPrecMust(r, GetCoercePrec(a.real, prec)); + + if (VpIsNaN(a.real)) return CheckGetValue(a); + if (VpIsNaN(b.real)) return CheckGetValue(b); + + mx = GetAddSubPrec(a.real, b.real); + if (mx == (size_t)-1L) { + /* a or b is inf */ + c = NewZeroWrap(1, BASE_FIG); + VpAddSub(c.real, a.real, b.real, operation); + } + else { + c = NewZeroWrap(1, (mx + 1) * BASE_FIG); + size_t pl = VpGetPrecLimit(); + if (prec) VpSetPrecLimit(prec); + // Let VpAddSub round the result + VpAddSub(c.real, a.real, b.real, operation); + if (prec) VpSetPrecLimit(pl); + } + + RB_GC_GUARD(a.bigdecimal); + RB_GC_GUARD(b.bigdecimal); + return CheckGetValue(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) +{ + if (!is_coerceable_to_BigDecimal(r)) return DoSomeOne(self, r, '-'); + return BigDecimal_addsub_with_coerce(self, r, 0, -1); +} + +static VALUE +BigDecimalCmp(VALUE self, VALUE r,char op) +{ + SIGNED_VALUE e; + BDVALUE a = GetBDValueMust(self); + NULLABLE_BDVALUE b = GetBDValueWithPrec(r, GetCoercePrec(a.real, 0)); + + if (b.real_or_null == 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); + } + e = VpComp(a.real, b.real_or_null); + + RB_GC_GUARD(a.bigdecimal); + RB_GC_GUARD(b.bigdecimal_or_nil); + + 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 = GetSelfVpValue(self); + return VpIsZero(a) ? Qtrue : Qfalse; +} + +/* Returns self if the value is non-zero, nil otherwise. */ +static VALUE +BigDecimal_nonzero(VALUE self) +{ + Real *a = GetSelfVpValue(self); + 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) +{ + BDVALUE a = GetBDValueMust(self); + BDVALUE c = NewZeroWrap(1, a.real->Prec * BASE_FIG); + VpAsgn(c.real, a.real, -10); + RB_GC_GUARD(a.bigdecimal); + return CheckGetValue(c); +} + +/* + * call-seq: + * a * b -> bigdecimal + * + * Multiply by the specified value. + * + * The result precision will be the precision of the sum of each precision. + * + * See BigDecimal#mult. + */ +static VALUE +BigDecimal_mult(VALUE self, VALUE r) +{ + if (!is_coerceable_to_BigDecimal(r)) return DoSomeOne(self, r, '*'); + return BigDecimal_mult_with_coerce(self, r, 0); +} + +static VALUE +BigDecimal_mult_with_coerce(VALUE self, VALUE r, size_t prec) +{ + BDVALUE a, b, c; + + a = GetBDValueMust(self); + b = GetBDValueWithPrecMust(r, GetCoercePrec(a.real, prec)); + + c = NewZeroWrap(1, VPMULT_RESULT_PREC(a.real, b.real) * BASE_FIG); + VpMult(c.real, a.real, b.real); + if (prec) { + VpLeftRound(c.real, VpGetRoundMode(), prec); + } + else { + VpLimitRound(c.real, 0); + } + + RB_GC_GUARD(a.bigdecimal); + RB_GC_GUARD(b.bigdecimal); + return CheckGetValue(c); +} + +static bool BigDecimal_DoDivmod(VALUE self, VALUE r, NULLABLE_BDVALUE *div, NULLABLE_BDVALUE *mod, bool truncate); + +/* 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 */ +{ + if (!is_coerceable_to_BigDecimal(r)) return DoSomeOne(self, r, '/'); + return BigDecimal_div2(self, r, INT2FIX(0)); +} + +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 + * In truncate mode, use truncate instead of floor. + */ +static bool +BigDecimal_DoDivmod(VALUE self, VALUE r, NULLABLE_BDVALUE *div, NULLABLE_BDVALUE *mod, bool truncate) +{ + BDVALUE a, b, dv, md, res; + NULLABLE_BDVALUE b2; + ssize_t a_exponent, b_exponent; + size_t mx, rx, pl; + + a = GetBDValueMust(self); + + b2 = GetBDValueWithPrec(r, GetCoercePrec(a.real, 0)); + if (!b2.real_or_null) return false; + b = bdvalue_nonnullable(b2); + + if (VpIsNaN(a.real) || VpIsNaN(b.real) || (VpIsInf(a.real) && VpIsInf(b.real))) { + VALUE nan = BigDecimal_nan(); + *div = *mod = (NULLABLE_BDVALUE) { nan, DATA_PTR(nan) }; + goto Done; + } + if (VpIsZero(b.real)) { + rb_raise(rb_eZeroDivError, "divided by 0"); + } + if (VpIsInf(a.real)) { + if (VpGetSign(a.real) == VpGetSign(b.real)) { + VALUE inf = BigDecimal_positive_infinity(); + *div = (NULLABLE_BDVALUE) { inf, DATA_PTR(inf) }; + } + else { + VALUE inf = BigDecimal_negative_infinity(); + *div = (NULLABLE_BDVALUE) { inf, DATA_PTR(inf) }; + } + VALUE nan = BigDecimal_nan(); + *mod = (NULLABLE_BDVALUE) { nan, DATA_PTR(nan) }; + goto Done; + } + if (VpIsZero(a.real)) { + VALUE zero = BigDecimal_positive_zero(); + *div = (NULLABLE_BDVALUE) { zero, DATA_PTR(zero) }; + *mod = bdvalue_nullable(a); + goto Done; + } + if (VpIsInf(b.real)) { + if (!truncate && VpGetSign(a.real) * VpGetSign(b.real) < 0) { + BDVALUE minus_one = NewZeroWrap(1, BASE_FIG); + VpSetOne(minus_one.real); + VpSetSign(minus_one.real, -1); + RB_GC_GUARD(minus_one.bigdecimal); + *div = bdvalue_nullable(minus_one); + *mod = bdvalue_nullable(b); + } else { + VALUE zero = BigDecimal_positive_zero(); + *div = (NULLABLE_BDVALUE) { zero, DATA_PTR(zero) }; + *mod = bdvalue_nullable(a); + } + goto Done; + } + + a_exponent = VpExponent10(a.real); + b_exponent = VpExponent10(b.real); + mx = a_exponent > b_exponent ? a_exponent - b_exponent + 1 : 1; + dv = NewZeroWrap(1, VPDIVD_QUO_DIGITS(mx)); + + /* res is reused for VpDivd remainder and VpMult result */ + rx = VPDIVD_REM_PREC(a.real, b.real, dv.real); + mx = VPMULT_RESULT_PREC(dv.real, b.real); + res = NewZeroWrap(1, Max(rx, mx) * BASE_FIG); + /* AddSub needs one more prec */ + md = NewZeroWrap(1, (res.real->MaxPrec + 1) * BASE_FIG); + + VpDivd(dv.real, res.real, a.real, b.real); + VpMidRound(dv.real, VP_ROUND_DOWN, 0); + VpMult(res.real, dv.real, b.real); + pl = VpGetPrecLimit(); + VpSetPrecLimit(0); + VpAddSub(md.real, a.real, res.real, -1); + VpSetPrecLimit(pl); + + if (!truncate && !VpIsZero(md.real) && (VpGetSign(a.real) * VpGetSign(b.real) < 0)) { + /* result adjustment for negative case */ + BDVALUE dv2 = NewZeroWrap(1, (dv.real->MaxPrec + 1) * BASE_FIG); + BDVALUE md2 = NewZeroWrap(1, (GetAddSubPrec(md.real, b.real) + 1) * BASE_FIG); + VpSetPrecLimit(0); + VpAddSub(dv2.real, dv.real, VpOne(), -1); + VpAddSub(md2.real, md.real, b.real, 1); + VpSetPrecLimit(pl); + *div = bdvalue_nullable(dv2); + *mod = bdvalue_nullable(md2); + RB_GC_GUARD(dv2.bigdecimal); + RB_GC_GUARD(md2.bigdecimal); + } + else { + *div = bdvalue_nullable(dv); + *mod = bdvalue_nullable(md); + } + +Done: + RB_GC_GUARD(a.bigdecimal); + RB_GC_GUARD(b.bigdecimal); + RB_GC_GUARD(dv.bigdecimal); + RB_GC_GUARD(md.bigdecimal); + RB_GC_GUARD(res.bigdecimal); + return true; +} + +/* 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 */ +{ + NULLABLE_BDVALUE div, mod; + + if (BigDecimal_DoDivmod(self, r, &div, &mod, false)) { + return CheckGetValue(bdvalue_nonnullable(mod)); + } + return DoSomeOne(self, r, '%'); +} + +/* 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 */ +{ + NULLABLE_BDVALUE div, mod = { Qnil, NULL }; + + if (BigDecimal_DoDivmod(self, r, &div, &mod, true)) { + return CheckGetValue(bdvalue_nonnullable(mod)); + } + return DoSomeOne(self, r, rb_intern("remainder")); +} + +/* 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) +{ + NULLABLE_BDVALUE div, mod; + + if (BigDecimal_DoDivmod(self, r, &div, &mod, false)) { + return rb_assoc_new(BigDecimal_to_i(CheckGetValue(bdvalue_nonnullable(div))), CheckGetValue(bdvalue_nonnullable(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) +{ + SIGNED_VALUE ix; + BDVALUE av, bv, cv, res; + + if (NIL_P(n)) { /* div in Float sense */ + NULLABLE_BDVALUE div; + NULLABLE_BDVALUE mod; + if (BigDecimal_DoDivmod(self, b, &div, &mod, false)) { + return BigDecimal_to_i(CheckGetValue(bdvalue_nonnullable(div))); + } + return DoSomeOne(self, b, rb_intern("div")); + } + + /* div in BigDecimal sense */ + ix = check_int_precision(n); + + av = GetBDValueMust(self); + bv = GetBDValueWithPrecMust(b, GetCoercePrec(av.real, ix)); + + if (ix == 0) { + ssize_t a_prec, b_prec, limit = VpGetPrecLimit(); + VpCountPrecisionAndScale(av.real, &a_prec, NULL); + VpCountPrecisionAndScale(bv.real, &b_prec, NULL); + ix = ((a_prec > b_prec) ? a_prec : b_prec) + BIGDECIMAL_DOUBLE_FIGURES; + if (2 * BIGDECIMAL_DOUBLE_FIGURES > ix) + ix = 2 * BIGDECIMAL_DOUBLE_FIGURES; + if (limit && limit < ix) ix = limit; + } + + // Needs to calculate 1 extra digit for rounding. + cv = NewZeroWrap(1, VPDIVD_QUO_DIGITS(ix + 1)); + res = NewZeroWrap(1, VPDIVD_REM_PREC(av.real, bv.real, cv.real) * BASE_FIG); + VpDivd(cv.real, res.real, av.real, bv.real); + + if (!VpIsZero(res.real)) { + // Remainder value affects rounding result. + // ROUND_UP cv = 0.1e0 with idx=10 will be: + // 0.1e0 if remainder == 0 + // 0.1000000001e0 if remainder != 0 + size_t idx = roomof(ix, BASE_FIG); + while (cv.real->Prec <= idx) cv.real->frac[cv.real->Prec++] = 0; + if (cv.real->frac[idx] == 0 || cv.real->frac[idx] == HALF_BASE) cv.real->frac[idx]++; + } + VpLeftRound(cv.real, VpGetRoundMode(), ix); + + RB_GC_GUARD(av.bigdecimal); + RB_GC_GUARD(bv.bigdecimal); + RB_GC_GUARD(res.bigdecimal); + return CheckGetValue(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) +{ + return BigDecimal_addsub_with_coerce(self, b, check_int_precision(n), +1); +} + +/* 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) +{ + return BigDecimal_addsub_with_coerce(self, b, check_int_precision(n), -1); +} + + /* + * 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) +{ + return BigDecimal_mult_with_coerce(self, b, check_int_precision(n)); +} + +/* + * 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) +{ + BDVALUE a = GetBDValueMust(self); + BDVALUE c = NewZeroWrap(1, a.real->Prec * BASE_FIG); + VpAsgn(c.real, a.real, 10); + VpChangeSign(c.real, 1); + RB_GC_GUARD(a.bigdecimal); + return CheckGetValue(c); +} + +/* Return the integer part of the number, as a BigDecimal. + */ +static VALUE +BigDecimal_fix(VALUE self) +{ + BDVALUE a = GetBDValueMust(self); + BDVALUE c = NewZeroWrap(1, (a.real->Prec + 1) * BASE_FIG); + VpActiveRound(c.real, a.real, VP_ROUND_DOWN, 0); /* 0: round off */ + RB_GC_GUARD(a.bigdecimal); + return CheckGetValue(c); +} + +/* call-seq: + * round(n, mode) + * + * Round to the nearest integer (by default), returning the result as a + * BigDecimal if n is specified and positive, 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" + * BigDecimal('3.14159').round(0).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) +{ + BDVALUE c, a; + int iLoc = 0; + VALUE vLoc; + VALUE vRound; + int round_to_int = 0; + size_t mx; + + 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; + } + + a = GetBDValueMust(self); + mx = (a.real->Prec + 1) * BASE_FIG; + c = NewZeroWrap(1, mx); + + VpActiveRound(c.real, a.real, sw, iLoc); + + RB_GC_GUARD(a.bigdecimal); + + if (round_to_int) { + return BigDecimal_to_i(CheckGetValue(c)); + } + return CheckGetValue(c); +} + +static VALUE +BigDecimal_truncate_floor_ceil(int argc, VALUE *argv, VALUE self, unsigned short rounding_mode) +{ + BDVALUE c, a; + int iLoc; + VALUE vLoc; + size_t mx; + + if (rb_scan_args(argc, argv, "01", &vLoc) == 0) { + iLoc = 0; + } + else { + iLoc = NUM2INT(vLoc); + } + + a = GetBDValueMust(self); + mx = (a.real->Prec + 1) * BASE_FIG; + c = NewZeroWrap(1, mx); + VpActiveRound(c.real, a.real, rounding_mode, iLoc); + + RB_GC_GUARD(a.bigdecimal); + + if (argc == 0) { + return BigDecimal_to_i(CheckGetValue(c)); + } + return CheckGetValue(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) +{ + return BigDecimal_truncate_floor_ceil(argc, argv, self, VP_ROUND_DOWN); +} + +/* Return the fractional part of the number, as a BigDecimal. + */ +static VALUE +BigDecimal_frac(VALUE self) +{ + BDVALUE a = GetBDValueMust(self); + BDVALUE c = NewZeroWrap(1, (a.real->Prec + 1) * BASE_FIG); + VpFrac(c.real, a.real); + RB_GC_GUARD(a.bigdecimal); + return CheckGetValue(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) +{ + return BigDecimal_truncate_floor_ceil(argc, argv, self, VP_ROUND_FLOOR); +} + +/* 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) +{ + return BigDecimal_truncate_floor_ceil(argc, argv, self, VP_ROUND_CEIL); +} + +/* 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 + * digits, starting from '.' and counting outwards. + * + * If s ends with an 'E', scientific notation (0.xxxxEnn) is used. + * + * If s ends with an 'F', conventional floating point notation is used. + * + * Examples: + * + * BigDecimal('-1234567890123.45678901234567890').to_s('5F') + * #=> '-123 45678 90123.45678 90123 45678 9' + * + * BigDecimal('1234567890123.45678901234567890').to_s('+8F') + * #=> '+12345 67890123.45678901 23456789' + * + * BigDecimal('1234567890123.45678901234567890').to_s(' F') + * #=> ' 1234567890123.4567890123456789' + */ +static VALUE +BigDecimal_to_s(int argc, VALUE *argv, VALUE self) +{ + int fmt = 0; /* 0: E format, 1: F format */ + int fPlus = 0; /* 0: default, 1: set ' ' before digits, 2: set '+' before digits. */ + BDVALUE v; + volatile VALUE str; + char *psz; + char ch; + size_t nc, mc = 0; + SIGNED_VALUE m; + VALUE f; + + v = GetBDValueMust(self); + + 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(v.real, "F"); + } + else { + nc = VpNumOfChars(v.real, "E"); + } + if (mc > 0) { + nc += (nc + mc - 1) / mc + 1; + } + + str = rb_usascii_str_new(0, nc); + psz = RSTRING_PTR(str); + + if (fmt) { + VpToFString(v.real, psz, RSTRING_LEN(str), mc, fPlus); + } + else { + VpToString (v.real, psz, RSTRING_LEN(str), mc, fPlus); + } + rb_str_resize(str, strlen(psz)); + + RB_GC_GUARD(v.bigdecimal); + 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) +{ + BDVALUE v; + VALUE obj,str; + ssize_t e, s; + char *psz1; + + v = GetBDValueMust(self); + str = rb_str_new(0, VpNumOfChars(v.real, "E")); + psz1 = RSTRING_PTR(str); + VpSzMantissa(v.real, 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(v.real); + 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)); + + RB_GC_GUARD(v.bigdecimal); + 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(GetSelfVpValue(self)); + return SSIZET2NUM(e); +} + +/* Returns a string representation of self. + * + * BigDecimal("1234.5678").inspect + * #=> "0.12345678e4" + */ +static VALUE +BigDecimal_inspect(VALUE self) +{ + BDVALUE v; + volatile VALUE str; + size_t nc; + + v = GetBDValueMust(self); + nc = VpNumOfChars(v.real, "E"); + + str = rb_str_new(0, nc); + VpToString(v.real, RSTRING_PTR(str), RSTRING_LEN(str), 0, 0); + rb_str_resize(str, strlen(RSTRING_PTR(str))); + + RB_GC_GUARD(v.bigdecimal); + return str; +} + +/* Returns self * 10**v without changing the precision. + * This method is currently for internal use. + * + * BigDecimal("0.123e10")._decimal_shift(20) #=> "0.123e30" + * BigDecimal("0.123e10")._decimal_shift(-20) #=> "0.123e-10" + */ +static VALUE +BigDecimal_decimal_shift(VALUE self, VALUE v) +{ + BDVALUE a, c; + ssize_t shift, exponentShift; + bool shiftDown; + size_t prec; + DECDIG ex, iex; + + a = GetBDValueMust(self); + shift = NUM2SSIZET(rb_to_int(v)); + + if (VpIsZero(a.real) || VpIsNaN(a.real) || VpIsInf(a.real) || shift == 0) return CheckGetValue(a); + + exponentShift = shift > 0 ? shift / BASE_FIG : (shift + 1) / BASE_FIG - 1; + shift -= exponentShift * BASE_FIG; + ex = 1; + for (int i = 0; i < shift; i++) ex *= 10; + shiftDown = a.real->frac[0] * (DECDIG_DBL)ex >= BASE; + iex = BASE / ex; + + prec = a.real->Prec + shiftDown; + c = NewZeroWrap(1, prec * BASE_FIG); + if (shift == 0) { + VpAsgn(c.real, a.real, 10); + } else if (shiftDown) { + DECDIG carry = 0; + exponentShift++; + for (size_t i = 0; i < a.real->Prec; i++) { + DECDIG v = a.real->frac[i]; + c.real->frac[i] = carry * ex + v / iex; + carry = v % iex; + } + c.real->frac[a.real->Prec] = carry * ex; + } else { + DECDIG carry = 0; + for (ssize_t i = a.real->Prec - 1; i >= 0; i--) { + DECDIG v = a.real->frac[i]; + c.real->frac[i] = v % iex * ex + carry; + carry = v / iex; + } + } + while (c.real->frac[prec - 1] == 0) prec--; + c.real->Prec = prec; + c.real->sign = a.real->sign; + c.real->exponent = a.real->exponent; + AddExponent(c.real, exponentShift); + RB_GC_GUARD(a.bigdecimal); + return CheckGetValue(c); +} + +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))); +} + +/* :nodoc: */ +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); + VpCheckException(vp, false); + + return bd; +} + +static VALUE +rb_uint64_convert_to_BigDecimal(uint64_t uval) +{ + NULL_WRAPPED_VALUE null_wrapped = BigDecimal_alloc_empty_struct(rb_cBigDecimal); + Real *vp; + if (uval == 0) { + vp = rbd_allocate_struct(1); + vp->Prec = 1; + vp->exponent = 1; + VpSetZero(vp, 1); + vp->frac[0] = 0; + } + else if (uval < BASE) { + vp = rbd_allocate_struct(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->Prec = len; + vp->exponent = exp; + VpSetSign(vp, 1); + MEMCPY(vp->frac, buf + BIGDECIMAL_INT64_MAX_LENGTH - len, DECDIG, len); + } + + return BigDecimal_wrap_struct(null_wrapped, vp); +} + +static VALUE +rb_int64_convert_to_BigDecimal(int64_t ival) +{ + const uint64_t uval = (ival < 0) ? (((uint64_t)-(ival+1))+1) : (uint64_t)ival; + VALUE bd = rb_uint64_convert_to_BigDecimal(uval); + 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) +{ + 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)); + } + else { + return rb_uint64_convert_to_BigDecimal(NUM2ULONG(val)); + } + } +#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)); + } + else { + return rb_uint64_convert_to_BigDecimal(NUM2ULL(val)); + } + } +#endif + else { + VALUE str = rb_big2str(val, 10); + BDVALUE v = bdvalue_nonnullable(CreateFromString( + RSTRING_PTR(str), + rb_cBigDecimal, + true, + true + )); + RB_GC_GUARD(str); + return CheckGetValue(v); + } +} + +static VALUE +rb_inum_convert_to_BigDecimal(VALUE val) +{ + assert(RB_INTEGER_TYPE_P(val)); + if (FIXNUM_P(val)) { + return rb_int64_convert_to_BigDecimal(FIX2LONG(val)); + } + else { + return rb_big_convert_to_BigDecimal(val); + } +} + +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) { + digs = 0; + } + 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); + 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)); + VALUE d = BigDecimal_div2(num, rb_rational_den(val), SIZET2NUM(digs)); + return d; +} + +static VALUE +rb_cstr_convert_to_BigDecimal(const char *c_str, int raise_exception) +{ + NULLABLE_BDVALUE v = CreateFromString(c_str, rb_cBigDecimal, true, raise_exception); + if (v.bigdecimal_or_nil == Qnil) return Qnil; + return CheckGetValue(bdvalue_nonnullable(v)); +} + +static inline VALUE +rb_str_convert_to_BigDecimal(VALUE val, int raise_exception) +{ + const char *c_str = StringValueCStr(val); + return rb_cstr_convert_to_BigDecimal(c_str, 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); + + NULL_WRAPPED_VALUE null_wrapped = BigDecimal_alloc_empty_struct(rb_cBigDecimal); + Real *vp; + TypedData_Get_Struct(val, Real, &BigDecimal_data_type, vp); + vp = VpCopy(NULL, vp); + RB_GC_GUARD(val); + + VALUE copy = BigDecimal_wrap_struct(null_wrapped, vp); + /* TODO: rounding */ + return check_exception(copy); + } + else if (RB_INTEGER_TYPE_P(val)) { + return rb_inum_convert_to_BigDecimal(val); + } + 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, 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, 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, Float, 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 + * BigDecimal(2.0) # => 0.2e1 + * # Rational value requires ndigits. + * 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 :to_str: + * 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); +} + +/* call-seq: + * BigDecimal.interpret_loosely(string) -> bigdecimal + * + * Returns the +BigDecimal+ converted loosely from +string+. + */ + +static VALUE +BigDecimal_s_interpret_loosely(VALUE klass, VALUE str) +{ + char const *c_str = StringValueCStr(str); + NULLABLE_BDVALUE v = CreateFromString(c_str, klass, false, true); + if (v.bigdecimal_or_nil == Qnil) + return Qnil; + else + return CheckGetValue(bdvalue_nonnullable(v)); +} + + /* + * 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 = GetSelfVpValue(self)->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; +} + +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; +} + +static inline VALUE +BigDecimal_literal(const char *str) +{ + VALUE arg = rb_str_new_cstr(str); + VALUE val = f_BigDecimal(1, &arg, rb_cBigDecimal); + rb_gc_register_mark_object(val); + return val; +} + +#define BIGDECIMAL_LITERAL(var, val) (BIGDECIMAL_ ## var = BigDecimal_literal(#val)) + +#ifdef BIGDECIMAL_USE_VP_TEST_METHODS +VALUE +BigDecimal_vpdivd(VALUE self, VALUE r, VALUE cprec) { + BDVALUE a,b,c,d; + size_t cn = NUM2INT(cprec); + a = GetBDValueMust(self); + b = GetBDValueMust(r); + c = NewZeroWrap(1, cn * BASE_FIG); + d = NewZeroWrap(1, VPDIVD_REM_PREC(a.real, b.real, c.real) * BASE_FIG); + VpDivd(c.real, d.real, a.real, b.real); + RB_GC_GUARD(a.bigdecimal); + RB_GC_GUARD(b.bigdecimal); + return rb_assoc_new(c.bigdecimal, d.bigdecimal); +} + +VALUE +BigDecimal_vpmult(VALUE self, VALUE v) { + BDVALUE a,b,c; + a = GetBDValueMust(self); + b = GetBDValueMust(v); + c = NewZeroWrap(1, VPMULT_RESULT_PREC(a.real, b.real) * BASE_FIG); + VpMult(c.real, a.real, b.real); + RB_GC_GUARD(a.bigdecimal); + RB_GC_GUARD(b.bigdecimal); + return c.bigdecimal; +} +#endif /* BIGDECIMAL_USE_VP_TEST_METHODS */ + +/* 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 + * 'Infinity', '+Infinity' and + * '-Infinity' (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, + * String, Complex, and NilClass 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 + * Complex(0.1234567, 0).to_d(4) # => 0.1235e0 + * nil.to_d # => 0.0 + * + * == Methods for Working with \JSON + * + * - {::json_create}[https://docs.ruby-lang.org/en/master/BigDecimal.html#method-c-json_create]: + * Returns a new \BigDecimal object constructed from the given object. + * - {#as_json}[https://docs.ruby-lang.org/en/master/BigDecimal.html#method-i-as_json]: + * Returns a 2-element hash representing +self+. + * - {#to_json}[https://docs.ruby-lang.org/en/master/BigDecimal.html#method-i-to_json]: + * Returns a \JSON string representing +self+. + * + * These methods are provided by the {JSON gem}[https://github.com/flori/json]. To make these methods available: + * + * require 'json/add/bigdecimal' + * + * * == License + * + * Copyright (C) 2002 by Shigeo Kobayashi . + * + * BigDecimal is released under the Ruby and 2-clause BSD licenses. + * See LICENSE.txt for details. + * + * Maintained by mrkn and ruby-core members. + * + * Documented by zzak , mathew , and + * many other contributors. + */ +void +Init_bigdecimal(void) +{ +#ifdef HAVE_RB_EXT_RACTOR_SAFE + rb_ext_ractor_safe(true); +#endif + + 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)BASE)); + + /* 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. */ + BIGDECIMAL_LITERAL(POSITIVE_ZERO, +0); + + /* Negative zero value. */ + BIGDECIMAL_LITERAL(NEGATIVE_ZERO, -0); + + /* Positive infinity[rdoc-ref:BigDecimal@Infinity] value. */ + rb_define_const(rb_cBigDecimal, "INFINITY", BIGDECIMAL_LITERAL(POSITIVE_INFINITY, +Infinity)); + + /* Negative infinity value. */ + BIGDECIMAL_LITERAL(NEGATIVE_INFINITY, -Infinity); + + /* '{Not a Number}[rdoc-ref:BigDecimal@Not+a+Number]' value. */ + rb_define_const(rb_cBigDecimal, "NAN", BIGDECIMAL_LITERAL(NAN, NaN)); + + /* instance methods */ + 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, "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, "<=>", 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, "_decimal_shift", BigDecimal_decimal_shift, 1); + rb_define_method(rb_cBigDecimal, "_dump", BigDecimal_dump, -1); + +#ifdef BIGDECIMAL_USE_VP_TEST_METHODS + rb_define_method(rb_cBigDecimal, "vpdivd", BigDecimal_vpdivd, 2); + rb_define_method(rb_cBigDecimal, "vpmult", BigDecimal_vpmult, 1); +#endif /* BIGDECIMAL_USE_VP_TEST_METHODS */ + +#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 */ +#endif /* BIGDECIMAL_DEBUG */ + +static Real *VpConstOne; /* constant 1.0 */ + +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 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 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 +CheckGetValue(BDVALUE v) +{ + VpCheckException(v.real, false); + return v.bigdecimal; +} + +/* + * 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 void +VpSetPrecLimit(size_t n) +{ + bigdecimal_set_thread_local_precision_limit(n); +} + +/* + * 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; +} + +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 + 25; /* "-0."(3) + digits_chars + "e-"(2) + 64bit_exponent_chars(19) + null(1) */ + } + 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 = NewZero(1, 1); + VpSetOne(VpConstOne); + +#ifdef BIGDECIMAL_DEBUG + gnAlloc = 0; +#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; + if (e > 0 && n > 0) { + if (n > VP_EXPONENT_MAX - e) goto overflow; + } else if (e < 0 && n < 0) { + if (n < VP_EXPONENT_MIN - e) goto underflow; + } else if (m > VP_EXPONENT_MAX) { + goto overflow; + } else if (m < VP_EXPONENT_MIN) { + 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); + 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; +} + +struct VpCtoV_args { + Real *a; + const char *int_chr; + size_t ni; + const char *frac; + size_t nf; + const char *exp_chr; + size_t ne; +}; + +static VALUE +call_VpCtoV(VALUE arg) +{ + struct VpCtoV_args *x = (struct VpCtoV_args *)arg; + return (VALUE)VpCtoV(x->a, x->int_chr, x->ni, x->frac, x->nf, x->exp_chr, x->ne); +} + +static int +protected_VpCtoV(Real *a, const char *int_chr, size_t ni, const char *frac, size_t nf, const char *exp_chr, size_t ne, int free_on_error) +{ + struct VpCtoV_args args; + int state = 0; + + args.a = a; + args.int_chr = int_chr; + args.ni = ni; + args.frac = frac; + args.nf = nf; + args.exp_chr = exp_chr; + args.ne = ne; + + VALUE result = rb_protect(call_VpCtoV, (VALUE)&args, &state); + if (state) { + if (free_on_error) { + rbd_free_struct(a); + } + rb_jump_tag(state); + } + + return (int)result; +} + +/* + * Allocates variable. + * [Input] + * szVal ... The value assigned(char). + * + * [Returns] + * Pointer to the newly allocated variable, or + * NULL be returned if memory allocation is failed,or any error. + */ +VP_EXPORT Real * +VpAlloc(const char *szVal, int strict_p, int exc) +{ + const char *orig_szVal = szVal; + size_t i, j, ni, ipf, nf, ipe, ne, exp_seen, nalloc; + char v, *psz; + int sign=1; + Real *vp = NULL; + VALUE buf; + + /* Skipping leading spaces */ + while (ISSPACE(*szVal)) szVal++; + + /* Check on Inf & NaN */ + if ((vp = bigdecimal_parse_special_string(szVal)) != NULL) { + return vp; + } + + /* Skip leading `#`. + * It used to be a mark to indicate that an extra MaxPrec should be allocated, + * but now it has no effect. + */ + if (*szVal == '#') ++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 */ + exp_seen = 0; + + if (v != '\0') { + /* Scanning fractional part */ + if ((psz[i] = szVal[j]) == '.') { + ++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'; + 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 && nf == 0) || (exp_seen && ne == 0))) { + VALUE str; + invalid_value: + if (!strict_p) { + return NewZero(1, 1); + } + 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[] */ + vp = rbd_allocate_struct(nalloc); + VpSetZero(vp, sign); + protected_VpCtoV(vp, psz, ni, psz + ipf, nf, psz + ipe, ne, true); + 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 && 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, sign; + Real *a_ptr, *b_ptr; + size_t n, na, nb, i; + DECDIG mrv; + + 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); + sign = isw / 2; + } + else { /* subtraction */ + VpSetSign(c, 1); + mrv = VpSubAbs(a_ptr, b_ptr, c); + sign = a_ptr == a ? VpGetSign(a) : VpGetSign(a_ptr) * sw; + } + if (VpIsInf(c)) { + VpSetInf(c, sign); + } + else { + VpSetSign(c, sign); + VpInternalRound(c, 0, (c->Prec > 0) ? c->frac[c->Prec-1] : 0, mrv); + } + + 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; + + 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: + + 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; + + 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: + 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; + size_t ind_c, i, ii, nc; + size_t ind_as, ind_ae, ind_bs; + DECDIG carry; + DECDIG_DBL s; + + 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, 10 * VpGetSign(a)); + goto Exit; + } + if (VpIsOne(b)) { + VpAsgn(c, a, 10 * VpGetSign(b)); + goto Exit; + } + if (b->Prec > a->Prec) { + /* Adjust so that digits(a)>digits(b) */ + Real *w = a; + a = b; + b = w; + } + MxIndA = a->Prec - 1; + MxIndB = b->Prec - 1; + MxIndAB = a->Prec + b->Prec - 1; + + /* set LHSV c info */ + + c->exponent = a->exponent; /* set exponent */ + VpSetSign(c, VpGetSign(a) * VpGetSign(b)); /* set sign */ + if (!AddExponent(c, b->exponent)) return 0; + 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; + } + } + } + } + } + VpNmlz(c); + +Exit: + return c->Prec*BASE_FIG; +} + +/* + * c = a / b, remainder = r + * XXXX_YYYY_ZZZZ / 0001 = XXXX_YYYY_ZZZZ + * XXXX_YYYY_ZZZZ / 1111 = 000X_000Y_000Z + * 00XX_XXYY_YYZZ / 1000 = 0000_0XXX_XYYY + * 0001_0000_0000 / 9999 = 0000_0001_0001 + */ +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 borrow1, borrow2; + DECDIG_DBL qb; + + 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; + } + + word_a = a->Prec; + word_b = b->Prec; + word_c = c->MaxPrec; + word_r = r->MaxPrec; + + if (word_a > word_r || word_b + word_c - 2 >= word_r) goto space_error; + + for (i = 0; i < word_a; ++i) r->frac[i] = a->frac[i]; + for (i = word_a; i < word_r; ++i) r->frac[i] = 0; + for (i = 0; i < word_c; ++i) c->frac[i] = 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 */ + nLoop = Min(word_c, word_r); + ind_c = 0; + while (ind_c < nLoop) { + if (r->frac[ind_c] == 0) { + ++ind_c; + continue; + } + r1r2 = (DECDIG_DBL)r->frac[ind_c] * BASE + (ind_c + 1 < word_r ? r->frac[ind_c + 1] : 0); + 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. */ + q = 1; + ++c->frac[ind_c]; + ind_r = b->Prec + ind_c - 1; + goto sub_mult; + } + /* 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; + } + /* End of operation, now final arrangement */ +out_side: + c->Prec = word_c; + c->exponent = a->exponent; + VpSetSign(c, VpGetSign(a) * VpGetSign(b)); + if (!AddExponent(c, 1)) return 0; + if (!AddExponent(c, -(b->exponent))) return 0; + + VpNmlz(c); /* normalize c */ + r->Prec = word_r; + r->exponent = a->exponent; + VpSetSign(r, VpGetSign(a)); + VpNmlz(r); /* normalize r(remainder) */ + goto Exit; + +space_error: + rb_bug("ERROR(VpDivd): space for remainder too small."); + +Exit: + 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 ... asign - 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; /* aexponent > 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; + + 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 iend, idig = 0, iexp = 0, nspaces; + char *p; + + if (fFmt == 0) return; + + iend = strlen(psz); + + if ((p = strchr(psz, '.'))) { + idig = (p - psz) + 1; + } + if ((p = strchr(psz, 'E')) || (p = strchr(psz, 'e'))) { + iexp = p - psz; + } + if (idig == 0 || idig > iexp) return; + + nspaces = (iexp - idig - 1) / fFmt; + p = psz + iend + 1; + for (size_t i = nspaces; i > 0; i--) { + char *src = psz + idig + i * fFmt; + char *dst = psz + idig + i * (fFmt + 1); + memmove(dst, src, p - src); + dst[-1] = ' '; + p = src; + } +} + +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; + char *p = buf; + size_t plen = buflen, delim = fFmt; + ssize_t ex; + + if (VpToSpecialString(a, buf, buflen, fPlus)) return; + +#define APPEND(c, group) do { \ + if (plen < 1) goto overflow; \ + if (group && delim == 0) { \ + *p = ' '; \ + p += 1; \ + plen -= 1; \ + } \ + if (plen < 1) goto overflow; \ + *p = c; \ + p += 1; \ + plen -= 1; \ + if (group) delim = (delim + 1) % fFmt; \ +} while (0) + + + if (BIGDECIMAL_NEGATIVE_P(a)) { + APPEND('-', false); + } + else if (fPlus == 1) { + APPEND(' ', false); + } + else if (fPlus == 2) { + APPEND('+', false); + } + + n = a->Prec; + ex = a->exponent; + if (ex <= 0) { + APPEND('0', false); + APPEND('.', false); + } + while (ex < 0) { + for (i=0; i < BASE_FIG; ++i) { + APPEND('0', fFmt > 0); + } + ++ex; + } + + for (i = 0; i < n; ++i) { + m = BASE1; + e = a->frac[i]; + if (i == 0 && ex > 0) { + for (delim = 0; e / m == 0; delim++) { + m /= 10; + } + if (fFmt > 0) { + delim = 2*fFmt - (ex * BASE_FIG - delim) % fFmt; + } + } + while (m && (e || (i < n - 1) || ex > 0)) { + APPEND((char)(e / m + '0'), fFmt > 0); + e %= m; + m /= 10; + } + if (--ex == 0) { + APPEND('.', false); + delim = fFmt; + } + } + + while (ex > 0) { + for (i=0; i < BASE_FIG; ++i) { + APPEND('0', fFmt > 0); + } + if (--ex == 0) { + APPEND('.', false); + } + } + + *p = '\0'; + if (p - 1 > buf && p[-1] == '.') { + snprintf(p, plen, "0"); + } + + overflow: + return; +#undef APPEND +} + +/* + * [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; + 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) { + int dig = exp_chr[i] - '0'; + if (MUL_OVERFLOW_SIGNED_VALUE_P(e, 10) || + ADD_OVERFLOW_SIGNED_VALUE_P(e * 10, signe * dig)) { + exponent_overflow = 1; + break; + } + e = e * 10 + signe * dig; + ++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; + } + } + /* skip leading zeros in integer part */ + while (i < mi && int_chr[i] == '0') { + ++i; + --ni; + } + + /* set actual exponent size. */ + if (ADD_OVERFLOW_SIGNED_VALUE_P(e, (SIGNED_VALUE)ni)) { + exponent_overflow = 1; + } else { + e += ni; + } + + /* Adjust the exponent so that it is the multiple of BASE_FIG. */ + j = (BASE_FIG - e % BASE_FIG) % BASE_FIG; + if (ADD_OVERFLOW_SIGNED_VALUE_P(e, (SIGNED_VALUE)j)) { + exponent_overflow = 1; + } else { + e += j; + } + + if (exponent_overflow || e < EXPONENT_MIN || e > EXPONENT_MAX) { + 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 && e > 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 = e / (SIGNED_VALUE)BASE_FIG; + 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, 0Prec); + *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: + return f; +} + +/* + * 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 -= y->exponent * (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, 10); + 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, 10); + 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: + return; +} + +#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/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/ext/bigdecimal/bigdecimal.h b/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/ext/bigdecimal/bigdecimal.h new file mode 100644 index 0000000..82c88a2 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/ext/bigdecimal/bigdecimal.h @@ -0,0 +1,292 @@ +/* + * + * 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 +#endif + +#if defined(HAVE_INT64_T) && !defined(BIGDECIMAL_USE_DECDIG_UINT16_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 { + 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. + * ------------------ + */ + +#define VpBaseFig() BIGDECIMAL_COMPONENT_FIGURES + +/* 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 void 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); +VP_EXPORT size_t VpNumOfChars(Real *vp,const char *pszFmt); +VP_EXPORT size_t VpInit(DECDIG BaseVal); +VP_EXPORT Real *VpAlloc(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 VpNmlz(Real *a); +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 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 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)) + +/* 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)) +#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/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/ext/bigdecimal/bits.h b/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/ext/bigdecimal/bits.h new file mode 100644 index 0000000..66efce4 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/ext/bigdecimal/bits.h @@ -0,0 +1,144 @@ +#ifndef BIGDECIMAL_BITS_H +#define BIGDECIMAL_BITS_H + +#include "feature.h" +#include "static_assert.h" + +#if defined(__x86_64__) && defined(HAVE_X86INTRIN_H) +# include /* for _lzcnt_u64, etc. */ +#elif defined(_MSC_VER) && defined(HAVE_INTRIN_H) +# include /* 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))) + +#define ADD_OVERFLOW_SIGNED_INTEGER_P(a, b, min, max) ( \ + ((a) > 0) == ((b) > 0) && ((a) > 0 ? (max) - (a) < (b) : (min) - (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/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/ext/bigdecimal/extconf.rb b/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/ext/bigdecimal/extconf.rb new file mode 100644 index 0000000..0fcc7c9 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/ext/bigdecimal/extconf.rb @@ -0,0 +1,60 @@ +# 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(< +#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 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/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/ext/bigdecimal/missing.c b/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/ext/bigdecimal/missing.c new file mode 100644 index 0000000..1454c28 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/ext/bigdecimal/missing.c @@ -0,0 +1,28 @@ +#include + +#ifdef HAVE_RUBY_ATOMIC_H +# include +#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") \ + y +#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/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/ext/bigdecimal/missing.h b/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/ext/bigdecimal/missing.h new file mode 100644 index 0000000..e8a8cab --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/ext/bigdecimal/missing.h @@ -0,0 +1,104 @@ +#ifndef MISSING_H +#define MISSING_H 1 + +#if defined(__cplusplus) +extern "C" { +#if 0 +} /* satisfy cc-mode */ +#endif +#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 */ + +#ifndef __bool_true_false_are_defined +# include +#endif + +/* dtoa */ +char *BigDecimal_dtoa(double d_, int mode, int ndigits, int *decpt, int *sign, char **rve); + +/* 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/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/ext/bigdecimal/missing/dtoa.c b/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/ext/bigdecimal/missing/dtoa.c new file mode 100644 index 0000000..41b0a22 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/ext/bigdecimal/missing/dtoa.c @@ -0,0 +1,3462 @@ +/**************************************************************** + * + * 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 + +#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 +#define Bug(x) {fprintf(stderr, "%s\n", (x)); exit(EXIT_FAILURE);} +#endif + +#ifndef ISDIGIT +#include +#define ISDIGIT(c) isdigit(c) +#endif +#include +#include +#include + +#ifdef USE_LOCALE +#include +#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 +#endif /* Bad_float_h */ + +#include + +#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 + * 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/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/ext/bigdecimal/static_assert.h b/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/ext/bigdecimal/static_assert.h new file mode 100644 index 0000000..9295729 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/ext/bigdecimal/static_assert.h @@ -0,0 +1,54 @@ +#ifndef BIGDECIMAL_STATIC_ASSERT_H +#define BIGDECIMAL_STATIC_ASSERT_H + +#include "feature.h" + +#ifdef HAVE_RUBY_INTERNAL_STATIC_ASSERT_H +# include +#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/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/lib/bigdecimal.rb b/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/lib/bigdecimal.rb new file mode 100644 index 0000000..12250ce --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/lib/bigdecimal.rb @@ -0,0 +1,360 @@ +if RUBY_ENGINE == 'jruby' + JRuby::Util.load_ext("org.jruby.ext.bigdecimal.BigDecimalLibrary") + + class BigDecimal + def _decimal_shift(i) # :nodoc: + to_java.move_point_right(i).to_d + end + end +else + require 'bigdecimal.so' +end + +class BigDecimal + module Internal # :nodoc: + + # Coerce x to BigDecimal with the specified precision. + # TODO: some methods (example: BigMath.exp) require more precision than specified to coerce. + def self.coerce_to_bigdecimal(x, prec, method_name) # :nodoc: + case x + when BigDecimal + return x + when Integer, Float + return BigDecimal(x, 0) + when Rational + return BigDecimal(x, [prec, 2 * BigDecimal.double_fig].max) + end + raise ArgumentError, "#{x.inspect} can't be coerced into BigDecimal" + end + + def self.coerce_validate_prec(prec, method_name, accept_zero: false) # :nodoc: + unless Integer === prec + original = prec + # Emulate Integer.try_convert for ruby < 3.1 + if prec.respond_to?(:to_int) + prec = prec.to_int + else + raise TypeError, "no implicit conversion of #{original.class} into Integer" + end + raise TypeError, "can't convert #{original.class} to Integer" unless Integer === prec + end + + if accept_zero + raise ArgumentError, "Negative precision for #{method_name}" if prec < 0 + else + raise ArgumentError, "Zero or negative precision for #{method_name}" if prec <= 0 + end + prec + end + + def self.infinity_computation_result # :nodoc: + if BigDecimal.mode(BigDecimal::EXCEPTION_ALL).anybits?(BigDecimal::EXCEPTION_INFINITY) + raise FloatDomainError, "Computation results in 'Infinity'" + end + BigDecimal::INFINITY + end + + def self.nan_computation_result # :nodoc: + if BigDecimal.mode(BigDecimal::EXCEPTION_ALL).anybits?(BigDecimal::EXCEPTION_NaN) + raise FloatDomainError, "Computation results to 'NaN'" + end + BigDecimal::NAN + end + end + + # 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. + # + def **(y) + case y + when BigDecimal, Integer, Float, Rational + power(y) + when nil + raise TypeError, 'wrong argument type NilClass' + else + x, y = y.coerce(self) + x**y + end + end + + # call-seq: + # power(n) + # power(n, prec) + # + # Returns the value raised to the power of n. + # + # Also available as the operator **. + # + def power(y, prec = 0) + prec = Internal.coerce_validate_prec(prec, :power, accept_zero: true) + x = self + y = Internal.coerce_to_bigdecimal(y, prec.nonzero? || n_significant_digits, :power) + + return Internal.nan_computation_result if x.nan? || y.nan? + return BigDecimal(1) if y.zero? + + if y.infinite? + if x < 0 + return BigDecimal(0) if x < -1 && y.negative? + return BigDecimal(0) if x > -1 && y.positive? + raise Math::DomainError, 'Result undefined for negative base raised to infinite power' + elsif x < 1 + return y.positive? ? BigDecimal(0) : BigDecimal::Internal.infinity_computation_result + elsif x == 1 + return BigDecimal(1) + else + return y.positive? ? BigDecimal::Internal.infinity_computation_result : BigDecimal(0) + end + end + + if x.infinite? && y < 0 + # Computation result will be +0 or -0. Avoid overflow. + neg = x < 0 && y.frac.zero? && y % 2 == 1 + return neg ? -BigDecimal(0) : BigDecimal(0) + end + + if x.zero? + return BigDecimal(1) if y.zero? + return BigDecimal(0) if y > 0 + if y.frac.zero? && y % 2 == 1 && x.sign == -1 + return -BigDecimal::Internal.infinity_computation_result + else + return BigDecimal::Internal.infinity_computation_result + end + elsif x < 0 + if y.frac.zero? + if y % 2 == 0 + return (-x).power(y, prec) + else + return -(-x).power(y, prec) + end + else + raise Math::DomainError, 'Computation results in complex number' + end + elsif x == 1 + return BigDecimal(1) + end + + limit = BigDecimal.limit + frac_part = y.frac + + if frac_part.zero? && prec.zero? && limit.zero? + # Infinite precision calculation for `x ** int` and `x.power(int)` + int_part = y.fix.to_i + int_part = -int_part if (neg = int_part < 0) + ans = BigDecimal(1) + n = 1 + xn = x + while true + ans *= xn if int_part.allbits?(n) + n <<= 1 + break if n > int_part + xn *= xn + # Detect overflow/underflow before consuming infinite memory + if (xn.exponent.abs - 1) * int_part / n >= 0x7FFFFFFFFFFFFFFF + return ((xn.exponent > 0) ^ neg ? BigDecimal::Internal.infinity_computation_result : BigDecimal(0)) * (int_part.even? || x > 0 ? 1 : -1) + end + end + return neg ? BigDecimal(1) / ans : ans + end + + result_prec = prec.nonzero? || [x.n_significant_digits, y.n_significant_digits, BigDecimal.double_fig].max + BigDecimal.double_fig + result_prec = [result_prec, limit].min if prec.zero? && limit.nonzero? + + prec2 = result_prec + BigDecimal.double_fig + + if y < 0 + inv = x.power(-y, prec2) + return BigDecimal(0) if inv.infinite? + return BigDecimal::Internal.infinity_computation_result if inv.zero? + return BigDecimal(1).div(inv, result_prec) + end + + if frac_part.zero? && y.exponent < Math.log(result_prec) * 5 + 20 + # Use exponentiation by squaring if y is an integer and not too large + pow_prec = prec2 + y.exponent + n = 1 + xn = x + ans = BigDecimal(1) + int_part = y.fix.to_i + while true + ans = ans.mult(xn, pow_prec) if int_part.allbits?(n) + n <<= 1 + break if n > int_part + xn = xn.mult(xn, pow_prec) + end + ans.mult(1, result_prec) + else + if x > 1 && x.finite? + # To calculate exp(z, prec), z needs prec+max(z.exponent, 0) precision if z > 0. + # Estimate (y*log(x)).exponent + logx_exponent = x < 2 ? (x - 1).exponent : Math.log10(x.exponent).round + ylogx_exponent = y.exponent + logx_exponent + prec2 += [ylogx_exponent, 0].max + end + BigMath.exp(BigMath.log(x, prec2).mult(y, prec2), result_prec) + end + end + + # Returns the square root of the value. + # + # Result has at least prec significant digits. + # + def sqrt(prec) + prec = Internal.coerce_validate_prec(prec, :sqrt, accept_zero: true) + return Internal.infinity_computation_result if infinite? == 1 + + raise FloatDomainError, 'sqrt of negative value' if self < 0 + raise FloatDomainError, "sqrt of 'NaN'(Not a Number)" if nan? + return self if zero? + + if prec == 0 + limit = BigDecimal.limit + prec = n_significant_digits + BigDecimal.double_fig + prec = [limit, prec].min if limit.nonzero? + end + + ex = exponent / 2 + x = _decimal_shift(-2 * ex) + y = BigDecimal(Math.sqrt(x.to_f), 0) + precs = [prec + BigDecimal.double_fig] + precs << 2 + precs.last / 2 while precs.last > BigDecimal.double_fig + precs.reverse_each do |p| + y = y.add(x.div(y, p), p).div(2, p) + end + y._decimal_shift(ex).mult(1, prec) + end +end + +# Core BigMath methods for BigDecimal (log, exp) are defined here. +# Other methods (sin, cos, atan) are defined in 'bigdecimal/math.rb'. +module BigMath + module_function + + # 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. + # + def log(x, prec) + prec = BigDecimal::Internal.coerce_validate_prec(prec, :log) + raise Math::DomainError, 'Complex argument for BigMath.log' if Complex === x + + x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :log) + return BigDecimal::Internal.nan_computation_result if x.nan? + raise Math::DomainError, 'Negative argument for log' if x < 0 + return -BigDecimal::Internal.infinity_computation_result if x.zero? + return BigDecimal::Internal.infinity_computation_result if x.infinite? + return BigDecimal(0) if x == 1 + + prec2 = prec + BigDecimal.double_fig + BigDecimal.save_limit do + BigDecimal.limit(0) + if x > 10 || x < 0.1 + log10 = log(BigDecimal(10), prec2) + exponent = x.exponent + x = x._decimal_shift(-exponent) + if x < 0.3 + x *= 10 + exponent -= 1 + end + return (log10 * exponent).add(log(x, prec2), prec) + end + + x_minus_one_exponent = (x - 1).exponent + + # log(x) = log(sqrt(sqrt(sqrt(sqrt(x))))) * 2**sqrt_steps + sqrt_steps = [Integer.sqrt(prec2) + 3 * x_minus_one_exponent, 0].max + + lg2 = 0.3010299956639812 + sqrt_prec = prec2 + [-x_minus_one_exponent, 0].max + (sqrt_steps * lg2).ceil + + sqrt_steps.times do + x = x.sqrt(sqrt_prec) + end + + # Taylor series for log(x) around 1 + # log(x) = -log((1 + X) / (1 - X)) where X = (x - 1) / (x + 1) + # log(x) = 2 * (X + X**3 / 3 + X**5 / 5 + X**7 / 7 + ...) + x = (x - 1).div(x + 1, sqrt_prec) + y = x + x2 = x.mult(x, prec2) + 1.step do |i| + n = prec2 + x.exponent - y.exponent + x2.exponent + break if n <= 0 || x.zero? + x = x.mult(x2.round(n - x2.exponent), n) + y = y.add(x.div(2 * i + 1, n), prec2) + end + + y.mult(2 ** (sqrt_steps + 1), prec) + end + end + + # Taylor series for exp(x) around 0 + private_class_method def _exp_taylor(x, prec) # :nodoc: + xn = BigDecimal(1) + y = BigDecimal(1) + 1.step do |i| + n = prec + xn.exponent + break if n <= 0 || xn.zero? + xn = xn.mult(x, n).div(i, n) + y = y.add(xn, prec) + end + y + end + + # 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. + # + def exp(x, prec) + prec = BigDecimal::Internal.coerce_validate_prec(prec, :exp) + x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :exp) + return BigDecimal::Internal.nan_computation_result if x.nan? + return x.positive? ? BigDecimal::Internal.infinity_computation_result : BigDecimal(0) if x.infinite? + return BigDecimal(1) if x.zero? + + # exp(x * 10**cnt) = exp(x)**(10**cnt) + cnt = x < -1 || x > 1 ? x.exponent : 0 + prec2 = prec + BigDecimal.double_fig + cnt + x = x._decimal_shift(-cnt) + + # Calculation of exp(small_prec) is fast because calculation of x**n is fast + # Calculation of exp(small_abs) converges fast. + # exp(x) = exp(small_prec_part + small_abs_part) = exp(small_prec_part) * exp(small_abs_part) + x_small_prec = x.round(Integer.sqrt(prec2)) + y = _exp_taylor(x_small_prec, prec2).mult(_exp_taylor(x.sub(x_small_prec, prec2), prec2), prec2) + + # calculate exp(x * 10**cnt) from exp(x) + # exp(x * 10**k) = exp(x * 10**(k - 1)) ** 10 + cnt.times do + y2 = y.mult(y, prec2) + y5 = y2.mult(y2, prec2).mult(y, prec2) + y = y5.mult(y5, prec2) + end + + y.mult(1, prec) + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/lib/bigdecimal.so b/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/lib/bigdecimal.so new file mode 100755 index 0000000..12bf3f3 Binary files /dev/null and b/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/lib/bigdecimal.so differ diff --git a/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/lib/bigdecimal/jacobian.rb b/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/lib/bigdecimal/jacobian.rb new file mode 100644 index 0000000..9a5e5be --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/lib/bigdecimal/jacobian.rb @@ -0,0 +1,92 @@ +# frozen_string_literal: false + +require 'bigdecimal' + +warn "'bigdecimal/jacobian' is deprecated and will be removed in a future release." + +# 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/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/lib/bigdecimal/ludcmp.rb b/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/lib/bigdecimal/ludcmp.rb new file mode 100644 index 0000000..d112fc1 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/lib/bigdecimal/ludcmp.rb @@ -0,0 +1,91 @@ +# frozen_string_literal: false +require 'bigdecimal' + +warn "'bigdecimal/ludcmp' is deprecated and will be removed in a future release." + +# +# 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/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/lib/bigdecimal/math.rb b/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/lib/bigdecimal/math.rb new file mode 100644 index 0000000..a57ad11 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/lib/bigdecimal/math.rb @@ -0,0 +1,948 @@ +# frozen_string_literal: false +require 'bigdecimal' + +# +#-- +# Contents: +# sqrt(x, prec) +# cbrt(x, prec) +# hypot(x, y, prec) +# sin (x, prec) +# cos (x, prec) +# tan (x, prec) +# asin(x, prec) +# acos(x, prec) +# atan(x, prec) +# atan2(y, x, prec) +# sinh (x, prec) +# cosh (x, prec) +# tanh (x, prec) +# asinh(x, prec) +# acosh(x, prec) +# atanh(x, prec) +# log2 (x, prec) +# log10(x, prec) +# log1p(x, prec) +# expm1(x, prec) +# erf (x, prec) +# erfc(x, prec) +# gamma(x, prec) +# lgamma(x, prec) +# frexp(x) +# ldexp(x, exponent) +# PI (prec) +# E (prec) == exp(1.0,prec) +# +# where: +# x, y ... BigDecimal number to be computed. +# prec ... Number of digits to be obtained. +#++ +# +# Provides mathematical functions. +# +# Example: +# +# require "bigdecimal/math" +# +# include BigMath +# +# a = BigDecimal((PI(49)/2).to_s) +# puts sin(a,100) # => 0.9999999999...9999999986e0 +# +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'), 32).to_s + # #=> "0.14142135623730950488016887242097e1" + # + def sqrt(x, prec) + prec = BigDecimal::Internal.coerce_validate_prec(prec, :sqrt) + x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :sqrt) + x.sqrt(prec) + end + + + # Returns [sign, reduced_x] where reduced_x is in -pi/2..pi/2 + # and satisfies sin(x) = sign * sin(reduced_x) + # If add_half_pi is true, adds pi/2 to x before reduction. + # Precision of pi is adjusted to ensure reduced_x has the required precision. + private_class_method def _sin_periodic_reduction(x, prec, add_half_pi: false) # :nodoc: + return [1, x] if -Math::PI/2 <= x && x <= Math::PI/2 && !add_half_pi + + mod_prec = prec + BigDecimal.double_fig + pi_extra_prec = [x.exponent, 0].max + BigDecimal.double_fig + while true + pi = PI(mod_prec + pi_extra_prec) + half_pi = pi / 2 + div, mod = (add_half_pi ? x + pi : x + half_pi).divmod(pi) + mod -= half_pi + if mod.zero? || mod_prec + mod.exponent <= 0 + # mod is too small to estimate required pi precision + mod_prec = mod_prec * 3 / 2 + BigDecimal.double_fig + elsif mod_prec + mod.exponent < prec + # Estimate required precision of pi + mod_prec = prec - mod.exponent + BigDecimal.double_fig + else + return [div % 2 == 0 ? 1 : -1, mod.mult(1, prec)] + end + end + end + + # call-seq: + # cbrt(decimal, numeric) -> BigDecimal + # + # Computes the cube root of +decimal+ to the specified number of digits of + # precision, +numeric+. + # + # BigMath.cbrt(BigDecimal('2'), 32).to_s + # #=> "0.12599210498948731647672106072782e1" + # + def cbrt(x, prec) + prec = BigDecimal::Internal.coerce_validate_prec(prec, :cbrt) + x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :cbrt) + return BigDecimal::Internal.nan_computation_result if x.nan? + return BigDecimal::Internal.infinity_computation_result * x.infinite? if x.infinite? + return BigDecimal(0) if x.zero? + + x = -x if neg = x < 0 + ex = x.exponent / 3 + x = x._decimal_shift(-3 * ex) + y = BigDecimal(Math.cbrt(x.to_f), 0) + precs = [prec + BigDecimal.double_fig] + precs << 2 + precs.last / 2 while precs.last > BigDecimal.double_fig + precs.reverse_each do |p| + y = (2 * y + x.div(y, p).div(y, p)).div(3, p) + end + y._decimal_shift(ex).mult(neg ? -1 : 1, prec) + end + + # call-seq: + # hypot(x, y, numeric) -> BigDecimal + # + # Returns sqrt(x**2 + y**2) to the specified number of digits of + # precision, +numeric+. + # + # BigMath.hypot(BigDecimal('1'), BigDecimal('2'), 32).to_s + # #=> "0.22360679774997896964091736687313e1" + # + def hypot(x, y, prec) + prec = BigDecimal::Internal.coerce_validate_prec(prec, :hypot) + x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :hypot) + y = BigDecimal::Internal.coerce_to_bigdecimal(y, prec, :hypot) + return BigDecimal::Internal.nan_computation_result if x.nan? || y.nan? + return BigDecimal::Internal.infinity_computation_result if x.infinite? || y.infinite? + prec2 = prec + BigDecimal.double_fig + sqrt(x.mult(x, prec2) + y.mult(y, prec2), 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, 32).to_s + # #=> "0.70710807985947359435812921837984e0" + # + def sin(x, prec) + prec = BigDecimal::Internal.coerce_validate_prec(prec, :sin) + x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :sin) + return BigDecimal::Internal.nan_computation_result if x.infinite? || x.nan? + n = prec + BigDecimal.double_fig + one = BigDecimal("1") + two = BigDecimal("2") + sign, x = _sin_periodic_reduction(x, n) + x1 = x + x2 = x.mult(x,n) + 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 + x1 = -x2.mult(x1,n) + i += two + z *= (i-one) * i + d = x1.div(z,m) + y += d + end + y = BigDecimal("1") if y > 1 + y.mult(sign, prec) + 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(16), 32).to_s + # #=> "-0.99999999999999999999999999999997e0" + # + def cos(x, prec) + prec = BigDecimal::Internal.coerce_validate_prec(prec, :cos) + x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :cos) + return BigDecimal::Internal.nan_computation_result if x.infinite? || x.nan? + sign, x = _sin_periodic_reduction(x, prec + BigDecimal.double_fig, add_half_pi: true) + sign * sin(x, prec) + end + + # call-seq: + # tan(decimal, numeric) -> BigDecimal + # + # Computes the tangent of +decimal+ to the specified number of digits of + # precision, +numeric+. + # + # If +decimal+ is Infinity or NaN, returns NaN. + # + # BigMath.tan(BigDecimal("0.0"), 4).to_s + # #=> "0.0" + # + # BigMath.tan(BigMath.PI(24) / 4, 32).to_s + # #=> "0.99999999999999999999999830836025e0" + # + def tan(x, prec) + prec = BigDecimal::Internal.coerce_validate_prec(prec, :tan) + sin(x, prec + BigDecimal.double_fig).div(cos(x, prec + BigDecimal.double_fig), prec) + end + + # call-seq: + # asin(decimal, numeric) -> BigDecimal + # + # Computes the arcsine of +decimal+ to the specified number of digits of + # precision, +numeric+. + # + # If +decimal+ is NaN, returns NaN. + # + # BigMath.asin(BigDecimal('0.5'), 32).to_s + # #=> "0.52359877559829887307710723054658e0" + # + def asin(x, prec) + prec = BigDecimal::Internal.coerce_validate_prec(prec, :asin) + x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :asin) + raise Math::DomainError, "Out of domain argument for asin" if x < -1 || x > 1 + return BigDecimal::Internal.nan_computation_result if x.nan? + + prec2 = prec + BigDecimal.double_fig + cos = (1 - x**2).sqrt(prec2) + if cos.zero? + PI(prec2).div(x > 0 ? 2 : -2, prec) + else + atan(x.div(cos, prec2), prec) + end + end + + # call-seq: + # acos(decimal, numeric) -> BigDecimal + # + # Computes the arccosine of +decimal+ to the specified number of digits of + # precision, +numeric+. + # + # If +decimal+ is NaN, returns NaN. + # + # BigMath.acos(BigDecimal('0.5'), 32).to_s + # #=> "0.10471975511965977461542144610932e1" + # + def acos(x, prec) + prec = BigDecimal::Internal.coerce_validate_prec(prec, :acos) + x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :acos) + raise Math::DomainError, "Out of domain argument for acos" if x < -1 || x > 1 + return BigDecimal::Internal.nan_computation_result if x.nan? + + prec2 = prec + BigDecimal.double_fig + return (PI(prec2) / 2).sub(asin(x, prec2), prec) if x < 0 + return PI(prec2).div(2, prec) if x.zero? + + sin = (1 - x**2).sqrt(prec2) + atan(sin.div(x, prec2), prec) + 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'), 32).to_s + # #=> "-0.78539816339744830961566084581988e0" + # + def atan(x, prec) + prec = BigDecimal::Internal.coerce_validate_prec(prec, :atan) + x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :atan) + return BigDecimal::Internal.nan_computation_result if x.nan? + n = prec + BigDecimal.double_fig + pi = PI(n) + x = -x if neg = x < 0 + return pi.div(neg ? -2 : 2, prec) if x.infinite? + return pi.div(neg ? -4 : 4, prec) if x.round(n) == 1 + x = BigDecimal("1").div(x, n) if inv = x > 1 + x = (-1 + sqrt(1 + x.mult(x, n), n)).div(x, n) if dbl = x > 0.5 + 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.mult(1, prec) + end + + # call-seq: + # atan2(decimal, decimal, numeric) -> BigDecimal + # + # Computes the arctangent of y and x to the specified number of digits of + # precision, +numeric+. + # + # BigMath.atan2(BigDecimal('-1'), BigDecimal('1'), 32).to_s + # #=> "-0.78539816339744830961566084581988e0" + # + def atan2(y, x, prec) + prec = BigDecimal::Internal.coerce_validate_prec(prec, :atan2) + x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :atan2) + y = BigDecimal::Internal.coerce_to_bigdecimal(y, prec, :atan2) + return BigDecimal::Internal.nan_computation_result if x.nan? || y.nan? + + if x.infinite? || y.infinite? + one = BigDecimal(1) + zero = BigDecimal(0) + x = x.infinite? ? (x > 0 ? one : -one) : zero + y = y.infinite? ? (y > 0 ? one : -one) : y.sign * zero + end + + return x.sign >= 0 ? BigDecimal(0) : y.sign * PI(prec) if y.zero? + + y = -y if neg = y < 0 + xlarge = y.abs < x.abs + prec2 = prec + BigDecimal.double_fig + if x > 0 + v = xlarge ? atan(y.div(x, prec2), prec) : PI(prec2) / 2 - atan(x.div(y, prec2), prec2) + else + v = xlarge ? PI(prec2) - atan(-y.div(x, prec2), prec2) : PI(prec2) / 2 + atan(x.div(-y, prec2), prec2) + end + v.mult(neg ? -1 : 1, prec) + end + + # call-seq: + # sinh(decimal, numeric) -> BigDecimal + # + # Computes the hyperbolic sine of +decimal+ to the specified number of digits of + # precision, +numeric+. + # + # If +decimal+ is NaN, returns NaN. + # + # BigMath.sinh(BigDecimal('1'), 32).to_s + # #=> "0.11752011936438014568823818505956e1" + # + def sinh(x, prec) + prec = BigDecimal::Internal.coerce_validate_prec(prec, :sinh) + x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :sinh) + return BigDecimal::Internal.nan_computation_result if x.nan? + return BigDecimal::Internal.infinity_computation_result * x.infinite? if x.infinite? + + prec2 = prec + BigDecimal.double_fig + prec2 -= x.exponent if x.exponent < 0 + e = exp(x, prec2) + (e - BigDecimal(1).div(e, prec2)).div(2, prec) + end + + # call-seq: + # cosh(decimal, numeric) -> BigDecimal + # + # Computes the hyperbolic cosine of +decimal+ to the specified number of digits of + # precision, +numeric+. + # + # If +decimal+ is NaN, returns NaN. + # + # BigMath.cosh(BigDecimal('1'), 32).to_s + # #=> "0.15430806348152437784779056207571e1" + # + def cosh(x, prec) + prec = BigDecimal::Internal.coerce_validate_prec(prec, :cosh) + x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :cosh) + return BigDecimal::Internal.nan_computation_result if x.nan? + return BigDecimal::Internal.infinity_computation_result if x.infinite? + + prec2 = prec + BigDecimal.double_fig + e = exp(x, prec2) + (e + BigDecimal(1).div(e, prec2)).div(2, prec) + end + + # call-seq: + # tanh(decimal, numeric) -> BigDecimal + # + # Computes the hyperbolic tangent of +decimal+ to the specified number of digits of + # precision, +numeric+. + # + # If +decimal+ is NaN, returns NaN. + # + # BigMath.tanh(BigDecimal('1'), 32).to_s + # #=> "0.76159415595576488811945828260479e0" + # + def tanh(x, prec) + prec = BigDecimal::Internal.coerce_validate_prec(prec, :tanh) + x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :tanh) + return BigDecimal::Internal.nan_computation_result if x.nan? + return BigDecimal(x.infinite?) if x.infinite? + + prec2 = prec + BigDecimal.double_fig + [-x.exponent, 0].max + e = exp(x, prec2) + einv = BigDecimal(1).div(e, prec2) + (e - einv).div(e + einv, prec) + end + + # call-seq: + # asinh(decimal, numeric) -> BigDecimal + # + # Computes the inverse hyperbolic sine of +decimal+ to the specified number of digits of + # precision, +numeric+. + # + # If +decimal+ is NaN, returns NaN. + # + # BigMath.asinh(BigDecimal('1'), 32).to_s + # #=> "0.88137358701954302523260932497979e0" + # + def asinh(x, prec) + prec = BigDecimal::Internal.coerce_validate_prec(prec, :asinh) + x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :asinh) + return BigDecimal::Internal.nan_computation_result if x.nan? + return BigDecimal::Internal.infinity_computation_result * x.infinite? if x.infinite? + return -asinh(-x, prec) if x < 0 + + sqrt_prec = prec + [-x.exponent, 0].max + BigDecimal.double_fig + log(x + sqrt(x**2 + 1, sqrt_prec), prec) + end + + # call-seq: + # acosh(decimal, numeric) -> BigDecimal + # + # Computes the inverse hyperbolic cosine of +decimal+ to the specified number of digits of + # precision, +numeric+. + # + # If +decimal+ is NaN, returns NaN. + # + # BigMath.acosh(BigDecimal('2'), 32).to_s + # #=> "0.1316957896924816708625046347308e1" + # + def acosh(x, prec) + prec = BigDecimal::Internal.coerce_validate_prec(prec, :acosh) + x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :acosh) + raise Math::DomainError, "Out of domain argument for acosh" if x < 1 + return BigDecimal::Internal.infinity_computation_result if x.infinite? + return BigDecimal::Internal.nan_computation_result if x.nan? + + log(x + sqrt(x**2 - 1, prec + BigDecimal.double_fig), prec) + end + + # call-seq: + # atanh(decimal, numeric) -> BigDecimal + # + # Computes the inverse hyperbolic tangent of +decimal+ to the specified number of digits of + # precision, +numeric+. + # + # If +decimal+ is NaN, returns NaN. + # + # BigMath.atanh(BigDecimal('0.5'), 32).to_s + # #=> "0.54930614433405484569762261846126e0" + # + def atanh(x, prec) + prec = BigDecimal::Internal.coerce_validate_prec(prec, :atanh) + x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :atanh) + raise Math::DomainError, "Out of domain argument for atanh" if x < -1 || x > 1 + return BigDecimal::Internal.nan_computation_result if x.nan? + return BigDecimal::Internal.infinity_computation_result if x == 1 + return -BigDecimal::Internal.infinity_computation_result if x == -1 + + prec2 = prec + BigDecimal.double_fig + (log(x + 1, prec2) - log(1 - x, prec2)).div(2, prec) + end + + # call-seq: + # BigMath.log2(decimal, numeric) -> BigDecimal + # + # Computes the base 2 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. + # + # BigMath.log2(BigDecimal('3'), 32).to_s + # #=> "0.15849625007211561814537389439478e1" + # + def log2(x, prec) + prec = BigDecimal::Internal.coerce_validate_prec(prec, :log2) + x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :log2) + return BigDecimal::Internal.nan_computation_result if x.nan? + return BigDecimal::Internal.infinity_computation_result if x.infinite? == 1 + + prec2 = prec + BigDecimal.double_fig * 3 / 2 + v = log(x, prec2).div(log(BigDecimal(2), prec2), prec2) + # Perform half-up rounding to calculate log2(2**n)==n correctly in every rounding mode + v = v.round(prec + BigDecimal.double_fig - (v.exponent < 0 ? v.exponent : 0), BigDecimal::ROUND_HALF_UP) + v.mult(1, prec) + end + + # call-seq: + # BigMath.log10(decimal, numeric) -> BigDecimal + # + # Computes the base 10 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. + # + # BigMath.log10(BigDecimal('3'), 32).to_s + # #=> "0.47712125471966243729502790325512e0" + # + def log10(x, prec) + prec = BigDecimal::Internal.coerce_validate_prec(prec, :log10) + x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :log10) + return BigDecimal::Internal.nan_computation_result if x.nan? + return BigDecimal::Internal.infinity_computation_result if x.infinite? == 1 + + prec2 = prec + BigDecimal.double_fig * 3 / 2 + v = log(x, prec2).div(log(BigDecimal(10), prec2), prec2) + # Perform half-up rounding to calculate log10(10**n)==n correctly in every rounding mode + v = v.round(prec + BigDecimal.double_fig - (v.exponent < 0 ? v.exponent : 0), BigDecimal::ROUND_HALF_UP) + v.mult(1, prec) + end + + # call-seq: + # BigMath.log1p(decimal, numeric) -> BigDecimal + # + # Computes log(1 + decimal) to the specified number of digits of precision, +numeric+. + # + # BigMath.log1p(BigDecimal('0.1'), 32).to_s + # #=> "0.95310179804324860043952123280765e-1" + # + def log1p(x, prec) + prec = BigDecimal::Internal.coerce_validate_prec(prec, :log1p) + x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :log1p) + raise Math::DomainError, 'Out of domain argument for log1p' if x < -1 + + return log(x + 1, prec) + end + + # call-seq: + # BigMath.expm1(decimal, numeric) -> BigDecimal + # + # Computes exp(decimal) - 1 to the specified number of digits of precision, +numeric+. + # + # BigMath.expm1(BigDecimal('0.1'), 32).to_s + # #=> "0.10517091807564762481170782649025e0" + # + def expm1(x, prec) + prec = BigDecimal::Internal.coerce_validate_prec(prec, :expm1) + x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :expm1) + return BigDecimal(-1) if x.infinite? == -1 + + exp_prec = prec + if x < -1 + # log10(exp(x)) = x * log10(e) + lg_e = 0.4342944819032518 + exp_prec = prec + (lg_e * x).ceil + BigDecimal.double_fig + elsif x < 1 + exp_prec = prec - x.exponent + BigDecimal.double_fig + else + exp_prec = prec + end + + return BigDecimal(-1) if exp_prec <= 0 + + exp = exp(x, exp_prec) + + if exp.exponent > prec + BigDecimal.double_fig + # Workaroudn for https://github.com/ruby/bigdecimal/issues/464 + exp + else + exp.sub(1, prec) + end + end + + # erf(decimal, numeric) -> BigDecimal + # + # Computes the error function of +decimal+ to the specified number of digits of + # precision, +numeric+. + # + # If +decimal+ is NaN, returns NaN. + # + # BigMath.erf(BigDecimal('1'), 32).to_s + # #=> "0.84270079294971486934122063508261e0" + # + def erf(x, prec) + prec = BigDecimal::Internal.coerce_validate_prec(prec, :erf) + x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :erf) + return BigDecimal::Internal.nan_computation_result if x.nan? + return BigDecimal(x.infinite?) if x.infinite? + return BigDecimal(0) if x == 0 + return -erf(-x, prec) if x < 0 + return BigDecimal(1) if x > 5000000000 # erf(5000000000) > 1 - 1e-10000000000000000000 + + if x > 8 + xf = x.to_f + log10_erfc = -xf ** 2 / Math.log(10) - Math.log10(xf * Math::PI ** 0.5) + erfc_prec = [prec + log10_erfc.ceil, 1].max + erfc = _erfc_asymptotic(x, erfc_prec) + if erfc + # Workaround for https://github.com/ruby/bigdecimal/issues/464 + return BigDecimal(1) if erfc.exponent < -prec - BigDecimal.double_fig + + return BigDecimal(1).sub(erfc, prec) + end + end + + prec2 = prec + BigDecimal.double_fig + x_smallprec = x.mult(1, Integer.sqrt(prec2) / 2) + # Taylor series of x with small precision is fast + erf1 = _erf_taylor(x_smallprec, BigDecimal(0), BigDecimal(0), prec2) + # Taylor series converges quickly for small x + _erf_taylor(x - x_smallprec, x_smallprec, erf1, prec2).mult(1, prec) + end + + # call-seq: + # erfc(decimal, numeric) -> BigDecimal + # + # Computes the complementary error function of +decimal+ to the specified number of digits of + # precision, +numeric+. + # + # If +decimal+ is NaN, returns NaN. + # + # BigMath.erfc(BigDecimal('10'), 32).to_s + # #=> "0.20884875837625447570007862949578e-44" + # + def erfc(x, prec) + prec = BigDecimal::Internal.coerce_validate_prec(prec, :erfc) + x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :erfc) + return BigDecimal::Internal.nan_computation_result if x.nan? + return BigDecimal(1 - x.infinite?) if x.infinite? + return BigDecimal(1).sub(erf(x, prec + BigDecimal.double_fig), prec) if x < 0 + return BigDecimal(0) if x > 5000000000 # erfc(5000000000) < 1e-10000000000000000000 (underflow) + + if x >= 8 + y = _erfc_asymptotic(x, prec) + return y.mult(1, prec) if y + end + + # erfc(x) = 1 - erf(x) < exp(-x**2)/x/sqrt(pi) + # Precision of erf(x) needs about log10(exp(-x**2)) extra digits + log10 = 2.302585092994046 + high_prec = prec + BigDecimal.double_fig + (x.ceil**2 / log10).ceil + BigDecimal(1).sub(erf(x, high_prec), prec) + end + + # Calculates erf(x + a) + private_class_method def _erf_taylor(x, a, erf_a, prec) # :nodoc: + return erf_a if x.zero? + # Let f(x+a) = erf(x+a)*exp((x+a)**2)*sqrt(pi)/2 + # = c0 + c1*x + c2*x**2 + c3*x**3 + c4*x**4 + ... + # f'(x+a) = 1+2*(x+a)*f(x+a) + # f'(x+a) = c1 + 2*c2*x + 3*c3*x**2 + 4*c4*x**3 + 5*c5*x**4 + ... + # = 1+2*(x+a)*(c0 + c1*x + c2*x**2 + c3*x**3 + c4*x**4 + ...) + # therefore, + # c0 = f(a) + # c1 = 2 * a * c0 + 1 + # c2 = (2 * c0 + 2 * a * c1) / 2 + # c3 = (2 * c1 + 2 * a * c2) / 3 + # c4 = (2 * c2 + 2 * a * c3) / 4 + # + # All coefficients are positive when a >= 0 + + scale = BigDecimal(2).div(sqrt(PI(prec), prec), prec) + c_prev = erf_a.div(scale.mult(exp(-a*a, prec), prec), prec) + c_next = (2 * a * c_prev).add(1, prec).mult(x, prec) + sum = c_prev.add(c_next, prec) + + 2.step do |k| + cn = (c_prev.mult(x, prec) + a * c_next).mult(2, prec).mult(x, prec).div(k, prec) + sum = sum.add(cn, prec) + c_prev, c_next = c_next, cn + break if [c_prev, c_next].all? { |c| c.zero? || (c.exponent < sum.exponent - prec) } + end + value = sum.mult(scale.mult(exp(-(x + a).mult(x + a, prec), prec), prec), prec) + value > 1 ? BigDecimal(1) : value + end + + private_class_method def _erfc_asymptotic(x, prec) # :nodoc: + # Let f(x) = erfc(x)*sqrt(pi)*exp(x**2)/2 + # f(x) satisfies the following differential equation: + # 2*x*f(x) = f'(x) + 1 + # From the above equation, we can derive the following asymptotic expansion: + # f(x) = (0..kmax).sum { (-1)**k * (2*k)! / 4**k / k! / x**(2*k)) } / x + + # This asymptotic expansion does not converge. + # But if there is a k that satisfies (2*k)! / 4**k / k! / x**(2*k) < 10**(-prec), + # It is enough to calculate erfc within the given precision. + # Using Stirling's approximation, we can simplify this condition to: + # sqrt(2)/2 + k*log(k) - k - 2*k*log(x) < -prec*log(10) + # and the left side is minimized when k = x**2. + prec += BigDecimal.double_fig + xf = x.to_f + kmax = (1..(xf ** 2).floor).bsearch do |k| + Math.log(2) / 2 + k * Math.log(k) - k - 2 * k * Math.log(xf) < -prec * Math.log(10) + end + return unless kmax + + sum = BigDecimal(1) + x2 = x.mult(x, prec) + d = BigDecimal(1) + (1..kmax).each do |k| + d = d.div(x2, prec).mult(1 - 2 * k, prec).div(2, prec) + sum = sum.add(d, prec) + end + sum.div(exp(x2, prec).mult(PI(prec).sqrt(prec), prec), prec).div(x, prec) + end + + # call-seq: + # BigMath.gamma(decimal, numeric) -> BigDecimal + # + # Computes the gamma function of +decimal+ to the specified number of + # digits of precision, +numeric+. + # + # BigMath.gamma(BigDecimal('0.5'), 32).to_s + # #=> "0.17724538509055160272981674833411e1" + # + def gamma(x, prec) + prec = BigDecimal::Internal.coerce_validate_prec(prec, :gamma) + x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :gamma) + prec2 = prec + BigDecimal.double_fig + if x < 0.5 + raise Math::DomainError, 'Numerical argument is out of domain - gamma' if x.frac.zero? + + # Euler's reflection formula: gamma(z) * gamma(1-z) = pi/sin(pi*z) + pi = PI(prec2) + sin = _sinpix(x, pi, prec2) + return pi.div(gamma(1 - x, prec2).mult(sin, prec2), prec) + elsif x.frac.zero? && x < 1000 * prec + return _gamma_positive_integer(x, prec2).mult(1, prec) + end + + a, sum = _gamma_spouge_sum_part(x, prec2) + (x + (a - 1)).power(x - 0.5, prec2).mult(BigMath.exp(1 - x, prec2), prec2).mult(sum, prec) + end + + # call-seq: + # BigMath.lgamma(decimal, numeric) -> [BigDecimal, Integer] + # + # Computes the natural logarithm of the absolute value of the gamma function + # of +decimal+ to the specified number of digits of precision, +numeric+ and its sign. + # + # BigMath.lgamma(BigDecimal('0.5'), 32) + # #=> [0.57236494292470008707171367567653e0, 1] + # + def lgamma(x, prec) + prec = BigDecimal::Internal.coerce_validate_prec(prec, :lgamma) + x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :lgamma) + prec2 = prec + BigDecimal.double_fig + if x < 0.5 + return [BigDecimal::INFINITY, 1] if x.frac.zero? + + loop do + # Euler's reflection formula: gamma(z) * gamma(1-z) = pi/sin(pi*z) + pi = PI(prec2) + sin = _sinpix(x, pi, prec2) + log_gamma = BigMath.log(pi, prec2).sub(lgamma(1 - x, prec2).first + BigMath.log(sin.abs, prec2), prec) + return [log_gamma, sin > 0 ? 1 : -1] if prec2 + log_gamma.exponent > prec + BigDecimal.double_fig + + # Retry with higher precision if loss of significance is too large + prec2 = prec2 * 3 / 2 + end + elsif x.frac.zero? && x < 1000 * prec + log_gamma = BigMath.log(_gamma_positive_integer(x, prec2), prec) + [log_gamma, 1] + else + # if x is close to 1 or 2, increase precision to reduce loss of significance + diff1_exponent = (x - 1).exponent + diff2_exponent = (x - 2).exponent + extremely_near_one = diff1_exponent < -prec2 + extremely_near_two = diff2_exponent < -prec2 + + if extremely_near_one || extremely_near_two + # If x is extreamely close to base = 1 or 2, linear interpolation is accurate enough. + # Taylor expansion at x = base is: (x - base) * digamma(base) + (x - base) ** 2 * trigamma(base) / 2 + ... + # And we can ignore (x - base) ** 2 and higher order terms. + base = extremely_near_one ? 1 : 2 + d = BigDecimal(1)._decimal_shift(1 - prec2) + log_gamma_d, sign = lgamma(base + d, prec2) + return [log_gamma_d.mult(x - base, prec2).div(d, prec), sign] + end + + prec2 += [-diff1_exponent, -diff2_exponent, 0].max + a, sum = _gamma_spouge_sum_part(x, prec2) + log_gamma = BigMath.log(sum, prec2).add((x - 0.5).mult(BigMath.log(x.add(a - 1, prec2), prec2), prec2) + 1 - x, prec) + [log_gamma, 1] + end + end + + # Returns sum part: sqrt(2*pi) and c[k]/(x+k) terms of Spouge's approximation + private_class_method def _gamma_spouge_sum_part(x, prec) # :nodoc: + x -= 1 + # Spouge's approximation + # x! = (x + a)**(x + 0.5) * exp(-x - a) * (sqrt(2 * pi) + (1..a - 1).sum{|k| c[k] / (x + k) } + epsilon) + # where c[k] = (-1)**k * (a - k)**(k - 0.5) * exp(a - k) / (k - 1)! + # and epsilon is bounded by a**(-0.5) * (2 * pi) ** (-a - 0.5) + + # Estimate required a for given precision + a = (prec / Math.log10(2 * Math::PI)).ceil + + # Calculate exponent of c[k] in low precision to estimate required precision + low_prec = 16 + log10f = Math.log(10) + x_low_prec = x.mult(1, low_prec) + loggamma_k = 0 + ck_exponents = (1..a-1).map do |k| + loggamma_k += Math.log10(k - 1) if k > 1 + -loggamma_k - k / log10f + (k - 0.5) * Math.log10(a - k) - BigMath.log10(x_low_prec.add(k, low_prec), low_prec) + end + + # Estimate exponent of sum by Stirling's approximation + approx_sum_exponent = x < 1 ? -Math.log10(a) / 2 : Math.log10(2 * Math::PI) / 2 + x_low_prec.add(0.5, low_prec) * Math.log10(x_low_prec / x_low_prec.add(a, low_prec)) + + # Determine required precision of c[k] + prec2 = [ck_exponents.max.ceil - approx_sum_exponent.floor, 0].max + prec + + einv = BigMath.exp(-1, prec2) + sum = (PI(prec) * 2).sqrt(prec).mult(BigMath.exp(-a, prec), prec) + y = BigDecimal(1) + (1..a - 1).each do |k| + # c[k] = (-1)**k * (a - k)**(k - 0.5) * exp(-k) / (k-1)! / (x + k) + y = y.div(1 - k, prec2) if k > 1 + y = y.mult(einv, prec2) + z = y.mult(BigDecimal((a - k) ** k), prec2).div(BigDecimal(a - k).sqrt(prec2).mult(x.add(k, prec2), prec2), prec2) + # sum += c[k] / (x + k) + sum = sum.add(z, prec2) + end + [a, sum] + end + + private_class_method def _gamma_positive_integer(x, prec) # :nodoc: + return x if x == 1 + numbers = (1..x - 1).map {|i| BigDecimal(i) } + while numbers.size > 1 + numbers = numbers.each_slice(2).map {|a, b| b ? a.mult(b, prec) : a } + end + numbers.first + end + + # Returns sin(pi * x), for gamma reflection formula calculation + private_class_method def _sinpix(x, pi, prec) # :nodoc: + x = x % 2 + sign = x > 1 ? -1 : 1 + x %= 1 + x = 1 - x if x > 0.5 # to avoid sin(pi*x) loss of precision for x close to 1 + sign * sin(x.mult(pi, prec), prec) + end + + # call-seq: + # frexp(x) -> [BigDecimal, Integer] + # + # Decomposes +x+ into a normalized fraction and an integral power of ten. + # + # BigMath.frexp(BigDecimal(123.456)) + # #=> [0.123456e0, 3] + # + def frexp(x) + x = BigDecimal::Internal.coerce_to_bigdecimal(x, 0, :frexp) + return [x, 0] unless x.finite? + + exponent = x.exponent + [x._decimal_shift(-exponent), exponent] + end + + # call-seq: + # ldexp(fraction, exponent) -> BigDecimal + # + # Inverse of +frexp+. + # Returns the value of fraction * 10**exponent. + # + # BigMath.ldexp(BigDecimal("0.123456e0"), 3) + # #=> 0.123456e3 + # + def ldexp(x, exponent) + x = BigDecimal::Internal.coerce_to_bigdecimal(x, 0, :ldexp) + x.finite? ? x._decimal_shift(exponent) : x + end + + # call-seq: + # PI(numeric) -> BigDecimal + # + # Computes the value of pi to the specified number of digits of precision, + # +numeric+. + # + # BigMath.PI(32).to_s + # #=> "0.31415926535897932384626433832795e1" + # + def PI(prec) + prec = BigDecimal::Internal.coerce_validate_prec(prec, :PI) + 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.mult(1, prec) + end + + # call-seq: + # E(numeric) -> BigDecimal + # + # Computes e (the base of natural logarithms) to the specified number of + # digits of precision, +numeric+. + # + # BigMath.E(32).to_s + # #=> "0.27182818284590452353602874713527e1" + # + def E(prec) + prec = BigDecimal::Internal.coerce_validate_prec(prec, :E) + exp(1, prec) + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/lib/bigdecimal/newton.rb b/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/lib/bigdecimal/newton.rb new file mode 100644 index 0000000..bb70d47 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/lib/bigdecimal/newton.rb @@ -0,0 +1,82 @@ +# frozen_string_literal: false +require "bigdecimal/ludcmp" +require "bigdecimal/jacobian" + +warn "'bigdecimal/newton' is deprecated and will be removed in a future release." + +# +# 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/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/lib/bigdecimal/util.rb b/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/lib/bigdecimal/util.rb new file mode 100644 index 0000000..7c5f32e --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/lib/bigdecimal/util.rb @@ -0,0 +1,186 @@ +# 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 Kernel.BigDecimal. + # + 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 Kernel.BigDecimal. + # + 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 Kernel.BigDecimal. + # + 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 +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' + # + # Rational(22, 7).to_d(3) # => 0.314e1 + # + # See also Kernel.BigDecimal. + # + def to_d(precision=0) + 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. + # If the imaginary part is not +0+, an error is raised + # + # 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' + # + # Complex(0.1234567, 0).to_d(4) # => 0.1235e0 + # Complex(Rational(22, 7), 0).to_d(3) # => 0.314e1 + # Complex(1, 1).to_d # raises ArgumentError + # + # See also Kernel.BigDecimal. + # + def to_d(precision=0) + BigDecimal(self) unless self.imag.zero? # to raise error + + BigDecimal(self.real, precision) + 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/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/sample/linear.rb b/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/sample/linear.rb new file mode 100644 index 0000000..516c247 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/sample/linear.rb @@ -0,0 +1,74 @@ +#!/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/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/sample/nlsolve.rb b/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/sample/nlsolve.rb new file mode 100644 index 0000000..c2227da --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/sample/nlsolve.rb @@ -0,0 +1,40 @@ +#!/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/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/sample/pi.rb b/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/sample/pi.rb new file mode 100644 index 0000000..ea96638 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/bigdecimal-4.0.1/sample/pi.rb @@ -0,0 +1,21 @@ +#!/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/vendor/bundle/ruby/3.3.0/gems/crack-1.0.1/History b/vendor/bundle/ruby/3.3.0/gems/crack-1.0.1/History new file mode 100644 index 0000000..8ca5c1d --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/crack-1.0.1/History @@ -0,0 +1,58 @@ +== 1.0.1 2025-09-22 + +* minor patches + * Ruby 3.4 support + * Handle new "No root element" error from REXML + +== 1.0.0 2024-02-09 + +This version is identical with the previous one, there's no code changes. + +Releasing v1.0.0 indicates that the gem has a stable API and it's going to follow semantic versioning. + +== 0.4.6 2024-01-29 + +* minor patches + * Ruby 3.1, 3.2, 3.3 support + * Ship LICENSE file + * Add BigDecimal to gem dependency + * Move CI to Github Actions + * Fix parse issue with consecutive dates + +== 0.4.5 2020-12-26 + +* 1 minor patch + * Add REXML as gem runtime dependency + +== 0.4.4 2020-09-17 + +* 1 minor patch + * Remove safe_yaml + +== 0.1.7 2010-02-19 +* 1 minor patch + * Added patch from @purp for ISO 8601 date/time format + +== 0.1.6 2010-01-31 +* 1 minor patch + * Added Crack::VERSION constant - http://weblog.rubyonrails.org/2009/9/1/gem-packaging-best-practices + +== 0.1.5 2010-01-27 +* 1 minor patch + * Strings that begin with dates shouldn't be parsed as such (sandro) + +== 0.1.3 2009-06-22 +* 1 minor patch + * Parsing a text node with attributes stores them in the attributes method (tamalw) + +== 0.1.2 2009-04-21 +* 2 minor patches + * Correct unnormalization of attribute values (der-flo) + * Fix error in parsing YAML in the case where a hash value ends with backslashes, and there are subsequent values in the hash (deadprogrammer) + +== 0.1.1 2009-03-31 +* 1 minor patch + * Parsing empty or blank xml now returns empty hash instead of raising error. + +== 0.1.0 2009-03-28 +* Initial release. diff --git a/vendor/bundle/ruby/3.3.0/gems/crack-1.0.1/LICENSE b/vendor/bundle/ruby/3.3.0/gems/crack-1.0.1/LICENSE new file mode 100644 index 0000000..fbbebe1 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/crack-1.0.1/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2009 John Nunemaker + +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/vendor/bundle/ruby/3.3.0/gems/crack-1.0.1/README.md b/vendor/bundle/ruby/3.3.0/gems/crack-1.0.1/README.md new file mode 100644 index 0000000..78bba7f --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/crack-1.0.1/README.md @@ -0,0 +1,43 @@ +# crack + +[![Test](https://github.com/jnunemaker/crack/actions/workflows/test.yml/badge.svg)](https://github.com/jnunemaker/crack/actions/workflows/test.yml) +[![Gem Version](https://badge.fury.io/rb/crack.svg)](https://badge.fury.io/rb/crack) +![downloads](https://img.shields.io/gem/dt/crack?label=downloads) + +Really simple JSON and XML parsing, ripped from Merb and Rails. The XML parser is ripped from Merb and the JSON parser is ripped from Rails. I take no credit, just packaged them for all to enjoy and easily use. + +## compatibility + +* Ruby 2.x +* Ruby 3.x + +## note on patches/pull requests + +* Fork the project. +* Make your feature addition or bug fix. +* Add tests for it. This is important so I don't break it in a future version unintentionally. +* Run the tests with `rake test` +* Open a Pull Request with the changes + +## usage + +```ruby +gem 'crack' # in Gemfile +require 'crack' # for xml and json +require 'crack/json' # for just json +require 'crack/xml' # for just xml +``` + +## examples + +```ruby +Crack::XML.parse("This is the contents") +# => {'tag' => 'This is the contents'} + +Crack::JSON.parse('{"tag":"This is the contents"}') +# => {'tag' => 'This is the contents'} +``` + +## Copyright + +Copyright (c) 2009 John Nunemaker. See LICENSE for details. diff --git a/vendor/bundle/ruby/3.3.0/gems/crack-1.0.1/lib/crack.rb b/vendor/bundle/ruby/3.3.0/gems/crack-1.0.1/lib/crack.rb new file mode 100644 index 0000000..d6af02d --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/crack-1.0.1/lib/crack.rb @@ -0,0 +1,8 @@ +module Crack + class ParseError < StandardError; end +end + +require 'crack/version' +require 'crack/util' +require 'crack/json' +require 'crack/xml' diff --git a/vendor/bundle/ruby/3.3.0/gems/crack-1.0.1/lib/crack/json.rb b/vendor/bundle/ruby/3.3.0/gems/crack-1.0.1/lib/crack/json.rb new file mode 100644 index 0000000..42f39e1 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/crack-1.0.1/lib/crack/json.rb @@ -0,0 +1,113 @@ +# Copyright (c) 2004-2008 David Heinemeier Hansson +# 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. + +require 'strscan' +require 'psych' + +module Crack + class JSON + def self.parser_exceptions + @parser_exceptions ||= [ArgumentError, Psych::SyntaxError] + end + + if Gem::Version.new(Psych::VERSION) >= Gem::Version.new('3.1.0') + def self.parse(json) + yaml = unescape(convert_json_to_yaml(json)) + YAML.safe_load(yaml, permitted_classes: [Regexp, Date, Time]) + rescue *parser_exceptions + raise ParseError, "Invalid JSON string" + rescue Psych::DisallowedClass + yaml + end + else # Ruby < 2.6 + def self.parse(json) + yaml = unescape(convert_json_to_yaml(json)) + YAML.safe_load(yaml, [Regexp, Date, Time]) + rescue *parser_exceptions + raise ParseError, "Invalid JSON string" + rescue Psych::DisallowedClass + yaml + end + end + + protected + def self.unescape(str) + # Force the encoding to be UTF-8 so we can perform regular expressions + # on 1.9.2 without blowing up. + # see http://stackoverflow.com/questions/1224204/ruby-mechanize-getting-force-encoding-exception for a similar issue + str.force_encoding('UTF-8') if defined?(Encoding) && str.respond_to?(:force_encoding) + str.gsub(/\\u0000/, "").gsub(/\\[u|U]([0-9a-fA-F]{4})/) { [$1.hex].pack("U") } + end + + # matches YAML-formatted dates + DATE_REGEX = /^\d{4}-\d{2}-\d{2}$|^\d{4}-\d{1,2}-\d{1,2}[T \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?)$/ + + # Ensure that ":" and "," are always followed by a space + def self.convert_json_to_yaml(json) #:nodoc: + json = String.new(json) #can't modify a frozen string + scanner, quoting, marks, pos, date_starts, date_ends = StringScanner.new(json), false, [], nil, [], [] + while scanner.scan_until(/(\\['"]|['":,\/\\]|\\.)/) + case char = scanner[1] + when '"', "'" + if !quoting + quoting = char + pos = scanner.pos + elsif quoting == char + if json[pos..scanner.pos-2] =~ DATE_REGEX + # found a date, track the exact positions of the quotes so we can remove them later. + # oh, and increment them for each current mark, each one is an extra padded space that bumps + # the position in the final YAML output + total_marks = marks.size + date_starts << pos+total_marks + date_ends << scanner.pos+total_marks + end + quoting = false + end + when "/" + if !quoting + json[scanner.pos - 1] = "!ruby/regexp /" + scanner.pos += 13 + scanner.scan_until(/\/[mix]*/) + end + when ":","," + marks << scanner.pos - 1 unless quoting + when "\\" + scanner.skip(/\\/) + end + end + + if marks.empty? + json.gsub(/\\\//, '/') + else + left_pos = marks.clone.unshift(-1) + right_pos = marks << json.length + output = [] + left_pos.each_with_index do |left, i| + output << json[left.succ..right_pos[i]] + end + output = output * " " + + format_dates(output, date_starts, date_ends) + output.gsub!(/\\\//, '/') + output + end + end + + def self.format_dates(output, date_starts, date_ends) + if YAML.constants.include?('Syck') + (date_starts + date_ends).each { |i| output[i-1] = ' ' } + else + extra_chars_to_be_added = 0 + timestamp_marker = '!!timestamp ' + timestamp_marker_size = timestamp_marker.size + + date_starts.each do |i| + output[i-2+extra_chars_to_be_added] = timestamp_marker + extra_chars_to_be_added += timestamp_marker_size - 1 + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/crack-1.0.1/lib/crack/util.rb b/vendor/bundle/ruby/3.3.0/gems/crack-1.0.1/lib/crack/util.rb new file mode 100644 index 0000000..60c9442 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/crack-1.0.1/lib/crack/util.rb @@ -0,0 +1,17 @@ +module Crack + module Util + def snake_case(str) + return str.downcase if str =~ /^[A-Z]+$/ + str.gsub(/([A-Z]+)(?=[A-Z][a-z]?)|\B[A-Z]/, '_\&') =~ /_*(.*)/ + return $+.downcase + end + + def to_xml_attributes(hash) + hash.map do |k,v| + %{#{Crack::Util.snake_case(k.to_s).sub(/^(.{1,1})/) { |m| m.downcase }}="#{v.to_s.gsub('"', '"')}"} + end.join(' ') + end + + extend self + end +end \ No newline at end of file diff --git a/vendor/bundle/ruby/3.3.0/gems/crack-1.0.1/lib/crack/version.rb b/vendor/bundle/ruby/3.3.0/gems/crack-1.0.1/lib/crack/version.rb new file mode 100644 index 0000000..8aaef0c --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/crack-1.0.1/lib/crack/version.rb @@ -0,0 +1,3 @@ +module Crack + VERSION = "1.0.1" +end diff --git a/vendor/bundle/ruby/3.3.0/gems/crack-1.0.1/lib/crack/xml.rb b/vendor/bundle/ruby/3.3.0/gems/crack-1.0.1/lib/crack/xml.rb new file mode 100644 index 0000000..985ba80 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/crack-1.0.1/lib/crack/xml.rb @@ -0,0 +1,240 @@ +require 'rexml/parsers/streamparser' +require 'rexml/parsers/baseparser' +require 'rexml/light/node' +require 'rexml/text' +require "rexml/document" +require 'date' +require 'time' +require 'yaml' +require 'bigdecimal' + +# The Reason behind redefining the String Class for this specific plugin is to +# avoid the dynamic insertion of stuff on it (see version previous to this commit). +# Doing that disables the possibility of efectuating a dump on the structure. This way it goes. +class REXMLUtiliyNodeString < String + attr_accessor :attributes +end + +# This is a slighly modified version of the XMLUtilityNode from +# http://merb.devjavu.com/projects/merb/ticket/95 (has.sox@gmail.com) +# It's mainly just adding vowels, as I ht cd wth n vwls :) +# This represents the hard part of the work, all I did was change the +# underlying parser. +class REXMLUtilityNode #:nodoc: + attr_accessor :name, :attributes, :children, :type + + def self.typecasts + @@typecasts + end + + def self.typecasts=(obj) + @@typecasts = obj + end + + def self.available_typecasts + @@available_typecasts + end + + def self.available_typecasts=(obj) + @@available_typecasts = obj + end + + self.typecasts = {} + self.typecasts["integer"] = lambda{|v| v.nil? ? nil : v.to_i} + self.typecasts["boolean"] = lambda{|v| v.nil? ? nil : (v.strip != "false")} + self.typecasts["datetime"] = lambda{|v| v.nil? ? nil : Time.parse(v).utc} + self.typecasts["date"] = lambda{|v| v.nil? ? nil : Date.parse(v)} + self.typecasts["dateTime"] = lambda{|v| v.nil? ? nil : Time.parse(v).utc} + self.typecasts["decimal"] = lambda{|v| v.nil? ? nil : BigDecimal(v.to_s)} + self.typecasts["double"] = lambda{|v| v.nil? ? nil : v.to_f} + self.typecasts["float"] = lambda{|v| v.nil? ? nil : v.to_f} + self.typecasts["string"] = lambda{|v| v.to_s} + self.typecasts["base64Binary"] = lambda{|v| v.unpack('m').first } + + self.available_typecasts = self.typecasts.keys + + def initialize(name, normalized_attributes = {}) + + # unnormalize attribute values + attributes = Hash[* normalized_attributes.map { |key, value| + [ key, unnormalize_xml_entities(value) ] + }.flatten] + + @name = name.tr("-", "_") + # leave the type alone if we don't know what it is + @type = self.class.available_typecasts.include?(attributes["type"]) ? attributes.delete("type") : attributes["type"] + + @nil_element = attributes.delete("nil") == "true" + @attributes = undasherize_keys(attributes) + @children = [] + @text = false + end + + def add_node(node) + @text = true if node.is_a? String + @children << node + end + + def to_hash + # ACG: Added a check here to prevent an exception a type == "file" tag has nodes within it + if @type == "file" and (@children.first.nil? or @children.first.is_a?(String)) + f = StringIO.new((@children.first || '').unpack('m').first) + class << f + attr_accessor :original_filename, :content_type + end + f.original_filename = attributes['name'] || 'untitled' + f.content_type = attributes['content_type'] || 'application/octet-stream' + return {name => f} + end + + if @text + t = typecast_value( unnormalize_xml_entities( inner_html ) ) + if t.is_a?(String) + t = REXMLUtiliyNodeString.new(t) + t.attributes = attributes + end + return { name => t } + else + #change repeating groups into an array + groups = @children.inject({}) { |s,e| (s[e.name] ||= []) << e; s } + + out = nil + if @type == "array" + out = [] + groups.each do |k, v| + if v.size == 1 + out << v.first.to_hash.entries.first.last + else + out << v.map{|e| e.to_hash[k]} + end + end + out = out.flatten + + else # If Hash + out = {} + groups.each do |k,v| + if v.size == 1 + out.merge!(v.first) + else + out.merge!( k => v.map{|e| e.to_hash[k]}) + end + end + out.merge! attributes unless attributes.empty? + out = out.empty? ? nil : out + end + + if @type && out.nil? + { name => typecast_value(out) } + else + { name => out } + end + end + end + + # Typecasts a value based upon its type. For instance, if + # +node+ has #type == "integer", + # {{[node.typecast_value("12") #=> 12]}} + # + # @param value The value that is being typecast. + # + # @details [:type options] + # "integer":: + # converts +value+ to an integer with #to_i + # "boolean":: + # checks whether +value+, after removing spaces, is the literal + # "true" + # "datetime":: + # Parses +value+ using Time.parse, and returns a UTC Time + # "date":: + # Parses +value+ using Date.parse + # + # @return + # The result of typecasting +value+. + # + # @note + # If +self+ does not have a "type" key, or if it's not one of the + # options specified above, the raw +value+ will be returned. + def typecast_value(value) + return value unless @type + proc = self.class.typecasts[@type] + proc.nil? ? value : proc.call(value) + end + + # Take keys of the form foo-bar and convert them to foo_bar + def undasherize_keys(params) + params.keys.each do |key, value| + params[key.tr("-", "_")] = params.delete(key) + end + params + end + + # Get the inner_html of the REXML node. + def inner_html + @children.join + end + + # Converts the node into a readable HTML node. + # + # @return The HTML node in text form. + def to_html + attributes.merge!(:type => @type ) if @type + "<#{name}#{Crack::Util.to_xml_attributes(attributes)}>#{@nil_element ? '' : inner_html}" + end + + # @alias #to_html #to_s + def to_s + to_html + end + + private + + def unnormalize_xml_entities value + REXML::Text.unnormalize(value) + end +end + +module Crack + class REXMLParser + def self.parse(xml) + stack = [] + parser = REXML::Parsers::BaseParser.new(xml) + + while true + event = parser.pull + case event[0] + when :end_document + break + when :end_doctype, :start_doctype + # do nothing + when :start_element + stack.push REXMLUtilityNode.new(event[1], event[2]) + when :end_element + if stack.size > 1 + temp = stack.pop + stack.last.add_node(temp) + end + when :text, :cdata + stack.last.add_node(event[1]) unless event[1].strip.length == 0 || stack.empty? + end + end + + stack.length > 0 ? stack.pop.to_hash : {} + end + end + + class XML + def self.parser + @@parser ||= REXMLParser + end + + def self.parser=(parser) + @@parser = parser + end + + def self.parse(xml) + return {} if xml.strip == '' + + parser.parse(xml) + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/CHANGELOG.md b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/CHANGELOG.md new file mode 100644 index 0000000..95f091a --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/CHANGELOG.md @@ -0,0 +1,574 @@ +# Faraday Changelog + +## The changelog has moved! + +This file is not being updated anymore. Instead, please check the [Releases](https://github.com/lostisland/faraday/releases) page. + +## [2.2.0](https://github.com/lostisland/faraday/compare/v2.1.0...v2.2.0) (2022-02-03) + +* Reintroduce the possibility to register middleware with symbols, strings or procs in [#1391](https://github.com/lostisland/faraday/pull/1391) + +## [2.1.0](https://github.com/lostisland/faraday/compare/v2.0.1...v2.1.0) (2022-01-15) + +* Fix test adapter thread safety by @iMacTia in [#1380](https://github.com/lostisland/faraday/pull/1380) +* Add default adapter options by @hirasawayuki in [#1382](https://github.com/lostisland/faraday/pull/1382) +* CI: Add Ruby 3.1 to matrix by @petergoldstein in [#1374](https://github.com/lostisland/faraday/pull/1374) +* docs: fix regex pattern in logger.md examples by @hirasawayuki in [#1378](https://github.com/lostisland/faraday/pull/1378) + +## [2.0.1](https://github.com/lostisland/faraday/compare/v2.0.0...v2.0.1) (2022-01-05) + +* Re-add `faraday-net_http` as default adapter by @iMacTia in [#1366](https://github.com/lostisland/faraday/pull/1366) +* Updated sample format in UPGRADING.md by @vimutter in [#1361](https://github.com/lostisland/faraday/pull/1361) +* docs: Make UPGRADING examples more copyable by @olleolleolle in [#1363](https://github.com/lostisland/faraday/pull/1363) + +## [2.0.0](https://github.com/lostisland/faraday/compare/v1.8.0...v2.0.0) (2022-01-04) + +The next major release is here, and it comes almost 2 years after the release of v1.0! + +This release changes the way you use Faraday and embraces a new paradigm of Faraday as an ecosystem, rather than a library. + +What does that mean? It means that Faraday is less of a bundled tool and more of a framework for the community to build on top of. + +As a result, all adapters and some middleware have moved out and are now shipped as standalone gems 🙌! + +But this doesn't mean that upgrading from Faraday 1.x to Faraday 2.0 should be hard, in fact we've listed everything you need to do in the [UPGRADING.md](https://github.com/lostisland/faraday/blob/main/UPGRADING.md) doc. + +Moreover, we've setup a new [awesome-faraday](https://github.com/lostisland/awesome-faraday) repository that will showcase a curated list of adapters and middleware 😎. + +This release was the result of the efforts of the core team and all the contributors, new and old, that have helped achieve this milestone 👏. + +## What's Changed + +* Autoloading, dependency loading and middleware registry cleanup by @iMacTia in [#1301](https://github.com/lostisland/faraday/pull/1301) +* Move JSON middleware (request and response) from faraday_middleware by @iMacTia in [#1300](https://github.com/lostisland/faraday/pull/1300) +* Remove deprecated `Faraday::Request#method` by @olleolleolle in [#1303](https://github.com/lostisland/faraday/pull/1303) +* Remove deprecated `Faraday::UploadIO` by @iMacTia in [#1307](https://github.com/lostisland/faraday/pull/1307) +* [1.x] Deprecate Authorization helpers in `Faraday::Connection` by @iMacTia in [#1306](https://github.com/lostisland/faraday/pull/1306) +* Drop deprecated auth helpers from Connection and refactor auth middleware by @iMacTia in [#1308](https://github.com/lostisland/faraday/pull/1308) +* Add Faraday 1.x examples in authentication.md docs by @iMacTia in [#1320](https://github.com/lostisland/faraday/pull/1320) +* Fix passing a URL with embedded basic auth by @iMacTia in [#1324](https://github.com/lostisland/faraday/pull/1324) +* Register JSON middleware by @mollerhoj in [#1331](https://github.com/lostisland/faraday/pull/1331) +* Retry middleware should handle string exception class name consistently by @jrochkind in [#1334](https://github.com/lostisland/faraday/pull/1334) +* Improve request info in exceptions raised by RaiseError Middleware by @willianzocolau in [#1335](https://github.com/lostisland/faraday/pull/1335) +* Remove net-http adapter and update docs by @iMacTia in [#1336](https://github.com/lostisland/faraday/pull/1336) +* Explain plan for faraday_middleware in UPGRADING.md by @iMacTia in [#1339](https://github.com/lostisland/faraday/pull/1339) +* Scripts folder cleanup by @iMacTia in [#1340](https://github.com/lostisland/faraday/pull/1340) +* Replace `Hash#merge` with `Utils#deep_merge` for connection options by @xkwd in [#1343](https://github.com/lostisland/faraday/pull/1343) +* Callable authorizers by @sled in [#1345](https://github.com/lostisland/faraday/pull/1345) +* Default value for exc error by @DariuszMusielak in [#1351](https://github.com/lostisland/faraday/pull/1351) +* Don't call `retry_block` unless a retry is going to happen by @jrochkind in [#1350](https://github.com/lostisland/faraday/pull/1350) +* Improve documentation for v2 by @iMacTia in [#1353](https://github.com/lostisland/faraday/pull/1353) +* Remove default `default_adapter` (yes, you read that right) by @iMacTia in [#1354](https://github.com/lostisland/faraday/pull/1354) +* Remove retry middleware by @iMacTia in [#1356](https://github.com/lostisland/faraday/pull/1356) +* Remove multipart middleware and all its documentation and tests by @iMacTia in [#1357](https://github.com/lostisland/faraday/pull/1357) + +## [1.9.3](https://github.com/lostisland/faraday/compare/v1.9.2...v1.9.3) (2022-01-06) + +* Re-add support for Ruby 2.4+ by @iMacTia in [#1371](https://github.com/lostisland/faraday/pull/1371) + +## [1.9.2](https://github.com/lostisland/faraday/compare/v1.9.1...v1.9.2) (2022-01-06) + +* Add alias with legacy name to gemified middleware by @iMacTia in [#1372](https://github.com/lostisland/faraday/pull/1372) + +## [1.9.1](https://github.com/lostisland/faraday/compare/v1.9.0...v1.9.1) (2022-01-06) + +* Update adapter dependencies in Gemspec by @iMacTia in [#1370](https://github.com/lostisland/faraday/pull/1370) + +## [1.9.0](https://github.com/lostisland/faraday/compare/v1.8.0...v1.9.0) (2022-01-06) + +* Use external multipart and retry middleware by @iMacTia in [#1367](https://github.com/lostisland/faraday/pull/1367) + +## [1.8.0](https://github.com/lostisland/faraday/releases/tag/v1.8.0) (2021-09-18) + +### Features + +* Backport authorization procs (#1322, @jarl-dk) + +## [v1.7.0](https://github.com/lostisland/faraday/releases/tag/v1.7.0) (2021-08-09) + +### Features + +* Add strict_mode to Test::Stubs (#1298, @yykamei) + +## [v1.6.0](https://github.com/lostisland/faraday/releases/tag/v1.6.0) (2021-08-01) + +### Misc + +* Use external Rack adapter (#1296, @iMacTia) + +## [v1.5.1](https://github.com/lostisland/faraday/releases/tag/v1.5.1) (2021-07-11) + +### Fixes + +* Fix JRuby incompatibility after moving out EM adapters (#1294, @ahorek) + +### Documentation + +* Update YARD to follow RackBuilder (#1292, @kachick) + +## [v1.5.0](https://github.com/lostisland/faraday/releases/tag/v1.5.0) (2021-07-04) + +### Misc + +* Use external httpclient adapter (#1289, @iMacTia) +* Use external patron adapter (#1290, @iMacTia) + +## [v1.4.3](https://github.com/lostisland/faraday/releases/tag/v1.4.3) (2021-06-24) + +### Fixes + +* Silence warning (#1286, @gurgeous) +* Always dup url_prefix in Connection#build_exclusive_url (#1288, @alexeyds) + +## [v1.4.2](https://github.com/lostisland/faraday/releases/tag/v1.4.2) (2021-05-22) + +### Fixes +* Add proxy setting when url_prefix is changed (#1276, @ci) +* Default proxy scheme to http:// if necessary, fixes #1282 (#1283, @gurgeous) + +### Documentation +* Improve introduction page (#1273, @gurgeous) +* Docs: add more middleware examples (#1277, @gurgeous) + +### Misc +* Use external `em_http` and `em_synchrony` adapters (#1274, @iMacTia) + +## [v1.4.1](https://github.com/lostisland/faraday/releases/tag/v1.4.1) (2021-04-18) + +### Fixes + +* Fix dependencies from external adapter gems (#1269, @iMacTia) + +## [v1.4.0](https://github.com/lostisland/faraday/releases/tag/v1.4.0) (2021-04-16) + +### Highlights + +With this release, we continue the work of gradually moving out adapters into their own gems 🎉 +Thanks to @MikeRogers0 for helping the Faraday team in progressing with this quest 👏 + +And thanks to @olleolleolle efforts, Faraday is becoming more inclusive than ever 🤗 +Faraday's `master` branch has been renamed into `main`, we have an official policy on inclusive language and even a rubocop plugin to check for non-inclusive words ❤️! +Checkout the "Misc" section below for more details 🙌 ! + +### Fixes + +* Fix NoMethodError undefined method 'coverage' (#1255, @Maroo-b) + +### Documentation + +* Some docs on EventMachine adapters. (#1232, @damau) +* CONTRIBUTING: Fix grammar and layout (#1261, @olleolleolle) + +### Misc + +* Replacing Net::HTTP::Persistent with faraday-net_http_persistent (#1250, @MikeRogers0) +* CI: Configure the regenerated Coveralls token (#1256, @olleolleolle) +* Replace Excon adapter with Faraday::Excon gem, and fix autoloading issue with Faraday::NetHttpPersistent (#1257, @iMacTia) +* Drop CodeClimate (#1259, @olleolleolle) +* CI: Rename default branch to main (#1263, @olleolleolle) +* Drop RDoc support file .document (#1264, @olleolleolle, @iMacTia) +* CONTRIBUTING: add a policy on inclusive language (#1262, @olleolleolle) +* Add rubocop-inclusivity (#1267, @olleolleolle, @iMacTia) + +## [v1.3.1](https://github.com/lostisland/faraday/releases/tag/v1.3.1) (2021-04-16) + +### Fixes + +* Escape colon in path segment (#1237, @yarafan) +* Handle IPv6 address String on Faraday::Connection#proxy_from_env (#1252, @cosmo0920) + +### Documentation + +* Fix broken Rubydoc.info links (#1236, @nickcampbell18) +* Add httpx to list of external adapters (#1246, @HoneyryderChuck) + +### Misc + +* Refactor CI to remove duplicated line (#1230, @tricknotes) +* Gemspec: Pick a good ruby2_keywords release (#1241, @olleolleolle) + +## [v1.3.0](https://github.com/lostisland/faraday/releases/tag/v1.3.0) (2020-12-31) + +### Highlights +Faraday v1.3.0 is the first release to officially support Ruby 3.0 in the CI pipeline 🎉 🍾! + +This is also the first release with a previously "included" adapter (Net::HTTP) being isolated into a [separate gem](https://github.com/lostisland/faraday-net_http) 🎊! +The new adapter is added to Faraday as a dependency for now, so that means full backwards-compatibility, but just to be safe be careful when upgrading! + +This is a huge step towards are Faraday v2.0 objective of pushing adapters and middleware into separate gems. +Many thanks to the Faraday Team, @JanDintel and everyone who attended the [ROSS Conf remote event](https://www.rossconf.io/event/remote/) + +### Features + +* Improves consistency with Faraday::Error and Faraday::RaiseError (#1229, @qsona, @iMacTia) + +### Fixes + +* Don't assign to global ::Timer (#1227, @bpo) + +### Documentation + +* CHANGELOG: add releases after 1.0 (#1225, @olleolleolle) +* Improves retry middleware documentation. (#1228, @iMacTia) + +### Misc + +* Move out Net::HTTP adapter (#1222, @JanDintel, @iMacTia) +* Adds Ruby 3.0 to CI Matrix (#1226, @iMacTia) + + +## [v1.2.0](https://github.com/lostisland/faraday/releases/tag/v1.2.0) (2020-12-23) + +### Features + +* Introduces `on_request` and `on_complete` methods in `Faraday::Middleware`. (#1194, @iMacTia) + +### Fixes + +* Require 'date' to avoid retry exception (#1206, @rustygeldmacher) +* Fix rdebug recursion issue (#1205, @native-api) +* Update call to `em_http_ssl_patch` (#1202, @kylekeesling) +* `EmHttp` adapter: drop superfluous loaded? check (#1213, @olleolleolle) +* Avoid 1 use of keyword hackery (#1211, @grosser) +* Fix #1219 `Net::HTTP` still uses env proxy (#1221, @iMacTia) + +### Documentation + +* Add comment in gemspec to explain exposure of `examples` and `spec` folders. (#1192, @iMacTia) +* Adapters, how to create them (#1193, @olleolleolle) +* Update documentation on using the logger (#1196, @tijmenb) +* Adjust the retry documentation and spec to align with implementation (#1198, @nbeyer) + +### Misc + +* Test against ruby head (#1208, @grosser) + +## [v1.1.0](https://github.com/lostisland/faraday/releases/tag/v1.1.0) (2020-10-17) + +### Features + +* Makes parameters sorting configurable (#1162 @wishdev) +* Introduces `flat_encode` option for multipart adapter. (#1163 @iMacTia) +* Include request info in exceptions raised by RaiseError Middleware (#1181 @SandroDamilano) + +### Fixes + +* Avoid `last arg as keyword param` warning when building user middleware on Ruby 2.7 (#1153 @dgholz) +* Limits net-http-persistent version to < 4.0 (#1156 @iMacTia) +* Update `typhoeus` to new stable version (`1.4`) (#1159 @AlexWayfer) +* Properly fix test failure with Rack 2.1+. (#1171 @voxik) + +### Documentation + +* Improves documentation on how to contribute to the site by using Docker. (#1175 @iMacTia) +* Remove retry_change_requests from documentation (#1185 @stim371) + +### Misc + +* Link from GitHub Actions badge to CI workflow (#1141 @olleolleolle) +* Return tests of `Test` adapter (#1147 @AlexWayfer) +* Add 1.0 release to wording in CONTRIBUTING (#1155 @olleolleolle) +* Fix linting bumping Rubocop to 0.90.0 (#1182 @iMacTia) +* Drop `git ls-files` in gemspec (#1183 @utkarsh2102) +* Upgrade CI to ruby/setup-ruby (#1187 @gogainda) + +## [v1.0.1](https://github.com/lostisland/faraday/releases/tag/v1.0.1) (2020-03-29) + +### Fixes + +* Use Net::HTTP#start(&block) to ensure closed TCP connections (#1117) +* Fully qualify constants to be checked (#1122) +* Allows `parse` method to be private/protected in response middleware (#1123) +* Encode Spaces in Query Strings as '%20' Instead of '+' (#1125) +* Limits rack to v2.0.x (#1127) +* Adapter Registry reads also use mutex (#1136) + +### Documentation + +* Retry middleware documentation fix (#1109) +* Docs(retry): precise usage of retry-after (#1111) +* README: Link the logo to the website (#1112) +* Website: add search bar (#1116) +* Fix request/response mix-up in docs text (#1132) + +## [v1.0](https://github.com/lostisland/faraday/releases/tag/v1.0.0) (2020-01-22) + +Features: + +* Add #trace support to Faraday::Connection #861 (@technoweenie) +* Add the log formatter that is easy to override and safe to inherit #889 (@prikha) +* Support standalone adapters #941 (@iMacTia) +* Introduce Faraday::ConflictError for 409 response code #979 (@lucasmoreno) +* Add support for setting `read_timeout` option separately #1003 (@springerigor) +* Refactor and cleanup timeout settings across adapters #1022 (@technoweenie) +* Create ParamPart class to allow multipart posts with JSON content and file upload at the same time #1017 (@jeremy-israel) +* Copy UploadIO const -> FilePart for consistency with ParamPart #1018, #1021 (@technoweenie) +* Implement streaming responses in the Excon adapter #1026 (@technoweenie) +* Add default implementation of `Middleware#close`. #1069 (@ioquatix) +* Add `Adapter#close` so that derived classes can call super. #1091 (@ioquatix) +* Add log_level option to logger default formatter #1079 (@amrrbakry) +* Fix empty array for FlatParamsEncoder `{key: []} -> "key="` #1084 (@mrexox) + +Bugs: + +* Explicitly require date for DateTime library in Retry middleware #844 (@nickpresta) +* Refactor Adapter as final endpoints #846 (@iMacTia) +* Separate Request and Response bodies in Faraday::Env #847 (@iMacTia) +* Implement Faraday::Connection#options to make HTTP requests with the OPTIONS verb. #857 (@technoweenie) +* Multipart: Drop Ruby 1.8 String behavior compat #892 (@olleolleolle) +* Fix Ruby warnings in Faraday::Options.memoized #962 (@technoweenie) +* Allow setting min/max SSL version for a Net::HTTP::Persistent connection #972, #973 (@bdewater, @olleolleolle) +* Fix instances of frozen empty string literals #1040 (@BobbyMcWho) +* remove temp_proxy and improve proxy tests #1063 (@technoweenie) +* improve error initializer consistency #1095 (@technoweenie) + +Misc: + +* Convert minitest suite to RSpec #832 (@iMacTia, with help from @gaynetdinov, @Insti, @technoweenie) +* Major effort to update code to RuboCop standards. #854 (@olleolleolle, @iMacTia, @technoweenie, @htwroclau, @jherdman, @Drenmi, @Insti) +* Rubocop #1044, #1047 (@BobbyMcWho, @olleolleolle) +* Documentation tweaks (@adsteel, @Hubro, @iMacTia, @olleolleolle, @technoweenie) +* Update license year #981 (@Kevin-Kawai) +* Configure Jekyll plugin jekyll-remote-theme to support Docker usage #999 (@Lewiscowles1986) +* Fix Ruby 2.7 warnings #1009 (@tenderlove) +* Cleanup adapter connections #1023 (@technoweenie) +* Describe clearing cached stubs #1045 (@viraptor) +* Add project metadata to the gemspec #1046 (@orien) + +## v0.17.4 + +Fixes: + +* NetHttp adapter: wrap Errno::EADDRNOTAVAIL (#1114, @embs) +* Fix === for subclasses of deprecated classes (#1243, @mervync) + +## v0.17.3 + +Fixes: + +* Reverts changes in error classes hierarchy. #1092 (@iMacTia) +* Fix Ruby 1.9 syntax errors and improve Error class testing #1094 (@BanzaiMan, + @mrexox, @technoweenie) + +Misc: + +* Stops using `&Proc.new` for block forwarding. #1083 (@olleolleolle) +* Update CI to test against ruby 2.0-2.7 #1087, #1099 (@iMacTia, @olleolleolle, + @technoweenie) +* require FARADAY_DEPRECATE=warn to show Faraday v1.0 deprecation warnings + #1098 (@technoweenie) + +## v0.17.1 + +Final release before Faraday v1.0, with important fixes for Ruby 2.7. + +Fixes: + +* RaiseError response middleware raises exception if HTTP client returns a nil + status. #1042 (@jonnyom, @BobbyMcWho) + +Misc: + +* Fix Ruby 2.7 warnings (#1009) +* Add `Faraday::Deprecate` to warn about upcoming v1.0 changes. (#1054, #1059, + #1076, #1077) +* Add release notes up to current in CHANGELOG.md (#1066) +* Port minimal rspec suite from main branch to run backported tests. (#1058) + +## v0.17.0 + +This release is the same as v0.15.4. It was pushed to cover up releases +v0.16.0-v0.16.2. + +## v0.15.4 + +* Expose `pool_size` as a option for the NetHttpPersistent adapter (#834) + +## v0.15.3 + +* Make Faraday::Request serialisable with Marshal. (#803) +* Add DEFAULT_EXCEPTIONS constant to Request::Retry (#814) +* Add support for Ruby 2.6 Net::HTTP write_timeout (#824) + +## v0.15.2 + +* Prevents `Net::HTTP` adapters to retry request internally by setting `max_retries` to 0 if available (Ruby 2.5+). (#799) +* Fixes `NestedParamsEncoder` handling of empty array values (#801) + +## v0.15.1 + +* NetHttpPersistent adapter better reuse of SSL connections (#793) +* Refactor: inline cached_connection (#797) +* Logger middleware: use $stdout instead of STDOUT (#794) +* Fix: do not memoize/reuse Patron session (#796) + +Also in this release: + +* Allow setting min/max ssl version for Net::HTTP (#792) +* Allow setting min/max ssl version for Excon (#795) + +## v0.15.0 + +Features: + +* Added retry block option to retry middleware. (#770) +* Retry middleware improvements (honour Retry-After header, retry statuses) (#773) +* Improve response logger middleware output (#784) + +Fixes: + +* Remove unused class error (#767) +* Fix minor typo in README (#760) +* Reuse persistent connections when using net-http-persistent (#778) +* Fix Retry middleware documentation (#781) +* Returns the http response when giving up on retrying by status (#783) + +## v0.14.0 + +Features: + +* Allow overriding env proxy #754 (@iMacTia) +* Remove legacy Typhoeus adapter #715 (@olleolleolle) +* External Typhoeus Adapter Compatibility #748 (@iMacTia) +* Warn about missing adapter when making a request #743 (@antstorm) +* Faraday::Adapter::Test stubs now support entire urls (with host) #741 (@erik-escobedo) + +Fixes: + +* If proxy is manually provided, this takes priority over `find_proxy` #724 (@iMacTia) +* Fixes the behaviour for Excon's open_timeout (not setting write_timeout anymore) #731 (@apachelogger) +* Handle all connection timeout messages in Patron #687 (@stayhero) + +## v0.13.1 + +* Fixes an incompatibility with Addressable::URI being used as uri_parser + +## v0.13.0 + +Features: + +* Dynamically reloads the proxy when performing a request on an absolute domain (#701) +* Adapter support for Net::HTTP::Persistent v3.0.0 (#619) + +Fixes: + +* Prefer #hostname over #host. (#714) +* Fixes an edge-case issue with response headers parsing (missing HTTP header) (#719) + +## v0.12.2 + +* Parse headers from aggregated proxy requests/responses (#681) +* Guard against invalid middleware configuration with warning (#685) +* Do not use :insecure option by default in Patron (#691) +* Fixes an issue with HTTPClient not raising a `Faraday::ConnectionFailed` (#702) +* Fixes YAML serialization/deserialization for `Faraday::Utils::Headers` (#690) +* Fixes an issue with Options having a nil value (#694) +* Fixes an issue with Faraday.default_connection not using Faraday.default_connection_options (#698) +* Fixes an issue with Options.merge! and Faraday instrumentation middleware (#710) + +## v0.12.1 + +* Fix an issue with Patron tests failing on jruby +* Fix an issue with new `rewind_files` feature that was causing an exception when the body was not an Hash +* Expose wrapped_exception in all client errors +* Add Authentication Section to the ReadMe + +## v0.12.0.1 + +* Hotfix release to address an issue with TravisCI deploy on Rubygems + +## v0.12.0 + +Features: + +* Proxy feature now relies on Ruby `URI::Generic#find_proxy` and can use `no_proxy` ENV variable (not compatible with ruby < 2.0) +* Adds support for `context` request option to pass arbitrary information to middlewares + +Fixes: + +* Fix an issue with options that was causing new options to override defaults ones unexpectedly +* Rewind `UploadIO`s on retry to fix a compatibility issue +* Make multipart boundary unique +* Improvements in `README.md` + +## v0.11.0 + +Features: + +* Add `filter` method to Logger middleware +* Add support for Ruby2.4 and Minitest 6 +* Introduce block syntax to customise the adapter + +Fixes: + +* Fix an issue that was allowing to override `default_connection_options` from a connection instance +* Fix a bug that was causing newline escape characters ("\n") to be used when building the Authorization header + +## v0.10.1 + +- Fix an issue with HTTPClient adapter that was causing the SSL to be reset on every request +- Rescue `IOError` instead of specific subclass +- `Faraday::Utils::Headers` can now be successfully serialised in YAML +- Handle `default_connection_options` set with hash + +## v0.10.0 + +Breaking changes: +- Drop support for Ruby 1.8 + +Features: +- Include wrapped exception/response in ClientErrors +- Add `response.reason_phrase` +- Provide option to selectively skip logging request/response headers +- Add regex support for pattern matching in `test` adapter + +Fixes: +- Add `Faraday.respond_to?` to find methods managed by `method_missing` +- em-http: `request.host` instead of `connection.host` should be taken for SSL validations +- Allow `default_connection_options` to be merged when options are passed as url parameter +- Improve splitting key-value pairs in raw HTTP headers + +## v0.9.2 + +Adapters: +- Enable gzip compression for httpclient +- Fixes default certificate store for httpclient not having default paths. +- Make excon adapter compatible with 0.44 excon version +- Add compatibility with Patron 0.4.20 +- Determine default port numbers in Net::HTTP adapters (Addressable compatibility) +- em-http: wrap "connection closed by server" as ConnectionFailed type +- Wrap Errno::ETIMEDOUT in Faraday::Error::TimeoutError + +Utils: +- Add Rack-compatible support for parsing `a[][b]=c` nested queries +- Encode nil values in queries different than empty strings. Before: `a=`; now: `a`. +- Have `Faraday::Utils::Headers#replace` clear internal key cache +- Dup the internal key cache when a Headers hash is copied + +Env and middleware: +- Ensure `env` stored on middleware response has reference to the response +- Ensure that Response properties are initialized during `on_complete` (VCR compatibility) +- Copy request options in Faraday::Connection#dup +- Env custom members should be copied by Env.from(env) +- Honour per-request `request.options.params_encoder` +- Fix `interval_randomness` data type for Retry middleware +- Add maximum interval option for Retry middleware + +## v0.9.1 + +* Refactor Net:HTTP adapter so that with_net_http_connection can be overridden to allow pooled connections. (@Ben-M) +* Add configurable methods that bypass `retry_if` in the Retry request middleware. (@mike-bourgeous) + +## v0.9.0 + +* Add HTTPClient adapter (@hakanensari) +* Improve Retry handler (@mislav) +* Remove autoloading by default (@technoweenie) +* Improve internal docs (@technoweenie, @mislav) +* Respect user/password in http proxy string (@mislav) +* Adapter options are structs. Reinforces consistent options across adapters + (@technoweenie) +* Stop stripping trailing / off base URLs in a Faraday::Connection. (@technoweenie) +* Add a configurable URI parser. (@technoweenie) +* Remove need to manually autoload when using the authorization header helpers on `Faraday::Connection`. (@technoweenie) +* `Faraday::Adapter::Test` respects the `Faraday::RequestOptions#params_encoder` option. (@technoweenie) diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/LICENSE.md b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/LICENSE.md new file mode 100644 index 0000000..3877615 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/LICENSE.md @@ -0,0 +1,20 @@ +Copyright (c) 2009-2023 Rick Olson, Zack Hobson + +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/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/README.md b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/README.md new file mode 100644 index 0000000..0cca6db --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/README.md @@ -0,0 +1,67 @@ +# [![Faraday](./docs/_media/home-logo.svg)][website] + +[![Gem Version](https://badge.fury.io/rb/faraday.svg)](https://rubygems.org/gems/faraday) +[![GitHub Actions CI](https://github.com/lostisland/faraday/workflows/CI/badge.svg)](https://github.com/lostisland/faraday/actions?query=workflow%3ACI) +[![GitHub Discussions](https://img.shields.io/github/discussions/lostisland/faraday?logo=github)](https://github.com/lostisland/faraday/discussions) + +Faraday is an HTTP client library abstraction layer that provides a common interface over many +adapters (such as Net::HTTP) and embraces the concept of Rack middleware when processing the request/response cycle. +Take a look at [Awesome Faraday][awesome] for a list of available adapters and middleware. + +## Why use Faraday? + +Faraday gives you the power of Rack middleware for manipulating HTTP requests and responses, +making it easier to build sophisticated API clients or web service libraries that abstract away +the details of how HTTP requests are made. + +Faraday comes with a lot of features out of the box, such as: +* Support for multiple adapters (Net::HTTP, Typhoeus, Patron, Excon, HTTPClient, and more) +* Persistent connections (keep-alive) +* Parallel requests +* Automatic response parsing (JSON, XML, YAML) +* Customization of the request/response cycle with middleware +* Support for streaming responses +* Support for uploading files +* And much more! + +## Getting Started + +The best starting point is the [Faraday Website][website], with its introduction and explanation. + +Need more details? See the [Faraday API Documentation][apidoc] to see how it works internally, or take a look at [Advanced techniques for calling HTTP APIs in Ruby](https://mattbrictson.com/blog/advanced-http-techniques-in-ruby) blog post from [@mattbrictson](https://github.com/mattbrictson) 🚀 + +## Supported Ruby versions + +This library aims to support and is [tested against][actions] the currently officially supported Ruby +implementations. This means that, even without a major release, we could add or drop support for Ruby versions, +following their [EOL](https://endoflife.date/ruby). +Currently that means we support Ruby 3.0+ + +If something doesn't work on one of these Ruby versions, it's a bug. + +This library may inadvertently work (or seem to work) on other Ruby +implementations and versions, however support will only be provided for the versions listed +above. + +If you would like this library to support another Ruby version, you may +volunteer to be a maintainer. Being a maintainer entails making sure all tests +run and pass on that implementation. When something breaks on your +implementation, you will be responsible for providing patches in a timely +fashion. If critical issues for a particular implementation exist at the time +of a major release, support for that Ruby version may be dropped. + +## Contribute + +Do you want to contribute to Faraday? +Open the issues page and check for the `help wanted` label! +But before you start coding, please read our [Contributing Guide][contributing] + +## Copyright + +© 2009 - 2023, the Faraday Team. Website and branding design by [Elena Lo Piccolo](https://elelopic.design). + +[awesome]: https://github.com/lostisland/awesome-faraday/#adapters +[website]: https://lostisland.github.io/faraday +[contributing]: https://github.com/lostisland/faraday/blob/main/.github/CONTRIBUTING.md +[apidoc]: https://www.rubydoc.info/github/lostisland/faraday +[actions]: https://github.com/lostisland/faraday/actions diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/Rakefile b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/Rakefile new file mode 100644 index 0000000..a98c511 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/Rakefile @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require 'rspec/core/rake_task' +require 'bundler' + +Bundler::GemHelper.install_tasks + +RSpec::Core::RakeTask.new(:spec) do |task| + task.ruby_opts = %w[-W] +end + +task default: :spec diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/examples/client_spec.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/examples/client_spec.rb new file mode 100644 index 0000000..e30d86f --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/examples/client_spec.rb @@ -0,0 +1,119 @@ +# frozen_string_literal: true + +# Requires Ruby with rspec and faraday gems. +# rspec client_spec.rb + +require 'faraday' +require 'json' + +# Example API client +class Client + def initialize(conn) + @conn = conn + end + + def httpbingo(jname, params: {}) + res = @conn.get("/#{jname}", params) + data = JSON.parse(res.body) + data['origin'] + end + + def foo(params) + res = @conn.post('/foo', JSON.dump(params)) + res.status + end +end + +RSpec.describe Client do + let(:stubs) { Faraday::Adapter::Test::Stubs.new } + let(:conn) { Faraday.new { |b| b.adapter(:test, stubs) } } + let(:client) { Client.new(conn) } + + it 'parses origin' do + stubs.get('/ip') do |env| + # optional: you can inspect the Faraday::Env + expect(env.url.path).to eq('/ip') + [ + 200, + { 'Content-Type': 'application/javascript' }, + '{"origin": "127.0.0.1"}' + ] + end + + # uncomment to trigger stubs.verify_stubbed_calls failure + # stubs.get('/unused') { [404, {}, ''] } + + expect(client.httpbingo('ip')).to eq('127.0.0.1') + stubs.verify_stubbed_calls + end + + it 'handles 404' do + stubs.get('/api') do + [ + 404, + { 'Content-Type': 'application/javascript' }, + '{}' + ] + end + expect(client.httpbingo('api')).to be_nil + stubs.verify_stubbed_calls + end + + it 'handles exception' do + stubs.get('/api') do + raise Faraday::ConnectionFailed + end + + expect { client.httpbingo('api') }.to raise_error(Faraday::ConnectionFailed) + stubs.verify_stubbed_calls + end + + context 'When the test stub is run in strict_mode' do + let(:stubs) { Faraday::Adapter::Test::Stubs.new(strict_mode: true) } + + it 'verifies the all parameter values are identical' do + stubs.get('/api?abc=123') do + [ + 200, + { 'Content-Type': 'application/javascript' }, + '{"origin": "127.0.0.1"}' + ] + end + + # uncomment to raise Stubs::NotFound + # expect(client.httpbingo('api', params: { abc: 123, foo: 'Kappa' })).to eq('127.0.0.1') + expect(client.httpbingo('api', params: { abc: 123 })).to eq('127.0.0.1') + stubs.verify_stubbed_calls + end + end + + context 'When the Faraday connection is configured with FlatParamsEncoder' do + let(:conn) { Faraday.new(request: { params_encoder: Faraday::FlatParamsEncoder }) { |b| b.adapter(:test, stubs) } } + + it 'handles the same multiple URL parameters' do + stubs.get('/api?a=x&a=y&a=z') { [200, { 'Content-Type' => 'application/json' }, '{"origin": "127.0.0.1"}'] } + + # uncomment to raise Stubs::NotFound + # expect(client.httpbingo('api', params: { a: %w[x y] })).to eq('127.0.0.1') + expect(client.httpbingo('api', params: { a: %w[x y z] })).to eq('127.0.0.1') + stubs.verify_stubbed_calls + end + end + + context 'When you want to test the body, you can use a proc as well as string' do + it 'tests with a string' do + stubs.post('/foo', '{"name":"YK"}') { [200, {}, ''] } + + expect(client.foo(name: 'YK')).to eq 200 + stubs.verify_stubbed_calls + end + + it 'tests with a proc' do + check = ->(request_body) { JSON.parse(request_body).slice('name') == { 'name' => 'YK' } } + stubs.post('/foo', check) { [200, {}, ''] } + + expect(client.foo(name: 'YK', created_at: Time.now)).to eq 200 + stubs.verify_stubbed_calls + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/examples/client_test.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/examples/client_test.rb new file mode 100644 index 0000000..3aad957 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/examples/client_test.rb @@ -0,0 +1,144 @@ +# frozen_string_literal: true + +# Requires Ruby with test-unit and faraday gems. +# ruby client_test.rb + +require 'faraday' +require 'json' +require 'test/unit' + +# Example API client +class Client + def initialize(conn) + @conn = conn + end + + def httpbingo(jname, params: {}) + res = @conn.get("/#{jname}", params) + data = JSON.parse(res.body) + data['origin'] + end + + def foo(params) + res = @conn.post('/foo', JSON.dump(params)) + res.status + end +end + +# Example API client test +class ClientTest < Test::Unit::TestCase + def test_httpbingo_name + stubs = Faraday::Adapter::Test::Stubs.new + stubs.get('/api') do |env| + # optional: you can inspect the Faraday::Env + assert_equal '/api', env.url.path + [ + 200, + { 'Content-Type': 'application/javascript' }, + '{"origin": "127.0.0.1"}' + ] + end + + # uncomment to trigger stubs.verify_stubbed_calls failure + # stubs.get('/unused') { [404, {}, ''] } + + cli = client(stubs) + assert_equal '127.0.0.1', cli.httpbingo('api') + stubs.verify_stubbed_calls + end + + def test_httpbingo_not_found + stubs = Faraday::Adapter::Test::Stubs.new + stubs.get('/api') do + [ + 404, + { 'Content-Type': 'application/javascript' }, + '{}' + ] + end + + cli = client(stubs) + assert_nil cli.httpbingo('api') + stubs.verify_stubbed_calls + end + + def test_httpbingo_exception + stubs = Faraday::Adapter::Test::Stubs.new + stubs.get('/api') do + raise Faraday::ConnectionFailed + end + + cli = client(stubs) + assert_raise Faraday::ConnectionFailed do + cli.httpbingo('api') + end + stubs.verify_stubbed_calls + end + + def test_strict_mode + stubs = Faraday::Adapter::Test::Stubs.new(strict_mode: true) + stubs.get('/api?abc=123') do + [ + 200, + { 'Content-Type': 'application/javascript' }, + '{"origin": "127.0.0.1"}' + ] + end + + cli = client(stubs) + assert_equal '127.0.0.1', cli.httpbingo('api', params: { abc: 123 }) + + # uncomment to raise Stubs::NotFound + # assert_equal '127.0.0.1', cli.httpbingo('api', params: { abc: 123, foo: 'Kappa' }) + stubs.verify_stubbed_calls + end + + def test_non_default_params_encoder + stubs = Faraday::Adapter::Test::Stubs.new(strict_mode: true) + stubs.get('/api?a=x&a=y&a=z') do + [ + 200, + { 'Content-Type': 'application/javascript' }, + '{"origin": "127.0.0.1"}' + ] + end + conn = Faraday.new(request: { params_encoder: Faraday::FlatParamsEncoder }) do |builder| + builder.adapter :test, stubs + end + + cli = Client.new(conn) + assert_equal '127.0.0.1', cli.httpbingo('api', params: { a: %w[x y z] }) + + # uncomment to raise Stubs::NotFound + # assert_equal '127.0.0.1', cli.httpbingo('api', params: { a: %w[x y] }) + stubs.verify_stubbed_calls + end + + def test_with_string_body + stubs = Faraday::Adapter::Test::Stubs.new do |stub| + stub.post('/foo', '{"name":"YK"}') { [200, {}, ''] } + end + cli = client(stubs) + assert_equal 200, cli.foo(name: 'YK') + + stubs.verify_stubbed_calls + end + + def test_with_proc_body + stubs = Faraday::Adapter::Test::Stubs.new do |stub| + check = ->(request_body) { JSON.parse(request_body).slice('name') == { 'name' => 'YK' } } + stub.post('/foo', check) { [200, {}, ''] } + end + cli = client(stubs) + assert_equal 200, cli.foo(name: 'YK', created_at: Time.now) + + stubs.verify_stubbed_calls + end + + def client(stubs) + conn = Faraday.new do |builder| + builder.adapter :test, stubs + end + Client.new(conn) + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday.rb new file mode 100644 index 0000000..34f3270 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday.rb @@ -0,0 +1,158 @@ +# frozen_string_literal: true + +require 'cgi/escape' +require 'cgi/util' if RUBY_VERSION < '3.5' +require 'date' +require 'set' +require 'forwardable' +require 'faraday/version' +require 'faraday/methods' +require 'faraday/error' +require 'faraday/middleware_registry' +require 'faraday/utils' +require 'faraday/options' +require 'faraday/connection' +require 'faraday/rack_builder' +require 'faraday/parameters' +require 'faraday/middleware' +require 'faraday/adapter' +require 'faraday/request' +require 'faraday/response' +require 'faraday/net_http' +# This is the main namespace for Faraday. +# +# It provides methods to create {Connection} objects, and HTTP-related +# methods to use directly. +# +# @example Helpful class methods for easy usage +# Faraday.get "http://faraday.com" +# +# @example Helpful class method `.new` to create {Connection} objects. +# conn = Faraday.new "http://faraday.com" +# conn.get '/' +# +module Faraday + CONTENT_TYPE = 'Content-Type' + + class << self + # The root path that Faraday is being loaded from. + # + # This is the root from where the libraries are auto-loaded. + # + # @return [String] + attr_accessor :root_path + + # Gets or sets the path that the Faraday libs are loaded from. + # @return [String] + attr_accessor :lib_path + + # @overload default_adapter + # Gets the Symbol key identifying a default Adapter to use + # for the default {Faraday::Connection}. Defaults to `:net_http`. + # @return [Symbol] the default adapter + # @overload default_adapter=(adapter) + # Updates default adapter while resetting {.default_connection}. + # @return [Symbol] the new default_adapter. + attr_reader :default_adapter + + # Option for the default_adapter + # @return [Hash] default_adapter options + attr_accessor :default_adapter_options + + # Documented below, see default_connection + attr_writer :default_connection + + # Tells Faraday to ignore the environment proxy (http_proxy). + # Defaults to `false`. + # @return [Boolean] + attr_accessor :ignore_env_proxy + + # Initializes a new {Connection}. + # + # @param url [String,Hash] The optional String base URL to use as a prefix + # for all requests. Can also be the options Hash. Any of these + # values will be set on every request made, unless overridden + # for a specific request. + # @param options [Hash] + # @option options [String] :url Base URL + # @option options [Hash] :params Hash of unencoded URI query params. + # @option options [Hash] :headers Hash of unencoded HTTP headers. + # @option options [Hash] :request Hash of request options. + # @option options [Hash] :ssl Hash of SSL options. + # @option options [Hash] :proxy Hash of Proxy options. + # @return [Faraday::Connection] + # + # @example With an URL argument + # Faraday.new 'http://faraday.com' + # # => Faraday::Connection to http://faraday.com + # + # @example With an URL argument and an options hash + # Faraday.new 'http://faraday.com', params: { page: 1 } + # # => Faraday::Connection to http://faraday.com?page=1 + # + # @example With everything in an options hash + # Faraday.new url: 'http://faraday.com', + # params: { page: 1 } + # # => Faraday::Connection to http://faraday.com?page=1 + def new(url = nil, options = {}, &block) + options = Utils.deep_merge(default_connection_options, options) + Faraday::Connection.new(url, options, &block) + end + + # Documented elsewhere, see default_adapter reader + def default_adapter=(adapter) + @default_connection = nil + @default_adapter = adapter + end + + def respond_to_missing?(symbol, include_private = false) + default_connection.respond_to?(symbol, include_private) || super + end + + # @overload default_connection + # Gets the default connection used for simple scripts. + # @return [Faraday::Connection] a connection configured with + # the default_adapter. + # @overload default_connection=(connection) + # @param connection [Faraday::Connection] + # Sets the default {Faraday::Connection} for simple scripts that + # access the Faraday constant directly, such as + # Faraday.get "https://faraday.com". + def default_connection + @default_connection ||= Connection.new(default_connection_options) + end + + # Gets the default connection options used when calling {Faraday#new}. + # + # @return [Faraday::ConnectionOptions] + def default_connection_options + @default_connection_options ||= ConnectionOptions.new + end + + # Sets the default options used when calling {Faraday#new}. + # + # @param options [Hash, Faraday::ConnectionOptions] + def default_connection_options=(options) + @default_connection = nil + @default_connection_options = ConnectionOptions.from(options) + end + + private + + # Internal: Proxies method calls on the Faraday constant to + # .default_connection. + def method_missing(name, *args, &block) + if default_connection.respond_to?(name) + default_connection.send(name, *args, &block) + else + super + end + end + end + + self.ignore_env_proxy = false + self.root_path = File.expand_path __dir__ + self.lib_path = File.expand_path 'faraday', __dir__ + self.default_adapter = :net_http + self.default_adapter_options = {} +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/adapter.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/adapter.rb new file mode 100644 index 0000000..1d9a450 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/adapter.rb @@ -0,0 +1,101 @@ +# frozen_string_literal: true + +module Faraday + # Base class for all Faraday adapters. Adapters are + # responsible for fulfilling a Faraday request. + class Adapter + extend MiddlewareRegistry + + CONTENT_LENGTH = 'Content-Length' + + # This module marks an Adapter as supporting parallel requests. + module Parallelism + attr_writer :supports_parallel + + def supports_parallel? + @supports_parallel + end + + def inherited(subclass) + super + subclass.supports_parallel = supports_parallel? + end + end + + extend Parallelism + self.supports_parallel = false + + def initialize(_app = nil, opts = {}, &block) + @app = lambda(&:response) + @connection_options = opts + @config_block = block + end + + # Yields or returns an adapter's configured connection. Depends on + # #build_connection being defined on this adapter. + # + # @param env [Faraday::Env, Hash] The env object for a faraday request. + # + # @return The return value of the given block, or the HTTP connection object + # if no block is given. + def connection(env) + conn = build_connection(env) + return conn unless block_given? + + yield conn + end + + # Close any persistent connections. The adapter should still be usable + # after calling close. + def close + # Possible implementation: + # @app.close if @app.respond_to?(:close) + end + + def call(env) + env.clear_body if env.needs_body? + env.response = Response.new + end + + private + + def save_response(env, status, body, headers = nil, reason_phrase = nil, finished: true) + env.status = status + env.body = body + env.reason_phrase = reason_phrase&.to_s&.strip + env.response_headers = Utils::Headers.new.tap do |response_headers| + response_headers.update headers unless headers.nil? + yield(response_headers) if block_given? + end + + env.response.finish(env) unless env.parallel? || !finished + env.response + end + + # Fetches either a read, write, or open timeout setting. Defaults to the + # :timeout value if a more specific one is not given. + # + # @param type [Symbol] Describes which timeout setting to get: :read, + # :write, or :open. + # @param options [Hash] Hash containing Symbol keys like :timeout, + # :read_timeout, :write_timeout, or :open_timeout + # + # @return [Integer, nil] Timeout duration in seconds, or nil if no timeout + # has been set. + def request_timeout(type, options) + key = TIMEOUT_KEYS.fetch(type) do + msg = "Expected :read, :write, :open. Got #{type.inspect} :(" + raise ArgumentError, msg + end + options[key] || options[:timeout] + end + + TIMEOUT_KEYS = { + read: :read_timeout, + open: :open_timeout, + write: :write_timeout + }.freeze + end +end + +require 'faraday/adapter/test' diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/adapter/test.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/adapter/test.rb new file mode 100644 index 0000000..c637d13 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/adapter/test.rb @@ -0,0 +1,311 @@ +# frozen_string_literal: true + +require 'timeout' + +module Faraday + class Adapter + # @example + # test = Faraday::Connection.new do + # use Faraday::Adapter::Test do |stub| + # # Define matcher to match the request + # stub.get '/resource.json' do + # # return static content + # [200, {'Content-Type' => 'application/json'}, 'hi world'] + # end + # + # # response with content generated based on request + # stub.get '/showget' do |env| + # [200, {'Content-Type' => 'text/plain'}, env[:method].to_s] + # end + # + # # A regular expression can be used as matching filter + # stub.get /\A\/items\/(\d+)\z/ do |env, meta| + # # in case regular expression is used, an instance of MatchData + # # can be received + # [200, + # {'Content-Type' => 'text/plain'}, + # "showing item: #{meta[:match_data][1]}" + # ] + # end + # + # # Test the request body is the same as the stubbed body + # stub.post('/bar', 'name=YK&word=call') { [200, {}, ''] } + # + # # You can pass a proc as a stubbed body and check the request body in your way. + # # In this case, the proc should return true or false. + # stub.post('/foo', ->(request_body) do + # JSON.parse(request_body).slice('name') == { 'name' => 'YK' } }) { [200, {}, ''] + # end + # + # # You can set strict_mode to exactly match the stubbed requests. + # stub.strict_mode = true + # end + # end + # + # resp = test.get '/resource.json' + # resp.body # => 'hi world' + # + # resp = test.get '/showget' + # resp.body # => 'get' + # + # resp = test.get '/items/1' + # resp.body # => 'showing item: 1' + # + # resp = test.get '/items/2' + # resp.body # => 'showing item: 2' + # + # resp = test.post '/bar', 'name=YK&word=call' + # resp.status # => 200 + # + # resp = test.post '/foo', JSON.dump(name: 'YK', created_at: Time.now) + # resp.status # => 200 + class Test < Faraday::Adapter + attr_accessor :stubs + + # A stack of Stubs + class Stubs + class NotFound < StandardError + end + + def initialize(strict_mode: false) + # { get: [Stub, Stub] } + @stack = {} + @consumed = {} + @strict_mode = strict_mode + @stubs_mutex = Monitor.new + yield(self) if block_given? + end + + def empty? + @stack.empty? + end + + # @param env [Faraday::Env] + def match(env) + request_method = env[:method] + return false unless @stack.key?(request_method) + + stack = @stack[request_method] + consumed = (@consumed[request_method] ||= []) + + @stubs_mutex.synchronize do + stub, meta = matches?(stack, env) + if stub + removed = stack.delete(stub) + consumed << removed unless removed.nil? + return stub, meta + end + end + matches?(consumed, env) + end + + def get(path, headers = {}, &block) + new_stub(:get, path, headers, &block) + end + + def head(path, headers = {}, &block) + new_stub(:head, path, headers, &block) + end + + def post(path, body = nil, headers = {}, &block) + new_stub(:post, path, headers, body, &block) + end + + def put(path, body = nil, headers = {}, &block) + new_stub(:put, path, headers, body, &block) + end + + def patch(path, body = nil, headers = {}, &block) + new_stub(:patch, path, headers, body, &block) + end + + def delete(path, headers = {}, &block) + new_stub(:delete, path, headers, &block) + end + + def options(path, headers = {}, &block) + new_stub(:options, path, headers, &block) + end + + # Raises an error if any of the stubbed calls have not been made. + def verify_stubbed_calls + failed_stubs = [] + @stack.each do |method, stubs| + next if stubs.empty? + + failed_stubs.concat( + stubs.map do |stub| + "Expected #{method} #{stub}." + end + ) + end + raise failed_stubs.join(' ') unless failed_stubs.empty? + end + + # Set strict_mode. If the value is true, this adapter tries to find matched requests strictly, + # which means that all of a path, parameters, and headers must be the same as an actual request. + def strict_mode=(value) + @strict_mode = value + @stack.each_value do |stubs| + stubs.each do |stub| + stub.strict_mode = value + end + end + end + + protected + + def new_stub(request_method, path, headers = {}, body = nil, &block) + normalized_path, host = + if path.is_a?(Regexp) + path + else + [ + Faraday::Utils.normalize_path(path), + Faraday::Utils.URI(path).host + ] + end + path, query = normalized_path.respond_to?(:split) ? normalized_path.split('?') : normalized_path + headers = Utils::Headers.new(headers) + + stub = Stub.new(host, path, query, headers, body, @strict_mode, block) + (@stack[request_method] ||= []) << stub + end + + # @param stack [Hash] + # @param env [Faraday::Env] + def matches?(stack, env) + stack.each do |stub| + match_result, meta = stub.matches?(env) + return stub, meta if match_result + end + nil + end + end + + # Stub request + Stub = Struct.new(:host, :path, :query, :headers, :body, :strict_mode, :block) do + # @param env [Faraday::Env] + def matches?(env) + request_host = env[:url].host + request_path = Faraday::Utils.normalize_path(env[:url].path) + request_headers = env.request_headers + request_body = env[:body] + + # meta is a hash used as carrier + # that will be yielded to consumer block + meta = {} + [(host.nil? || host == request_host) && + path_match?(request_path, meta) && + params_match?(env) && + body_match?(request_body) && + headers_match?(request_headers), meta] + end + + def path_match?(request_path, meta) + if path.is_a?(Regexp) + !!(meta[:match_data] = path.match(request_path)) + else + path == request_path + end + end + + # @param env [Faraday::Env] + def params_match?(env) + request_params = env[:params] + params = env.params_encoder.decode(query) || {} + + if strict_mode + return Set.new(params) == Set.new(request_params) + end + + params.keys.all? do |key| + request_params[key] == params[key] + end + end + + def headers_match?(request_headers) + if strict_mode + headers_with_user_agent = headers.dup.tap do |hs| + # NOTE: Set User-Agent in case it's not set when creating Stubs. + # Users would not want to set Faraday's User-Agent explicitly. + hs[:user_agent] ||= Connection::USER_AGENT + end + return Set.new(headers_with_user_agent) == Set.new(request_headers) + end + + headers.keys.all? do |key| + request_headers[key] == headers[key] + end + end + + def body_match?(request_body) + return true if body.to_s.empty? + + case body + when Proc + body.call(request_body) + else + request_body == body + end + end + + def to_s + "#{path} #{body}" + end + end + + def initialize(app, stubs = nil, &block) + super(app) + @stubs = stubs || Stubs.new + configure(&block) if block + end + + def configure + yield(stubs) + end + + # @param env [Faraday::Env] + def call(env) + super + + env.request.params_encoder ||= Faraday::Utils.default_params_encoder + env[:params] = env.params_encoder.decode(env[:url].query) || {} + stub, meta = stubs.match(env) + + unless stub + raise Stubs::NotFound, "no stubbed request for #{env[:method]} " \ + "#{env[:url]} #{env[:body]} #{env[:headers]}" + end + + block_arity = stub.block.arity + params = if block_arity >= 0 + [env, meta].take(block_arity) + else + [env, meta] + end + + timeout = request_timeout(:open, env[:request]) + timeout ||= request_timeout(:read, env[:request]) + + status, headers, body = + if timeout + ::Timeout.timeout(timeout, Faraday::TimeoutError) do + stub.block.call(*params) + end + else + stub.block.call(*params) + end + + # We need to explicitly pass `reason_phrase = nil` here to avoid keyword args conflicts. + # See https://github.com/lostisland/faraday/issues/1444 + # TODO: remove `nil` explicit reason_phrase once Ruby 3.0 becomes minimum req. version + save_response(env, status, body, headers, nil) + + @app.call(env) + end + end + end +end + +Faraday::Adapter.register_middleware(test: Faraday::Adapter::Test) diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/adapter_registry.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/adapter_registry.rb new file mode 100644 index 0000000..1cd1e7e --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/adapter_registry.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +require 'monitor' + +module Faraday + # AdapterRegistry registers adapter class names so they can be looked up by a + # String or Symbol name. + class AdapterRegistry + def initialize + @lock = Monitor.new + @constants = {} + end + + def get(name) + klass = @lock.synchronize do + @constants[name] + end + return klass if klass + + Object.const_get(name).tap { |c| set(c, name) } + end + + def set(klass, name = nil) + name ||= klass.to_s + @lock.synchronize do + @constants[name] = klass + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/connection.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/connection.rb new file mode 100644 index 0000000..543cd4b --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/connection.rb @@ -0,0 +1,564 @@ +# frozen_string_literal: true + +module Faraday + # Connection objects manage the default properties and the middleware + # stack for fulfilling an HTTP request. + # + # @example + # + # conn = Faraday::Connection.new 'http://httpbingo.org' + # + # # GET http://httpbingo.org/nigiri + # conn.get 'nigiri' + # # => # + # + class Connection + # A Set of allowed HTTP verbs. + METHODS = Set.new %i[get post put delete head patch options trace] + USER_AGENT = "Faraday v#{VERSION}".freeze + + # @return [Hash] URI query unencoded key/value pairs. + attr_reader :params + + # @return [Hash] unencoded HTTP header key/value pairs. + attr_reader :headers + + # @return [String] a URI with the prefix used for all requests from this + # Connection. This includes a default host name, scheme, port, and path. + attr_reader :url_prefix + + # @return [Faraday::RackBuilder] Builder for this Connection. + attr_reader :builder + + # @return [Hash] SSL options. + attr_reader :ssl + + # @return [Object] the parallel manager for this Connection. + attr_reader :parallel_manager + + # Sets the default parallel manager for this connection. + attr_writer :default_parallel_manager + + # @return [Hash] proxy options. + attr_reader :proxy + + # Initializes a new Faraday::Connection. + # + # @param url [URI, String] URI or String base URL to use as a prefix for all + # requests (optional). + # @param options [Hash, Faraday::ConnectionOptions] + # @option options [URI, String] :url ('http:/') URI or String base URL + # @option options [Hash String>] :params URI query unencoded + # key/value pairs. + # @option options [Hash String>] :headers Hash of unencoded HTTP + # header key/value pairs. + # @option options [Hash] :request Hash of request options. + # @option options [Hash] :ssl Hash of SSL options. + # @option options [Hash, URI, String] :proxy proxy options, either as a URL + # or as a Hash + # @option options [URI, String] :proxy[:uri] + # @option options [String] :proxy[:user] + # @option options [String] :proxy[:password] + # @yield [self] after all setup has been done + def initialize(url = nil, options = nil) + options = ConnectionOptions.from(options) + + if url.is_a?(Hash) || url.is_a?(ConnectionOptions) + options = Utils.deep_merge(options, url) + url = options.url + end + + @parallel_manager = nil + @headers = Utils::Headers.new + @params = Utils::ParamsHash.new + @options = options.request + @ssl = options.ssl + @default_parallel_manager = options.parallel_manager + @manual_proxy = nil + + @builder = options.builder || begin + # pass an empty block to Builder so it doesn't assume default middleware + options.new_builder(block_given? ? proc { |b| } : nil) + end + + self.url_prefix = url || 'http:/' + + @params.update(options.params) if options.params + @headers.update(options.headers) if options.headers + + initialize_proxy(url, options) + + yield(self) if block_given? + + @headers[:user_agent] ||= USER_AGENT + end + + def initialize_proxy(url, options) + @manual_proxy = !!options.proxy + @proxy = + if options.proxy + ProxyOptions.from(options.proxy) + else + proxy_from_env(url) + end + end + + # Sets the Hash of URI query unencoded key/value pairs. + # @param hash [Hash] + def params=(hash) + @params.replace hash + end + + # Sets the Hash of unencoded HTTP header key/value pairs. + # @param hash [Hash] + def headers=(hash) + @headers.replace hash + end + + extend Forwardable + + def_delegators :builder, :use, :request, :response, :adapter, :app + + # Closes the underlying resources and/or connections. In the case of + # persistent connections, this closes all currently open connections + # but does not prevent new connections from being made. + def close + app.close + end + + # @!method get(url = nil, params = nil, headers = nil) + # Makes a GET HTTP request without a body. + # @!scope class + # + # @param url [String, URI, nil] The optional String base URL to use as a prefix for + # all requests. Can also be the options Hash. + # @param params [Hash, nil] Hash of URI query unencoded key/value pairs. + # @param headers [Hash, nil] unencoded HTTP header key/value pairs. + # + # @example + # conn.get '/items', { page: 1 }, :accept => 'application/json' + # + # # ElasticSearch example sending a body with GET. + # conn.get '/twitter/tweet/_search' do |req| + # req.headers[:content_type] = 'application/json' + # req.params[:routing] = 'kimchy' + # req.body = JSON.generate(query: {...}) + # end + # + # @yield [Faraday::Request] for further request customizations + # @return [Faraday::Response] + + # @!method head(url = nil, params = nil, headers = nil) + # Makes a HEAD HTTP request without a body. + # @!scope class + # + # @param url [String, URI, nil] The optional String base URL to use as a prefix for + # all requests. Can also be the options Hash. + # @param params [Hash, nil] Hash of URI query unencoded key/value pairs. + # @param headers [Hash, nil] unencoded HTTP header key/value pairs. + # + # @example + # conn.head '/items/1' + # + # @yield [Faraday::Request] for further request customizations + # @return [Faraday::Response] + + # @!method delete(url = nil, params = nil, headers = nil) + # Makes a DELETE HTTP request without a body. + # @!scope class + # + # @param url [String, URI, nil] The optional String base URL to use as a prefix for + # all requests. Can also be the options Hash. + # @param params [Hash, nil] Hash of URI query unencoded key/value pairs. + # @param headers [Hash, nil] unencoded HTTP header key/value pairs. + # + # @example + # conn.delete '/items/1' + # + # @yield [Faraday::Request] for further request customizations + # @return [Faraday::Response] + + # @!method trace(url = nil, params = nil, headers = nil) + # Makes a TRACE HTTP request without a body. + # @!scope class + # + # @param url [String, URI, nil] The optional String base URL to use as a prefix for + # all requests. Can also be the options Hash. + # @param params [Hash, nil] Hash of URI query unencoded key/value pairs. + # @param headers [Hash, nil] unencoded HTTP header key/value pairs. + # + # @example + # conn.connect '/items/1' + # + # @yield [Faraday::Request] for further request customizations + # @return [Faraday::Response] + + # @!visibility private + METHODS_WITH_QUERY.each do |method| + class_eval <<-RUBY, __FILE__, __LINE__ + 1 + def #{method}(url = nil, params = nil, headers = nil) + run_request(:#{method}, url, nil, headers) do |request| + request.params.update(params) if params + yield request if block_given? + end + end + RUBY + end + + # @overload options() + # Returns current Connection options. + # + # @overload options(url, params = nil, headers = nil) + # Makes an OPTIONS HTTP request to the given URL. + # @param url [String, URI, nil] String base URL to sue as a prefix for all requests. + # @param params [Hash, nil] Hash of URI query unencoded key/value pairs. + # @param headers [Hash, nil] unencoded HTTP header key/value pairs. + # + # @example + # conn.options '/items/1' + # + # @yield [Faraday::Request] for further request customizations + # @return [Faraday::Response] + def options(*args) + return @options if args.empty? + + url, params, headers = *args + run_request(:options, url, nil, headers) do |request| + request.params.update(params) if params + yield request if block_given? + end + end + + # @!method post(url = nil, body = nil, headers = nil) + # Makes a POST HTTP request with a body. + # @!scope class + # + # @param url [String, URI, nil] The optional String base URL to use as a prefix for + # all requests. Can also be the options Hash. + # @param body [String, nil] body for the request. + # @param headers [Hash, nil] unencoded HTTP header key/value pairs. + # + # @example + # conn.post '/items', data, content_type: 'application/json' + # + # # Simple ElasticSearch indexing sample. + # conn.post '/twitter/tweet' do |req| + # req.headers[:content_type] = 'application/json' + # req.params[:routing] = 'kimchy' + # req.body = JSON.generate(user: 'kimchy', ...) + # end + # + # @yield [Faraday::Request] for further request customizations + # @return [Faraday::Response] + + # @!method put(url = nil, body = nil, headers = nil) + # Makes a PUT HTTP request with a body. + # @!scope class + # + # @param url [String, URI, nil] The optional String base URL to use as a prefix for + # all requests. Can also be the options Hash. + # @param body [String, nil] body for the request. + # @param headers [Hash, nil] unencoded HTTP header key/value pairs. + # + # @example + # conn.put '/products/123', data, content_type: 'application/json' + # + # # Star a gist. + # conn.put 'https://api.github.com/gists/GIST_ID/star' do |req| + # req.headers['Accept'] = 'application/vnd.github+json' + # req.headers['Authorization'] = 'Bearer ' + # req.headers['X-GitHub-Api-Version'] = '2022-11-28' + # end + # + # @yield [Faraday::Request] for further request customizations + # @return [Faraday::Response] + + # @!visibility private + METHODS_WITH_BODY.each do |method| + class_eval <<-RUBY, __FILE__, __LINE__ + 1 + def #{method}(url = nil, body = nil, headers = nil, &block) + run_request(:#{method}, url, body, headers, &block) + end + RUBY + end + + # Check if the adapter is parallel-capable. + # + # @yield if the adapter isn't parallel-capable, or if no adapter is set yet. + # + # @return [Object, nil] a parallel manager or nil if yielded + # @api private + def default_parallel_manager + @default_parallel_manager ||= begin + adapter = @builder.adapter.klass if @builder.adapter + + if support_parallel?(adapter) + adapter.setup_parallel_manager + elsif block_given? + yield + end + end + end + + # Determine if this Faraday::Connection can make parallel requests. + # + # @return [Boolean] + def in_parallel? + !!@parallel_manager + end + + # Sets up the parallel manager to make a set of requests. + # + # @param manager [Object] The parallel manager that this Connection's + # Adapter uses. + # + # @yield a block to execute multiple requests. + # @return [void] + def in_parallel(manager = nil, &block) + @parallel_manager = manager || default_parallel_manager do + warn 'Warning: `in_parallel` called but no parallel-capable adapter ' \ + 'on Faraday stack' + warn caller[2, 10].join("\n") + nil + end + return yield unless @parallel_manager + + if @parallel_manager.respond_to?(:execute) + # Execute is the new method that is responsible for executing the block. + @parallel_manager.execute(&block) + else + # TODO: Old behaviour, deprecate and remove in 3.0 + yield + @parallel_manager.run + end + ensure + @parallel_manager = nil + end + + # Sets the Hash proxy options. + # + # @param new_value [Object] + def proxy=(new_value) + @manual_proxy = true + @proxy = new_value ? ProxyOptions.from(new_value) : nil + end + + def_delegators :url_prefix, :scheme, :scheme=, :host, :host=, :port, :port= + def_delegator :url_prefix, :path, :path_prefix + + # Parses the given URL with URI and stores the individual + # components in this connection. These components serve as defaults for + # requests made by this connection. + # + # @param url [String, URI] + # @param encoder [Object] + # + # @example + # + # conn = Faraday::Connection.new { ... } + # conn.url_prefix = "https://httpbingo.org/api" + # conn.scheme # => https + # conn.path_prefix # => "/api" + # + # conn.get("nigiri?page=2") # accesses https://httpbingo.org/api/nigiri + def url_prefix=(url, encoder = nil) + uri = @url_prefix = Utils.URI(url) + self.path_prefix = uri.path + + params.merge_query(uri.query, encoder) + uri.query = nil + + with_uri_credentials(uri) do |user, password| + set_basic_auth(user, password) + uri.user = uri.password = nil + end + + @proxy = proxy_from_env(url) unless @manual_proxy + end + + def set_basic_auth(user, password) + header = Faraday::Utils.basic_header_from(user, password) + headers[Faraday::Request::Authorization::KEY] = header + end + + # Sets the path prefix and ensures that it always has a leading + # slash. + # + # @param value [String] + # + # @return [String] the new path prefix + def path_prefix=(value) + url_prefix.path = if value + value = "/#{value}" unless value[0, 1] == '/' + value + end + end + + # Takes a relative url for a request and combines it with the defaults + # set on the connection instance. + # + # @param url [String, URI, nil] + # @param extra_params [Hash] + # + # @example + # conn = Faraday::Connection.new { ... } + # conn.url_prefix = "https://httpbingo.org/api?token=abc" + # conn.scheme # => https + # conn.path_prefix # => "/api" + # + # conn.build_url("nigiri?page=2") + # # => https://httpbingo.org/api/nigiri?token=abc&page=2 + # + # conn.build_url("nigiri", page: 2) + # # => https://httpbingo.org/api/nigiri?token=abc&page=2 + # + def build_url(url = nil, extra_params = nil) + uri = build_exclusive_url(url) + + query_values = params.dup.merge_query(uri.query, options.params_encoder) + query_values.update(extra_params) if extra_params + uri.query = + if query_values.empty? + nil + else + query_values.to_query(options.params_encoder) + end + + uri + end + + # Builds and runs the Faraday::Request. + # + # @param method [Symbol] HTTP method. + # @param url [String, URI, nil] String or URI to access. + # @param body [String, Hash, Array, nil] The request body that will eventually be converted to + # a string; middlewares can be used to support more complex types. + # @param headers [Hash, nil] unencoded HTTP header key/value pairs. + # + # @return [Faraday::Response] + def run_request(method, url, body, headers) + unless METHODS.include?(method) + raise ArgumentError, "unknown http method: #{method}" + end + + request = build_request(method) do |req| + req.options.proxy = proxy_for_request(url) + req.url(url) if url + req.headers.update(headers) if headers + req.body = body if body + yield(req) if block_given? + end + + builder.build_response(self, request) + end + + # Creates and configures the request object. + # + # @param method [Symbol] + # + # @yield [Faraday::Request] if block given + # @return [Faraday::Request] + def build_request(method) + Request.create(method) do |req| + req.params = params.dup + req.headers = headers.dup + req.options = options.dup + yield(req) if block_given? + end + end + + # Build an absolute URL based on url_prefix. + # + # @param url [String, URI, nil] + # @param params [Faraday::Utils::ParamsHash] A Faraday::Utils::ParamsHash to + # replace the query values + # of the resulting url (default: nil). + # + # @return [URI] + def build_exclusive_url(url = nil, params = nil, params_encoder = nil) + url = nil if url.respond_to?(:empty?) && url.empty? + base = url_prefix.dup + if url && !base.path.end_with?('/') + base.path = "#{base.path}/" # ensure trailing slash + end + # Ensure relative url will be parsed correctly (such as `service:search` ) + url = "./#{url}" if url.respond_to?(:start_with?) && !url.start_with?('http://', 'https://', '/', './', '../') + uri = url ? base + url : base + if params + uri.query = params.to_query(params_encoder || options.params_encoder) + end + uri.query = nil if uri.query && uri.query.empty? + uri + end + + # Creates a duplicate of this Faraday::Connection. + # + # @api private + # + # @return [Faraday::Connection] + def dup + self.class.new(build_exclusive_url, + headers: headers.dup, + params: params.dup, + builder: builder.dup, + ssl: ssl.dup, + request: options.dup) + end + + # Yields username and password extracted from a URI if they both exist. + # + # @param uri [URI] + # @yield [username, password] any username and password + # @yieldparam username [String] any username from URI + # @yieldparam password [String] any password from URI + # @return [void] + # @api private + def with_uri_credentials(uri) + return unless uri.user && uri.password + + yield(Utils.unescape(uri.user), Utils.unescape(uri.password)) + end + + def proxy_from_env(url) + return if Faraday.ignore_env_proxy + + uri = nil + case url + when String + uri = Utils.URI(url) + uri = if uri.host.nil? + find_default_proxy + else + URI.parse("#{uri.scheme}://#{uri.host}").find_proxy + end + when URI + uri = url.find_proxy + when nil + uri = find_default_proxy + end + ProxyOptions.from(uri) if uri + end + + def find_default_proxy + uri = ENV.fetch('http_proxy', nil) + return unless uri && !uri.empty? + + uri = "http://#{uri}" unless uri.match?(/^http/i) + uri + end + + def proxy_for_request(url) + return proxy if @manual_proxy + + if url && Utils.URI(url).absolute? + proxy_from_env(url) + else + proxy + end + end + + def support_parallel?(adapter) + adapter.respond_to?(:supports_parallel?) && adapter&.supports_parallel? + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/encoders/flat_params_encoder.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/encoders/flat_params_encoder.rb new file mode 100644 index 0000000..7bbf9c4 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/encoders/flat_params_encoder.rb @@ -0,0 +1,105 @@ +# frozen_string_literal: true + +module Faraday + # FlatParamsEncoder manages URI params as a flat hash. Any Array values repeat + # the parameter multiple times. + module FlatParamsEncoder + class << self + extend Forwardable + def_delegators :'Faraday::Utils', :escape, :unescape + end + + # Encode converts the given param into a URI querystring. Keys and values + # will converted to strings and appropriately escaped for the URI. + # + # @param params [Hash] query arguments to convert. + # + # @example + # + # encode({a: %w[one two three], b: true, c: "C"}) + # # => 'a=one&a=two&a=three&b=true&c=C' + # + # @return [String] the URI querystring (without the leading '?') + def self.encode(params) + return nil if params.nil? + + unless params.is_a?(Array) + unless params.respond_to?(:to_hash) + raise TypeError, + "Can't convert #{params.class} into Hash." + end + params = params.to_hash + params = params.map do |key, value| + key = key.to_s if key.is_a?(Symbol) + [key, value] + end + + # Only to be used for non-Array inputs. Arrays should preserve order. + params.sort! if @sort_params + end + + # The params have form [['key1', 'value1'], ['key2', 'value2']]. + buffer = +'' + params.each do |key, value| + encoded_key = escape(key) + if value.nil? + buffer << "#{encoded_key}&" + elsif value.is_a?(Array) + if value.empty? + buffer << "#{encoded_key}=&" + else + value.each do |sub_value| + encoded_value = escape(sub_value) + buffer << "#{encoded_key}=#{encoded_value}&" + end + end + else + encoded_value = escape(value) + buffer << "#{encoded_key}=#{encoded_value}&" + end + end + buffer.chop + end + + # Decode converts the given URI querystring into a hash. + # + # @param query [String] query arguments to parse. + # + # @example + # + # decode('a=one&a=two&a=three&b=true&c=C') + # # => {"a"=>["one", "two", "three"], "b"=>"true", "c"=>"C"} + # + # @return [Hash] parsed keys and value strings from the querystring. + def self.decode(query) + return nil if query.nil? + + empty_accumulator = {} + + split_query = query.split('&').filter_map do |pair| + pair.split('=', 2) if pair && !pair.empty? + end + split_query.each_with_object(empty_accumulator.dup) do |pair, accu| + pair[0] = unescape(pair[0]) + pair[1] = true if pair[1].nil? + if pair[1].respond_to?(:to_str) + pair[1] = unescape(pair[1].to_str.tr('+', ' ')) + end + if accu[pair[0]].is_a?(Array) + accu[pair[0]] << pair[1] + elsif accu[pair[0]] + accu[pair[0]] = [accu[pair[0]], pair[1]] + else + accu[pair[0]] = pair[1] + end + end + end + + class << self + attr_accessor :sort_params + end + + # Useful default for OAuth and caching. + @sort_params = true + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/encoders/nested_params_encoder.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/encoders/nested_params_encoder.rb new file mode 100644 index 0000000..3ca3e73 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/encoders/nested_params_encoder.rb @@ -0,0 +1,183 @@ +# frozen_string_literal: true + +module Faraday + # Sub-module for encoding parameters into query-string. + module EncodeMethods + # @param params [nil, Array, #to_hash] parameters to be encoded + # + # @return [String] the encoded params + # + # @raise [TypeError] if params can not be converted to a Hash + def encode(params) + return nil if params.nil? + + unless params.is_a?(Array) + unless params.respond_to?(:to_hash) + raise TypeError, "Can't convert #{params.class} into Hash." + end + + params = params.to_hash + params = params.map do |key, value| + key = key.to_s if key.is_a?(Symbol) + [key, value] + end + + # Only to be used for non-Array inputs. Arrays should preserve order. + params.sort! if @sort_params + end + + # The params have form [['key1', 'value1'], ['key2', 'value2']]. + buffer = +'' + params.each do |parent, value| + encoded_parent = escape(parent) + buffer << "#{encode_pair(encoded_parent, value)}&" + end + buffer.chop + end + + protected + + def encode_pair(parent, value) + if value.is_a?(Hash) + encode_hash(parent, value) + elsif value.is_a?(Array) + encode_array(parent, value) + elsif value.nil? + parent + else + encoded_value = escape(value) + "#{parent}=#{encoded_value}" + end + end + + def encode_hash(parent, value) + value = value.map { |key, val| [escape(key), val] }.sort + + buffer = +'' + value.each do |key, val| + new_parent = "#{parent}%5B#{key}%5D" + buffer << "#{encode_pair(new_parent, val)}&" + end + buffer.chop + end + + def encode_array(parent, value) + return "#{parent}%5B%5D" if value.empty? + + buffer = +'' + value.each_with_index do |val, index| + new_parent = if @array_indices + "#{parent}%5B#{index}%5D" + else + "#{parent}%5B%5D" + end + buffer << "#{encode_pair(new_parent, val)}&" + end + buffer.chop + end + end + + # Sub-module for decoding query-string into parameters. + module DecodeMethods + # @param query [nil, String] + # + # @return [Array] the decoded params + # + # @raise [TypeError] if the nesting is incorrect + def decode(query) + return nil if query.nil? + + params = {} + query.split('&').each do |pair| + next if pair.empty? + + key, value = pair.split('=', 2) + key = unescape(key) + value = unescape(value.tr('+', ' ')) if value + decode_pair(key, value, params) + end + + dehash(params, 0) + end + + protected + + SUBKEYS_REGEX = /[^\[\]]+(?:\]?\[\])?/ + + def decode_pair(key, value, context) + subkeys = key.scan(SUBKEYS_REGEX) + subkeys.each_with_index do |subkey, i| + is_array = subkey =~ /[\[\]]+\Z/ + subkey = Regexp.last_match.pre_match if is_array + last_subkey = i == subkeys.length - 1 + + context = prepare_context(context, subkey, is_array, last_subkey) + add_to_context(is_array, context, value, subkey) if last_subkey + end + end + + def prepare_context(context, subkey, is_array, last_subkey) + if !last_subkey || is_array + context = new_context(subkey, is_array, context) + end + if context.is_a?(Array) && !is_array + context = match_context(context, subkey) + end + context + end + + def new_context(subkey, is_array, context) + value_type = is_array ? Array : Hash + if context[subkey] && !context[subkey].is_a?(value_type) + raise TypeError, "expected #{value_type.name} " \ + "(got #{context[subkey].class.name}) for param `#{subkey}'" + end + + context[subkey] ||= value_type.new + end + + def match_context(context, subkey) + context << {} if !context.last.is_a?(Hash) || context.last.key?(subkey) + context.last + end + + def add_to_context(is_array, context, value, subkey) + is_array ? context << value : context[subkey] = value + end + + # Internal: convert a nested hash with purely numeric keys into an array. + # FIXME: this is not compatible with Rack::Utils.parse_nested_query + # @!visibility private + def dehash(hash, depth) + hash.each do |key, value| + hash[key] = dehash(value, depth + 1) if value.is_a?(Hash) + end + + if depth.positive? && !hash.empty? && hash.keys.all? { |k| k =~ /^\d+$/ } + hash.sort.map(&:last) + else + hash + end + end + end + + # This is the default encoder for Faraday requests. + # Using this encoder, parameters will be encoded respecting their structure, + # so you can send objects such as Arrays or Hashes as parameters + # for your requests. + module NestedParamsEncoder + class << self + attr_accessor :sort_params, :array_indices + + extend Forwardable + def_delegators :'Faraday::Utils', :escape, :unescape + end + + # Useful default for OAuth and caching. + @sort_params = true + @array_indices = false + + extend EncodeMethods + extend DecodeMethods + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/error.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/error.rb new file mode 100644 index 0000000..791f8ee --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/error.rb @@ -0,0 +1,202 @@ +# frozen_string_literal: true + +# Faraday namespace. +module Faraday + # Faraday error base class. + class Error < StandardError + attr_reader :response, :wrapped_exception + + def initialize(exc = nil, response = nil) + @wrapped_exception = nil unless defined?(@wrapped_exception) + @response = nil unless defined?(@response) + super(exc_msg_and_response!(exc, response)) + end + + def backtrace + if @wrapped_exception + @wrapped_exception.backtrace + else + super + end + end + + def inspect + inner = +'' + inner << " wrapped=#{@wrapped_exception.inspect}" if @wrapped_exception + inner << " response=#{@response.inspect}" if @response + inner << " #{super}" if inner.empty? + %(#<#{self.class}#{inner}>) + end + + def response_status + return unless @response + + @response.is_a?(Faraday::Response) ? @response.status : @response[:status] + end + + def response_headers + return unless @response + + @response.is_a?(Faraday::Response) ? @response.headers : @response[:headers] + end + + def response_body + return unless @response + + @response.is_a?(Faraday::Response) ? @response.body : @response[:body] + end + + protected + + # Pulls out potential parent exception and response hash, storing them in + # instance variables. + # exc - Either an Exception, a string message, or a response hash. + # response - Hash + # :status - Optional integer HTTP response status + # :headers - String key/value hash of HTTP response header + # values. + # :body - Optional string HTTP response body. + # :request - Hash + # :method - Symbol with the request HTTP method. + # :url - URI object with the url requested. + # :url_path - String with the url path requested. + # :params - String key/value hash of query params + # present in the request. + # :headers - String key/value hash of HTTP request + # header values. + # :body - String HTTP request body. + # + # If a subclass has to call this, then it should pass a string message + # to `super`. See NilStatusError. + def exc_msg_and_response!(exc, response = nil) + if @response.nil? && @wrapped_exception.nil? + @wrapped_exception, msg, @response = exc_msg_and_response(exc, response) + return msg + end + + exc.to_s + end + + # Pulls out potential parent exception and response hash. + def exc_msg_and_response(exc, response = nil) + case exc + when Exception + [exc, exc.message, response] + when Hash + [nil, build_error_message_from_hash(exc), exc] + when Faraday::Env + [nil, build_error_message_from_env(exc), exc] + else + [nil, exc.to_s, response] + end + end + + private + + def build_error_message_from_hash(hash) + # Be defensive with external Hash objects - they might be missing keys + status = hash.fetch(:status, nil) + request = hash.fetch(:request, nil) + + return fallback_error_message(status) if request.nil? + + method = request.fetch(:method, nil) + url = request.fetch(:url, nil) + build_status_error_message(status, method, url) + end + + def build_error_message_from_env(env) + # Faraday::Env is internal - we can make reasonable assumptions about its structure + build_status_error_message(env.status, env.method, env.url) + end + + def build_status_error_message(status, method, url) + method_str = method ? method.to_s.upcase : '' + url_str = url ? url.to_s : '' + "the server responded with status #{status} for #{method_str} #{url_str}" + end + + def fallback_error_message(status) + "the server responded with status #{status} - method and url are not available " \ + 'due to include_request: false on Faraday::Response::RaiseError middleware' + end + end + + # Faraday client error class. Represents 4xx status responses. + class ClientError < Error + end + + # Raised by Faraday::Response::RaiseError in case of a 400 response. + class BadRequestError < ClientError + end + + # Raised by Faraday::Response::RaiseError in case of a 401 response. + class UnauthorizedError < ClientError + end + + # Raised by Faraday::Response::RaiseError in case of a 403 response. + class ForbiddenError < ClientError + end + + # Raised by Faraday::Response::RaiseError in case of a 404 response. + class ResourceNotFound < ClientError + end + + # Raised by Faraday::Response::RaiseError in case of a 407 response. + class ProxyAuthError < ClientError + end + + # Raised by Faraday::Response::RaiseError in case of a 408 response. + class RequestTimeoutError < ClientError + end + + # Raised by Faraday::Response::RaiseError in case of a 409 response. + class ConflictError < ClientError + end + + # Raised by Faraday::Response::RaiseError in case of a 422 response. + class UnprocessableContentError < ClientError + end + + # Used to provide compatibility with legacy error name. + UnprocessableEntityError = UnprocessableContentError + + # Raised by Faraday::Response::RaiseError in case of a 429 response. + class TooManyRequestsError < ClientError + end + + # Faraday server error class. Represents 5xx status responses. + class ServerError < Error + end + + # A unified client error for timeouts. + class TimeoutError < ServerError + def initialize(exc = 'timeout', response = nil) + super(exc, response) + end + end + + # Raised by Faraday::Response::RaiseError in case of a nil status in response. + class NilStatusError < ServerError + def initialize(exc, response = nil) + exc_msg_and_response!(exc, response) + super('http status could not be derived from the server response') + end + end + + # A unified error for failed connections. + class ConnectionFailed < Error + end + + # A unified client error for SSL errors. + class SSLError < Error + end + + # Raised by middlewares that parse the response, like the JSON response middleware. + class ParsingError < Error + end + + # Raised by Faraday::Middleware and subclasses when invalid default_options are used + class InitializationError < Error + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/logging/formatter.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/logging/formatter.rb new file mode 100644 index 0000000..c066b0f --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/logging/formatter.rb @@ -0,0 +1,118 @@ +# frozen_string_literal: true + +require 'pp' # This require is necessary for Hash#pretty_inspect to work, do not remove it, people rely on it. + +module Faraday + module Logging + # Serves as an integration point to customize logging + class Formatter + extend Forwardable + + DEFAULT_OPTIONS = { headers: true, bodies: false, errors: false, + log_level: :info }.freeze + + def initialize(logger:, options:) + @logger = logger + @options = DEFAULT_OPTIONS.merge(options) + unless %i[debug info warn error fatal].include?(@options[:log_level]) + @options[:log_level] = :info + end + @filter = [] + end + + def_delegators :@logger, :debug, :info, :warn, :error, :fatal + + def request(env) + public_send(log_level) do + "request: #{env.method.upcase} #{apply_filters(env.url.to_s)}" + end + + log_headers('request', env.request_headers) if log_headers?(:request) + log_body('request', env[:body]) if env[:body] && log_body?(:request) + end + + def response(env) + public_send(log_level) { "response: Status #{env.status}" } + + log_headers('response', env.response_headers) if log_headers?(:response) + log_body('response', env[:body]) if env[:body] && log_body?(:response) + end + + def exception(exc) + return unless log_errors? + + public_send(log_level) { "error: #{exc.full_message}" } + + log_headers('error', exc.response_headers) if exc.respond_to?(:response_headers) && log_headers?(:error) + return unless exc.respond_to?(:response_body) && exc.response_body && log_body?(:error) + + log_body('error', exc.response_body) + end + + def filter(filter_word, filter_replacement) + @filter.push([filter_word, filter_replacement]) + end + + private + + def dump_headers(headers) + return if headers.nil? + + headers.map { |k, v| "#{k}: #{v.inspect}" }.join("\n") + end + + def dump_body(body) + if body.respond_to?(:to_str) + body.to_str.encode(Encoding::UTF_8, undef: :replace, invalid: :replace) + else + pretty_inspect(body) + end + end + + def pretty_inspect(body) + body.pretty_inspect + end + + def log_headers?(type) + case @options[:headers] + when Hash + @options[:headers][type] + else + @options[:headers] + end + end + + def log_body?(type) + case @options[:bodies] + when Hash + @options[:bodies][type] + else + @options[:bodies] + end + end + + def log_errors? + @options[:errors] + end + + def apply_filters(output) + @filter.each do |pattern, replacement| + output = output.to_s.gsub(pattern, replacement) + end + output + end + + def log_level + @options[:log_level] + end + + def log_headers(type, headers) + public_send(log_level) { "#{type}: #{apply_filters(dump_headers(headers))}" } + end + + def log_body(type, body) + public_send(log_level) { "#{type}: #{apply_filters(dump_body(body))}" } + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/methods.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/methods.rb new file mode 100644 index 0000000..53e3903 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/methods.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +module Faraday + METHODS_WITH_QUERY = %w[get head delete trace].freeze + METHODS_WITH_BODY = %w[post put patch].freeze +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/middleware.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/middleware.rb new file mode 100644 index 0000000..52d8287 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/middleware.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +require 'monitor' + +module Faraday + # Middleware is the basic base class of any Faraday middleware. + class Middleware + extend MiddlewareRegistry + + attr_reader :app, :options + + DEFAULT_OPTIONS = {}.freeze + LOCK = Mutex.new + + def initialize(app = nil, options = {}) + @app = app + @options = self.class.default_options.merge(options) + end + + class << self + # Faraday::Middleware::default_options= allows user to set default options at the Faraday::Middleware + # class level. + # + # @example Set the Faraday::Response::RaiseError option, `include_request` to `false` + # my_app/config/initializers/my_faraday_middleware.rb + # + # Faraday::Response::RaiseError.default_options = { include_request: false } + # + def default_options=(options = {}) + validate_default_options(options) + LOCK.synchronize do + @default_options = default_options.merge(options) + end + end + + # default_options attr_reader that initializes class instance variable + # with the values of any Faraday::Middleware defaults, and merges with + # subclass defaults + def default_options + @default_options ||= DEFAULT_OPTIONS.merge(self::DEFAULT_OPTIONS) + end + + private + + def validate_default_options(options) + invalid_keys = options.keys.reject { |opt| self::DEFAULT_OPTIONS.key?(opt) } + return unless invalid_keys.any? + + raise(Faraday::InitializationError, + "Invalid options provided. Keys not found in #{self}::DEFAULT_OPTIONS: #{invalid_keys.join(', ')}") + end + end + + def call(env) + on_request(env) if respond_to?(:on_request) + app.call(env).on_complete do |environment| + on_complete(environment) if respond_to?(:on_complete) + end + rescue StandardError => e + on_error(e) if respond_to?(:on_error) + raise + end + + def close + if app.respond_to?(:close) + app.close + else + warn "#{app} does not implement \#close!" + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/middleware_registry.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/middleware_registry.rb new file mode 100644 index 0000000..fc70e2b --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/middleware_registry.rb @@ -0,0 +1,83 @@ +# frozen_string_literal: true + +require 'monitor' + +module Faraday + # Adds the ability for other modules to register and lookup + # middleware classes. + module MiddlewareRegistry + def registered_middleware + @registered_middleware ||= {} + end + + # Register middleware class(es) on the current module. + # + # @param mappings [Hash] Middleware mappings from a lookup symbol to a middleware class. + # @return [void] + # + # @example Lookup by a constant + # + # module Faraday + # class Whatever < Middleware + # # Middleware looked up by :foo returns Faraday::Whatever::Foo. + # register_middleware(foo: Whatever) + # end + # end + def register_middleware(**mappings) + middleware_mutex do + registered_middleware.update(mappings) + end + end + + # Unregister a previously registered middleware class. + # + # @param key [Symbol] key for the registered middleware. + def unregister_middleware(key) + registered_middleware.delete(key) + end + + # Lookup middleware class with a registered Symbol shortcut. + # + # @param key [Symbol] key for the registered middleware. + # @return [Class] a middleware Class. + # @raise [Faraday::Error] if given key is not registered + # + # @example + # + # module Faraday + # class Whatever < Middleware + # register_middleware(foo: Whatever) + # end + # end + # + # Faraday::Middleware.lookup_middleware(:foo) + # # => Faraday::Whatever + def lookup_middleware(key) + load_middleware(key) || + raise(Faraday::Error, "#{key.inspect} is not registered on #{self}") + end + + private + + def middleware_mutex(&block) + @middleware_mutex ||= Monitor.new + @middleware_mutex.synchronize(&block) + end + + def load_middleware(key) + value = registered_middleware[key] + case value + when Module + value + when Symbol, String + middleware_mutex do + @registered_middleware[key] = const_get(value) + end + when Proc + middleware_mutex do + @registered_middleware[key] = value.call + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/options.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/options.rb new file mode 100644 index 0000000..b3e0dea --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/options.rb @@ -0,0 +1,219 @@ +# frozen_string_literal: true + +module Faraday + # Subclasses Struct with some special helpers for converting from a Hash to + # a Struct. + class Options < Struct + # Public + def self.from(value) + value ? new.update(value) : new + end + + # Public + def each + return to_enum(:each) unless block_given? + + members.each do |key| + yield(key.to_sym, send(key)) + end + end + + # Public + def update(obj) + obj.each do |key, value| + sub_options = self.class.options_for(key) + if sub_options + new_value = sub_options.from(value) if value + elsif value.is_a?(Hash) + new_value = value.dup + else + new_value = value + end + + send(:"#{key}=", new_value) unless new_value.nil? + end + self + end + + # Public + def delete(key) + value = send(key) + send(:"#{key}=", nil) + value + end + + # Public + def clear + members.each { |member| delete(member) } + end + + # Public + def merge!(other) + other.each do |key, other_value| + self_value = send(key) + sub_options = self.class.options_for(key) + new_value = if self_value && sub_options && other_value + self_value.merge(other_value) + else + other_value + end + send(:"#{key}=", new_value) unless new_value.nil? + end + self + end + + # Public + def merge(other) + dup.merge!(other) + end + + # Public + def deep_dup + self.class.from(self) + end + + # Public + def fetch(key, *args) + unless symbolized_key_set.include?(key.to_sym) + key_setter = "#{key}=" + if !args.empty? + send(key_setter, args.first) + elsif block_given? + send(key_setter, yield(key)) + else + raise self.class.fetch_error_class, "key not found: #{key.inspect}" + end + end + send(key) + end + + # Public + def values_at(*keys) + keys.map { |key| send(key) } + end + + # Public + def keys + members.reject { |member| send(member).nil? } + end + + # Public + def empty? + keys.empty? + end + + # Public + def each_key(&block) + return to_enum(:each_key) unless block + + keys.each(&block) + end + + # Public + def key?(key) + keys.include?(key) + end + + alias has_key? key? + + # Public + def each_value(&block) + return to_enum(:each_value) unless block + + values.each(&block) + end + + # Public + def value?(value) + values.include?(value) + end + + alias has_value? value? + + # Public + def to_hash + hash = {} + members.each do |key| + value = send(key) + hash[key.to_sym] = value unless value.nil? + end + hash + end + + # Internal + def inspect + values = [] + members.each do |member| + value = send(member) + values << "#{member}=#{value.inspect}" if value + end + values = values.empty? ? '(empty)' : values.join(', ') + + %(#<#{self.class} #{values}>) + end + + # Internal + def self.options(mapping) + attribute_options.update(mapping) + end + + # Internal + def self.options_for(key) + attribute_options[key] + end + + # Internal + def self.attribute_options + @attribute_options ||= {} + end + + def self.memoized(key, &block) + unless block + raise ArgumentError, '#memoized must be called with a block' + end + + memoized_attributes[key.to_sym] = block + class_eval <<-RUBY, __FILE__, __LINE__ + 1 + remove_method(key) if method_defined?(key, false) + def #{key}() self[:#{key}]; end + RUBY + end + + def self.memoized_attributes + @memoized_attributes ||= {} + end + + def [](key) + key = key.to_sym + if (method = self.class.memoized_attributes[key]) + super(key) || (self[key] = instance_eval(&method)) + else + super + end + end + + def symbolized_key_set + @symbolized_key_set ||= Set.new(keys.map(&:to_sym)) + end + + def self.inherited(subclass) + super + subclass.attribute_options.update(attribute_options) + subclass.memoized_attributes.update(memoized_attributes) + end + + def self.fetch_error_class + @fetch_error_class ||= if Object.const_defined?(:KeyError) + ::KeyError + else + ::IndexError + end + end + end +end + +require 'faraday/options/request_options' +require 'faraday/options/ssl_options' +require 'faraday/options/proxy_options' +require 'faraday/options/connection_options' +require 'faraday/options/env' diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/options/connection_options.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/options/connection_options.rb new file mode 100644 index 0000000..0698940 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/options/connection_options.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Faraday + # @!parse + # # ConnectionOptions contains the configurable properties for a Faraday + # # connection object. + # class ConnectionOptions < Options; end + ConnectionOptions = Options.new(:request, :proxy, :ssl, :builder, :url, + :parallel_manager, :params, :headers, + :builder_class) do + options request: RequestOptions, ssl: SSLOptions + + memoized(:request) { self.class.options_for(:request).new } + + memoized(:ssl) { self.class.options_for(:ssl).new } + + memoized(:builder_class) { RackBuilder } + + def new_builder(block) + builder_class.new(&block) + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/options/env.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/options/env.rb new file mode 100644 index 0000000..63d814b --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/options/env.rb @@ -0,0 +1,204 @@ +# frozen_string_literal: true + +module Faraday + # @!parse + # # @!attribute method + # # @return [Symbol] HTTP method (`:get`, `:post`) + # # + # # @!attribute body + # # @return [String] The request body that will eventually be converted to a + # # string. + # # + # # @!attribute url + # # @return [URI] URI instance for the current request. + # # + # # @!attribute request + # # @return [Hash] options for configuring the request. + # # Options for configuring the request. + # # + # # - `:timeout` - time limit for the entire request (Integer in + # # seconds) + # # - `:open_timeout` - time limit for just the connection phase (e.g. + # # handshake) (Integer in seconds) + # # - `:read_timeout` - time limit for the first response byte received from + # # the server (Integer in seconds) + # # - `:write_timeout` - time limit for the client to send the request to the + # # server (Integer in seconds) + # # - `:on_data` - Proc for streaming + # # - `:proxy` - Hash of proxy options + # # - `:uri` - Proxy server URI + # # - `:user` - Proxy server username + # # - `:password` - Proxy server password + # # + # # @!attribute request_headers + # # @return [Hash] HTTP Headers to be sent to the server. + # # + # # @!attribute ssl + # # @return [Hash] options for configuring SSL requests + # # + # # @!attribute parallel_manager + # # @return [Object] sent if the connection is in parallel mode + # # + # # @!attribute params + # # @return [Hash] + # # + # # @!attribute response + # # @return [Response] + # # + # # @!attribute response_headers + # # @return [Hash] HTTP headers from the server + # # + # # @!attribute status + # # @return [Integer] HTTP response status code + # # + # # @!attribute reason_phrase + # # @return [String] + # class Env < Options; end + Env = Options.new(:method, :request_body, :url, :request, + :request_headers, :ssl, :parallel_manager, :params, + :response, :response_headers, :status, + :reason_phrase, :response_body) do + const_set(:ContentLength, 'Content-Length') + const_set(:StatusesWithoutBody, Set.new([204, 304])) + const_set(:SuccessfulStatuses, 200..299) + + # A Set of HTTP verbs that typically send a body. If no body is set for + # these requests, the Content-Length header is set to 0. + const_set(:MethodsWithBodies, Set.new(Faraday::METHODS_WITH_BODY.map(&:to_sym))) + + options request: RequestOptions, + request_headers: Utils::Headers, response_headers: Utils::Headers + + extend Forwardable + + def_delegators :request, :params_encoder + + # Build a new Env from given value. Respects and updates `custom_members`. + # + # @param value [Object] a value fitting Option.from(v). + # @return [Env] from given value + def self.from(value) + env = super(value) + if value.respond_to?(:custom_members) + env.custom_members.update(value.custom_members) + end + env + end + + # @param key [Object] + def [](key) + return self[current_body] if key == :body + + if in_member_set?(key) + super(key) + else + custom_members[key] + end + end + + # @param key [Object] + # @param value [Object] + def []=(key, value) + if key == :body + super(current_body, value) + return + end + + if in_member_set?(key) + super(key, value) + else + custom_members[key] = value + end + end + + def current_body + !!status ? :response_body : :request_body + end + + def body + self[:body] + end + + def body=(value) + self[:body] = value + end + + # @return [Boolean] true if status is in the set of {SuccessfulStatuses}. + def success? + Env::SuccessfulStatuses.include?(status) + end + + # @return [Boolean] true if there's no body yet, and the method is in the + # set of {Env::MethodsWithBodies}. + def needs_body? + !body && Env::MethodsWithBodies.include?(method) + end + + # Sets content length to zero and the body to the empty string. + def clear_body + request_headers[Env::ContentLength] = '0' + self.body = +'' + end + + # @return [Boolean] true if the status isn't in the set of + # {Env::StatusesWithoutBody}. + def parse_body? + !Env::StatusesWithoutBody.include?(status) + end + + # @return [Boolean] true if there is a parallel_manager + def parallel? + !!parallel_manager + end + + def inspect + attrs = [nil] + members.each do |mem| + if (value = send(mem)) + attrs << "@#{mem}=#{value.inspect}" + end + end + attrs << "@custom=#{custom_members.inspect}" unless custom_members.empty? + %(#<#{self.class}#{attrs.join(' ')}>) + end + + def stream_response? + request.stream_response? + end + + def stream_response(&block) + size = 0 + yielded = false + block_result = block.call do |chunk| + if chunk.bytesize.positive? || size.positive? + yielded = true + size += chunk.bytesize + request.on_data.call(chunk, size, self) + end + end + request.on_data.call(+'', 0, self) unless yielded + block_result + end + + # @private + def custom_members + @custom_members ||= {} + end + + # @private + if members.first.is_a?(Symbol) + def in_member_set?(key) + self.class.member_set.include?(key.to_sym) + end + else + def in_member_set?(key) + self.class.member_set.include?(key.to_s) + end + end + + # @private + def self.member_set + @member_set ||= Set.new(members) + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/options/proxy_options.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/options/proxy_options.rb new file mode 100644 index 0000000..028f3d2 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/options/proxy_options.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +module Faraday + # @!parse + # # ProxyOptions contains the configurable properties for the proxy + # # configuration used when making an HTTP request. + # class ProxyOptions < Options; end + ProxyOptions = Options.new(:uri, :user, :password) do + extend Forwardable + def_delegators :uri, :scheme, :scheme=, :host, :host=, :port, :port=, + :path, :path= + + def self.from(value) + case value + when '' + value = nil + when String + # URIs without a scheme should default to http (like 'example:123'). + # This fixes #1282 and prevents a silent failure in some adapters. + value = "http://#{value}" unless value.include?('://') + value = { uri: Utils.URI(value) } + when URI + value = { uri: value } + when Hash, Options + if value[:uri] + value = value.dup.tap do |duped| + duped[:uri] = Utils.URI(duped[:uri]) + end + end + end + + super(value) + end + + memoized(:user) { uri&.user && Utils.unescape(uri.user) } + memoized(:password) { uri&.password && Utils.unescape(uri.password) } + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/options/request_options.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/options/request_options.rb new file mode 100644 index 0000000..3bb67c8 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/options/request_options.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Faraday + # @!parse + # # RequestOptions contains the configurable properties for a Faraday request. + # class RequestOptions < Options; end + RequestOptions = Options.new(:params_encoder, :proxy, :bind, + :timeout, :open_timeout, :read_timeout, + :write_timeout, :boundary, :oauth, + :context, :on_data) do + def []=(key, value) + if key && key.to_sym == :proxy + super(key, value ? ProxyOptions.from(value) : nil) + else + super(key, value) + end + end + + def stream_response? + on_data.is_a?(Proc) + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/options/ssl_options.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/options/ssl_options.rb new file mode 100644 index 0000000..71eef15 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/options/ssl_options.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true + +module Faraday + # @!parse + # # SSL-related options. + # # + # # @!attribute verify + # # @return [Boolean] whether to verify SSL certificates or not + # # + # # @!attribute verify_hostname + # # @return [Boolean] whether to enable hostname verification on server certificates + # # during the handshake or not (see https://github.com/ruby/openssl/pull/60) + # # + # # @!attribute hostname + # # @return [String] Server hostname used for SNI (see https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL/SSL/SSLSocket.html#method-i-hostname-3D) + # # + # # @!attribute ca_file + # # @return [String] CA file + # # + # # @!attribute ca_path + # # @return [String] CA path + # # + # # @!attribute verify_mode + # # @return [Integer] Any `OpenSSL::SSL::` constant (see https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL/SSL.html) + # # + # # @!attribute cert_store + # # @return [OpenSSL::X509::Store] certificate store + # # + # # @!attribute client_cert + # # @return [String, OpenSSL::X509::Certificate] client certificate + # # + # # @!attribute client_key + # # @return [String, OpenSSL::PKey::RSA, OpenSSL::PKey::DSA] client key + # # + # # @!attribute certificate + # # @return [OpenSSL::X509::Certificate] certificate (Excon only) + # # + # # @!attribute private_key + # # @return [OpenSSL::PKey::RSA, OpenSSL::PKey::DSA] private key (Excon only) + # # + # # @!attribute verify_depth + # # @return [Integer] maximum depth for the certificate chain verification + # # + # # @!attribute version + # # @return [String, Symbol] SSL version (see https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html#method-i-ssl_version-3D) + # # + # # @!attribute min_version + # # @return [String, Symbol] minimum SSL version (see https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html#method-i-min_version-3D) + # # + # # @!attribute max_version + # # @return [String, Symbol] maximum SSL version (see https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html#method-i-max_version-3D) + # # + # # @!attribute ciphers + # # @return [String] cipher list in OpenSSL format (see https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html#method-i-ciphers-3D) + # class SSLOptions < Options; end + SSLOptions = Options.new(:verify, :verify_hostname, :hostname, + :ca_file, :ca_path, :verify_mode, + :cert_store, :client_cert, :client_key, + :certificate, :private_key, :verify_depth, + :version, :min_version, :max_version, :ciphers) do + # @return [Boolean] true if should verify + def verify? + verify != false + end + + # @return [Boolean] true if should not verify + def disable? + !verify? + end + + # @return [Boolean] true if should verify_hostname + def verify_hostname? + verify_hostname != false + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/parameters.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/parameters.rb new file mode 100644 index 0000000..cfb35d0 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/parameters.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +require 'forwardable' +require 'faraday/encoders/nested_params_encoder' +require 'faraday/encoders/flat_params_encoder' diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/rack_builder.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/rack_builder.rb new file mode 100644 index 0000000..1d86d35 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/rack_builder.rb @@ -0,0 +1,248 @@ +# frozen_string_literal: true + +require 'faraday/adapter_registry' + +module Faraday + # A Builder that processes requests into responses by passing through an inner + # middleware stack (heavily inspired by Rack). + # + # @example + # Faraday::Connection.new(url: 'http://httpbingo.org') do |builder| + # builder.request :url_encoded # Faraday::Request::UrlEncoded + # builder.adapter :net_http # Faraday::Adapter::NetHttp + # end + class RackBuilder + # Used to detect missing arguments + NO_ARGUMENT = Object.new + + attr_accessor :handlers + + # Error raised when trying to modify the stack after calling `lock!` + class StackLocked < RuntimeError; end + + # borrowed from ActiveSupport::Dependencies::Reference & + # ActionDispatch::MiddlewareStack::Middleware + class Handler + REGISTRY = Faraday::AdapterRegistry.new + + attr_reader :name + + def initialize(klass, *args, **kwargs, &block) + @name = klass.to_s + REGISTRY.set(klass) if klass.respond_to?(:name) + @args = args + @kwargs = kwargs + @block = block + end + + def klass + REGISTRY.get(@name) + end + + def inspect + @name + end + + def ==(other) + if other.is_a? Handler + name == other.name + elsif other.respond_to? :name + klass == other + else + @name == other.to_s + end + end + + def build(app = nil) + klass.new(app, *@args, **@kwargs, &@block) + end + end + + def initialize(&block) + @adapter = nil + @handlers = [] + build(&block) + end + + def initialize_dup(original) + super + @adapter = original.adapter + @handlers = original.handlers.dup + end + + def build + raise_if_locked + block_given? ? yield(self) : request(:url_encoded) + adapter(Faraday.default_adapter, **Faraday.default_adapter_options) unless @adapter + end + + def [](idx) + @handlers[idx] + end + + # Locks the middleware stack to ensure no further modifications are made. + def lock! + @handlers.freeze + end + + def locked? + @handlers.frozen? + end + + def use(klass, ...) + if klass.is_a? Symbol + use_symbol(Faraday::Middleware, klass, ...) + else + raise_if_locked + raise_if_adapter(klass) + @handlers << self.class::Handler.new(klass, ...) + end + end + + def request(key, ...) + use_symbol(Faraday::Request, key, ...) + end + + def response(...) + use_symbol(Faraday::Response, ...) + end + + def adapter(klass = NO_ARGUMENT, *args, **kwargs, &block) + return @adapter if klass == NO_ARGUMENT || klass.nil? + + klass = Faraday::Adapter.lookup_middleware(klass) if klass.is_a?(Symbol) + @adapter = self.class::Handler.new(klass, *args, **kwargs, &block) + end + + ## methods to push onto the various positions in the stack: + + def insert(index, ...) + raise_if_locked + index = assert_index(index) + handler = self.class::Handler.new(...) + @handlers.insert(index, handler) + end + + alias insert_before insert + + def insert_after(index, ...) + index = assert_index(index) + insert(index + 1, ...) + end + + def swap(index, ...) + raise_if_locked + index = assert_index(index) + @handlers.delete_at(index) + insert(index, ...) + end + + def delete(handler) + raise_if_locked + @handlers.delete(handler) + end + + # Processes a Request into a Response by passing it through this Builder's + # middleware stack. + # + # @param connection [Faraday::Connection] + # @param request [Faraday::Request] + # + # @return [Faraday::Response] + def build_response(connection, request) + app.call(build_env(connection, request)) + end + + # The "rack app" wrapped in middleware. All requests are sent here. + # + # The builder is responsible for creating the app object. After this, + # the builder gets locked to ensure no further modifications are made + # to the middleware stack. + # + # Returns an object that responds to `call` and returns a Response. + def app + @app ||= begin + lock! + ensure_adapter! + to_app + end + end + + def to_app + # last added handler is the deepest and thus closest to the inner app + # adapter is always the last one + @handlers.reverse.inject(@adapter.build) do |app, handler| + handler.build(app) + end + end + + def ==(other) + other.is_a?(self.class) && + @handlers == other.handlers && + @adapter == other.adapter + end + + # ENV Keys + # :http_method - a symbolized request HTTP method (:get, :post) + # :body - the request body that will eventually be converted to a string. + # :url - URI instance for the current request. + # :status - HTTP response status code + # :request_headers - hash of HTTP Headers to be sent to the server + # :response_headers - Hash of HTTP headers from the server + # :parallel_manager - sent if the connection is in parallel mode + # :request - Hash of options for configuring the request. + # :timeout - open/read timeout Integer in seconds + # :open_timeout - read timeout Integer in seconds + # :proxy - Hash of proxy options + # :uri - Proxy Server URI + # :user - Proxy server username + # :password - Proxy server password + # :ssl - Hash of options for configuring SSL requests. + def build_env(connection, request) + exclusive_url = connection.build_exclusive_url( + request.path, request.params, + request.options.params_encoder + ) + + Env.new(request.http_method, request.body, exclusive_url, + request.options, request.headers, connection.ssl, + connection.parallel_manager) + end + + private + + LOCK_ERR = "can't modify middleware stack after making a request" + MISSING_ADAPTER_ERROR = "An attempt to run a request with a Faraday::Connection without adapter has been made.\n" \ + "Please set Faraday.default_adapter or provide one when initializing the connection.\n" \ + 'For more info, check https://lostisland.github.io/faraday/usage/.' + + def raise_if_locked + raise StackLocked, LOCK_ERR if locked? + end + + def raise_if_adapter(klass) + return unless klass <= Faraday::Adapter + + raise 'Adapter should be set using the `adapter` method, not `use`' + end + + def ensure_adapter! + raise MISSING_ADAPTER_ERROR unless @adapter + end + + def adapter_set? + !@adapter.nil? + end + + def use_symbol(mod, key, ...) + use(mod.lookup_middleware(key), ...) + end + + def assert_index(index) + idx = index.is_a?(Integer) ? index : @handlers.index(index) + raise "No such handler: #{index.inspect}" unless idx + + idx + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/request.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/request.rb new file mode 100644 index 0000000..8c5bf95 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/request.rb @@ -0,0 +1,139 @@ +# frozen_string_literal: true + +module Faraday + # Used to setup URLs, params, headers, and the request body in a sane manner. + # + # @example + # @connection.post do |req| + # req.url 'http://localhost', 'a' => '1' # 'http://localhost?a=1' + # req.headers['b'] = '2' # Header + # req.params['c'] = '3' # GET Param + # req['b'] = '2' # also Header + # req.body = 'abc' + # end + # + # @!attribute http_method + # @return [Symbol] the HTTP method of the Request + # @!attribute path + # @return [URI, String] the path + # @!attribute params + # @return [Hash] query parameters + # @!attribute headers + # @return [Faraday::Utils::Headers] headers + # @!attribute body + # @return [String] body + # @!attribute options + # @return [RequestOptions] options + Request = Struct.new(:http_method, :path, :params, :headers, :body, :options) do + extend MiddlewareRegistry + + alias_method :member_get, :[] + private :member_get + alias_method :member_set, :[]= + private :member_set + + # @param request_method [String] + # @yield [request] for block customization, if block given + # @yieldparam request [Request] + # @return [Request] + def self.create(request_method) + new(request_method).tap do |request| + yield(request) if block_given? + end + end + + remove_method :params= + # Replace params, preserving the existing hash type. + # + # @param hash [Hash] new params + def params=(hash) + if params + params.replace hash + else + member_set(:params, hash) + end + end + + remove_method :headers= + # Replace request headers, preserving the existing hash type. + # + # @param hash [Hash] new headers + def headers=(hash) + if headers + headers.replace hash + else + member_set(:headers, hash) + end + end + + # Update path and params. + # + # @param path [URI, String] + # @param params [Hash, nil] + # @return [void] + def url(path, params = nil) + if path.respond_to? :query + if (query = path.query) + path = path.dup + path.query = nil + end + else + anchor_index = path.index('#') + path = path.slice(0, anchor_index) unless anchor_index.nil? + path, query = path.split('?', 2) + end + self.path = path + self.params.merge_query query, options.params_encoder + self.params.update(params) if params + end + + # @param key [Object] key to look up in headers + # @return [Object] value of the given header name + def [](key) + headers[key] + end + + # @param key [Object] key of header to write + # @param value [Object] value of header + def []=(key, value) + headers[key] = value + end + + # Marshal serialization support. + # + # @return [Hash] the hash ready to be serialized in Marshal. + def marshal_dump + { + http_method: http_method, + body: body, + headers: headers, + path: path, + params: params, + options: options + } + end + + # Marshal serialization support. + # Restores the instance variables according to the +serialised+. + # @param serialised [Hash] the serialised object. + def marshal_load(serialised) + self.http_method = serialised[:http_method] + self.body = serialised[:body] + self.headers = serialised[:headers] + self.path = serialised[:path] + self.params = serialised[:params] + self.options = serialised[:options] + end + + # @return [Env] the Env for this Request + def to_env(connection) + Env.new(http_method, body, connection.build_exclusive_url(path, params), + options, headers, connection.ssl, connection.parallel_manager) + end + end +end + +require 'faraday/request/authorization' +require 'faraday/request/instrumentation' +require 'faraday/request/json' +require 'faraday/request/url_encoded' diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/request/authorization.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/request/authorization.rb new file mode 100644 index 0000000..4373243 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/request/authorization.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +module Faraday + class Request + # Request middleware for the Authorization HTTP header + class Authorization < Faraday::Middleware + KEY = 'Authorization' + + # @param app [#call] + # @param type [String, Symbol] Type of Authorization + # @param params [Array] parameters to build the Authorization header. + # If the type is `:basic`, then these can be a login and password pair. + # Otherwise, a single value is expected that will be appended after the type. + # This value can be a proc or an object responding to `.call`, in which case + # it will be invoked on each request. + def initialize(app, type, *params) + @type = type + @params = params + super(app) + end + + # @param env [Faraday::Env] + def on_request(env) + return if env.request_headers[KEY] + + env.request_headers[KEY] = header_from(@type, env, *@params) + end + + private + + # @param type [String, Symbol] + # @param env [Faraday::Env] + # @param params [Array] + # @return [String] a header value + def header_from(type, env, *params) + if type.to_s.casecmp('basic').zero? && params.size == 2 + Utils.basic_header_from(*params) + elsif params.size != 1 + raise ArgumentError, "Unexpected params received (got #{params.size} instead of 1)" + else + value = params.first + if (value.is_a?(Proc) && value.arity == 1) || (value.respond_to?(:call) && value.method(:call).arity == 1) + value = value.call(env) + elsif value.is_a?(Proc) || value.respond_to?(:call) + value = value.call + end + "#{type} #{value}" + end + end + end + end +end + +Faraday::Request.register_middleware(authorization: Faraday::Request::Authorization) diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/request/instrumentation.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/request/instrumentation.rb new file mode 100644 index 0000000..a502059 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/request/instrumentation.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +module Faraday + class Request + # Middleware for instrumenting Requests. + class Instrumentation < Faraday::Middleware + # Options class used in Request::Instrumentation class. + Options = Faraday::Options.new(:name, :instrumenter) do + remove_method :name + # @return [String] + def name + self[:name] ||= 'request.faraday' + end + + remove_method :instrumenter + # @return [Class] + def instrumenter + self[:instrumenter] ||= ActiveSupport::Notifications + end + end + + # Instruments requests using Active Support. + # + # Measures time spent only for synchronous requests. + # + # @example Using ActiveSupport::Notifications to measure time spent + # for Faraday requests. + # ActiveSupport::Notifications + # .subscribe('request.faraday') do |name, starts, ends, _, env| + # url = env[:url] + # http_method = env[:method].to_s.upcase + # duration = ends - starts + # $stderr.puts '[%s] %s %s (%.3f s)' % + # [url.host, http_method, url.request_uri, duration] + # end + # @param app [#call] + # @param options [nil, Hash] Options hash + # @option options [String] :name ('request.faraday') + # Name of the instrumenter + # @option options [Class] :instrumenter (ActiveSupport::Notifications) + # Active Support instrumenter class. + def initialize(app, options = nil) + super(app) + @name, @instrumenter = Options.from(options) + .values_at(:name, :instrumenter) + end + + # @param env [Faraday::Env] + def call(env) + @instrumenter.instrument(@name, env) do + @app.call(env) + end + end + end + end +end + +Faraday::Request.register_middleware(instrumentation: Faraday::Request::Instrumentation) diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/request/json.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/request/json.rb new file mode 100644 index 0000000..f12ebe2 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/request/json.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true + +require 'json' + +module Faraday + class Request + # Request middleware that encodes the body as JSON. + # + # Processes only requests with matching Content-type or those without a type. + # If a request doesn't have a type but has a body, it sets the Content-type + # to JSON MIME-type. + # + # Doesn't try to encode bodies that already are in string form. + class Json < Middleware + MIME_TYPE = 'application/json' + MIME_TYPE_REGEX = %r{^application/(vnd\..+\+)?json$} + + def on_request(env) + match_content_type(env) do |data| + env[:body] = encode(data) + end + end + + private + + def encode(data) + if options[:encoder].is_a?(Array) && options[:encoder].size >= 2 + options[:encoder][0].public_send(options[:encoder][1], data) + elsif options[:encoder].respond_to?(:dump) + options[:encoder].dump(data) + else + ::JSON.generate(data) + end + end + + def match_content_type(env) + return unless process_request?(env) + + env[:request_headers][CONTENT_TYPE] ||= MIME_TYPE + yield env[:body] unless env[:body].respond_to?(:to_str) + end + + def process_request?(env) + type = request_type(env) + body?(env) && (type.empty? || type.match?(MIME_TYPE_REGEX)) + end + + def body?(env) + body = env[:body] + case body + when true, false + true + when nil + # NOTE: nil can be converted to `"null"`, but this middleware doesn't process `nil` for the compatibility. + false + else + !(body.respond_to?(:to_str) && body.empty?) + end + end + + def request_type(env) + type = env[:request_headers][CONTENT_TYPE].to_s + type = type.split(';', 2).first if type.index(';') + type + end + end + end +end + +Faraday::Request.register_middleware(json: Faraday::Request::Json) diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/request/url_encoded.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/request/url_encoded.rb new file mode 100644 index 0000000..5ac7dcb --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/request/url_encoded.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +module Faraday + class Request + # Middleware for supporting urlencoded requests. + class UrlEncoded < Faraday::Middleware + unless defined?(::Faraday::Request::UrlEncoded::CONTENT_TYPE) + CONTENT_TYPE = 'Content-Type' + end + + class << self + attr_accessor :mime_type + end + self.mime_type = 'application/x-www-form-urlencoded' + + # Encodes as "application/x-www-form-urlencoded" if not already encoded or + # of another type. + # + # @param env [Faraday::Env] + def call(env) + match_content_type(env) do |data| + params = Faraday::Utils::ParamsHash[data] + env.body = params.to_query(env.params_encoder) + end + @app.call env + end + + # @param env [Faraday::Env] + # @yield [request_body] Body of the request + def match_content_type(env) + return unless process_request?(env) + + env.request_headers[CONTENT_TYPE] ||= self.class.mime_type + return if env.body.respond_to?(:to_str) || env.body.respond_to?(:read) + + yield(env.body) + end + + # @param env [Faraday::Env] + # + # @return [Boolean] True if the request has a body and its Content-Type is + # urlencoded. + def process_request?(env) + type = request_type(env) + env.body && (type.empty? || (type == self.class.mime_type)) + end + + # @param env [Faraday::Env] + # + # @return [String] + def request_type(env) + type = env.request_headers[CONTENT_TYPE].to_s + type = type.split(';', 2).first if type.index(';') + type + end + end + end +end + +Faraday::Request.register_middleware(url_encoded: Faraday::Request::UrlEncoded) diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/response.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/response.rb new file mode 100644 index 0000000..738c849 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/response.rb @@ -0,0 +1,95 @@ +# frozen_string_literal: true + +require 'forwardable' + +module Faraday + # Response represents an HTTP response from making an HTTP request. + class Response + extend Forwardable + extend MiddlewareRegistry + + def initialize(env = nil) + @env = Env.from(env) if env + @on_complete_callbacks = [] + end + + attr_reader :env + + def status + finished? ? env.status : nil + end + + def reason_phrase + finished? ? env.reason_phrase : nil + end + + def headers + finished? ? env.response_headers : {} + end + + def_delegator :headers, :[] + + def body + finished? ? env.body : nil + end + + def url + finished? ? env.url : nil + end + + def finished? + !!env + end + + def on_complete(&block) + if finished? + yield(env) + else + @on_complete_callbacks << block + end + self + end + + def finish(env) + raise 'response already finished' if finished? + + @env = env.is_a?(Env) ? env : Env.from(env) + @on_complete_callbacks.each { |callback| callback.call(@env) } + self + end + + def success? + finished? && env.success? + end + + def to_hash + { + status: status, body: body, + response_headers: headers, + url: url + } + end + + # because @on_complete_callbacks cannot be marshalled + def marshal_dump + finished? ? to_hash : nil + end + + def marshal_load(env) + @env = Env.from(env) + end + + # Expand the env with more properties, without overriding existing ones. + # Useful for applying request params after restoring a marshalled Response. + def apply_request(request_env) + raise "response didn't finish yet" unless finished? + + @env = Env.from(request_env).update(@env) + self + end + end +end + +require 'faraday/response/json' +require 'faraday/response/logger' +require 'faraday/response/raise_error' diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/response/json.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/response/json.rb new file mode 100644 index 0000000..71a57ed --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/response/json.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: true + +require 'json' + +module Faraday + class Response + # Parse response bodies as JSON. + class Json < Middleware + def initialize(app = nil, parser_options: nil, content_type: /\bjson$/, preserve_raw: false) + super(app) + @parser_options = parser_options + @content_types = Array(content_type) + @preserve_raw = preserve_raw + + process_parser_options + end + + def on_complete(env) + process_response(env) if parse_response?(env) + end + + private + + def process_response(env) + env[:raw_body] = env[:body] if @preserve_raw + env[:body] = parse(env[:body]) + rescue StandardError, SyntaxError => e + raise Faraday::ParsingError.new(e, env[:response]) + end + + def parse(body) + return if body.strip.empty? + + decoder, method_name = @decoder_options + + decoder.public_send(method_name, body, @parser_options || {}) + end + + def parse_response?(env) + process_response_type?(env) && + env[:body].respond_to?(:to_str) + end + + def process_response_type?(env) + type = response_type(env) + @content_types.empty? || @content_types.any? do |pattern| + pattern.is_a?(Regexp) ? type.match?(pattern) : type == pattern + end + end + + def response_type(env) + type = env[:response_headers][CONTENT_TYPE].to_s + type = type.split(';', 2).first if type.index(';') + type + end + + def process_parser_options + @decoder_options = @parser_options&.delete(:decoder) + + @decoder_options = + if @decoder_options.is_a?(Array) && @decoder_options.size >= 2 + @decoder_options.slice(0, 2) + elsif @decoder_options&.respond_to?(:load) # rubocop:disable Lint/RedundantSafeNavigation + # In some versions of Rails, `nil` responds to `load` - hence the safe navigation check above + [@decoder_options, :load] + else + [::JSON, :parse] + end + end + end + end +end + +Faraday::Response.register_middleware(json: Faraday::Response::Json) diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/response/logger.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/response/logger.rb new file mode 100644 index 0000000..94dbc92 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/response/logger.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +require 'forwardable' +require 'logger' +require 'faraday/logging/formatter' + +module Faraday + class Response + # Logger is a middleware that logs internal events in the HTTP request + # lifecycle to a given Logger object. By default, this logs to STDOUT. See + # Faraday::Logging::Formatter to see specifically what is logged. + class Logger < Middleware + DEFAULT_OPTIONS = { formatter: Logging::Formatter }.merge(Logging::Formatter::DEFAULT_OPTIONS).freeze + + def initialize(app, logger = nil, options = {}) + super(app, options) + logger ||= ::Logger.new($stdout) + formatter_class = @options.delete(:formatter) + @formatter = formatter_class.new(logger: logger, options: @options) + yield @formatter if block_given? + end + + def call(env) + @formatter.request(env) + super + end + + def on_complete(env) + @formatter.response(env) + end + + def on_error(exc) + @formatter.exception(exc) if @formatter.respond_to?(:exception) + end + end + end +end + +Faraday::Response.register_middleware(logger: Faraday::Response::Logger) diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/response/raise_error.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/response/raise_error.rb new file mode 100644 index 0000000..48afd75 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/response/raise_error.rb @@ -0,0 +1,83 @@ +# frozen_string_literal: true + +module Faraday + class Response + # RaiseError is a Faraday middleware that raises exceptions on common HTTP + # client or server error responses. + class RaiseError < Middleware + # rubocop:disable Naming/ConstantName + ClientErrorStatuses = (400...500) + ServerErrorStatuses = (500...600) + ClientErrorStatusesWithCustomExceptions = { + 400 => Faraday::BadRequestError, + 401 => Faraday::UnauthorizedError, + 403 => Faraday::ForbiddenError, + 404 => Faraday::ResourceNotFound, + 408 => Faraday::RequestTimeoutError, + 409 => Faraday::ConflictError, + 422 => Faraday::UnprocessableContentError, + 429 => Faraday::TooManyRequestsError + }.freeze + # rubocop:enable Naming/ConstantName + + DEFAULT_OPTIONS = { include_request: true, allowed_statuses: [] }.freeze + + def on_complete(env) + return if Array(options[:allowed_statuses]).include?(env[:status]) + + case env[:status] + when *ClientErrorStatusesWithCustomExceptions.keys + raise ClientErrorStatusesWithCustomExceptions[env[:status]], response_values(env) + when 407 + # mimic the behavior that we get with proxy requests with HTTPS + msg = %(407 "Proxy Authentication Required") + raise Faraday::ProxyAuthError.new(msg, response_values(env)) + when ClientErrorStatuses + raise Faraday::ClientError, response_values(env) + when ServerErrorStatuses + raise Faraday::ServerError, response_values(env) + when nil + raise Faraday::NilStatusError, response_values(env) + end + end + + # Returns a hash of response data with the following keys: + # - status + # - headers + # - body + # - request + # + # The `request` key is omitted when the middleware is explicitly + # configured with the option `include_request: false`. + def response_values(env) + response = { + status: env.status, + headers: env.response_headers, + body: env.body + } + + # Include the request data by default. If the middleware was explicitly + # configured to _not_ include request data, then omit it. + return response unless options[:include_request] + + response.merge( + request: { + method: env.method, + url: env.url, + url_path: env.url.path, + params: query_params(env), + headers: env.request_headers, + body: env.request_body + } + ) + end + + def query_params(env) + env.request.params_encoder ||= Faraday::Utils.default_params_encoder + env.params_encoder.decode(env.url.query) + end + end + end +end + +Faraday::Response.register_middleware(raise_error: Faraday::Response::RaiseError) diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/utils.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/utils.rb new file mode 100644 index 0000000..809b3a8 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/utils.rb @@ -0,0 +1,121 @@ +# frozen_string_literal: true + +require 'uri' +require 'faraday/utils/headers' +require 'faraday/utils/params_hash' + +module Faraday + # Utils contains various static helper methods. + module Utils + module_function + + def build_query(params) + FlatParamsEncoder.encode(params) + end + + def build_nested_query(params) + NestedParamsEncoder.encode(params) + end + + def default_space_encoding + @default_space_encoding ||= '+' + end + + class << self + attr_writer :default_space_encoding + end + + ESCAPE_RE = /[^a-zA-Z0-9 .~_-]/ + + def escape(str) + str.to_s.gsub(ESCAPE_RE) do |match| + "%#{match.unpack('H2' * match.bytesize).join('%').upcase}" + end.gsub(' ', default_space_encoding) + end + + def unescape(str) + CGI.unescape str.to_s + end + + DEFAULT_SEP = /[&;] */n + + # Adapted from Rack + def parse_query(query) + FlatParamsEncoder.decode(query) + end + + def parse_nested_query(query) + NestedParamsEncoder.decode(query) + end + + def default_params_encoder + @default_params_encoder ||= NestedParamsEncoder + end + + def basic_header_from(login, pass) + value = ["#{login}:#{pass}"].pack('m') # Base64 encoding + value.delete!("\n") + "Basic #{value}" + end + + class << self + attr_writer :default_params_encoder + end + + # Normalize URI() behavior across Ruby versions + # + # url - A String or URI. + # + # Returns a parsed URI. + def URI(url) # rubocop:disable Naming/MethodName + if url.respond_to?(:host) + url + elsif url.respond_to?(:to_str) + default_uri_parser.call(url) + else + raise ArgumentError, 'bad argument (expected URI object or URI string)' + end + end + + def default_uri_parser + @default_uri_parser ||= Kernel.method(:URI) + end + + def default_uri_parser=(parser) + @default_uri_parser = if parser.respond_to?(:call) || parser.nil? + parser + else + parser.method(:parse) + end + end + + # Receives a String or URI and returns just + # the path with the query string sorted. + def normalize_path(url) + url = URI(url) + (url.path.start_with?('/') ? url.path : "/#{url.path}") + + (url.query ? "?#{sort_query_params(url.query)}" : '') + end + + # Recursive hash update + def deep_merge!(target, hash) + hash.each do |key, value| + target[key] = if value.is_a?(Hash) && (target[key].is_a?(Hash) || target[key].is_a?(Options)) + deep_merge(target[key], value) + else + value + end + end + target + end + + # Recursive hash merge + def deep_merge(source, hash) + deep_merge!(source.dup, hash) + end + + def sort_query_params(query) + query.split('&').sort.join('&') + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/utils/headers.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/utils/headers.rb new file mode 100644 index 0000000..27b06e9 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/utils/headers.rb @@ -0,0 +1,150 @@ +# frozen_string_literal: true + +module Faraday + module Utils + # A case-insensitive Hash that preserves the original case of a header + # when set. + # + # Adapted from Rack::Utils::HeaderHash + class Headers < ::Hash + def self.from(value) + new(value) + end + + def self.allocate + new_self = super + new_self.initialize_names + new_self + end + + def initialize(hash = nil) + super() + @names = {} + update(hash || {}) + end + + def initialize_names + @names = {} + end + + # on dup/clone, we need to duplicate @names hash + def initialize_copy(other) + super + @names = other.names.dup + end + + # need to synchronize concurrent writes to the shared KeyMap + keymap_mutex = Mutex.new + + # symbol -> string mapper + cache + KeyMap = Hash.new do |map, key| + value = if key.respond_to?(:to_str) + key + else + key.to_s.split('_') # user_agent: %w(user agent) + .each(&:capitalize!) # => %w(User Agent) + .join('-') # => "User-Agent" + end + keymap_mutex.synchronize { map[key] = value } + end + KeyMap[:etag] = 'ETag' + + def [](key) + key = KeyMap[key] + super(key) || super(@names[key.downcase]) + end + + def []=(key, val) + key = KeyMap[key] + key = (@names[key.downcase] ||= key) + # join multiple values with a comma + val = val.to_ary.join(', ') if val.respond_to?(:to_ary) + super(key, val) + end + + def fetch(key, ...) + key = KeyMap[key] + key = @names.fetch(key.downcase, key) + super(key, ...) + end + + def delete(key) + key = KeyMap[key] + key = @names[key.downcase] + return unless key + + @names.delete key.downcase + super(key) + end + + def dig(key, *rest) + key = KeyMap[key] + key = @names.fetch(key.downcase, key) + super(key, *rest) + end + + def include?(key) + @names.include? key.downcase + end + + alias has_key? include? + alias member? include? + alias key? include? + + def merge!(other) + other.each { |k, v| self[k] = v } + self + end + + alias update merge! + + def merge(other) + hash = dup + hash.merge! other + end + + def replace(other) + clear + @names.clear + update other + self + end + + def to_hash + {}.update(self) + end + + def parse(header_string) + return unless header_string && !header_string.empty? + + headers = header_string.split("\r\n") + + # Find the last set of response headers. + start_index = headers.rindex { |x| x.start_with?('HTTP/') } || 0 + last_response = headers.slice(start_index, headers.size) + + last_response + .tap { |a| a.shift if a.first.start_with?('HTTP/') } + .map { |h| h.split(/:\s*/, 2) } # split key and value + .reject { |p| p[0].nil? } # ignore blank lines + .each { |key, value| add_parsed(key, value) } + end + + protected + + attr_reader :names + + private + + # Join multiple values with a comma. + def add_parsed(key, value) + if key?(key) + self[key] = self[key].to_s + self[key] << ', ' << value + else + self[key] = value + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/utils/params_hash.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/utils/params_hash.rb new file mode 100644 index 0000000..0e16d93 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/utils/params_hash.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +module Faraday + module Utils + # A hash with stringified keys. + class ParamsHash < Hash + def [](key) + super(convert_key(key)) + end + + def []=(key, value) + super(convert_key(key), value) + end + + def delete(key) + super(convert_key(key)) + end + + def include?(key) + super(convert_key(key)) + end + + alias has_key? include? + alias member? include? + alias key? include? + + def update(params) + params.each do |key, value| + self[key] = value + end + self + end + alias merge! update + + def merge(params) + dup.update(params) + end + + def replace(other) + clear + update(other) + end + + def merge_query(query, encoder = nil) + return self unless query && !query.empty? + + update((encoder || Utils.default_params_encoder).decode(query)) + end + + def to_query(encoder = nil) + (encoder || Utils.default_params_encoder).encode(self) + end + + private + + def convert_key(key) + key.to_s + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/version.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/version.rb new file mode 100644 index 0000000..65d8ad4 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/lib/faraday/version.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +module Faraday + VERSION = '2.14.0' +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/external_adapters/faraday_specs_setup.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/external_adapters/faraday_specs_setup.rb new file mode 100644 index 0000000..ac7f7b6 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/external_adapters/faraday_specs_setup.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +require 'webmock/rspec' +WebMock.disable_net_connect!(allow_localhost: true) + +require_relative '../support/helper_methods' +require_relative '../support/disabling_stub' +require_relative '../support/streaming_response_checker' +require_relative '../support/shared_examples/adapter' +require_relative '../support/shared_examples/request_method' + +RSpec.configure do |config| + config.include Faraday::HelperMethods +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/adapter/test_spec.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/adapter/test_spec.rb new file mode 100644 index 0000000..117bb78 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/adapter/test_spec.rb @@ -0,0 +1,442 @@ +# frozen_string_literal: true + +RSpec.describe Faraday::Adapter::Test do + let(:stubs) do + described_class::Stubs.new do |stub| + stub.get('http://domain.test/hello') do + [200, { 'Content-Type' => 'text/html' }, 'domain: hello'] + end + + stub.get('http://wrong.test/hello') do + [200, { 'Content-Type' => 'text/html' }, 'wrong: hello'] + end + + stub.get('http://wrong.test/bait') do + [404, { 'Content-Type' => 'text/html' }] + end + + stub.get('/hello') do + [200, { 'Content-Type' => 'text/html' }, 'hello'] + end + + stub.get('/method-echo') do |env| + [200, { 'Content-Type' => 'text/html' }, env[:method].to_s] + end + + stub.get(%r{\A/resources/\d+(?:\?|\z)}) do + [200, { 'Content-Type' => 'text/html' }, 'show'] + end + + stub.get(%r{\A/resources/(specified)\z}) do |_env, meta| + [200, { 'Content-Type' => 'text/html' }, "show #{meta[:match_data][1]}"] + end + end + end + + let(:connection) do + Faraday.new do |builder| + builder.adapter :test, stubs + end + end + + let(:response) { connection.get('/hello') } + + context 'with simple path sets status' do + subject { response.status } + + it { is_expected.to eq 200 } + end + + context 'with simple path sets headers' do + subject { response.headers['Content-Type'] } + + it { is_expected.to eq 'text/html' } + end + + context 'with simple path sets body' do + subject { response.body } + + it { is_expected.to eq 'hello' } + end + + context 'with host points to the right stub' do + subject { connection.get('http://domain.test/hello').body } + + it { is_expected.to eq 'domain: hello' } + end + + describe 'can be called several times' do + subject { connection.get('/hello').body } + + it { is_expected.to eq 'hello' } + end + + describe 'can handle regular expression path' do + subject { connection.get('/resources/1').body } + + it { is_expected.to eq 'show' } + end + + describe 'can handle single parameter block' do + subject { connection.get('/method-echo').body } + + it { is_expected.to eq 'get' } + end + + describe 'can handle regular expression path with captured result' do + subject { connection.get('/resources/specified').body } + + it { is_expected.to eq 'show specified' } + end + + context 'with get params' do + subject { connection.get('/param?a=1').body } + + before do + stubs.get('/param?a=1') { [200, {}, 'a'] } + end + + it { is_expected.to eq 'a' } + end + + describe 'ignoring unspecified get params' do + before do + stubs.get('/optional?a=1') { [200, {}, 'a'] } + end + + context 'with multiple params' do + subject { connection.get('/optional?a=1&b=1').body } + + it { is_expected.to eq 'a' } + end + + context 'with single param' do + subject { connection.get('/optional?a=1').body } + + it { is_expected.to eq 'a' } + end + + context 'without params' do + subject(:request) { connection.get('/optional') } + + it do + expect { request }.to raise_error( + Faraday::Adapter::Test::Stubs::NotFound + ) + end + end + end + + context 'with http headers' do + before do + stubs.get('/yo', 'X-HELLO' => 'hello') { [200, {}, 'a'] } + stubs.get('/yo') { [200, {}, 'b'] } + end + + context 'with header' do + subject do + connection.get('/yo') { |env| env.headers['X-HELLO'] = 'hello' }.body + end + + it { is_expected.to eq 'a' } + end + + context 'without header' do + subject do + connection.get('/yo').body + end + + it { is_expected.to eq 'b' } + end + end + + describe 'different outcomes for the same request' do + def make_request + connection.get('/foo') + end + + subject(:request) { make_request.body } + + before do + stubs.get('/foo') { [200, { 'Content-Type' => 'text/html' }, 'hello'] } + stubs.get('/foo') { [200, { 'Content-Type' => 'text/html' }, 'world'] } + end + + context 'the first request' do + it { is_expected.to eq 'hello' } + end + + context 'the second request' do + before do + make_request + end + + it { is_expected.to eq 'world' } + end + end + + describe 'yielding env to stubs' do + subject { connection.get('http://foo.com/foo?a=1').body } + + before do + stubs.get '/foo' do |env| + expect(env[:url].path).to eq '/foo' + expect(env[:url].host).to eq 'foo.com' + expect(env[:params]['a']).to eq '1' + expect(env[:request_headers]['Accept']).to eq 'text/plain' + [200, {}, 'a'] + end + + connection.headers['Accept'] = 'text/plain' + end + + it { is_expected.to eq 'a' } + end + + describe 'params parsing' do + subject { connection.get('http://foo.com/foo?a[b]=1').body } + + context 'with default encoder' do + before do + stubs.get '/foo' do |env| + expect(env[:params]['a']['b']).to eq '1' + [200, {}, 'a'] + end + end + + it { is_expected.to eq 'a' } + end + + context 'with nested encoder' do + before do + stubs.get '/foo' do |env| + expect(env[:params]['a']['b']).to eq '1' + [200, {}, 'a'] + end + + connection.options.params_encoder = Faraday::NestedParamsEncoder + end + + it { is_expected.to eq 'a' } + end + + context 'with flat encoder' do + before do + stubs.get '/foo' do |env| + expect(env[:params]['a[b]']).to eq '1' + [200, {}, 'a'] + end + + connection.options.params_encoder = Faraday::FlatParamsEncoder + end + + it { is_expected.to eq 'a' } + end + end + + describe 'raising an error if no stub was found' do + describe 'for request' do + subject(:request) { connection.get('/invalid') { [200, {}, []] } } + + it { expect { request }.to raise_error described_class::Stubs::NotFound } + end + + describe 'for specified host' do + subject(:request) { connection.get('http://domain.test/bait') } + + it { expect { request }.to raise_error described_class::Stubs::NotFound } + end + + describe 'for request without specified header' do + subject(:request) { connection.get('/yo') } + + before do + stubs.get('/yo', 'X-HELLO' => 'hello') { [200, {}, 'a'] } + end + + it { expect { request }.to raise_error described_class::Stubs::NotFound } + end + end + + describe 'for request with non default params encoder' do + let(:connection) do + Faraday.new(request: { params_encoder: Faraday::FlatParamsEncoder }) do |builder| + builder.adapter :test, stubs + end + end + let(:stubs) do + described_class::Stubs.new do |stubs| + stubs.get('/path?a=x&a=y&a=z') { [200, {}, 'a'] } + end + end + + context 'when all flat param values are correctly set' do + subject(:request) { connection.get('/path?a=x&a=y&a=z') } + + it { expect(request.status).to eq 200 } + end + + shared_examples 'raise NotFound when params do not satisfy the flat param values' do |params| + subject(:request) { connection.get('/path', params) } + + context "with #{params.inspect}" do + it { expect { request }.to raise_error described_class::Stubs::NotFound } + end + end + + it_behaves_like 'raise NotFound when params do not satisfy the flat param values', { a: %w[x] } + it_behaves_like 'raise NotFound when params do not satisfy the flat param values', { a: %w[x y] } + it_behaves_like 'raise NotFound when params do not satisfy the flat param values', { a: %w[x z y] } # NOTE: The order of the value is also compared. + it_behaves_like 'raise NotFound when params do not satisfy the flat param values', { b: %w[x y z] } + end + + describe 'strict_mode' do + let(:stubs) do + described_class::Stubs.new(strict_mode: true) do |stubs| + stubs.get('/strict?a=12&b=xy', 'Authorization' => 'Bearer m_ck', 'X-C' => 'hello') { [200, {}, 'a'] } + stubs.get('/with_user_agent?a=12&b=xy', authorization: 'Bearer m_ck', 'User-Agent' => 'My Agent') { [200, {}, 'a'] } + end + end + + context 'when params and headers are exactly set' do + subject(:request) { connection.get('/strict', { a: '12', b: 'xy' }, { authorization: 'Bearer m_ck', x_c: 'hello' }) } + + it { expect(request.status).to eq 200 } + end + + context 'when params and headers are exactly set with a custom user agent' do + subject(:request) { connection.get('/with_user_agent', { a: '12', b: 'xy' }, { authorization: 'Bearer m_ck', 'User-Agent' => 'My Agent' }) } + + it { expect(request.status).to eq 200 } + end + + shared_examples 'raise NotFound when params do not satisfy the strict check' do |params| + subject(:request) { connection.get('/strict', params, { 'Authorization' => 'Bearer m_ck', 'X-C' => 'hello' }) } + + context "with #{params.inspect}" do + it { expect { request }.to raise_error described_class::Stubs::NotFound } + end + end + + it_behaves_like 'raise NotFound when params do not satisfy the strict check', { a: '12' } + it_behaves_like 'raise NotFound when params do not satisfy the strict check', { b: 'xy' } + it_behaves_like 'raise NotFound when params do not satisfy the strict check', { a: '123', b: 'xy' } + it_behaves_like 'raise NotFound when params do not satisfy the strict check', { a: '12', b: 'xyz' } + it_behaves_like 'raise NotFound when params do not satisfy the strict check', { a: '12', b: 'xy', c: 'hello' } + it_behaves_like 'raise NotFound when params do not satisfy the strict check', { additional: 'special', a: '12', b: 'xy', c: 'hello' } + + shared_examples 'raise NotFound when headers do not satisfy the strict check' do |path, headers| + subject(:request) { connection.get(path, { a: 12, b: 'xy' }, headers) } + + context "with #{headers.inspect}" do + it { expect { request }.to raise_error described_class::Stubs::NotFound } + end + end + + it_behaves_like 'raise NotFound when headers do not satisfy the strict check', '/strict', { authorization: 'Bearer m_ck' } + it_behaves_like 'raise NotFound when headers do not satisfy the strict check', '/strict', { 'X-C' => 'hello' } + it_behaves_like 'raise NotFound when headers do not satisfy the strict check', '/strict', { authorization: 'Bearer m_ck', 'x-c': 'Hi' } + it_behaves_like 'raise NotFound when headers do not satisfy the strict check', '/strict', { authorization: 'Basic m_ck', 'x-c': 'hello' } + it_behaves_like 'raise NotFound when headers do not satisfy the strict check', '/strict', { authorization: 'Bearer m_ck', 'x-c': 'hello', x_special: 'special' } + it_behaves_like 'raise NotFound when headers do not satisfy the strict check', '/with_user_agent', { authorization: 'Bearer m_ck' } + it_behaves_like 'raise NotFound when headers do not satisfy the strict check', '/with_user_agent', { authorization: 'Bearer m_ck', user_agent: 'Unknown' } + it_behaves_like 'raise NotFound when headers do not satisfy the strict check', '/with_user_agent', { authorization: 'Bearer m_ck', user_agent: 'My Agent', x_special: 'special' } + + context 'when strict_mode is disabled' do + before do + stubs.strict_mode = false + end + + shared_examples 'does not raise NotFound even when params do not satisfy the strict check' do |params| + subject(:request) { connection.get('/strict', params, { 'Authorization' => 'Bearer m_ck', 'X-C' => 'hello' }) } + + context "with #{params.inspect}" do + it { expect(request.status).to eq 200 } + end + end + + it_behaves_like 'does not raise NotFound even when params do not satisfy the strict check', { a: '12', b: 'xy' } + it_behaves_like 'does not raise NotFound even when params do not satisfy the strict check', { a: '12', b: 'xy', c: 'hello' } + it_behaves_like 'does not raise NotFound even when params do not satisfy the strict check', { additional: 'special', a: '12', b: 'xy', c: 'hello' } + + shared_examples 'does not raise NotFound even when headers do not satisfy the strict check' do |path, headers| + subject(:request) { connection.get(path, { a: 12, b: 'xy' }, headers) } + + context "with #{headers.inspect}" do + it { expect(request.status).to eq 200 } + end + end + + it_behaves_like 'does not raise NotFound even when headers do not satisfy the strict check', '/strict', { authorization: 'Bearer m_ck', 'x-c': 'hello' } + it_behaves_like 'does not raise NotFound even when headers do not satisfy the strict check', '/strict', { authorization: 'Bearer m_ck', 'x-c': 'hello', x_special: 'special' } + it_behaves_like 'does not raise NotFound even when headers do not satisfy the strict check', '/strict', { authorization: 'Bearer m_ck', 'x-c': 'hello', user_agent: 'Special Agent' } + it_behaves_like 'does not raise NotFound even when headers do not satisfy the strict check', '/with_user_agent', { authorization: 'Bearer m_ck', user_agent: 'My Agent' } + it_behaves_like 'does not raise NotFound even when headers do not satisfy the strict check', '/with_user_agent', { authorization: 'Bearer m_ck', user_agent: 'My Agent', x_special: 'special' } + end + + describe 'body_match?' do + let(:stubs) do + described_class::Stubs.new do |stubs| + stubs.post('/no_check') { [200, {}, 'ok'] } + stubs.post('/with_string', 'abc') { [200, {}, 'ok'] } + stubs.post( + '/with_proc', + ->(request_body) { JSON.parse(request_body, symbolize_names: true) == { x: '!', a: [{ m: [{ a: true }], n: 123 }] } }, + { content_type: 'application/json' } + ) do + [200, {}, 'ok'] + end + end + end + + context 'when trying without any args for body' do + subject(:without_body) { connection.post('/no_check') } + + it { expect(without_body.status).to eq 200 } + end + + context 'when trying with string body stubs' do + subject(:with_string) { connection.post('/with_string', 'abc') } + + it { expect(with_string.status).to eq 200 } + end + + context 'when trying with proc body stubs' do + subject(:with_proc) do + connection.post('/with_proc', JSON.dump(a: [{ n: 123, m: [{ a: true }] }], x: '!'), { 'Content-Type' => 'application/json' }) + end + + it { expect(with_proc.status).to eq 200 } + end + end + end + + describe 'request timeout' do + subject(:request) do + connection.get('/sleep') do |req| + req.options.timeout = timeout + end + end + + before do + stubs.get('/sleep') do + sleep(0.01) + [200, {}, ''] + end + end + + context 'when request is within timeout' do + let(:timeout) { 1 } + + it { expect(request.status).to eq 200 } + end + + context 'when request is too slow' do + let(:timeout) { 0.001 } + + it 'raises an exception' do + expect { request }.to raise_error(Faraday::TimeoutError) + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/adapter_registry_spec.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/adapter_registry_spec.rb new file mode 100644 index 0000000..222e65e --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/adapter_registry_spec.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +RSpec.describe Faraday::AdapterRegistry do + describe '#initialize' do + subject(:registry) { described_class.new } + + it { expect { registry.get(:FinFangFoom) }.to raise_error(NameError) } + it { expect { registry.get('FinFangFoom') }.to raise_error(NameError) } + + it 'looks up class by string name' do + expect(registry.get('Faraday::Connection')).to eq(Faraday::Connection) + end + + it 'looks up class by symbol name' do + expect(registry.get(:Faraday)).to eq(Faraday) + end + + it 'caches lookups with implicit name' do + registry.set :symbol + expect(registry.get('symbol')).to eq(:symbol) + end + + it 'caches lookups with explicit name' do + registry.set 'string', :name + expect(registry.get(:name)).to eq('string') + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/adapter_spec.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/adapter_spec.rb new file mode 100644 index 0000000..22ef1d1 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/adapter_spec.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +RSpec.describe Faraday::Adapter do + let(:adapter) { Faraday::Adapter.new } + let(:request) { {} } + + context '#request_timeout' do + it 'gets :read timeout' do + expect(timeout(:read)).to eq(nil) + + request[:timeout] = 5 + request[:write_timeout] = 1 + + expect(timeout(:read)).to eq(5) + + request[:read_timeout] = 2 + + expect(timeout(:read)).to eq(2) + end + + it 'gets :open timeout' do + expect(timeout(:open)).to eq(nil) + + request[:timeout] = 5 + request[:write_timeout] = 1 + + expect(timeout(:open)).to eq(5) + + request[:open_timeout] = 2 + + expect(timeout(:open)).to eq(2) + end + + it 'gets :write timeout' do + expect(timeout(:write)).to eq(nil) + + request[:timeout] = 5 + request[:read_timeout] = 1 + + expect(timeout(:write)).to eq(5) + + request[:write_timeout] = 2 + + expect(timeout(:write)).to eq(2) + end + + it 'attempts unknown timeout type' do + expect { timeout(:unknown) }.to raise_error(ArgumentError) + end + + def timeout(type) + adapter.send(:request_timeout, type, request) + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/connection_spec.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/connection_spec.rb new file mode 100644 index 0000000..7fa726a --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/connection_spec.rb @@ -0,0 +1,808 @@ +# frozen_string_literal: true + +class CustomEncoder + def encode(params) + params.map { |k, v| "#{k.upcase}-#{v.to_s.upcase}" }.join(',') + end + + def decode(params) + params.split(',').to_h { |pair| pair.split('-') } + end +end + +shared_examples 'initializer with url' do + context 'with simple url' do + let(:address) { 'http://httpbingo.org' } + + it { expect(subject.host).to eq('httpbingo.org') } + it { expect(subject.port).to eq(80) } + it { expect(subject.scheme).to eq('http') } + it { expect(subject.path_prefix).to eq('/') } + it { expect(subject.params).to eq({}) } + end + + context 'with complex url' do + let(:address) { 'http://httpbingo.org:815/fish?a=1' } + + it { expect(subject.port).to eq(815) } + it { expect(subject.path_prefix).to eq('/fish') } + it { expect(subject.params).to eq('a' => '1') } + end + + context 'with IPv6 address' do + let(:address) { 'http://[::1]:85/' } + + it { expect(subject.host).to eq('[::1]') } + it { expect(subject.port).to eq(85) } + end +end + +shared_examples 'default connection options' do + after { Faraday.default_connection_options = nil } + + it 'works with implicit url' do + conn = Faraday.new 'http://httpbingo.org/foo' + expect(conn.options.timeout).to eq(10) + end + + it 'works with option url' do + conn = Faraday.new url: 'http://httpbingo.org/foo' + expect(conn.options.timeout).to eq(10) + end + + it 'works with instance connection options' do + conn = Faraday.new 'http://httpbingo.org/foo', request: { open_timeout: 1 } + expect(conn.options.timeout).to eq(10) + expect(conn.options.open_timeout).to eq(1) + end + + it 'default connection options persist with an instance overriding' do + conn = Faraday.new 'http://nigiri.com/bar' + conn.options.timeout = 1 + expect(Faraday.default_connection_options.request.timeout).to eq(10) + + other = Faraday.new url: 'https://httpbingo.org/foo' + other.options.timeout = 1 + + expect(Faraday.default_connection_options.request.timeout).to eq(10) + end + + it 'default connection uses default connection options' do + expect(Faraday.default_connection.options.timeout).to eq(10) + end +end + +RSpec.describe Faraday::Connection do + let(:conn) { Faraday::Connection.new(url, options) } + let(:url) { nil } + let(:options) { nil } + + describe '.new' do + subject { conn } + + context 'with implicit url param' do + # Faraday::Connection.new('http://httpbingo.org') + let(:url) { address } + + it_behaves_like 'initializer with url' + end + + context 'with explicit url param' do + # Faraday::Connection.new(url: 'http://httpbingo.org') + let(:url) { { url: address } } + + it_behaves_like 'initializer with url' + end + + context 'with custom builder' do + let(:custom_builder) { Faraday::RackBuilder.new } + let(:options) { { builder: custom_builder } } + + it { expect(subject.builder).to eq(custom_builder) } + end + + context 'with custom params' do + let(:options) { { params: { a: 1 } } } + + it { expect(subject.params).to eq('a' => 1) } + end + + context 'with custom params and params in url' do + let(:url) { 'http://httpbingo.org/fish?a=1&b=2' } + let(:options) { { params: { a: 3 } } } + it { expect(subject.params).to eq('a' => 3, 'b' => '2') } + end + + context 'with basic_auth in url' do + let(:url) { 'http://Aladdin:open%20sesame@httpbingo.org/fish' } + + it { expect(subject.headers['Authorization']).to eq('Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==') } + end + + context 'with custom headers' do + let(:options) { { headers: { user_agent: 'Faraday' } } } + + it { expect(subject.headers['User-agent']).to eq('Faraday') } + end + + context 'with ssl false' do + let(:options) { { ssl: { verify: false } } } + + it { expect(subject.ssl.verify?).to be_falsey } + end + + context 'with verify_hostname false' do + let(:options) { { ssl: { verify_hostname: false } } } + + it { expect(subject.ssl.verify_hostname?).to be_falsey } + end + + context 'with empty block' do + let(:conn) { Faraday::Connection.new {} } + + it { expect(conn.builder.handlers.size).to eq(0) } + end + + context 'with block' do + let(:conn) do + Faraday::Connection.new(params: { 'a' => '1' }) do |faraday| + faraday.adapter :test + faraday.url_prefix = 'http://httpbingo.org/omnom' + end + end + + it { expect(conn.builder.handlers.size).to eq(0) } + it { expect(conn.path_prefix).to eq('/omnom') } + end + end + + describe '#close' do + before { Faraday.default_adapter = :test } + after { Faraday.default_adapter = nil } + + it 'can close underlying app' do + expect(conn.app).to receive(:close) + conn.close + end + end + + describe '#build_exclusive_url' do + context 'with relative path' do + subject { conn.build_exclusive_url('sake.html') } + + it 'uses connection host as default host' do + conn.host = 'httpbingo.org' + expect(subject.host).to eq('httpbingo.org') + expect(subject.scheme).to eq('http') + end + + it do + conn.path_prefix = '/fish' + expect(subject.path).to eq('/fish/sake.html') + end + + it do + conn.path_prefix = '/' + expect(subject.path).to eq('/sake.html') + end + + it do + conn.path_prefix = 'fish' + expect(subject.path).to eq('/fish/sake.html') + end + + it do + conn.path_prefix = '/fish/' + expect(subject.path).to eq('/fish/sake.html') + end + end + + context 'with absolute path' do + subject { conn.build_exclusive_url('/sake.html') } + + after { expect(subject.path).to eq('/sake.html') } + + it { conn.path_prefix = '/fish' } + it { conn.path_prefix = '/' } + it { conn.path_prefix = 'fish' } + it { conn.path_prefix = '/fish/' } + end + + context 'with complete url' do + subject { conn.build_exclusive_url('http://httpbingo.org/sake.html?a=1') } + + it { expect(subject.scheme).to eq('http') } + it { expect(subject.host).to eq('httpbingo.org') } + it { expect(subject.port).to eq(80) } + it { expect(subject.path).to eq('/sake.html') } + it { expect(subject.query).to eq('a=1') } + end + + it 'overrides connection port for absolute url' do + conn.port = 23 + uri = conn.build_exclusive_url('http://httpbingo.org') + expect(uri.port).to eq(80) + end + + it 'does not add ending slash given nil url' do + conn.url_prefix = 'http://httpbingo.org/nigiri' + uri = conn.build_exclusive_url + expect(uri.path).to eq('/nigiri') + end + + it 'does not add ending slash given empty url' do + conn.url_prefix = 'http://httpbingo.org/nigiri' + uri = conn.build_exclusive_url('') + expect(uri.path).to eq('/nigiri') + end + + it 'does not use connection params' do + conn.url_prefix = 'http://httpbingo.org/nigiri' + conn.params = { a: 1 } + expect(conn.build_exclusive_url.to_s).to eq('http://httpbingo.org/nigiri') + end + + it 'allows to provide params argument' do + conn.url_prefix = 'http://httpbingo.org/nigiri' + conn.params = { a: 1 } + params = Faraday::Utils::ParamsHash.new + params[:a] = 2 + uri = conn.build_exclusive_url(nil, params) + expect(uri.to_s).to eq('http://httpbingo.org/nigiri?a=2') + end + + it 'handles uri instances' do + uri = conn.build_exclusive_url(URI('/sake.html')) + expect(uri.path).to eq('/sake.html') + end + + it 'always returns new URI instance' do + conn.url_prefix = 'http://httpbingo.org' + uri1 = conn.build_exclusive_url(nil) + uri2 = conn.build_exclusive_url(nil) + expect(uri1).not_to equal(uri2) + end + + context 'with url_prefixed connection' do + let(:url) { 'http://httpbingo.org/get/' } + + it 'parses url and changes scheme' do + conn.scheme = 'https' + uri = conn.build_exclusive_url('sake.html') + expect(uri.to_s).to eq('https://httpbingo.org/get/sake.html') + end + + it 'joins url to base with ending slash' do + uri = conn.build_exclusive_url('sake.html') + expect(uri.to_s).to eq('http://httpbingo.org/get/sake.html') + end + + it 'used default base with ending slash' do + uri = conn.build_exclusive_url + expect(uri.to_s).to eq('http://httpbingo.org/get/') + end + + it 'overrides base' do + uri = conn.build_exclusive_url('/sake/') + expect(uri.to_s).to eq('http://httpbingo.org/sake/') + end + end + + context 'with colon in path' do + let(:url) { 'http://service.com' } + + it 'joins url to base when used absolute path' do + conn = Faraday.new(url: url) + uri = conn.build_exclusive_url('/service:search?limit=400') + expect(uri.to_s).to eq('http://service.com/service:search?limit=400') + end + + it 'joins url to base when used relative path' do + conn = Faraday.new(url: url) + uri = conn.build_exclusive_url('service:search?limit=400') + expect(uri.to_s).to eq('http://service.com/service:search?limit=400') + end + + it 'joins url to base when used with path prefix' do + conn = Faraday.new(url: url) + conn.path_prefix = '/api' + uri = conn.build_exclusive_url('service:search?limit=400') + expect(uri.to_s).to eq('http://service.com/api/service:search?limit=400') + end + end + + context 'with a custom `default_uri_parser`' do + let(:url) { 'http://httpbingo.org' } + let(:parser) { Addressable::URI } + + around do |example| + with_default_uri_parser(parser) do + example.run + end + end + + it 'does not raise error' do + expect { conn.build_exclusive_url('/nigiri') }.not_to raise_error + end + end + end + + describe '#build_url' do + let(:url) { 'http://httpbingo.org/nigiri' } + + it 'uses params' do + conn.params = { a: 1, b: 1 } + expect(conn.build_url.to_s).to eq('http://httpbingo.org/nigiri?a=1&b=1') + end + + it 'merges params' do + conn.params = { a: 1, b: 1 } + url = conn.build_url(nil, b: 2, c: 3) + expect(url.to_s).to eq('http://httpbingo.org/nigiri?a=1&b=2&c=3') + end + end + + describe '#build_request' do + let(:url) { 'https://ahttpbingo.org/sake.html' } + let(:request) { conn.build_request(:get) } + + before do + conn.headers = { 'Authorization' => 'token abc123' } + request.headers.delete('Authorization') + end + + it { expect(conn.headers.keys).to eq(['Authorization']) } + it { expect(conn.headers.include?('Authorization')).to be_truthy } + it { expect(request.headers.keys).to be_empty } + it { expect(request.headers.include?('Authorization')).to be_falsey } + end + + describe '#to_env' do + subject { conn.build_request(:get).to_env(conn).url } + + let(:url) { 'http://httpbingo.org/sake.html' } + let(:options) { { params: @params } } + + it 'parses url params into query' do + @params = { 'a[b]' => '1 + 2' } + expect(subject.query).to eq('a%5Bb%5D=1+%2B+2') + end + + it 'escapes per spec' do + @params = { 'a' => '1+2 foo~bar.-baz' } + expect(subject.query).to eq('a=1%2B2+foo~bar.-baz') + end + + it 'bracketizes nested params in query' do + @params = { 'a' => { 'b' => 'c' } } + expect(subject.query).to eq('a%5Bb%5D=c') + end + + it 'bracketizes repeated params in query' do + @params = { 'a' => [1, 2] } + expect(subject.query).to eq('a%5B%5D=1&a%5B%5D=2') + end + + it 'without braketizing repeated params in query' do + @params = { 'a' => [1, 2] } + conn.options.params_encoder = Faraday::FlatParamsEncoder + expect(subject.query).to eq('a=1&a=2') + end + end + + describe 'proxy support' do + it 'accepts string' do + with_env 'http_proxy' => 'http://env-proxy.com:80' do + conn.proxy = 'http://proxy.com' + expect(conn.proxy.host).to eq('proxy.com') + end + end + + it 'accepts uri' do + with_env 'http_proxy' => 'http://env-proxy.com:80' do + conn.proxy = URI.parse('http://proxy.com') + expect(conn.proxy.host).to eq('proxy.com') + end + end + + it 'accepts hash with string uri' do + with_env 'http_proxy' => 'http://env-proxy.com:80' do + conn.proxy = { uri: 'http://proxy.com', user: 'rick' } + expect(conn.proxy.host).to eq('proxy.com') + expect(conn.proxy.user).to eq('rick') + end + end + + it 'accepts hash' do + with_env 'http_proxy' => 'http://env-proxy.com:80' do + conn.proxy = { uri: URI.parse('http://proxy.com'), user: 'rick' } + expect(conn.proxy.host).to eq('proxy.com') + expect(conn.proxy.user).to eq('rick') + end + end + + it 'accepts http env' do + with_env 'http_proxy' => 'http://env-proxy.com:80' do + expect(conn.proxy.host).to eq('env-proxy.com') + end + end + + it 'accepts http env with auth' do + with_env 'http_proxy' => 'http://a%40b:my%20pass@proxy.com:80' do + expect(conn.proxy.user).to eq('a@b') + expect(conn.proxy.password).to eq('my pass') + end + end + + it 'accepts env without scheme' do + with_env 'http_proxy' => 'localhost:8888' do + uri = conn.proxy[:uri] + expect(uri.host).to eq('localhost') + expect(uri.port).to eq(8888) + end + end + + it 'fetches no proxy from nil env' do + with_env 'http_proxy' => nil do + expect(conn.proxy).to be_nil + end + end + + it 'fetches no proxy from blank env' do + with_env 'http_proxy' => '' do + expect(conn.proxy).to be_nil + end + end + + it 'does not accept uppercase env' do + with_env 'HTTP_PROXY' => 'http://localhost:8888/' do + expect(conn.proxy).to be_nil + end + end + + it 'allows when url in no proxy list' do + with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example.com' do + conn = Faraday::Connection.new('http://example.com') + expect(conn.proxy).to be_nil + end + end + + it 'allows when url in no proxy list with url_prefix' do + with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example.com' do + conn = Faraday::Connection.new + conn.url_prefix = 'http://example.com' + expect(conn.proxy).to be_nil + end + end + + it 'allows when prefixed url is not in no proxy list' do + with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example.com' do + conn = Faraday::Connection.new('http://prefixedexample.com') + expect(conn.proxy.host).to eq('proxy.com') + end + end + + it 'allows when subdomain url is in no proxy list' do + with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example.com' do + conn = Faraday::Connection.new('http://subdomain.example.com') + expect(conn.proxy).to be_nil + end + end + + it 'allows when url not in no proxy list' do + with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example2.com' do + conn = Faraday::Connection.new('http://example.com') + expect(conn.proxy.host).to eq('proxy.com') + end + end + + it 'allows when ip address is not in no proxy list but url is' do + with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'localhost' do + conn = Faraday::Connection.new('http://127.0.0.1') + expect(conn.proxy).to be_nil + end + end + + it 'allows when url is not in no proxy list but ip address is' do + with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => '127.0.0.1' do + conn = Faraday::Connection.new('http://localhost') + expect(conn.proxy).to be_nil + end + end + + it 'allows in multi element no proxy list' do + with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example0.com,example.com,example1.com' do + expect(Faraday::Connection.new('http://example0.com').proxy).to be_nil + expect(Faraday::Connection.new('http://example.com').proxy).to be_nil + expect(Faraday::Connection.new('http://example1.com').proxy).to be_nil + expect(Faraday::Connection.new('http://example2.com').proxy.host).to eq('proxy.com') + end + end + + it 'test proxy requires uri' do + expect { conn.proxy = { uri: :bad_uri, user: 'rick' } }.to raise_error(ArgumentError) + end + + it 'uses env http_proxy' do + with_env 'http_proxy' => 'http://proxy.com' do + conn = Faraday.new + expect(conn.instance_variable_get(:@manual_proxy)).to be_falsey + expect(conn.proxy_for_request('http://google.co.uk').host).to eq('proxy.com') + end + end + + it 'uses processes no_proxy before http_proxy' do + with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'google.co.uk' do + conn = Faraday.new + expect(conn.instance_variable_get(:@manual_proxy)).to be_falsey + expect(conn.proxy_for_request('http://google.co.uk')).to be_nil + end + end + + it 'uses env https_proxy' do + with_env 'https_proxy' => 'https://proxy.com' do + conn = Faraday.new + expect(conn.instance_variable_get(:@manual_proxy)).to be_falsey + expect(conn.proxy_for_request('https://google.co.uk').host).to eq('proxy.com') + end + end + + it 'uses processes no_proxy before https_proxy' do + with_env 'https_proxy' => 'https://proxy.com', 'no_proxy' => 'google.co.uk' do + conn = Faraday.new + expect(conn.instance_variable_get(:@manual_proxy)).to be_falsey + expect(conn.proxy_for_request('https://google.co.uk')).to be_nil + end + end + + it 'gives priority to manually set proxy' do + with_env 'https_proxy' => 'https://proxy.com', 'no_proxy' => 'google.co.uk' do + conn = Faraday.new + conn.proxy = 'http://proxy2.com' + + expect(conn.instance_variable_get(:@manual_proxy)).to be_truthy + expect(conn.proxy_for_request('https://google.co.uk').host).to eq('proxy2.com') + end + end + + it 'ignores env proxy if set that way' do + with_env_proxy_disabled do + with_env 'http_proxy' => 'http://duncan.proxy.com:80' do + expect(conn.proxy).to be_nil + end + end + end + + context 'performing a request' do + let(:url) { 'http://example.com' } + let(:conn) do + Faraday.new do |f| + f.adapter :test do |stubs| + stubs.get(url) do + [200, {}, 'ok'] + end + end + end + end + + it 'dynamically checks proxy' do + with_env 'http_proxy' => 'http://proxy.com:80' do + expect(conn.proxy.uri.host).to eq('proxy.com') + + conn.get(url) do |req| + expect(req.options.proxy.uri.host).to eq('proxy.com') + end + end + + conn.get(url) + expect(conn.instance_variable_get(:@temp_proxy)).to be_nil + end + + it 'dynamically check no proxy' do + with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example.com' do + expect(conn.proxy.uri.host).to eq('proxy.com') + + conn.get('http://example.com') do |req| + expect(req.options.proxy).to be_nil + end + end + end + end + end + + describe '#dup' do + subject { conn.dup } + + let(:url) { 'http://httpbingo.org/foo' } + let(:options) do + { + ssl: { verify: :none }, + headers: { 'content-type' => 'text/plain' }, + params: { 'a' => '1' }, + request: { timeout: 5 } + } + end + + it { expect(subject.build_exclusive_url).to eq(conn.build_exclusive_url) } + it { expect(subject.headers['content-type']).to eq('text/plain') } + it { expect(subject.params['a']).to eq('1') } + + context 'after manual changes' do + before do + subject.headers['content-length'] = 12 + subject.params['b'] = '2' + subject.options[:open_timeout] = 10 + end + + it { expect(subject.builder.handlers.size).to eq(1) } + it { expect(conn.builder.handlers.size).to eq(1) } + it { expect(conn.headers.key?('content-length')).to be_falsey } + it { expect(conn.params.key?('b')).to be_falsey } + it { expect(subject.options[:timeout]).to eq(5) } + it { expect(conn.options[:open_timeout]).to be_nil } + end + end + + describe '#respond_to?' do + it { expect(Faraday.respond_to?(:get)).to be_truthy } + it { expect(Faraday.respond_to?(:post)).to be_truthy } + end + + describe 'default_connection_options' do + context 'assigning a default value' do + before do + Faraday.default_connection_options = nil + Faraday.default_connection_options.request.timeout = 10 + end + + it_behaves_like 'default connection options' + end + + context 'assigning a hash' do + before { Faraday.default_connection_options = { request: { timeout: 10 } } } + + it_behaves_like 'default connection options' + end + + context 'preserving a user_agent assigned via default_conncetion_options' do + around do |example| + old = Faraday.default_connection_options + Faraday.default_connection_options = { headers: { user_agent: 'My Agent 1.2' } } + example.run + Faraday.default_connection_options = old + end + + context 'when url is a Hash' do + let(:conn) { Faraday.new(url: 'http://example.co', headers: { 'CustomHeader' => 'CustomValue' }) } + + it { expect(conn.headers).to eq('CustomHeader' => 'CustomValue', 'User-Agent' => 'My Agent 1.2') } + end + + context 'when url is a String' do + let(:conn) { Faraday.new('http://example.co', headers: { 'CustomHeader' => 'CustomValue' }) } + + it { expect(conn.headers).to eq('CustomHeader' => 'CustomValue', 'User-Agent' => 'My Agent 1.2') } + end + end + end + + describe 'request params' do + context 'with simple url' do + let(:url) { 'http://example.com' } + let(:stubs) { Faraday::Adapter::Test::Stubs.new } + + before do + conn.adapter(:test, stubs) + stubs.get('http://example.com?a=a&p=3') do + [200, {}, 'ok'] + end + end + + after { stubs.verify_stubbed_calls } + + it 'test_overrides_request_params' do + conn.get('?p=2&a=a', p: 3) + end + + it 'test_overrides_request_params_block' do + conn.get('?p=1&a=a', p: 2) do |req| + req.params[:p] = 3 + end + end + + it 'test_overrides_request_params_block_url' do + conn.get(nil, p: 2) do |req| + req.url('?p=1&a=a', 'p' => 3) + end + end + end + + context 'with url and extra params' do + let(:url) { 'http://example.com?a=1&b=2' } + let(:options) { { params: { c: 3 } } } + let(:stubs) { Faraday::Adapter::Test::Stubs.new } + + before do + conn.adapter(:test, stubs) + end + + it 'merges connection and request params' do + expected = 'http://example.com?a=1&b=2&c=3&limit=5&page=1' + stubs.get(expected) { [200, {}, 'ok'] } + conn.get('?page=1', limit: 5) + stubs.verify_stubbed_calls + end + + it 'allows to override all params' do + expected = 'http://example.com?b=b' + stubs.get(expected) { [200, {}, 'ok'] } + conn.get('?p=1&a=a', p: 2) do |req| + expect(req.params[:a]).to eq('a') + expect(req.params['c']).to eq(3) + expect(req.params['p']).to eq(2) + req.params = { b: 'b' } + expect(req.params['b']).to eq('b') + end + stubs.verify_stubbed_calls + end + + it 'allows to set params_encoder for single request' do + encoder = CustomEncoder.new + expected = 'http://example.com/?A-1,B-2,C-3,FEELING-BLUE' + stubs.get(expected) { [200, {}, 'ok'] } + + conn.get('/', a: 1, b: 2, c: 3, feeling: 'blue') do |req| + req.options.params_encoder = encoder + end + stubs.verify_stubbed_calls + end + end + + context 'with default params encoder' do + let(:stubs) { Faraday::Adapter::Test::Stubs.new } + + before do + conn.adapter(:test, stubs) + stubs.get('http://example.com?color%5B%5D=blue&color%5B%5D=red') do + [200, {}, 'ok'] + end + end + + after { stubs.verify_stubbed_calls } + + it 'supports array params in url' do + conn.get('http://example.com?color[]=blue&color[]=red') + end + + it 'supports array params in params' do + conn.get('http://example.com', color: %w[blue red]) + end + end + + context 'with flat params encoder' do + let(:options) { { request: { params_encoder: Faraday::FlatParamsEncoder } } } + let(:stubs) { Faraday::Adapter::Test::Stubs.new } + + before do + conn.adapter(:test, stubs) + stubs.get('http://example.com?color=blue&color=red') do + [200, {}, 'ok'] + end + end + + after { stubs.verify_stubbed_calls } + + it 'supports array params in params' do + conn.get('http://example.com', color: %w[blue red]) + end + + context 'with array param in url' do + let(:url) { 'http://example.com?color[]=blue&color[]=red' } + + it do + conn.get('/') + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/error_spec.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/error_spec.rb new file mode 100644 index 0000000..bb50077 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/error_spec.rb @@ -0,0 +1,175 @@ +# frozen_string_literal: true + +RSpec.describe Faraday::Error do + describe '.initialize' do + subject { described_class.new(exception, response) } + let(:response) { nil } + + context 'with exception only' do + let(:exception) { RuntimeError.new('test') } + + it { expect(subject.wrapped_exception).to eq(exception) } + it { expect(subject.response).to be_nil } + it { expect(subject.message).to eq(exception.message) } + it { expect(subject.backtrace).to eq(exception.backtrace) } + it { expect(subject.inspect).to eq('#>') } + it { expect(subject.response_status).to be_nil } + it { expect(subject.response_headers).to be_nil } + it { expect(subject.response_body).to be_nil } + end + + context 'with response hash' do + let(:exception) { { status: 400 } } + + it { expect(subject.wrapped_exception).to be_nil } + it { expect(subject.response).to eq(exception) } + it { expect(subject.message).to eq('the server responded with status 400 - method and url are not available due to include_request: false on Faraday::Response::RaiseError middleware') } + if RUBY_VERSION >= '3.4' + it { expect(subject.inspect).to eq('#') } + else + it { expect(subject.inspect).to eq('#400}>') } + end + it { expect(subject.response_status).to eq(400) } + it { expect(subject.response_headers).to be_nil } + it { expect(subject.response_body).to be_nil } + end + + context 'with string' do + let(:exception) { 'custom message' } + + it { expect(subject.wrapped_exception).to be_nil } + it { expect(subject.response).to be_nil } + it { expect(subject.message).to eq('custom message') } + it { expect(subject.inspect).to eq('#>') } + it { expect(subject.response_status).to be_nil } + it { expect(subject.response_headers).to be_nil } + it { expect(subject.response_body).to be_nil } + end + + context 'with anything else #to_s' do + let(:exception) { %w[error1 error2] } + + it { expect(subject.wrapped_exception).to be_nil } + it { expect(subject.response).to be_nil } + it { expect(subject.message).to eq('["error1", "error2"]') } + it { expect(subject.inspect).to eq('#>') } + it { expect(subject.response_status).to be_nil } + it { expect(subject.response_headers).to be_nil } + it { expect(subject.response_body).to be_nil } + end + + context 'with exception string and response hash' do + let(:exception) { 'custom message' } + let(:response) { { status: 400 } } + + it { expect(subject.wrapped_exception).to be_nil } + it { expect(subject.response).to eq(response) } + it { expect(subject.message).to eq('custom message') } + if RUBY_VERSION >= '3.4' + it { expect(subject.inspect).to eq('#') } + else + it { expect(subject.inspect).to eq('#400}>') } + end + it { expect(subject.response_status).to eq(400) } + it { expect(subject.response_headers).to be_nil } + it { expect(subject.response_body).to be_nil } + end + + context 'with exception and response object' do + let(:exception) { RuntimeError.new('test') } + let(:body) { { test: 'test' } } + let(:headers) { { 'Content-Type' => 'application/json' } } + let(:response) { Faraday::Response.new(status: 400, response_headers: headers, response_body: body) } + + it { expect(subject.wrapped_exception).to eq(exception) } + it { expect(subject.response).to eq(response) } + it { expect(subject.message).to eq(exception.message) } + it { expect(subject.backtrace).to eq(exception.backtrace) } + it { expect(subject.response_status).to eq(400) } + it { expect(subject.response_headers).to eq(headers) } + it { expect(subject.response_body).to eq(body) } + end + + context 'with hash missing status key' do + let(:exception) { { body: 'error body' } } + + it { expect(subject.wrapped_exception).to be_nil } + it { expect(subject.response).to eq(exception) } + it { expect(subject.message).to eq('the server responded with status - method and url are not available due to include_request: false on Faraday::Response::RaiseError middleware') } + end + + context 'with hash with status but missing request data' do + let(:exception) { { status: 404, body: 'not found' } } # missing request key + + it { expect(subject.wrapped_exception).to be_nil } + it { expect(subject.response).to eq(exception) } + it { expect(subject.message).to eq('the server responded with status 404 - method and url are not available due to include_request: false on Faraday::Response::RaiseError middleware') } + end + + context 'with hash with status and request but missing method in request' do + let(:exception) { { status: 404, body: 'not found', request: { url: 'http://example.com/test' } } } # missing method + + it { expect(subject.wrapped_exception).to be_nil } + it { expect(subject.response).to eq(exception) } + it { expect(subject.message).to eq('the server responded with status 404 for http://example.com/test') } + end + + context 'with hash with status and request but missing url in request' do + let(:exception) { { status: 404, body: 'not found', request: { method: :get } } } # missing url + + it { expect(subject.wrapped_exception).to be_nil } + it { expect(subject.response).to eq(exception) } + it { expect(subject.message).to eq('the server responded with status 404 for GET ') } + end + + context 'with properly formed Faraday::Env' do + # This represents the normal case - a well-formed Faraday::Env object + # with all the standard properties populated as they would be during + # a typical HTTP request/response cycle + let(:exception) { Faraday::Env.new } + + before do + exception.status = 500 + exception.method = :post + exception.url = URI('https://api.example.com/users') + exception.request = Faraday::RequestOptions.new + exception.response_headers = { 'content-type' => 'application/json' } + exception.response_body = '{"error": "Internal server error"}' + exception.request_headers = { 'authorization' => 'Bearer token123' } + exception.request_body = '{"name": "John"}' + end + + it { expect(subject.wrapped_exception).to be_nil } + it { expect(subject.response).to eq(exception) } + it { expect(subject.message).to eq('the server responded with status 500 for POST https://api.example.com/users') } + end + + context 'with Faraday::Env missing status key' do + let(:exception) { Faraday::Env.new } + + before do + exception[:body] = 'error body' + # Intentionally not setting status + end + + it { expect(subject.wrapped_exception).to be_nil } + it { expect(subject.response).to eq(exception) } + it { expect(subject.message).to eq('the server responded with status for ') } + end + + context 'with Faraday::Env with direct method and url properties' do + let(:exception) { Faraday::Env.new } + + before do + exception.status = 404 + exception.method = :get + exception.url = URI('http://example.com/test') + exception[:body] = 'not found' + end + + it { expect(subject.wrapped_exception).to be_nil } + it { expect(subject.response).to eq(exception) } + it { expect(subject.message).to eq('the server responded with status 404 for GET http://example.com/test') } + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/middleware_registry_spec.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/middleware_registry_spec.rb new file mode 100644 index 0000000..a8fa7cc --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/middleware_registry_spec.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +RSpec.describe Faraday::MiddlewareRegistry do + before do + stub_const('CustomMiddleware', custom_middleware_klass) + end + let(:custom_middleware_klass) { Class.new(Faraday::Middleware) } + let(:dummy) { Class.new { extend Faraday::MiddlewareRegistry } } + + after { dummy.unregister_middleware(:custom) } + + it 'allows to register with constant' do + dummy.register_middleware(custom: custom_middleware_klass) + expect(dummy.lookup_middleware(:custom)).to eq(custom_middleware_klass) + end + + it 'allows to register with symbol' do + dummy.register_middleware(custom: :CustomMiddleware) + expect(dummy.lookup_middleware(:custom)).to eq(custom_middleware_klass) + end + + it 'allows to register with string' do + dummy.register_middleware(custom: 'CustomMiddleware') + expect(dummy.lookup_middleware(:custom)).to eq(custom_middleware_klass) + end + + it 'allows to register with Proc' do + dummy.register_middleware(custom: -> { custom_middleware_klass }) + expect(dummy.lookup_middleware(:custom)).to eq(custom_middleware_klass) + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/middleware_spec.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/middleware_spec.rb new file mode 100644 index 0000000..da1d368 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/middleware_spec.rb @@ -0,0 +1,213 @@ +# frozen_string_literal: true + +RSpec.describe Faraday::Middleware do + subject { described_class.new(app) } + let(:app) { double } + + describe 'options' do + context 'when options are passed to the middleware' do + subject { described_class.new(app, options) } + let(:options) { { field: 'value' } } + + it 'accepts options when initialized' do + expect(subject.options[:field]).to eq('value') + end + end + end + + describe '#on_request' do + subject do + Class.new(described_class) do + def on_request(env) + # do nothing + end + end.new(app) + end + + it 'is called by #call' do + expect(app).to receive(:call).and_return(app) + expect(app).to receive(:on_complete) + is_expected.to receive(:call).and_call_original + is_expected.to receive(:on_request) + subject.call(double) + end + end + + describe '#on_error' do + subject do + Class.new(described_class) do + def on_error(error) + # do nothing + end + end.new(app) + end + + it 'is called by #call' do + expect(app).to receive(:call).and_raise(Faraday::ConnectionFailed) + is_expected.to receive(:call).and_call_original + is_expected.to receive(:on_error) + + expect { subject.call(double) }.to raise_error(Faraday::ConnectionFailed) + end + end + + describe '#close' do + context "with app that doesn't support \#close" do + it 'should issue warning' do + is_expected.to receive(:warn) + subject.close + end + end + + context "with app that supports \#close" do + it 'should issue warning' do + expect(app).to receive(:close) + is_expected.to_not receive(:warn) + subject.close + end + end + end + + describe '::default_options' do + let(:subclass_no_options) { FaradayMiddlewareSubclasses::SubclassNoOptions } + let(:subclass_one_option) { FaradayMiddlewareSubclasses::SubclassOneOption } + let(:subclass_two_options) { FaradayMiddlewareSubclasses::SubclassTwoOptions } + + def build_conn(resp_middleware) + Faraday.new do |c| + c.adapter :test do |stub| + stub.get('/success') { [200, {}, 'ok'] } + end + c.response resp_middleware + end + end + + RSpec.shared_context 'reset @default_options' do + before(:each) do + FaradayMiddlewareSubclasses::SubclassNoOptions.instance_variable_set(:@default_options, nil) + FaradayMiddlewareSubclasses::SubclassOneOption.instance_variable_set(:@default_options, nil) + FaradayMiddlewareSubclasses::SubclassTwoOptions.instance_variable_set(:@default_options, nil) + Faraday::Middleware.instance_variable_set(:@default_options, nil) + end + end + + after(:all) do + FaradayMiddlewareSubclasses::SubclassNoOptions.instance_variable_set(:@default_options, nil) + FaradayMiddlewareSubclasses::SubclassOneOption.instance_variable_set(:@default_options, nil) + FaradayMiddlewareSubclasses::SubclassTwoOptions.instance_variable_set(:@default_options, nil) + Faraday::Middleware.instance_variable_set(:@default_options, nil) + end + + context 'with subclass DEFAULT_OPTIONS defined' do + include_context 'reset @default_options' + + context 'and without application options configured' do + let(:resp1) { build_conn(:one_option).get('/success') } + + it 'has only subclass defaults' do + expect(Faraday::Middleware.default_options).to eq(Faraday::Middleware::DEFAULT_OPTIONS) + expect(subclass_no_options.default_options).to eq(subclass_no_options::DEFAULT_OPTIONS) + expect(subclass_one_option.default_options).to eq(subclass_one_option::DEFAULT_OPTIONS) + expect(subclass_two_options.default_options).to eq(subclass_two_options::DEFAULT_OPTIONS) + end + + it { expect(resp1.body).to eq('ok') } + end + + context "and with one application's options changed" do + let(:resp2) { build_conn(:two_options).get('/success') } + + before(:each) do + FaradayMiddlewareSubclasses::SubclassTwoOptions.default_options = { some_option: false } + end + + it 'only updates default options of target subclass' do + expect(Faraday::Middleware.default_options).to eq(Faraday::Middleware::DEFAULT_OPTIONS) + expect(subclass_no_options.default_options).to eq(subclass_no_options::DEFAULT_OPTIONS) + expect(subclass_one_option.default_options).to eq(subclass_one_option::DEFAULT_OPTIONS) + expect(subclass_two_options.default_options).to eq({ some_option: false, some_other_option: false }) + end + + it { expect(resp2.body).to eq('ok') } + end + + context "and with two applications' options changed" do + let(:resp1) { build_conn(:one_option).get('/success') } + let(:resp2) { build_conn(:two_options).get('/success') } + + before(:each) do + FaradayMiddlewareSubclasses::SubclassOneOption.default_options = { some_other_option: true } + FaradayMiddlewareSubclasses::SubclassTwoOptions.default_options = { some_option: false } + end + + it 'updates subclasses and parent independent of each other' do + expect(Faraday::Middleware.default_options).to eq(Faraday::Middleware::DEFAULT_OPTIONS) + expect(subclass_no_options.default_options).to eq(subclass_no_options::DEFAULT_OPTIONS) + expect(subclass_one_option.default_options).to eq({ some_other_option: true }) + expect(subclass_two_options.default_options).to eq({ some_option: false, some_other_option: false }) + end + + it { expect(resp1.body).to eq('ok') } + it { expect(resp2.body).to eq('ok') } + end + end + + context 'with FARADAY::MIDDLEWARE DEFAULT_OPTIONS and with Subclass DEFAULT_OPTIONS' do + before(:each) do + stub_const('Faraday::Middleware::DEFAULT_OPTIONS', { its_magic: false }) + end + + # Must stub Faraday::Middleware::DEFAULT_OPTIONS before resetting default options + include_context 'reset @default_options' + + context 'and without application options configured' do + let(:resp1) { build_conn(:one_option).get('/success') } + + it 'has only subclass defaults' do + expect(Faraday::Middleware.default_options).to eq(Faraday::Middleware::DEFAULT_OPTIONS) + expect(FaradayMiddlewareSubclasses::SubclassNoOptions.default_options).to eq({ its_magic: false }) + expect(FaradayMiddlewareSubclasses::SubclassOneOption.default_options).to eq({ its_magic: false, some_other_option: false }) + expect(FaradayMiddlewareSubclasses::SubclassTwoOptions.default_options).to eq({ its_magic: false, some_option: true, some_other_option: false }) + end + + it { expect(resp1.body).to eq('ok') } + end + + context "and with two applications' options changed" do + let(:resp1) { build_conn(:one_option).get('/success') } + let(:resp2) { build_conn(:two_options).get('/success') } + + before(:each) do + FaradayMiddlewareSubclasses::SubclassOneOption.default_options = { some_other_option: true } + FaradayMiddlewareSubclasses::SubclassTwoOptions.default_options = { some_option: false } + end + + it 'updates subclasses and parent independent of each other' do + expect(Faraday::Middleware.default_options).to eq(Faraday::Middleware::DEFAULT_OPTIONS) + expect(FaradayMiddlewareSubclasses::SubclassNoOptions.default_options).to eq({ its_magic: false }) + expect(FaradayMiddlewareSubclasses::SubclassOneOption.default_options).to eq({ its_magic: false, some_other_option: true }) + expect(FaradayMiddlewareSubclasses::SubclassTwoOptions.default_options).to eq({ its_magic: false, some_option: false, some_other_option: false }) + end + + it { expect(resp1.body).to eq('ok') } + it { expect(resp2.body).to eq('ok') } + end + end + + describe 'default_options input validation' do + include_context 'reset @default_options' + + it 'raises error if Faraday::Middleware option does not exist' do + expect { Faraday::Middleware.default_options = { something_special: true } }.to raise_error(Faraday::InitializationError) do |e| + expect(e.message).to eq('Invalid options provided. Keys not found in Faraday::Middleware::DEFAULT_OPTIONS: something_special') + end + end + + it 'raises error if subclass option does not exist' do + expect { subclass_one_option.default_options = { this_is_a_typo: true } }.to raise_error(Faraday::InitializationError) do |e| + expect(e.message).to eq('Invalid options provided. Keys not found in FaradayMiddlewareSubclasses::SubclassOneOption::DEFAULT_OPTIONS: this_is_a_typo') + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/options/env_spec.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/options/env_spec.rb new file mode 100644 index 0000000..006bd5f --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/options/env_spec.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true + +RSpec.describe Faraday::Env do + subject(:env) { described_class.new } + + it 'allows to access members' do + expect(env.method).to be_nil + env.method = :get + expect(env.method).to eq(:get) + end + + it 'allows to access symbol non members' do + expect(env[:custom]).to be_nil + env[:custom] = :boom + expect(env[:custom]).to eq(:boom) + end + + it 'allows to access string non members' do + expect(env['custom']).to be_nil + env['custom'] = :boom + expect(env['custom']).to eq(:boom) + end + + it 'ignores false when fetching' do + ssl = Faraday::SSLOptions.new + ssl.verify = false + expect(ssl.fetch(:verify, true)).to be_falsey + end + + it 'handle verify_hostname when fetching' do + ssl = Faraday::SSLOptions.new + ssl.verify_hostname = true + expect(ssl.fetch(:verify_hostname, false)).to be_truthy + end + + it 'retains custom members' do + env[:foo] = 'custom 1' + env[:bar] = :custom2 + env2 = Faraday::Env.from(env) + env2[:baz] = 'custom 3' + + expect(env2[:foo]).to eq('custom 1') + expect(env2[:bar]).to eq(:custom2) + expect(env[:baz]).to be_nil + end + + describe '#body' do + subject(:env) { described_class.from(body: { foo: 'bar' }) } + + context 'when response is not finished yet' do + it 'returns the request body' do + expect(env.body).to eq(foo: 'bar') + end + end + + context 'when response is finished' do + before do + env.status = 200 + env.body = { bar: 'foo' } + env.response = Faraday::Response.new(env) + end + + it 'returns the response body' do + expect(env.body).to eq(bar: 'foo') + end + + it 'allows to access request_body' do + expect(env.request_body).to eq(foo: 'bar') + end + + it 'allows to access response_body' do + expect(env.response_body).to eq(bar: 'foo') + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/options/options_spec.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/options/options_spec.rb new file mode 100644 index 0000000..fc0b117 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/options/options_spec.rb @@ -0,0 +1,297 @@ +# frozen_string_literal: true + +RSpec.describe Faraday::Options do + SubOptions = Class.new(Faraday::Options.new(:sub_a, :sub_b)) + ParentOptions = Faraday::Options.new(:a, :b, :c) do + options c: SubOptions + end + + describe '#merge' do + it 'merges options with hashes' do + options = ParentOptions.new(1) + expect(options.a).to eq(1) + expect(options.b).to be_nil + + dup = options.merge a: 2, b: 3 + expect(dup.a).to eq(2) + expect(dup.b).to eq(3) + expect(options.a).to eq(1) + expect(options.b).to be_nil + end + + it 'deeply merges two options' do + sub_opts1 = SubOptions.from(sub_a: 3) + sub_opts2 = SubOptions.from(sub_b: 4) + opt1 = ParentOptions.from(a: 1, c: sub_opts1) + opt2 = ParentOptions.from(b: 2, c: sub_opts2) + + merged = opt1.merge(opt2) + + expected_sub_opts = SubOptions.from(sub_a: 3, sub_b: 4) + expected = ParentOptions.from(a: 1, b: 2, c: expected_sub_opts) + expect(merged).to eq(expected) + end + + it 'deeply merges options with hashes' do + sub_opts1 = SubOptions.from(sub_a: 3) + sub_opts2 = { sub_b: 4 } + opt1 = ParentOptions.from(a: 1, c: sub_opts1) + opt2 = { b: 2, c: sub_opts2 } + + merged = opt1.merge(opt2) + + expected_sub_opts = SubOptions.from(sub_a: 3, sub_b: 4) + expected = ParentOptions.from(a: 1, b: 2, c: expected_sub_opts) + expect(merged).to eq(expected) + end + + it 'deeply merges options with nil' do + sub_opts = SubOptions.new(3, 4) + options = ParentOptions.new(1, 2, sub_opts) + expect(options.a).to eq(1) + expect(options.b).to eq(2) + expect(options.c.sub_a).to eq(3) + expect(options.c.sub_b).to eq(4) + + options2 = ParentOptions.from(b: 5, c: nil) + + merged = options.merge(options2) + + expect(merged.b).to eq(5) + expect(merged.c).to eq(sub_opts) + end + + it 'deeply merges options with options having nil sub-options' do + options = ParentOptions.from(a: 1) + + sub_opts = SubOptions.new(3, 4) + options2 = ParentOptions.from(b: 2, c: sub_opts) + + expect(options.a).to eq(1) + expect(options2.b).to eq(2) + expect(options2.c.sub_a).to eq(3) + expect(options2.c.sub_b).to eq(4) + + merged = options.merge(options2) + + expect(merged.c).to eq(sub_opts) + end + + describe '#dup' do + it 'duplicate options but not sub-options' do + sub_opts = SubOptions.from(sub_a: 3) + opts = ParentOptions.from(b: 1, c: sub_opts) + + duped = opts.dup + duped.b = 2 + duped.c.sub_a = 4 + + expect(opts.b).to eq(1) + expect(opts.c.sub_a).to eq(4) + end + end + + describe '#deep_dup' do + it 'duplicate options and also suboptions' do + sub_opts = SubOptions.from(sub_a: 3) + opts = ParentOptions.from(b: 1, c: sub_opts) + + duped = opts.deep_dup + duped.b = 2 + duped.c.sub_a = 4 + + expect(opts.b).to eq(1) + expect(opts.c.sub_a).to eq(3) + end + end + + describe '#clear' do + it 'clears the options' do + options = SubOptions.new(1) + expect(options.empty?).not_to be_truthy + options.clear + expect(options.empty?).to be_truthy + end + end + + describe '#empty?' do + it 'returns true only if all options are nil' do + options = SubOptions.new + expect(options.empty?).to be_truthy + options.sub_a = 1 + expect(options.empty?).not_to be_truthy + options.delete(:sub_a) + expect(options.empty?).to be_truthy + end + end + + describe '#each_key' do + it 'allows to iterate through keys' do + options = ParentOptions.new(1, 2, 3) + enum = options.each_key + expect(enum.next.to_sym).to eq(:a) + expect(enum.next.to_sym).to eq(:b) + expect(enum.next.to_sym).to eq(:c) + end + end + + describe '#key?' do + it 'returns true if the key exists and is not nil' do + options = SubOptions.new + expect(options.key?(:sub_a)).not_to be_truthy + options.sub_a = 1 + expect(options.key?(:sub_a)).to be_truthy + end + end + + describe '#each_value' do + it 'allows to iterate through values' do + options = ParentOptions.new(1, 2, 3) + enum = options.each_value + expect(enum.next).to eq(1) + expect(enum.next).to eq(2) + expect(enum.next).to eq(3) + end + end + + describe '#value?' do + it 'returns true if any key has that value' do + options = SubOptions.new + expect(options.value?(1)).not_to be_truthy + options.sub_a = 1 + expect(options.value?(1)).to be_truthy + end + end + + describe '#update' do + it 'updates options from hashes' do + options = ParentOptions.new(1) + expect(options.a).to eq(1) + expect(options.b).to be_nil + + updated = options.update a: 2, b: 3 + expect(options.a).to eq(2) + expect(options.b).to eq(3) + expect(updated).to eq(options) + end + end + + describe '#delete' do + it 'allows to remove value for key' do + options = ParentOptions.new(1) + expect(options.a).to eq(1) + expect(options.delete(:a)).to eq(1) + expect(options.a).to be_nil + end + end + + describe '#from' do + it { expect { ParentOptions.from invalid: 1 }.to raise_error(NoMethodError) } + + it 'works with options' do + options = ParentOptions.new(1) + + value = ParentOptions.from(options) + expect(value.a).to eq(1) + expect(value.b).to be_nil + end + + it 'works with options with sub object' do + sub = SubOptions.new(1) + options = ParentOptions.from a: 1, c: sub + expect(options).to be_a_kind_of(ParentOptions) + expect(options.a).to eq(1) + expect(options.b).to be_nil + expect(options.c).to be_a_kind_of(SubOptions) + expect(options.c.sub_a).to eq(1) + end + + it 'works with hash' do + options = ParentOptions.from a: 1 + expect(options).to be_a_kind_of(ParentOptions) + expect(options.a).to eq(1) + expect(options.b).to be_nil + end + + it 'works with hash with sub object' do + options = ParentOptions.from a: 1, c: { sub_a: 1 } + expect(options).to be_a_kind_of(ParentOptions) + expect(options.a).to eq(1) + expect(options.b).to be_nil + expect(options.c).to be_a_kind_of(SubOptions) + expect(options.c.sub_a).to eq(1) + end + + it 'works with deep hash' do + hash = { b: 1 } + options = ParentOptions.from a: hash + expect(options.a[:b]).to eq(1) + + hash[:b] = 2 + expect(options.a[:b]).to eq(1) + + options.a[:b] = 3 + expect(hash[:b]).to eq(2) + expect(options.a[:b]).to eq(3) + end + + it 'works with nil' do + options = ParentOptions.from(nil) + expect(options).to be_a_kind_of(ParentOptions) + expect(options.a).to be_nil + expect(options.b).to be_nil + end + + it 'respects inheritance' do + subclass = Class.new(ParentOptions) + options = subclass.from(c: { sub_a: 'hello' }) + expect(options.c).to be_a_kind_of(SubOptions) + expect(options.c.sub_a).to eq('hello') + end + end + + describe '#memoized' do + subject(:options_class) { Class.new(ParentOptions) } + it 'requires block' do + expect { options_class.memoized(:a) }.to raise_error(ArgumentError) + end + + it 'accepts block' do + options_class.memoized(:a) { :foo } + expect(options_class.new.a).to eql(:foo) + end + end + + describe '#fetch' do + subject { SubOptions.new } + + context 'when the fetched key has no value' do + it 'uses falsey default' do + expect(subject.fetch(:sub_a, false) { |_| :blah }).to be_falsey + end + + it 'accepts block' do + expect(subject.fetch(:sub_a) { |k| "yo #{k.inspect}" }).to eq('yo :sub_a') + end + + it 'needs a default if key is missing' do + expect { subject.fetch(:sub_a) }.to raise_error(Faraday::Options.fetch_error_class) + end + end + + context 'when the fetched key has a value' do + before do + subject.sub_a = 1 + end + + it 'grabs value' do + expect(subject.fetch(:sub_a, false) { |_| :blah }).to eq(1) + end + + it 'works with key' do + expect(subject.fetch(:sub_a)).to eq(1) + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/options/proxy_options_spec.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/options/proxy_options_spec.rb new file mode 100644 index 0000000..15203ed --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/options/proxy_options_spec.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +RSpec.describe Faraday::ProxyOptions do + describe '#from' do + it 'works with string' do + options = Faraday::ProxyOptions.from 'http://user:pass@example.org' + expect(options.user).to eq('user') + expect(options.password).to eq('pass') + expect(options.uri).to be_a_kind_of(URI) + expect(options.path).to eq('') + expect(options.port).to eq(80) + expect(options.host).to eq('example.org') + expect(options.scheme).to eq('http') + expect(options.inspect).to match('#') + end + + it 'works with hash' do + hash = { user: 'user', password: 'pass', uri: 'http://@example.org' } + options = Faraday::ProxyOptions.from(hash) + expect(options.user).to eq('user') + expect(options.password).to eq('pass') + expect(options.uri).to be_a_kind_of(URI) + expect(options.path).to eq('') + expect(options.port).to eq(80) + expect(options.host).to eq('example.org') + expect(options.scheme).to eq('http') + expect(options.inspect).to match('# empty string + options = Faraday::ProxyOptions.from proxy_string + expect(options).to be_a_kind_of(Faraday::ProxyOptions) + expect(options.inspect).to eq('#') + end + end + + it 'allows hash access' do + proxy = Faraday::ProxyOptions.from 'http://a%40b:pw%20d@example.org' + expect(proxy.user).to eq('a@b') + expect(proxy[:user]).to eq('a@b') + expect(proxy.password).to eq('pw d') + expect(proxy[:password]).to eq('pw d') + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/options/request_options_spec.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/options/request_options_spec.rb new file mode 100644 index 0000000..8c1bb99 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/options/request_options_spec.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +RSpec.describe Faraday::RequestOptions do + subject(:options) { Faraday::RequestOptions.new } + + it 'allows to set the request proxy' do + expect(options.proxy).to be_nil + + expect { options[:proxy] = { booya: 1 } }.to raise_error(NoMethodError) + + options[:proxy] = { user: 'user' } + expect(options.proxy).to be_a_kind_of(Faraday::ProxyOptions) + expect(options.proxy.user).to eq('user') + + options.proxy = nil + expect(options.proxy).to be_nil + expect(options.inspect).to eq('#') + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/params_encoders/flat_spec.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/params_encoders/flat_spec.rb new file mode 100644 index 0000000..115342e --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/params_encoders/flat_spec.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require 'rack/utils' + +RSpec.describe Faraday::FlatParamsEncoder do + it_behaves_like 'a params encoder' + + it 'decodes arrays' do + query = 'a=one&a=two&a=three' + expected = { 'a' => %w[one two three] } + expect(subject.decode(query)).to eq(expected) + end + + it 'decodes boolean values' do + query = 'a=true&b=false' + expected = { 'a' => 'true', 'b' => 'false' } + expect(subject.decode(query)).to eq(expected) + end + + it 'encodes boolean values' do + params = { a: true, b: false } + expect(subject.encode(params)).to eq('a=true&b=false') + end + + it 'encodes boolean values in array' do + params = { a: [true, false] } + expect(subject.encode(params)).to eq('a=true&a=false') + end + + it 'encodes empty array in hash' do + params = { a: [] } + expect(subject.encode(params)).to eq('a=') + end + + it 'encodes unsorted when asked' do + params = { b: false, a: true } + expect(subject.encode(params)).to eq('a=true&b=false') + Faraday::FlatParamsEncoder.sort_params = false + expect(subject.encode(params)).to eq('b=false&a=true') + Faraday::FlatParamsEncoder.sort_params = true + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/params_encoders/nested_spec.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/params_encoders/nested_spec.rb new file mode 100644 index 0000000..83da22d --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/params_encoders/nested_spec.rb @@ -0,0 +1,151 @@ +# frozen_string_literal: true + +require 'rack/utils' + +RSpec.describe Faraday::NestedParamsEncoder do + it_behaves_like 'a params encoder' + + it 'decodes arrays' do + query = 'a[1]=one&a[2]=two&a[3]=three' + expected = { 'a' => %w[one two three] } + expect(subject.decode(query)).to eq(expected) + end + + it 'decodes hashes' do + query = 'a[b1]=one&a[b2]=two&a[b][c]=foo' + expected = { 'a' => { 'b1' => 'one', 'b2' => 'two', 'b' => { 'c' => 'foo' } } } + expect(subject.decode(query)).to eq(expected) + end + + it 'decodes nested arrays rack compat' do + query = 'a[][one]=1&a[][two]=2&a[][one]=3&a[][two]=4' + expected = Rack::Utils.parse_nested_query(query) + expect(subject.decode(query)).to eq(expected) + end + + it 'decodes nested array mixed types' do + query = 'a[][one]=1&a[]=2&a[]=&a[]' + expected = Rack::Utils.parse_nested_query(query) + expect(subject.decode(query)).to eq(expected) + end + + it 'decodes nested ignores invalid array' do + query = '[][a]=1&b=2' + expected = { 'a' => '1', 'b' => '2' } + expect(subject.decode(query)).to eq(expected) + end + + it 'decodes nested ignores repeated array notation' do + query = 'a[][][]=1' + expected = { 'a' => ['1'] } + expect(subject.decode(query)).to eq(expected) + end + + it 'decodes nested ignores malformed keys' do + query = '=1&[]=2' + expected = {} + expect(subject.decode(query)).to eq(expected) + end + + it 'decodes nested subkeys dont have to be in brackets' do + query = 'a[b]c[d]e=1' + expected = { 'a' => { 'b' => { 'c' => { 'd' => { 'e' => '1' } } } } } + expect(subject.decode(query)).to eq(expected) + end + + it 'decodes nested final value overrides any type' do + query = 'a[b][c]=1&a[b]=2' + expected = { 'a' => { 'b' => '2' } } + expect(subject.decode(query)).to eq(expected) + end + + it 'encodes rack compat' do + params = { a: [{ one: '1', two: '2' }, '3', ''] } + result = Faraday::Utils.unescape(Faraday::NestedParamsEncoder.encode(params)).split('&') + escaped = Rack::Utils.build_nested_query(params) + expected = Rack::Utils.unescape(escaped).split('&') + expect(result).to match_array(expected) + end + + it 'encodes empty string array value' do + expected = 'baz=&foo%5Bbar%5D=' + result = Faraday::NestedParamsEncoder.encode(foo: { bar: '' }, baz: '') + expect(result).to eq(expected) + end + + it 'encodes nil array value' do + expected = 'baz&foo%5Bbar%5D' + result = Faraday::NestedParamsEncoder.encode(foo: { bar: nil }, baz: nil) + expect(result).to eq(expected) + end + + it 'encodes empty array value' do + expected = 'baz%5B%5D&foo%5Bbar%5D%5B%5D' + result = Faraday::NestedParamsEncoder.encode(foo: { bar: [] }, baz: []) + expect(result).to eq(expected) + end + + it 'encodes boolean values' do + params = { a: true, b: false } + expect(subject.encode(params)).to eq('a=true&b=false') + end + + it 'encodes boolean values in array' do + params = { a: [true, false] } + expect(subject.encode(params)).to eq('a%5B%5D=true&a%5B%5D=false') + end + + it 'encodes unsorted when asked' do + params = { b: false, a: true } + expect(subject.encode(params)).to eq('a=true&b=false') + Faraday::NestedParamsEncoder.sort_params = false + expect(subject.encode(params)).to eq('b=false&a=true') + Faraday::NestedParamsEncoder.sort_params = true + end + + it 'encodes arrays indices when asked' do + params = { a: [0, 1, 2] } + expect(subject.encode(params)).to eq('a%5B%5D=0&a%5B%5D=1&a%5B%5D=2') + Faraday::NestedParamsEncoder.array_indices = true + expect(subject.encode(params)).to eq('a%5B0%5D=0&a%5B1%5D=1&a%5B2%5D=2') + Faraday::NestedParamsEncoder.array_indices = false + end + + shared_examples 'a wrong decoding' do + it do + expect { subject.decode(query) }.to raise_error(TypeError) do |e| + expect(e.message).to eq(error_message) + end + end + end + + context 'when expecting hash but getting string' do + let(:query) { 'a=1&a[b]=2' } + let(:error_message) { "expected Hash (got String) for param `a'" } + it_behaves_like 'a wrong decoding' + end + + context 'when expecting hash but getting array' do + let(:query) { 'a[]=1&a[b]=2' } + let(:error_message) { "expected Hash (got Array) for param `a'" } + it_behaves_like 'a wrong decoding' + end + + context 'when expecting nested hash but getting non nested' do + let(:query) { 'a[b]=1&a[b][c]=2' } + let(:error_message) { "expected Hash (got String) for param `b'" } + it_behaves_like 'a wrong decoding' + end + + context 'when expecting array but getting hash' do + let(:query) { 'a[b]=1&a[]=2' } + let(:error_message) { "expected Array (got Hash) for param `a'" } + it_behaves_like 'a wrong decoding' + end + + context 'when expecting array but getting string' do + let(:query) { 'a=1&a[]=2' } + let(:error_message) { "expected Array (got String) for param `a'" } + it_behaves_like 'a wrong decoding' + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/rack_builder_spec.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/rack_builder_spec.rb new file mode 100644 index 0000000..89f17ca --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/rack_builder_spec.rb @@ -0,0 +1,317 @@ +# frozen_string_literal: true + +RSpec.describe Faraday::RackBuilder do + # mock handler classes + (Handler = Struct.new(:app)).class_eval do + def call(env) + env[:request_headers]['X-Middleware'] ||= '' + env[:request_headers]['X-Middleware'] += ":#{self.class.name.split('::').last}" + app.call(env) + end + end + + class Apple < Handler + end + + class Orange < Handler + end + + class Banana < Handler + end + + subject { conn.builder } + before { Faraday.default_adapter = :test } + after { Faraday.default_adapter = nil } + + context 'with default stack' do + let(:conn) { Faraday::Connection.new } + + it { expect(subject[0]).to eq(Faraday::Request.lookup_middleware(:url_encoded)) } + it { expect(subject.adapter).to eq(Faraday::Adapter.lookup_middleware(Faraday.default_adapter)) } + end + + context 'with custom empty block' do + let(:conn) { Faraday::Connection.new {} } + + it { expect(subject[0]).to be_nil } + it { expect(subject.adapter).to eq(Faraday::Adapter.lookup_middleware(Faraday.default_adapter)) } + end + + context 'with custom adapter only' do + let(:conn) do + Faraday::Connection.new do |builder| + builder.adapter :test do |stub| + stub.get('/') { |_| [200, {}, ''] } + end + end + end + + it { expect(subject[0]).to be_nil } + it { expect(subject.adapter).to eq(Faraday::Adapter.lookup_middleware(:test)) } + end + + context 'with custom handler and adapter' do + let(:conn) do + Faraday::Connection.new do |builder| + builder.use Apple + builder.adapter :test do |stub| + stub.get('/') { |_| [200, {}, ''] } + end + end + end + + it 'locks the stack after making a request' do + expect(subject.locked?).to be_falsey + conn.get('/') + expect(subject.locked?).to be_truthy + expect { subject.use(Orange) }.to raise_error(Faraday::RackBuilder::StackLocked) + end + + it 'dup stack is unlocked' do + expect(subject.locked?).to be_falsey + subject.lock! + expect(subject.locked?).to be_truthy + dup = subject.dup + expect(dup).to eq(subject) + expect(dup.locked?).to be_falsey + end + + it 'allows to compare handlers' do + expect(subject.handlers.first).to eq(Faraday::RackBuilder::Handler.new(Apple)) + end + end + + context 'when having a single handler' do + let(:conn) { Faraday::Connection.new {} } + + before { subject.use(Apple) } + + it { expect(subject.handlers).to eq([Apple]) } + + it 'allows use' do + subject.use(Orange) + expect(subject.handlers).to eq([Apple, Orange]) + end + + it 'allows insert_before' do + subject.insert_before(Apple, Orange) + expect(subject.handlers).to eq([Orange, Apple]) + end + + it 'allows insert_after' do + subject.insert_after(Apple, Orange) + expect(subject.handlers).to eq([Apple, Orange]) + end + + it 'raises an error trying to use an unregistered symbol' do + expect { subject.use(:apple) }.to raise_error(Faraday::Error) do |err| + expect(err.message).to eq(':apple is not registered on Faraday::Middleware') + end + end + end + + context 'when having two handlers' do + let(:conn) { Faraday::Connection.new {} } + + before do + subject.use(Apple) + subject.use(Orange) + end + + it 'allows insert_before' do + subject.insert_before(Orange, Banana) + expect(subject.handlers).to eq([Apple, Banana, Orange]) + end + + it 'allows insert_after' do + subject.insert_after(Apple, Banana) + expect(subject.handlers).to eq([Apple, Banana, Orange]) + end + + it 'allows to swap handlers' do + subject.swap(Apple, Banana) + expect(subject.handlers).to eq([Banana, Orange]) + end + + it 'allows to delete a handler' do + subject.delete(Apple) + expect(subject.handlers).to eq([Orange]) + end + end + + context 'when adapter is added with named options' do + after { Faraday.default_adapter_options = {} } + let(:conn) { Faraday::Connection.new {} } + + let(:cat_adapter) do + Class.new(Faraday::Adapter) do + attr_accessor :name + + def initialize(app, name:) + super(app) + @name = name + end + end + end + + let(:cat) { subject.adapter.build } + + it 'adds a handler to construct adapter with named options' do + Faraday.default_adapter = cat_adapter + Faraday.default_adapter_options = { name: 'Chloe' } + expect { cat }.to_not output( + /warning: Using the last argument as keyword parameters is deprecated/ + ).to_stderr + expect(cat.name).to eq 'Chloe' + end + end + + context 'when middleware is added with named arguments' do + let(:conn) { Faraday::Connection.new {} } + + let(:dog_middleware) do + Class.new(Faraday::Middleware) do + attr_accessor :name + + def initialize(app, name:) + super(app) + @name = name + end + end + end + let(:dog) do + subject.handlers.find { |handler| handler == dog_middleware }.build + end + + it 'adds a handler to construct middleware with options passed to use' do + subject.use dog_middleware, name: 'Rex' + expect { dog }.to_not output( + /warning: Using the last argument as keyword parameters is deprecated/ + ).to_stderr + expect(dog.name).to eq('Rex') + end + end + + context 'when a middleware is added with named arguments' do + let(:conn) { Faraday::Connection.new {} } + + let(:cat_request) do + Class.new(Faraday::Middleware) do + attr_accessor :name + + def initialize(app, name:) + super(app) + @name = name + end + end + end + let(:cat) do + subject.handlers.find { |handler| handler == cat_request }.build + end + + it 'adds a handler to construct request adapter with options passed to request' do + Faraday::Request.register_middleware cat_request: cat_request + subject.request :cat_request, name: 'Felix' + expect { cat }.to_not output( + /warning: Using the last argument as keyword parameters is deprecated/ + ).to_stderr + expect(cat.name).to eq('Felix') + end + end + + context 'when a middleware is added with named arguments' do + let(:conn) { Faraday::Connection.new {} } + + let(:fish_response) do + Class.new(Faraday::Middleware) do + attr_accessor :name + + def initialize(app, name:) + super(app) + @name = name + end + end + end + let(:fish) do + subject.handlers.find { |handler| handler == fish_response }.build + end + + it 'adds a handler to construct response adapter with options passed to response' do + Faraday::Response.register_middleware fish_response: fish_response + subject.response :fish_response, name: 'Bubbles' + expect { fish }.to_not output( + /warning: Using the last argument as keyword parameters is deprecated/ + ).to_stderr + expect(fish.name).to eq('Bubbles') + end + end + + context 'when a plain adapter is added with named arguments' do + let(:conn) { Faraday::Connection.new {} } + + let(:rabbit_adapter) do + Class.new(Faraday::Adapter) do + attr_accessor :name + + def initialize(app, name:) + super(app) + @name = name + end + end + end + let(:rabbit) do + subject.adapter.build + end + + it 'adds a handler to construct adapter with options passed to adapter' do + Faraday::Adapter.register_middleware rabbit_adapter: rabbit_adapter + subject.adapter :rabbit_adapter, name: 'Thumper' + expect { rabbit }.to_not output( + /warning: Using the last argument as keyword parameters is deprecated/ + ).to_stderr + expect(rabbit.name).to eq('Thumper') + end + end + + context 'when handlers are directly added or updated' do + let(:conn) { Faraday::Connection.new {} } + + let(:rock_handler) do + Class.new do + attr_accessor :name + + def initialize(_app, name:) + @name = name + end + end + end + let(:rock) do + subject.handlers.find { |handler| handler == rock_handler }.build + end + + it 'adds a handler to construct adapter with options passed to insert' do + subject.insert 0, rock_handler, name: 'Stony' + expect { rock }.to_not output( + /warning: Using the last argument as keyword parameters is deprecated/ + ).to_stderr + expect(rock.name).to eq('Stony') + end + + it 'adds a handler with options passed to insert_after' do + subject.insert_after 0, rock_handler, name: 'Rocky' + expect { rock }.to_not output( + /warning: Using the last argument as keyword parameters is deprecated/ + ).to_stderr + expect(rock.name).to eq('Rocky') + end + + it 'adds a handler with options passed to swap' do + subject.insert 0, rock_handler, name: 'Flint' + subject.swap 0, rock_handler, name: 'Chert' + expect { rock }.to_not output( + /warning: Using the last argument as keyword parameters is deprecated/ + ).to_stderr + expect(rock.name).to eq('Chert') + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/request/authorization_spec.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/request/authorization_spec.rb new file mode 100644 index 0000000..437c88a --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/request/authorization_spec.rb @@ -0,0 +1,118 @@ +# frozen_string_literal: true + +RSpec.describe Faraday::Request::Authorization do + let(:conn) do + Faraday.new do |b| + b.request :authorization, auth_type, *auth_config + b.adapter :test do |stub| + stub.get('/auth-echo') do |env| + [200, {}, env[:request_headers]['Authorization']] + end + end + end + end + + shared_examples 'does not interfere with existing authentication' do + context 'and request already has an authentication header' do + let(:response) { conn.get('/auth-echo', nil, authorization: 'OAuth oauth_token') } + + it 'does not interfere with existing authorization' do + expect(response.body).to eq('OAuth oauth_token') + end + end + end + + let(:response) { conn.get('/auth-echo') } + + describe 'basic_auth' do + let(:auth_type) { :basic } + + context 'when passed correct params' do + let(:auth_config) { %w[aladdin opensesame] } + + it { expect(response.body).to eq('Basic YWxhZGRpbjpvcGVuc2VzYW1l') } + + include_examples 'does not interfere with existing authentication' + end + + context 'when passed very long values' do + let(:auth_config) { ['A' * 255, ''] } + + it { expect(response.body).to eq("Basic #{'QUFB' * 85}Og==") } + + include_examples 'does not interfere with existing authentication' + end + end + + describe 'authorization' do + let(:auth_type) { :Bearer } + + context 'when passed a string' do + let(:auth_config) { ['custom'] } + + it { expect(response.body).to eq('Bearer custom') } + + include_examples 'does not interfere with existing authentication' + end + + context 'when passed a proc' do + let(:auth_config) { [-> { 'custom_from_proc' }] } + + it { expect(response.body).to eq('Bearer custom_from_proc') } + + include_examples 'does not interfere with existing authentication' + end + + context 'when passed a callable' do + let(:callable) { double('Callable Authorizer', call: 'custom_from_callable') } + let(:auth_config) { [callable] } + + it { expect(response.body).to eq('Bearer custom_from_callable') } + + include_examples 'does not interfere with existing authentication' + end + + context 'with an argument' do + let(:response) { conn.get('/auth-echo', nil, 'middle' => 'crunchy surprise') } + + context 'when passed a proc' do + let(:auth_config) { [proc { |env| "proc #{env.request_headers['middle']}" }] } + + it { expect(response.body).to eq('Bearer proc crunchy surprise') } + + include_examples 'does not interfere with existing authentication' + end + + context 'when passed a lambda' do + let(:auth_config) { [->(env) { "lambda #{env.request_headers['middle']}" }] } + + it { expect(response.body).to eq('Bearer lambda crunchy surprise') } + + include_examples 'does not interfere with existing authentication' + end + + context 'when passed a callable with an argument' do + let(:callable) do + Class.new do + def call(env) + "callable #{env.request_headers['middle']}" + end + end.new + end + let(:auth_config) { [callable] } + + it { expect(response.body).to eq('Bearer callable crunchy surprise') } + + include_examples 'does not interfere with existing authentication' + end + end + + context 'when passed too many arguments' do + let(:auth_config) { %w[baz foo] } + + it { expect { response }.to raise_error(ArgumentError) } + + include_examples 'does not interfere with existing authentication' + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/request/instrumentation_spec.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/request/instrumentation_spec.rb new file mode 100644 index 0000000..d207c55 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/request/instrumentation_spec.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: true + +RSpec.describe Faraday::Request::Instrumentation do + class FakeInstrumenter + attr_reader :instrumentations + + def initialize + @instrumentations = [] + end + + def instrument(name, env) + @instrumentations << [name, env] + yield + end + end + + let(:config) { {} } + let(:options) { Faraday::Request::Instrumentation::Options.from config } + let(:instrumenter) { FakeInstrumenter.new } + let(:conn) do + Faraday.new do |f| + f.request :instrumentation, config.merge(instrumenter: instrumenter) + f.adapter :test do |stub| + stub.get '/' do + [200, {}, 'ok'] + end + end + end + end + + it { expect(options.name).to eq('request.faraday') } + it 'defaults to ActiveSupport::Notifications' do + res = options.instrumenter + rescue NameError => e + expect(e.to_s).to match('ActiveSupport') + else + expect(res).to eq(ActiveSupport::Notifications) + end + + it 'instruments with default name' do + expect(instrumenter.instrumentations.size).to eq(0) + + res = conn.get '/' + expect(res.body).to eq('ok') + expect(instrumenter.instrumentations.size).to eq(1) + + name, env = instrumenter.instrumentations.first + expect(name).to eq('request.faraday') + expect(env[:url].path).to eq('/') + end + + context 'with custom name' do + let(:config) { { name: 'custom' } } + + it { expect(options.name).to eq('custom') } + it 'instruments with custom name' do + expect(instrumenter.instrumentations.size).to eq(0) + + res = conn.get '/' + expect(res.body).to eq('ok') + expect(instrumenter.instrumentations.size).to eq(1) + + name, env = instrumenter.instrumentations.first + expect(name).to eq('custom') + expect(env[:url].path).to eq('/') + end + end + + context 'with custom instrumenter' do + let(:config) { { instrumenter: :custom } } + + it { expect(options.instrumenter).to eq(:custom) } + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/request/json_spec.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/request/json_spec.rb new file mode 100644 index 0000000..44dee79 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/request/json_spec.rb @@ -0,0 +1,199 @@ +# frozen_string_literal: true + +RSpec.describe Faraday::Request::Json do + let(:middleware) { described_class.new(->(env) { Faraday::Response.new(env) }) } + + def process(body, content_type = nil) + env = { body: body, request_headers: Faraday::Utils::Headers.new } + env[:request_headers]['content-type'] = content_type if content_type + middleware.call(Faraday::Env.from(env)).env + end + + def result_body + result[:body] + end + + def result_type + result[:request_headers]['content-type'] + end + + context 'no body' do + let(:result) { process(nil) } + + it "doesn't change body" do + expect(result_body).to be_nil + end + + it "doesn't add content type" do + expect(result_type).to be_nil + end + end + + context 'empty body' do + let(:result) { process('') } + + it "doesn't change body" do + expect(result_body).to be_empty + end + + it "doesn't add content type" do + expect(result_type).to be_nil + end + end + + context 'string body' do + let(:result) { process('{"a":1}') } + + it "doesn't change body" do + expect(result_body).to eq('{"a":1}') + end + + it 'adds content type' do + expect(result_type).to eq('application/json') + end + end + + context 'object body' do + let(:result) { process(a: 1) } + + it 'encodes body' do + expect(result_body).to eq('{"a":1}') + end + + it 'adds content type' do + expect(result_type).to eq('application/json') + end + end + + context 'empty object body' do + let(:result) { process({}) } + + it 'encodes body' do + expect(result_body).to eq('{}') + end + end + + context 'true body' do + let(:result) { process(true) } + + it 'encodes body' do + expect(result_body).to eq('true') + end + + it 'adds content type' do + expect(result_type).to eq('application/json') + end + end + + context 'false body' do + let(:result) { process(false) } + + it 'encodes body' do + expect(result_body).to eq('false') + end + + it 'adds content type' do + expect(result_type).to eq('application/json') + end + end + + context 'object body with json type' do + let(:result) { process({ a: 1 }, 'application/json; charset=utf-8') } + + it 'encodes body' do + expect(result_body).to eq('{"a":1}') + end + + it "doesn't change content type" do + expect(result_type).to eq('application/json; charset=utf-8') + end + end + + context 'object body with vendor json type' do + let(:result) { process({ a: 1 }, 'application/vnd.myapp.v1+json; charset=utf-8') } + + it 'encodes body' do + expect(result_body).to eq('{"a":1}') + end + + it "doesn't change content type" do + expect(result_type).to eq('application/vnd.myapp.v1+json; charset=utf-8') + end + end + + context 'object body with incompatible type' do + let(:result) { process({ a: 1 }, 'application/xml; charset=utf-8') } + + it "doesn't change body" do + expect(result_body).to eq(a: 1) + end + + it "doesn't change content type" do + expect(result_type).to eq('application/xml; charset=utf-8') + end + end + + context 'with encoder' do + let(:encoder) do + double('Encoder').tap do |e| + allow(e).to receive(:dump) { |s, opts| JSON.generate(s, opts) } + end + end + + let(:result) { process(a: 1) } + + context 'when encoder is passed as object' do + let(:middleware) { described_class.new(->(env) { Faraday::Response.new(env) }, { encoder: encoder }) } + + it 'calls specified JSON encoder\'s dump method' do + expect(encoder).to receive(:dump).with({ a: 1 }) + + result + end + + it 'encodes body' do + expect(result_body).to eq('{"a":1}') + end + + it 'adds content type' do + expect(result_type).to eq('application/json') + end + end + + context 'when encoder is passed as an object-method pair' do + let(:middleware) { described_class.new(->(env) { Faraday::Response.new(env) }, { encoder: [encoder, :dump] }) } + + it 'calls specified JSON encoder' do + expect(encoder).to receive(:dump).with({ a: 1 }) + + result + end + + it 'encodes body' do + expect(result_body).to eq('{"a":1}') + end + + it 'adds content type' do + expect(result_type).to eq('application/json') + end + end + + context 'when encoder is not passed' do + let(:middleware) { described_class.new(->(env) { Faraday::Response.new(env) }) } + + it 'calls JSON.generate' do + expect(JSON).to receive(:generate).with({ a: 1 }) + + result + end + + it 'encodes body' do + expect(result_body).to eq('{"a":1}') + end + + it 'adds content type' do + expect(result_type).to eq('application/json') + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/request/url_encoded_spec.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/request/url_encoded_spec.rb new file mode 100644 index 0000000..bdd9e0a --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/request/url_encoded_spec.rb @@ -0,0 +1,93 @@ +# frozen_string_literal: true + +require 'stringio' + +RSpec.describe Faraday::Request::UrlEncoded do + let(:conn) do + Faraday.new do |b| + b.request :url_encoded + b.adapter :test do |stub| + stub.post('/echo') do |env| + posted_as = env[:request_headers]['Content-Type'] + body = env[:body] + if body.respond_to?(:read) + body = body.read + end + [200, { 'Content-Type' => posted_as }, body] + end + end + end + end + + it 'does nothing without payload' do + response = conn.post('/echo') + expect(response.headers['Content-Type']).to be_nil + expect(response.body.empty?).to be_truthy + end + + it 'ignores custom content type' do + response = conn.post('/echo', { some: 'data' }, 'content-type' => 'application/x-foo') + expect(response.headers['Content-Type']).to eq('application/x-foo') + expect(response.body).to eq(some: 'data') + end + + it 'works with no headers' do + response = conn.post('/echo', fruit: %w[apples oranges]) + expect(response.headers['Content-Type']).to eq('application/x-www-form-urlencoded') + expect(response.body).to eq('fruit%5B%5D=apples&fruit%5B%5D=oranges') + end + + it 'works with with headers' do + response = conn.post('/echo', { 'a' => 123 }, 'content-type' => 'application/x-www-form-urlencoded') + expect(response.headers['Content-Type']).to eq('application/x-www-form-urlencoded') + expect(response.body).to eq('a=123') + end + + it 'works with nested params' do + response = conn.post('/echo', user: { name: 'Mislav', web: 'mislav.net' }) + expect(response.headers['Content-Type']).to eq('application/x-www-form-urlencoded') + expected = { 'user' => { 'name' => 'Mislav', 'web' => 'mislav.net' } } + expect(Faraday::Utils.parse_nested_query(response.body)).to eq(expected) + end + + it 'works with non nested params' do + response = conn.post('/echo', dimensions: %w[date location]) do |req| + req.options.params_encoder = Faraday::FlatParamsEncoder + end + expect(response.headers['Content-Type']).to eq('application/x-www-form-urlencoded') + expected = { 'dimensions' => %w[date location] } + expect(Faraday::Utils.parse_query(response.body)).to eq(expected) + expect(response.body).to eq('dimensions=date&dimensions=location') + end + + it 'works with unicode' do + err = capture_warnings do + response = conn.post('/echo', str: 'eé cç aã aâ') + expect(response.body).to eq('str=e%C3%A9+c%C3%A7+a%C3%A3+a%C3%A2') + end + expect(err.empty?).to be_truthy + end + + it 'works with nested keys' do + response = conn.post('/echo', 'a' => { 'b' => { 'c' => ['d'] } }) + expect(response.body).to eq('a%5Bb%5D%5Bc%5D%5B%5D=d') + end + + it 'works with files' do + response = conn.post('/echo', StringIO.new('str=apple')) + expect(response.body).to eq('str=apple') + end + + context 'customising default_space_encoding' do + around do |example| + Faraday::Utils.default_space_encoding = '%20' + example.run + Faraday::Utils.default_space_encoding = nil + end + + it 'uses the custom character to encode spaces' do + response = conn.post('/echo', str: 'apple banana') + expect(response.body).to eq('str=apple%20banana') + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/request_spec.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/request_spec.rb new file mode 100644 index 0000000..fbf85b5 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/request_spec.rb @@ -0,0 +1,110 @@ +# frozen_string_literal: true + +RSpec.describe Faraday::Request do + let(:conn) do + Faraday.new(url: 'http://httpbingo.org/api', + headers: { 'Mime-Version' => '1.0' }, + request: { oauth: { consumer_key: 'anonymous' } }) + end + let(:http_method) { :get } + let(:block) { nil } + + subject { conn.build_request(http_method, &block) } + + context 'when nothing particular is configured' do + it { expect(subject.http_method).to eq(:get) } + it { expect(subject.to_env(conn).ssl.verify).to be_falsey } + it { expect(subject.to_env(conn).ssl.verify_hostname).to be_falsey } + end + + context 'when HTTP method is post' do + let(:http_method) { :post } + + it { expect(subject.http_method).to eq(:post) } + end + + context 'when setting the url on setup with a URI' do + let(:block) { proc { |req| req.url URI.parse('foo.json?a=1') } } + + it { expect(subject.path).to eq(URI.parse('foo.json')) } + it { expect(subject.params).to eq('a' => '1') } + it { expect(subject.to_env(conn).url.to_s).to eq('http://httpbingo.org/api/foo.json?a=1') } + end + + context 'when setting the url on setup with a string path and params' do + let(:block) { proc { |req| req.url 'foo.json', 'a' => 1 } } + + it { expect(subject.path).to eq('foo.json') } + it { expect(subject.params).to eq('a' => 1) } + it { expect(subject.to_env(conn).url.to_s).to eq('http://httpbingo.org/api/foo.json?a=1') } + end + + context 'when setting the url on setup with a path including params' do + let(:block) { proc { |req| req.url 'foo.json?b=2&a=1#qqq' } } + + it { expect(subject.path).to eq('foo.json') } + it { expect(subject.params).to eq('a' => '1', 'b' => '2') } + it { expect(subject.to_env(conn).url.to_s).to eq('http://httpbingo.org/api/foo.json?a=1&b=2') } + end + + context 'when setting a header on setup with []= syntax' do + let(:block) { proc { |req| req['Server'] = 'Faraday' } } + let(:headers) { subject.to_env(conn).request_headers } + + it { expect(subject.headers['Server']).to eq('Faraday') } + it { expect(headers['mime-version']).to eq('1.0') } + it { expect(headers['server']).to eq('Faraday') } + end + + context 'when setting the body on setup' do + let(:block) { proc { |req| req.body = 'hi' } } + + it { expect(subject.body).to eq('hi') } + it { expect(subject.to_env(conn).body).to eq('hi') } + end + + context 'with global request options set' do + let(:env_request) { subject.to_env(conn).request } + + before do + conn.options.timeout = 3 + conn.options.open_timeout = 5 + conn.ssl.verify = false + conn.proxy = 'http://proxy.com' + end + + it { expect(subject.options.timeout).to eq(3) } + it { expect(subject.options.open_timeout).to eq(5) } + it { expect(env_request.timeout).to eq(3) } + it { expect(env_request.open_timeout).to eq(5) } + + context 'and per-request options set' do + let(:block) do + proc do |req| + req.options.timeout = 10 + req.options.boundary = 'boo' + req.options.oauth[:consumer_secret] = 'xyz' + req.options.context = { + foo: 'foo', + bar: 'bar' + } + end + end + + it { expect(subject.options.timeout).to eq(10) } + it { expect(subject.options.open_timeout).to eq(5) } + it { expect(env_request.timeout).to eq(10) } + it { expect(env_request.open_timeout).to eq(5) } + it { expect(env_request.boundary).to eq('boo') } + it { expect(env_request.context).to eq(foo: 'foo', bar: 'bar') } + it do + oauth_expected = { consumer_secret: 'xyz', consumer_key: 'anonymous' } + expect(env_request.oauth).to eq(oauth_expected) + end + end + end + + it 'supports marshal serialization' do + expect(Marshal.load(Marshal.dump(subject))).to eq(subject) + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/response/json_spec.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/response/json_spec.rb new file mode 100644 index 0000000..e6cbda3 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/response/json_spec.rb @@ -0,0 +1,206 @@ +# frozen_string_literal: true + +RSpec.describe Faraday::Response::Json, type: :response do + let(:options) { {} } + let(:headers) { {} } + let(:middleware) do + described_class.new(lambda { |env| + Faraday::Response.new(env) + }, **options) + end + + def process(body, content_type = 'application/json', options = {}) + env = { + body: body, request: options, + request_headers: Faraday::Utils::Headers.new, + response_headers: Faraday::Utils::Headers.new(headers) + } + env[:response_headers]['content-type'] = content_type if content_type + yield(env) if block_given? + middleware.call(Faraday::Env.from(env)) + end + + context 'no type matching' do + it "doesn't change nil body" do + expect(process(nil).body).to be_nil + end + + it 'nullifies empty body' do + expect(process('').body).to be_nil + end + + it 'parses json body' do + response = process('{"a":1}') + expect(response.body).to eq('a' => 1) + expect(response.env[:raw_body]).to be_nil + end + end + + context 'with preserving raw' do + let(:options) { { preserve_raw: true } } + + it 'parses json body' do + response = process('{"a":1}') + expect(response.body).to eq('a' => 1) + expect(response.env[:raw_body]).to eq('{"a":1}') + end + end + + context 'with default regexp type matching' do + it 'parses json body of correct type' do + response = process('{"a":1}', 'application/x-json') + expect(response.body).to eq('a' => 1) + end + + it 'ignores json body of incorrect type' do + response = process('{"a":1}', 'text/json-xml') + expect(response.body).to eq('{"a":1}') + end + end + + context 'with array type matching' do + let(:options) { { content_type: %w[a/b c/d] } } + + it 'parses json body of correct type' do + expect(process('{"a":1}', 'a/b').body).to be_a(Hash) + expect(process('{"a":1}', 'c/d').body).to be_a(Hash) + end + + it 'ignores json body of incorrect type' do + expect(process('{"a":1}', 'a/d').body).not_to be_a(Hash) + end + end + + it 'chokes on invalid json' do + expect { process('{!') }.to raise_error(Faraday::ParsingError) + end + + it 'includes the response on the ParsingError instance' do + process('{') { |env| env[:response] = Faraday::Response.new } + raise 'Parsing should have failed.' + rescue Faraday::ParsingError => e + expect(e.response).to be_a(Faraday::Response) + end + + context 'HEAD responses' do + it "nullifies the body if it's only one space" do + response = process(' ') + expect(response.body).to be_nil + end + + it "nullifies the body if it's two spaces" do + response = process(' ') + expect(response.body).to be_nil + end + end + + context 'JSON options' do + let(:body) { '{"a": 1}' } + let(:result) { { a: 1 } } + let(:options) do + { + parser_options: { + symbolize_names: true + } + } + end + + it 'passes relevant options to JSON parse' do + expect(::JSON).to receive(:parse) + .with(body, options[:parser_options]) + .and_return(result) + + response = process(body) + expect(response.body).to eq(result) + end + end + + context 'with decoder' do + let(:decoder) do + double('Decoder').tap do |e| + allow(e).to receive(:load) { |s, opts| JSON.parse(s, opts) } + end + end + + let(:body) { '{"a": 1}' } + let(:result) { { a: 1 } } + + context 'when decoder is passed as object' do + let(:options) do + { + parser_options: { + decoder: decoder, + option: :option_value, + symbolize_names: true + } + } + end + + it 'passes relevant options to specified decoder\'s load method' do + expect(decoder).to receive(:load) + .with(body, { option: :option_value, symbolize_names: true }) + .and_return(result) + + response = process(body) + expect(response.body).to eq(result) + end + end + + context 'when decoder is passed as an object-method pair' do + let(:options) do + { + parser_options: { + decoder: [decoder, :load], + option: :option_value, + symbolize_names: true + } + } + end + + it 'passes relevant options to specified decoder\'s method' do + expect(decoder).to receive(:load) + .with(body, { option: :option_value, symbolize_names: true }) + .and_return(result) + + response = process(body) + expect(response.body).to eq(result) + end + end + + context 'when decoder is not passed' do + let(:options) do + { + parser_options: { + symbolize_names: true + } + } + end + + it 'passes relevant options to JSON parse' do + expect(JSON).to receive(:parse) + .with(body, { symbolize_names: true }) + .and_return(result) + + response = process(body) + expect(response.body).to eq(result) + end + + it 'passes relevant options to JSON parse even when nil responds to :load' do + original_allow_message_expectations_on_nil = RSpec::Mocks.configuration.allow_message_expectations_on_nil + RSpec::Mocks.configuration.allow_message_expectations_on_nil = true + allow(nil).to receive(:respond_to?) + .with(:load) + .and_return(true) + + expect(JSON).to receive(:parse) + .with(body, { symbolize_names: true }) + .and_return(result) + + response = process(body) + expect(response.body).to eq(result) + ensure + RSpec::Mocks.configuration.allow_message_expectations_on_nil = original_allow_message_expectations_on_nil + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/response/logger_spec.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/response/logger_spec.rb new file mode 100644 index 0000000..6f8e11f --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/response/logger_spec.rb @@ -0,0 +1,299 @@ +# frozen_string_literal: true + +require 'stringio' +require 'logger' + +RSpec.describe Faraday::Response::Logger do + let(:string_io) { StringIO.new } + let(:logger) { Logger.new(string_io) } + let(:logger_options) { {} } + let(:conn) do + rubbles = ['Barney', 'Betty', 'Bam Bam'] + + Faraday.new do |b| + b.response :logger, logger, logger_options do |logger| + logger.filter(/(soylent green is) (.+)/, '\1 tasty') + logger.filter(/(api_key:).*"(.+)."/, '\1[API_KEY]') + logger.filter(/(password)=(.+)/, '\1=[HIDDEN]') + end + b.adapter :test do |stubs| + stubs.get('/hello') { [200, { 'Content-Type' => 'text/html' }, 'hello'] } + stubs.post('/ohai') { [200, { 'Content-Type' => 'text/html' }, 'fred'] } + stubs.post('/ohyes') { [200, { 'Content-Type' => 'text/html' }, 'pebbles'] } + stubs.get('/rubbles') { [200, { 'Content-Type' => 'application/json' }, rubbles] } + stubs.get('/8bit') { [200, { 'Content-Type' => 'text/html' }, (+'café!').force_encoding(Encoding::ASCII_8BIT)] } + stubs.get('/filtered_body') { [200, { 'Content-Type' => 'text/html' }, 'soylent green is people'] } + stubs.get('/filtered_headers') { [200, { 'Content-Type' => 'text/html' }, 'headers response'] } + stubs.get('/filtered_params') { [200, { 'Content-Type' => 'text/html' }, 'params response'] } + stubs.get('/filtered_url') { [200, { 'Content-Type' => 'text/html' }, 'url response'] } + stubs.get('/connection_failed') { raise Faraday::ConnectionFailed, 'Failed to open TCP connection' } + end + end + end + + before do + logger.level = Logger::DEBUG + end + + it 'still returns output' do + resp = conn.get '/hello', nil, accept: 'text/html' + expect(resp.body).to eq('hello') + end + + context 'without configuration' do + let(:conn) do + Faraday.new do |b| + b.response :logger + b.adapter :test do |stubs| + stubs.get('/hello') { [200, { 'Content-Type' => 'text/html' }, 'hello'] } + end + end + end + + it 'defaults to stdout' do + expect(Logger).to receive(:new).with($stdout).and_return(Logger.new(nil)) + conn.get('/hello') + end + end + + context 'when logger with program name' do + let(:logger) { Logger.new(string_io, progname: 'my_best_program') } + + it 'logs with program name' do + conn.get '/hello' + + expect(string_io.string).to match('-- my_best_program: request:') + expect(string_io.string).to match('-- my_best_program: response:') + end + end + + context 'when logger without program name' do + it 'logs without program name' do + conn.get '/hello' + + expect(string_io.string).to match('-- : request:') + expect(string_io.string).to match('-- : response:') + end + end + + context 'with default formatter' do + let(:formatter) { instance_double(Faraday::Logging::Formatter, request: true, response: true, filter: []) } + + before { allow(Faraday::Logging::Formatter).to receive(:new).and_return(formatter) } + + it 'delegates logging to the formatter' do + expect(formatter).to receive(:request).with(an_instance_of(Faraday::Env)) + expect(formatter).to receive(:response).with(an_instance_of(Faraday::Env)) + conn.get '/hello' + end + + context 'when no route' do + it 'delegates logging to the formatter' do + expect(formatter).to receive(:request).with(an_instance_of(Faraday::Env)) + expect(formatter).to receive(:exception).with(an_instance_of(Faraday::Adapter::Test::Stubs::NotFound)) + + expect { conn.get '/noroute' }.to raise_error(Faraday::Adapter::Test::Stubs::NotFound) + end + end + end + + context 'with custom formatter' do + let(:formatter_class) do + Class.new(Faraday::Logging::Formatter) do + def request(_env) + info 'Custom log formatter request' + end + + def response(_env) + info 'Custom log formatter response' + end + end + end + + let(:logger_options) { { formatter: formatter_class } } + + it 'logs with custom formatter' do + conn.get '/hello' + + expect(string_io.string).to match('Custom log formatter request') + expect(string_io.string).to match('Custom log formatter response') + end + end + + it 'logs method and url' do + conn.get '/hello', nil, accept: 'text/html' + expect(string_io.string).to match('GET http:/hello') + end + + it 'logs status' do + conn.get '/hello', nil, accept: 'text/html' + expect(string_io.string).to match('Status 200') + end + + it 'does not log error message by default' do + expect { conn.get '/noroute' }.to raise_error(Faraday::Adapter::Test::Stubs::NotFound) + expect(string_io.string).not_to match(%(no stubbed request for get http:/noroute)) + end + + it 'logs request headers by default' do + conn.get '/hello', nil, accept: 'text/html' + expect(string_io.string).to match(%(Accept: "text/html)) + end + + it 'logs response headers by default' do + conn.get '/hello', nil, accept: 'text/html' + expect(string_io.string).to match(%(Content-Type: "text/html)) + end + + it 'does not log request body by default' do + conn.post '/ohai', 'name=Unagi', accept: 'text/html' + expect(string_io.string).not_to match(%(name=Unagi)) + end + + it 'does not log response body by default' do + conn.post '/ohai', 'name=Toro', accept: 'text/html' + expect(string_io.string).not_to match(%(fred)) + end + + it 'logs filter headers' do + conn.headers = { 'api_key' => 'ABC123' } + conn.get '/filtered_headers', nil, accept: 'text/html' + expect(string_io.string).to match(%(api_key:)) + expect(string_io.string).to match(%([API_KEY])) + expect(string_io.string).not_to match(%(ABC123)) + end + + it 'logs filter url' do + conn.get '/filtered_url?password=hunter2', nil, accept: 'text/html' + expect(string_io.string).to match(%([HIDDEN])) + expect(string_io.string).not_to match(%(hunter2)) + end + + context 'when not logging request headers' do + let(:logger_options) { { headers: { request: false } } } + + it 'does not log request headers if option is false' do + conn.get '/hello', nil, accept: 'text/html' + expect(string_io.string).not_to match(%(Accept: "text/html)) + end + end + + context 'when not logging response headers' do + let(:logger_options) { { headers: { response: false } } } + + it 'does not log response headers if option is false' do + conn.get '/hello', nil, accept: 'text/html' + expect(string_io.string).not_to match(%(Content-Type: "text/html)) + end + end + + context 'when logging request body' do + let(:logger_options) { { bodies: { request: true } } } + + it 'logs only request body' do + conn.post '/ohyes', 'name=Tamago', accept: 'text/html' + expect(string_io.string).to match(%(name=Tamago)) + expect(string_io.string).not_to match(%(pebbles)) + end + end + + context 'when logging response body' do + let(:logger_options) { { bodies: { response: true } } } + + it 'logs only response body' do + conn.post '/ohyes', 'name=Hamachi', accept: 'text/html' + expect(string_io.string).to match(%(pebbles)) + expect(string_io.string).not_to match(%(name=Hamachi)) + end + end + + context 'when logging request and response bodies' do + let(:logger_options) { { bodies: true } } + + it 'logs request and response body' do + conn.post '/ohyes', 'name=Ebi', accept: 'text/html' + expect(string_io.string).to match(%(name=Ebi)) + expect(string_io.string).to match(%(pebbles)) + end + + it 'logs response body object' do + conn.get '/rubbles', nil, accept: 'text/html' + expect(string_io.string).to match(%([\"Barney\", \"Betty\", \"Bam Bam\"]\n)) + end + + it 'logs filter body' do + conn.get '/filtered_body', nil, accept: 'text/html' + expect(string_io.string).to match(%(soylent green is)) + expect(string_io.string).to match(%(tasty)) + expect(string_io.string).not_to match(%(people)) + end + end + + context 'when bodies are logged by default' do + before do + described_class.default_options = { bodies: true } + end + + it 'logs response body' do + conn.post '/ohai' + expect(string_io.string).to match(%(fred)) + end + + it 'converts to UTF-8' do + conn.get '/8bit' + expect(string_io.string).to match(%(caf��!)) + end + + after do + described_class.default_options = { bodies: false } + end + end + + context 'when logging errors' do + let(:logger_options) { { errors: true } } + + it 'logs error message' do + expect { conn.get '/noroute' }.to raise_error(Faraday::Adapter::Test::Stubs::NotFound) + expect(string_io.string).to match(%(no stubbed request for get http:/noroute)) + end + end + + context 'when logging headers and errors' do + let(:logger_options) { { headers: true, errors: true } } + + it 'logs error message' do + expect { conn.get '/connection_failed' }.to raise_error(Faraday::ConnectionFailed) + expect(string_io.string).to match(%(Failed to open TCP connection)) + end + end + + context 'when using log_level' do + let(:logger_options) { { bodies: true, log_level: :debug } } + + it 'logs request/request body on the specified level (debug)' do + logger.level = Logger::DEBUG + conn.post '/ohyes', 'name=Ebi', accept: 'text/html' + expect(string_io.string).to match(%(name=Ebi)) + expect(string_io.string).to match(%(pebbles)) + end + + it 'logs headers on the debug level' do + logger.level = Logger::DEBUG + conn.get '/hello', nil, accept: 'text/html' + expect(string_io.string).to match(%(Content-Type: "text/html)) + end + + it 'does not log request/response body on the info level' do + logger.level = Logger::INFO + conn.post '/ohyes', 'name=Ebi', accept: 'text/html' + expect(string_io.string).not_to match(%(name=Ebi)) + expect(string_io.string).not_to match(%(pebbles)) + end + + it 'does not log headers on the info level' do + logger.level = Logger::INFO + conn.get '/hello', nil, accept: 'text/html' + expect(string_io.string).not_to match(%(Content-Type: "text/html)) + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/response/raise_error_spec.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/response/raise_error_spec.rb new file mode 100644 index 0000000..5799126 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/response/raise_error_spec.rb @@ -0,0 +1,286 @@ +# frozen_string_literal: true + +RSpec.describe Faraday::Response::RaiseError do + let(:conn) do + Faraday.new do |b| + b.response :raise_error + b.adapter :test do |stub| + stub.get('ok') { [200, { 'Content-Type' => 'text/html' }, ''] } + stub.get('bad-request') { [400, { 'X-Reason' => 'because' }, 'keep looking'] } + stub.get('unauthorized') { [401, { 'X-Reason' => 'because' }, 'keep looking'] } + stub.get('forbidden') { [403, { 'X-Reason' => 'because' }, 'keep looking'] } + stub.get('not-found') { [404, { 'X-Reason' => 'because' }, 'keep looking'] } + stub.get('proxy-error') { [407, { 'X-Reason' => 'because' }, 'keep looking'] } + stub.get('request-timeout') { [408, { 'X-Reason' => 'because' }, 'keep looking'] } + stub.get('conflict') { [409, { 'X-Reason' => 'because' }, 'keep looking'] } + stub.get('unprocessable-content') { [422, { 'X-Reason' => 'because' }, 'keep looking'] } + stub.get('too-many-requests') { [429, { 'X-Reason' => 'because' }, 'keep looking'] } + stub.get('4xx') { [499, { 'X-Reason' => 'because' }, 'keep looking'] } + stub.get('nil-status') { [nil, { 'X-Reason' => 'nil' }, 'fail'] } + stub.get('server-error') { [500, { 'X-Error' => 'bailout' }, 'fail'] } + end + end + end + + it 'raises no exception for 200 responses' do + expect { conn.get('ok') }.not_to raise_error + end + + it 'raises Faraday::BadRequestError for 400 responses' do + expect { conn.get('bad-request') }.to raise_error(Faraday::BadRequestError) do |ex| + expect(ex.message).to eq('the server responded with status 400 for GET http:/bad-request') + expect(ex.response[:headers]['X-Reason']).to eq('because') + expect(ex.response[:status]).to eq(400) + expect(ex.response_status).to eq(400) + expect(ex.response_body).to eq('keep looking') + expect(ex.response_headers['X-Reason']).to eq('because') + end + end + + it 'raises Faraday::UnauthorizedError for 401 responses' do + expect { conn.get('unauthorized') }.to raise_error(Faraday::UnauthorizedError) do |ex| + expect(ex.message).to eq('the server responded with status 401 for GET http:/unauthorized') + expect(ex.response[:headers]['X-Reason']).to eq('because') + expect(ex.response[:status]).to eq(401) + expect(ex.response_status).to eq(401) + expect(ex.response_body).to eq('keep looking') + expect(ex.response_headers['X-Reason']).to eq('because') + end + end + + it 'raises Faraday::ForbiddenError for 403 responses' do + expect { conn.get('forbidden') }.to raise_error(Faraday::ForbiddenError) do |ex| + expect(ex.message).to eq('the server responded with status 403 for GET http:/forbidden') + expect(ex.response[:headers]['X-Reason']).to eq('because') + expect(ex.response[:status]).to eq(403) + expect(ex.response_status).to eq(403) + expect(ex.response_body).to eq('keep looking') + expect(ex.response_headers['X-Reason']).to eq('because') + end + end + + it 'raises Faraday::ResourceNotFound for 404 responses' do + expect { conn.get('not-found') }.to raise_error(Faraday::ResourceNotFound) do |ex| + expect(ex.message).to eq('the server responded with status 404 for GET http:/not-found') + expect(ex.response[:headers]['X-Reason']).to eq('because') + expect(ex.response[:status]).to eq(404) + expect(ex.response_status).to eq(404) + expect(ex.response_body).to eq('keep looking') + expect(ex.response_headers['X-Reason']).to eq('because') + end + end + + it 'raises Faraday::ProxyAuthError for 407 responses' do + expect { conn.get('proxy-error') }.to raise_error(Faraday::ProxyAuthError) do |ex| + expect(ex.message).to eq('407 "Proxy Authentication Required"') + expect(ex.response[:headers]['X-Reason']).to eq('because') + expect(ex.response[:status]).to eq(407) + expect(ex.response_status).to eq(407) + expect(ex.response_body).to eq('keep looking') + expect(ex.response_headers['X-Reason']).to eq('because') + end + end + + it 'raises Faraday::RequestTimeoutError for 408 responses' do + expect { conn.get('request-timeout') }.to raise_error(Faraday::RequestTimeoutError) do |ex| + expect(ex.message).to eq('the server responded with status 408 for GET http:/request-timeout') + expect(ex.response[:headers]['X-Reason']).to eq('because') + expect(ex.response[:status]).to eq(408) + expect(ex.response_status).to eq(408) + expect(ex.response_body).to eq('keep looking') + expect(ex.response_headers['X-Reason']).to eq('because') + end + end + + it 'raises Faraday::ConflictError for 409 responses' do + expect { conn.get('conflict') }.to raise_error(Faraday::ConflictError) do |ex| + expect(ex.message).to eq('the server responded with status 409 for GET http:/conflict') + expect(ex.response[:headers]['X-Reason']).to eq('because') + expect(ex.response[:status]).to eq(409) + expect(ex.response_status).to eq(409) + expect(ex.response_body).to eq('keep looking') + expect(ex.response_headers['X-Reason']).to eq('because') + end + end + + it 'raises legacy Faraday::UnprocessableEntityError for 422 responses' do + expect { conn.get('unprocessable-content') }.to raise_error(Faraday::UnprocessableEntityError) do |ex| + expect(ex.message).to eq('the server responded with status 422 for GET http:/unprocessable-content') + expect(ex.response[:headers]['X-Reason']).to eq('because') + expect(ex.response[:status]).to eq(422) + expect(ex.response_status).to eq(422) + expect(ex.response_body).to eq('keep looking') + expect(ex.response_headers['X-Reason']).to eq('because') + end + end + + it 'raises Faraday::UnprocessableContentError for 422 responses' do + expect { conn.get('unprocessable-content') }.to raise_error(Faraday::UnprocessableContentError) do |ex| + expect(ex.message).to eq('the server responded with status 422 for GET http:/unprocessable-content') + expect(ex.response[:headers]['X-Reason']).to eq('because') + expect(ex.response[:status]).to eq(422) + expect(ex.response_status).to eq(422) + expect(ex.response_body).to eq('keep looking') + expect(ex.response_headers['X-Reason']).to eq('because') + end + end + + it 'raises Faraday::TooManyRequestsError for 429 responses' do + expect { conn.get('too-many-requests') }.to raise_error(Faraday::TooManyRequestsError) do |ex| + expect(ex.message).to eq('the server responded with status 429 for GET http:/too-many-requests') + expect(ex.response[:headers]['X-Reason']).to eq('because') + expect(ex.response[:status]).to eq(429) + expect(ex.response_status).to eq(429) + expect(ex.response_body).to eq('keep looking') + expect(ex.response_headers['X-Reason']).to eq('because') + end + end + + it 'raises Faraday::NilStatusError for nil status in response' do + expect { conn.get('nil-status') }.to raise_error(Faraday::NilStatusError) do |ex| + expect(ex.message).to eq('http status could not be derived from the server response') + expect(ex.response[:headers]['X-Reason']).to eq('nil') + expect(ex.response[:status]).to be_nil + expect(ex.response_status).to be_nil + expect(ex.response_body).to eq('fail') + expect(ex.response_headers['X-Reason']).to eq('nil') + end + end + + it 'raises Faraday::ClientError for other 4xx responses' do + expect { conn.get('4xx') }.to raise_error(Faraday::ClientError) do |ex| + expect(ex.message).to eq('the server responded with status 499 for GET http:/4xx') + expect(ex.response[:headers]['X-Reason']).to eq('because') + expect(ex.response[:status]).to eq(499) + expect(ex.response_status).to eq(499) + expect(ex.response_body).to eq('keep looking') + expect(ex.response_headers['X-Reason']).to eq('because') + end + end + + it 'raises Faraday::ServerError for 500 responses' do + expect { conn.get('server-error') }.to raise_error(Faraday::ServerError) do |ex| + expect(ex.message).to eq('the server responded with status 500 for GET http:/server-error') + expect(ex.response[:headers]['X-Error']).to eq('bailout') + expect(ex.response[:status]).to eq(500) + expect(ex.response_status).to eq(500) + expect(ex.response_body).to eq('fail') + expect(ex.response_headers['X-Error']).to eq('bailout') + end + end + + describe 'request info' do + let(:conn) do + Faraday.new do |b| + b.response :raise_error, **middleware_options + b.adapter :test do |stub| + stub.post(url, request_body, request_headers) do + [400, { 'X-Reason' => 'because' }, 'keep looking'] + end + end + end + end + let(:middleware_options) { {} } + let(:request_body) { JSON.generate({ 'item' => 'sth' }) } + let(:request_headers) { { 'Authorization' => 'Basic 123' } } + let(:url_path) { 'request' } + let(:query_params) { 'full=true' } + let(:url) { "#{url_path}?#{query_params}" } + + subject(:perform_request) do + conn.post url do |req| + req.headers['Authorization'] = 'Basic 123' + req.body = request_body + end + end + + it 'returns the request info in the exception' do + expect { perform_request }.to raise_error(Faraday::BadRequestError) do |ex| + expect(ex.response[:request][:method]).to eq(:post) + expect(ex.response[:request][:url]).to eq(URI("http:/#{url}")) + expect(ex.response[:request][:url_path]).to eq("/#{url_path}") + expect(ex.response[:request][:params]).to eq({ 'full' => 'true' }) + expect(ex.response[:request][:headers]).to match(a_hash_including(request_headers)) + expect(ex.response[:request][:body]).to eq(request_body) + end + end + + describe 'DEFAULT_OPTION: include_request' do + before(:each) do + Faraday::Response::RaiseError.instance_variable_set(:@default_options, nil) + Faraday::Middleware.instance_variable_set(:@default_options, nil) + end + + after(:all) do + Faraday::Response::RaiseError.instance_variable_set(:@default_options, nil) + Faraday::Middleware.instance_variable_set(:@default_options, nil) + end + + context 'when RaiseError DEFAULT_OPTION (include_request: true) is used' do + it 'includes request info in the exception' do + expect { perform_request }.to raise_error(Faraday::BadRequestError) do |ex| + expect(ex.response.keys).to contain_exactly( + :status, + :headers, + :body, + :request + ) + end + end + end + + context 'when application sets default_options `include_request: false`' do + before(:each) do + Faraday::Response::RaiseError.default_options = { include_request: false } + end + + context 'and when include_request option is omitted' do + it 'does not include request info in the exception' do + expect { perform_request }.to raise_error(Faraday::BadRequestError) do |ex| + expect(ex.response.keys).to contain_exactly( + :status, + :headers, + :body + ) + end + end + end + + context 'and when include_request option is explicitly set for instance' do + let(:middleware_options) { { include_request: true } } + + it 'includes request info in the exception' do + expect { perform_request }.to raise_error(Faraday::BadRequestError) do |ex| + expect(ex.response.keys).to contain_exactly( + :status, + :headers, + :body, + :request + ) + end + end + end + end + end + end + + describe 'allowing certain status codes' do + let(:conn) do + Faraday.new do |b| + b.response :raise_error, allowed_statuses: [404] + b.adapter :test do |stub| + stub.get('bad-request') { [400, { 'X-Reason' => 'because' }, 'keep looking'] } + stub.get('not-found') { [404, { 'X-Reason' => 'because' }, 'keep looking'] } + end + end + end + + it 'raises an error for status codes that are not explicitly allowed' do + expect { conn.get('bad-request') }.to raise_error(Faraday::BadRequestError) + end + + it 'does not raise an error for allowed status codes' do + expect { conn.get('not-found') }.not_to raise_error + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/response_spec.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/response_spec.rb new file mode 100644 index 0000000..2050da0 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/response_spec.rb @@ -0,0 +1,84 @@ +# frozen_string_literal: true + +RSpec.describe Faraday::Response do + subject { Faraday::Response.new(env) } + + let(:env) do + Faraday::Env.from(status: 404, body: 'yikes', url: Faraday::Utils.URI('https://lostisland.github.io/faraday'), + response_headers: { 'Content-Type' => 'text/plain' }) + end + + it { expect(subject.finished?).to be_truthy } + it { expect { subject.finish({}) }.to raise_error(RuntimeError) } + it { expect(subject.success?).to be_falsey } + it { expect(subject.status).to eq(404) } + it { expect(subject.body).to eq('yikes') } + it { expect(subject.url).to eq(URI('https://lostisland.github.io/faraday')) } + it { expect(subject.headers['Content-Type']).to eq('text/plain') } + it { expect(subject['content-type']).to eq('text/plain') } + + describe '#apply_request' do + before { subject.apply_request(body: 'a=b', method: :post) } + + it { expect(subject.body).to eq('yikes') } + it { expect(subject.env[:method]).to eq(:post) } + end + + describe '#to_hash' do + let(:hash) { subject.to_hash } + + it { expect(hash).to be_a(Hash) } + it { expect(hash[:status]).to eq(subject.status) } + it { expect(hash[:response_headers]).to eq(subject.headers) } + it { expect(hash[:body]).to eq(subject.body) } + it { expect(hash[:url]).to eq(subject.env.url) } + + context 'when response is not finished' do + subject { Faraday::Response.new.to_hash } + + it { is_expected.to eq({ status: nil, body: nil, response_headers: {}, url: nil }) } + end + end + + describe 'marshal serialization support' do + subject { Faraday::Response.new } + let(:loaded) { Marshal.load(Marshal.dump(subject)) } + + before do + subject.on_complete {} + subject.finish(env.merge(params: 'moo')) + end + + it { expect(loaded.env[:params]).to be_nil } + it { expect(loaded.env[:body]).to eq(env[:body]) } + it { expect(loaded.env[:response_headers]).to eq(env[:response_headers]) } + it { expect(loaded.env[:status]).to eq(env[:status]) } + it { expect(loaded.env[:url]).to eq(env[:url]) } + end + + describe '#on_complete' do + subject { Faraday::Response.new } + + it 'parse body on finish' do + subject.on_complete { |env| env[:body] = env[:body].upcase } + subject.finish(env) + + expect(subject.body).to eq('YIKES') + end + + it 'can access response body in on_complete callback' do + subject.on_complete { |env| env[:body] = subject.body.upcase } + subject.finish(env) + + expect(subject.body).to eq('YIKES') + end + + it 'can access response body in on_complete callback' do + callback_env = nil + subject.on_complete { |env| callback_env = env } + subject.finish({}) + + expect(subject.env).to eq(callback_env) + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/utils/headers_spec.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/utils/headers_spec.rb new file mode 100644 index 0000000..238bfd9 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/utils/headers_spec.rb @@ -0,0 +1,109 @@ +# frozen_string_literal: true + +RSpec.describe Faraday::Utils::Headers do + subject { Faraday::Utils::Headers.new } + + context 'when Content-Type is set to application/json' do + before { subject['Content-Type'] = 'application/json' } + + it { expect(subject.keys).to eq(['Content-Type']) } + it { expect(subject['Content-Type']).to eq('application/json') } + it { expect(subject['CONTENT-TYPE']).to eq('application/json') } + it { expect(subject['content-type']).to eq('application/json') } + it { is_expected.to include('content-type') } + end + + context 'when Content-Type is set to application/xml' do + before { subject['Content-Type'] = 'application/xml' } + + it { expect(subject.keys).to eq(['Content-Type']) } + it { expect(subject['Content-Type']).to eq('application/xml') } + it { expect(subject['CONTENT-TYPE']).to eq('application/xml') } + it { expect(subject['content-type']).to eq('application/xml') } + it { is_expected.to include('content-type') } + end + + describe '#fetch' do + before { subject['Content-Type'] = 'application/json' } + + it { expect(subject.fetch('Content-Type')).to eq('application/json') } + it { expect(subject.fetch('CONTENT-TYPE')).to eq('application/json') } + it { expect(subject.fetch(:content_type)).to eq('application/json') } + it { expect(subject.fetch('invalid', 'default')).to eq('default') } + it { expect(subject.fetch('invalid', false)).to eq(false) } + it { expect(subject.fetch('invalid', nil)).to be_nil } + it { expect(subject.fetch('Invalid') { |key| "#{key} key" }).to eq('Invalid key') } + it 'calls a block when provided' do + block_called = false + expect(subject.fetch('content-type') { block_called = true }).to eq('application/json') + expect(block_called).to be_falsey + end + it 'raises an error if key not found' do + expected_error = defined?(KeyError) ? KeyError : IndexError + expect { subject.fetch('invalid') }.to raise_error(expected_error) + end + end + + describe '#delete' do + before do + subject['Content-Type'] = 'application/json' + @deleted = subject.delete('content-type') + end + + it { expect(@deleted).to eq('application/json') } + it { expect(subject.size).to eq(0) } + it { is_expected.not_to include('content-type') } + it { expect(subject.delete('content-type')).to be_nil } + end + + describe '#dig' do + before { subject['Content-Type'] = 'application/json' } + + it { expect(subject&.dig('Content-Type')).to eq('application/json') } + it { expect(subject&.dig('CONTENT-TYPE')).to eq('application/json') } + it { expect(subject&.dig(:content_type)).to eq('application/json') } + it { expect(subject&.dig('invalid')).to be_nil } + end + + describe '#parse' do + context 'when response headers leave http status line out' do + let(:headers) { "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n" } + + before { subject.parse(headers) } + + it { expect(subject.keys).to eq(%w[Content-Type]) } + it { expect(subject['Content-Type']).to eq('text/html') } + it { expect(subject['content-type']).to eq('text/html') } + end + + context 'when response headers values include a colon' do + let(:headers) { "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nLocation: http://httpbingo.org/\r\n\r\n" } + + before { subject.parse(headers) } + + it { expect(subject['location']).to eq('http://httpbingo.org/') } + end + + context 'when response headers include a blank line' do + let(:headers) { "HTTP/1.1 200 OK\r\n\r\nContent-Type: text/html\r\n\r\n" } + + before { subject.parse(headers) } + + it { expect(subject['content-type']).to eq('text/html') } + end + + context 'when response headers include already stored keys' do + let(:headers) { "HTTP/1.1 200 OK\r\nX-Numbers: 123\r\n\r\n" } + + before do + h = subject + h[:x_numbers] = 8 + h.parse(headers) + end + + it do + expect(subject[:x_numbers]).to eq('8, 123') + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/utils_spec.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/utils_spec.rb new file mode 100644 index 0000000..24269db --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday/utils_spec.rb @@ -0,0 +1,120 @@ +# frozen_string_literal: true + +RSpec.describe Faraday::Utils do + describe 'headers parsing' do + let(:multi_response_headers) do + "HTTP/1.x 500 OK\r\nContent-Type: text/html; charset=UTF-8\r\n" \ + "HTTP/1.x 200 OK\r\nContent-Type: application/json; charset=UTF-8\r\n\r\n" + end + + it 'parse headers for aggregated responses' do + headers = Faraday::Utils::Headers.new + headers.parse(multi_response_headers) + + result = headers.to_hash + + expect(result['Content-Type']).to eq('application/json; charset=UTF-8') + end + end + + describe 'URI parsing' do + let(:url) { 'http://example.com/abc' } + + it 'escapes safe buffer' do + str = FakeSafeBuffer.new('$32,000.00') + expect(Faraday::Utils.escape(str)).to eq('%2432%2C000.00') + end + + it 'parses with default parser' do + with_default_uri_parser(nil) do + uri = normalize(url) + expect(uri.host).to eq('example.com') + end + end + + it 'parses with URI' do + with_default_uri_parser(::URI) do + uri = normalize(url) + expect(uri.host).to eq('example.com') + end + end + + it 'parses with block' do + with_default_uri_parser(->(u) { "booya#{'!' * u.size}" }) do + expect(normalize(url)).to eq('booya!!!!!!!!!!!!!!!!!!!!!!') + end + end + + it 'replaces headers hash' do + headers = Faraday::Utils::Headers.new('authorization' => 't0ps3cr3t!') + expect(headers).to have_key('authorization') + + headers.replace('content-type' => 'text/plain') + expect(headers).not_to have_key('authorization') + end + end + + describe '.deep_merge!' do + let(:connection_options) { Faraday::ConnectionOptions.new } + let(:url) do + { + url: 'http://example.com/abc', + headers: { 'Mime-Version' => '1.0' }, + request: { oauth: { consumer_key: 'anonymous' } }, + ssl: { version: '2' } + } + end + + it 'recursively merges the headers' do + connection_options.headers = { user_agent: 'My Agent 1.0' } + deep_merge = Faraday::Utils.deep_merge!(connection_options, url) + + expect(deep_merge.headers).to eq('Mime-Version' => '1.0', user_agent: 'My Agent 1.0') + end + + context 'when a target hash has an Options Struct value' do + let(:request) do + { + params_encoder: nil, + proxy: nil, + bind: nil, + timeout: nil, + open_timeout: nil, + read_timeout: nil, + write_timeout: nil, + boundary: nil, + oauth: { consumer_key: 'anonymous' }, + context: nil, + on_data: nil + } + end + let(:ssl) do + { + verify: nil, + ca_file: nil, + ca_path: nil, + verify_mode: nil, + cert_store: nil, + client_cert: nil, + client_key: nil, + certificate: nil, + private_key: nil, + verify_depth: nil, + version: '2', + min_version: nil, + max_version: nil, + verify_hostname: nil, + hostname: nil, + ciphers: nil + } + end + + it 'does not overwrite an Options Struct value' do + deep_merge = Faraday::Utils.deep_merge!(connection_options, url) + + expect(deep_merge.request.to_h).to eq(request) + expect(deep_merge.ssl.to_h).to eq(ssl) + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday_spec.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday_spec.rb new file mode 100644 index 0000000..c3583f1 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/faraday_spec.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +RSpec.describe Faraday do + it 'has a version number' do + expect(Faraday::VERSION).not_to be nil + end + + context 'proxies to default_connection' do + let(:mock_connection) { double('Connection') } + before do + Faraday.default_connection = mock_connection + end + + it 'proxies methods that exist on the default_connection' do + expect(mock_connection).to receive(:this_should_be_proxied) + + Faraday.this_should_be_proxied + end + + it 'uses method_missing on Faraday if there is no proxyable method' do + expected_message = + if RUBY_VERSION >= '3.4' + "undefined method 'this_method_does_not_exist' for module Faraday" + elsif RUBY_VERSION >= '3.3' + "undefined method `this_method_does_not_exist' for module Faraday" + else + "undefined method `this_method_does_not_exist' for Faraday:Module" + end + + expect { Faraday.this_method_does_not_exist }.to raise_error(NoMethodError, expected_message) + end + + it 'proxied methods can be accessed' do + allow(mock_connection).to receive(:this_should_be_proxied) + + expect(Faraday.method(:this_should_be_proxied)).to be_a(Method) + end + + after do + Faraday.default_connection = nil + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/spec_helper.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/spec_helper.rb new file mode 100644 index 0000000..1b80ea2 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/spec_helper.rb @@ -0,0 +1,133 @@ +# frozen_string_literal: true + +# This file was generated by the `rspec --init` command. Conventionally, all +# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. +# The generated `.rspec` file contains `--require spec_helper` which will cause +# this file to always be loaded, without a need to explicitly require it in any +# files. +# +# Given that it is always loaded, you are encouraged to keep this file as +# light-weight as possible. Requiring heavyweight dependencies from this file +# will add to the boot time of your test suite on EVERY test run, even for an +# individual file that may not need all of that loaded. Instead, consider making +# a separate helper file that requires the additional dependencies and performs +# the additional setup, and require it from the spec files that actually need +# it. +# +# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration + +require 'simplecov' +require 'coveralls' +require 'webmock/rspec' +WebMock.disable_net_connect!(allow_localhost: true) + +SimpleCov.formatters = [SimpleCov::Formatter::HTMLFormatter, Coveralls::SimpleCov::Formatter] + +SimpleCov.start do + add_filter '/spec/' + minimum_coverage 84 + minimum_coverage_by_file 26 +end + +require 'faraday' +require 'pry' + +# Ensure all /lib files are loaded +# so they will be included in the test coverage report. +Dir['./lib/**/*.rb'].each { |file| require file } + +# Load all Rspec support files +Dir['./spec/support/**/*.rb'].each { |file| require file } + +RSpec.configure do |config| + # rspec-expectations config goes here. You can use an alternate + # assertion/expectation library such as wrong or the stdlib/minitest + # assertions if you prefer. + config.expect_with :rspec do |expectations| + # This option will default to `true` in RSpec 4. It makes the `description` + # and `failure_message` of custom matchers include text for helper methods + # defined using `chain`, e.g.: + # be_bigger_than(2).and_smaller_than(4).description + # # => "be bigger than 2 and smaller than 4" + # ...rather than: + # # => "be bigger than 2" + expectations.include_chain_clauses_in_custom_matcher_descriptions = true + end + + # rspec-mocks config goes here. You can use an alternate test double + # library (such as bogus or mocha) by changing the `mock_with` option here. + config.mock_with :rspec do |mocks| + # Prevents you from mocking or stubbing a method that does not exist on + # a real object. This is generally recommended, and will default to + # `true` in RSpec 4. + mocks.verify_partial_doubles = true + end + + # This option will default to `:apply_to_host_groups` in RSpec 4 (and will + # have no way to turn it off -- the option exists only for backwards + # compatibility in RSpec 3). It causes shared context metadata to be + # inherited by the metadata hash of host groups and examples, rather than + # triggering implicit auto-inclusion in groups with matching metadata. + config.shared_context_metadata_behavior = :apply_to_host_groups + + # This allows you to limit a spec run to individual examples or groups + # you care about by tagging them with `:focus` metadata. When nothing + # is tagged with `:focus`, all examples get run. RSpec also provides + # aliases for `it`, `describe`, and `context` that include `:focus` + # metadata: `fit`, `fdescribe` and `fcontext`, respectively. + # config.filter_run_when_matching :focus + + # Allows RSpec to persist some state between runs in order to support + # the `--only-failures` and `--next-failure` CLI options. We recommend + # you configure your source control system to ignore this file. + # config.example_status_persistence_file_path = "spec/examples.txt" + + # Limits the available syntax to the non-monkey patched syntax that is + # recommended. For more details, see: + # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/ + # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ + # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode + # config.disable_monkey_patching! + + # This setting enables warnings. It's recommended, but in some cases may + # be too noisy due to issues in dependencies. + # config.warnings = true + + # Many RSpec users commonly either run the entire suite or an individual + # file, and it's useful to allow more verbose output when running an + # individual spec file. + # if config.files_to_run.one? + # # Use the documentation formatter for detailed output, + # # unless a formatter has already been configured + # # (e.g. via a command-line flag). + # config.default_formatter = "doc" + # end + + # Print the 10 slowest examples and example groups at the + # end of the spec run, to help surface which specs are running + # particularly slow. + # config.profile_examples = 10 + + # Run specs in random order to surface order dependencies. If you find an + # order dependency and want to debug it, you can fix the order by providing + # the seed, which is printed after each run. + # --seed 1234 + config.order = :random + + # Seed global randomization in this process using the `--seed` CLI option. + # Setting this allows you to use `--seed` to deterministically reproduce + # test failures related to randomization by passing the same `--seed` value + # as the one that triggered the failure. + Kernel.srand config.seed + + config.include Faraday::HelperMethods +end + +# Extends RSpec DocumentationFormatter to hide skipped tests. +module FormatterOverrides + def example_pending(_arg); end + + def dump_pending(_arg); end + + RSpec::Core::Formatters::DocumentationFormatter.prepend self +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/support/disabling_stub.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/support/disabling_stub.rb new file mode 100644 index 0000000..3df2f21 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/support/disabling_stub.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +# Allows to disable WebMock stubs +module DisablingStub + def disable + @disabled = true + end + + def disabled? + @disabled + end + + WebMock::RequestStub.prepend self +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/support/fake_safe_buffer.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/support/fake_safe_buffer.rb new file mode 100644 index 0000000..69afd6e --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/support/fake_safe_buffer.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +# emulates ActiveSupport::SafeBuffer#gsub +FakeSafeBuffer = Struct.new(:string) do + def to_s + self + end + + def gsub(regex) + string.gsub(regex) do + match, = Regexp.last_match(0), '' =~ /a/ # rubocop:disable Performance/StringInclude + yield(match) + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/support/faraday_middleware_subclasses.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/support/faraday_middleware_subclasses.rb new file mode 100644 index 0000000..4e63f61 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/support/faraday_middleware_subclasses.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module FaradayMiddlewareSubclasses + class SubclassNoOptions < Faraday::Middleware + end + + class SubclassOneOption < Faraday::Middleware + DEFAULT_OPTIONS = { some_other_option: false }.freeze + end + + class SubclassTwoOptions < Faraday::Middleware + DEFAULT_OPTIONS = { some_option: true, some_other_option: false }.freeze + end +end + +Faraday::Response.register_middleware(no_options: FaradayMiddlewareSubclasses::SubclassNoOptions) +Faraday::Response.register_middleware(one_option: FaradayMiddlewareSubclasses::SubclassOneOption) +Faraday::Response.register_middleware(two_options: FaradayMiddlewareSubclasses::SubclassTwoOptions) diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/support/helper_methods.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/support/helper_methods.rb new file mode 100644 index 0000000..0f5d4f5 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/support/helper_methods.rb @@ -0,0 +1,96 @@ +# frozen_string_literal: true + +module Faraday + module HelperMethods + def self.included(base) + base.extend ClassMethods + end + + module ClassMethods + def features(*features) + @features = features + end + + def on_feature(name) + yield if block_given? && feature?(name) + end + + def feature?(name) + if @features.nil? + superclass.feature?(name) if superclass.respond_to?(:feature?) + elsif @features.include?(name) + true + end + end + + def method_with_body?(method) + METHODS_WITH_BODY.include?(method.to_s) + end + end + + def ssl_mode? + ENV['SSL'] == 'yes' + end + + def normalize(url) + Faraday::Utils::URI(url) + end + + def with_default_uri_parser(parser) + old_parser = Faraday::Utils.default_uri_parser + begin + Faraday::Utils.default_uri_parser = parser + yield + ensure + Faraday::Utils.default_uri_parser = old_parser + end + end + + def with_env(new_env) + old_env = {} + + new_env.each do |key, value| + old_env[key] = ENV.fetch(key, false) + ENV[key] = value + end + + begin + yield + ensure + old_env.each do |key, value| + value == false ? ENV.delete(key) : ENV[key] = value + end + end + end + + def with_env_proxy_disabled + Faraday.ignore_env_proxy = true + + begin + yield + ensure + Faraday.ignore_env_proxy = false + end + end + + def capture_warnings + old = $stderr + $stderr = StringIO.new + begin + yield + $stderr.string + ensure + $stderr = old + end + end + + def method_with_body?(method) + self.class.method_with_body?(method) + end + + def big_string + kb = 1024 + (32..126).map(&:chr).cycle.take(50 * kb).join + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/support/shared_examples/adapter.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/support/shared_examples/adapter.rb new file mode 100644 index 0000000..6256908 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/support/shared_examples/adapter.rb @@ -0,0 +1,105 @@ +# frozen_string_literal: true + +shared_examples 'an adapter' do |**options| + before { skip } if options[:skip] + + context 'with SSL enabled' do + before { ENV['SSL'] = 'yes' } + include_examples 'adapter examples', options + end + + context 'with SSL disabled' do + before { ENV['SSL'] = 'no' } + include_examples 'adapter examples', options + end +end + +shared_examples 'adapter examples' do |**options| + include Faraday::StreamingResponseChecker + + let(:adapter) { described_class.name.split('::').last } + + let(:conn_options) { { headers: { 'X-Faraday-Adapter' => adapter } }.merge(options[:conn_options] || {}) } + + let(:adapter_options) do + return [] unless options[:adapter_options] + + if options[:adapter_options].is_a?(Array) + options[:adapter_options] + else + [options[:adapter_options]] + end + end + + let(:protocol) { ssl_mode? ? 'https' : 'http' } + let(:remote) { "#{protocol}://example.com" } + let(:stub_remote) { remote } + + let(:conn) do + conn_options[:ssl] ||= {} + conn_options[:ssl][:ca_file] ||= ENV.fetch('SSL_FILE', nil) + conn_options[:ssl][:verify_hostname] ||= ENV['SSL_VERIFY_HOSTNAME'] == 'yes' + + Faraday.new(remote, conn_options) do |conn| + conn.request :url_encoded + conn.response :raise_error + conn.adapter described_class, *adapter_options + end + end + + let!(:request_stub) { stub_request(http_method, stub_remote) } + + after do + expect(request_stub).to have_been_requested unless request_stub.disabled? + end + + describe '#delete' do + let(:http_method) { :delete } + + it_behaves_like 'a request method', :delete + end + + describe '#get' do + let(:http_method) { :get } + + it_behaves_like 'a request method', :get + end + + describe '#head' do + let(:http_method) { :head } + + it_behaves_like 'a request method', :head + end + + describe '#options' do + let(:http_method) { :options } + + it_behaves_like 'a request method', :options + end + + describe '#patch' do + let(:http_method) { :patch } + + it_behaves_like 'a request method', :patch + end + + describe '#post' do + let(:http_method) { :post } + + it_behaves_like 'a request method', :post + end + + describe '#put' do + let(:http_method) { :put } + + it_behaves_like 'a request method', :put + end + + on_feature :trace_method do + describe '#trace' do + let(:http_method) { :trace } + + it_behaves_like 'a request method', :trace + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/support/shared_examples/params_encoder.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/support/shared_examples/params_encoder.rb new file mode 100644 index 0000000..38c8567 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/support/shared_examples/params_encoder.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +shared_examples 'a params encoder' do + it 'escapes safe buffer' do + monies = FakeSafeBuffer.new('$32,000.00') + expect(subject.encode('a' => monies)).to eq('a=%2432%2C000.00') + end + + it 'raises type error for empty string' do + expect { subject.encode('') }.to raise_error(TypeError) do |error| + expect(error.message).to eq("Can't convert String into Hash.") + end + end + + it 'encodes nil' do + expect(subject.encode('a' => nil)).to eq('a') + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/support/shared_examples/request_method.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/support/shared_examples/request_method.rb new file mode 100644 index 0000000..afa3376 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/support/shared_examples/request_method.rb @@ -0,0 +1,263 @@ +# frozen_string_literal: true + +shared_examples 'proxy examples' do + it 'handles requests with proxy' do + res = conn.public_send(http_method, '/') + + expect(res.status).to eq(200) + end + + it 'handles proxy failures' do + request_stub.to_return(status: 407) + + expect { conn.public_send(http_method, '/') }.to raise_error(Faraday::ProxyAuthError) + end +end + +shared_examples 'a request method' do |http_method| + let(:query_or_body) { method_with_body?(http_method) ? :body : :query } + let(:response) { conn.public_send(http_method, '/') } + + unless http_method == :head && feature?(:skip_response_body_on_head) + it 'retrieves the response body' do + res_body = 'test' + request_stub.to_return(body: res_body) + expect(conn.public_send(http_method, '/').body).to eq(res_body) + end + end + + it 'handles headers with multiple values' do + request_stub.to_return(headers: { 'Set-Cookie' => 'name=value' }) + expect(response.headers['set-cookie']).to eq('name=value') + end + + it 'retrieves the response headers' do + request_stub.to_return(headers: { 'Content-Type' => 'text/plain' }) + expect(response.headers['Content-Type']).to match(%r{text/plain}) + expect(response.headers['content-type']).to match(%r{text/plain}) + end + + it 'sends user agent' do + request_stub.with(headers: { 'User-Agent' => 'Agent Faraday' }) + conn.public_send(http_method, '/', nil, user_agent: 'Agent Faraday') + end + + it 'represents empty body response as blank string' do + expect(response.body).to eq('') + end + + it 'handles connection error' do + request_stub.disable + expect { conn.public_send(http_method, 'http://localhost:4') }.to raise_error(Faraday::ConnectionFailed) + end + + on_feature :local_socket_binding do + it 'binds local socket' do + stub_request(http_method, 'http://example.com') + + host = '1.2.3.4' + port = 1234 + conn_options[:request] = { bind: { host: host, port: port } } + + conn.public_send(http_method, '/') + + expect(conn.options[:bind][:host]).to eq(host) + expect(conn.options[:bind][:port]).to eq(port) + end + end + + # context 'when wrong ssl certificate is provided' do + # let(:ca_file_path) { 'tmp/faraday-different-ca-cert.crt' } + # before { conn_options.merge!(ssl: { ca_file: ca_file_path }) } + # + # it do + # expect { conn.public_send(http_method, '/') }.to raise_error(Faraday::SSLError) # do |ex| + # expect(ex.message).to include?('certificate') + # end + # end + # end + + on_feature :request_body_on_query_methods do + it 'sends request body' do + request_stub.with({ body: 'test' }) + res = if query_or_body == :body + conn.public_send(http_method, '/', 'test') + else + conn.public_send(http_method, '/') do |req| + req.body = 'test' + end + end + expect(res.env.request_body).to eq('test') + end + end + + it 'sends url encoded parameters' do + payload = { name: 'zack' } + request_stub.with({ query_or_body => payload }) + res = conn.public_send(http_method, '/', payload) + if query_or_body == :query + expect(res.env.request_body).to be_nil + else + expect(res.env.request_body).to eq('name=zack') + end + end + + it 'sends url encoded nested parameters' do + payload = { name: { first: 'zack' } } + request_stub.with({ query_or_body => payload }) + conn.public_send(http_method, '/', payload) + end + + # TODO: This needs reimplementation: see https://github.com/lostisland/faraday/issues/718 + # Should raise Faraday::TimeoutError + it 'supports timeout option' do + conn_options[:request] = { timeout: 1 } + request_stub.to_timeout + exc = adapter == 'NetHttp' ? Faraday::ConnectionFailed : Faraday::TimeoutError + expect { conn.public_send(http_method, '/') }.to raise_error(exc) + end + + # TODO: This needs reimplementation: see https://github.com/lostisland/faraday/issues/718 + # Should raise Faraday::ConnectionFailed + it 'supports open_timeout option' do + conn_options[:request] = { open_timeout: 1 } + request_stub.to_timeout + exc = adapter == 'NetHttp' ? Faraday::ConnectionFailed : Faraday::TimeoutError + expect { conn.public_send(http_method, '/') }.to raise_error(exc) + end + + on_feature :reason_phrase_parse do + it 'parses the reason phrase' do + request_stub.to_return(status: [200, 'OK']) + expect(response.reason_phrase).to eq('OK') + end + end + + on_feature :compression do + # Accept-Encoding header not sent for HEAD requests as body is not expected in the response. + unless http_method == :head + it 'handles gzip compression' do + request_stub.with(headers: { 'Accept-Encoding' => /\bgzip\b/ }) + conn.public_send(http_method, '/') + end + + it 'handles deflate compression' do + request_stub.with(headers: { 'Accept-Encoding' => /\bdeflate\b/ }) + conn.public_send(http_method, '/') + end + end + end + + on_feature :streaming do + describe 'streaming' do + let(:streamed) { [] } + + context 'when response is empty' do + it 'handles streaming' do + env = nil + conn.public_send(http_method, '/') do |req| + req.options.on_data = proc do |chunk, size, block_env| + streamed << [chunk, size] + env ||= block_env + end + end + + expect(streamed).to eq([['', 0]]) + # TODO: enable this after updating all existing adapters to the new streaming API + # expect(env).to be_a(Faraday::Env) + # expect(env.status).to eq(200) + end + end + + context 'when response contains big data' do + before { request_stub.to_return(body: big_string) } + + it 'handles streaming' do + env = nil + response = conn.public_send(http_method, '/') do |req| + req.options.on_data = proc do |chunk, size, block_env| + streamed << [chunk, size] + env ||= block_env + end + end + + expect(response.body).to eq('') + check_streaming_response(streamed, chunk_size: 16 * 1024) + # TODO: enable this after updating all existing adapters to the new streaming API + # expect(env).to be_a(Faraday::Env) + # expect(env.status).to eq(200) + end + end + end + end + + on_feature :parallel do + context 'with parallel setup' do + before do + @resp1 = nil + @resp2 = nil + @payload1 = { a: '1' } + @payload2 = { b: '2' } + + request_stub + .with({ query_or_body => @payload1 }) + .to_return(body: @payload1.to_json) + + stub_request(http_method, remote) + .with({ query_or_body => @payload2 }) + .to_return(body: @payload2.to_json) + + conn.in_parallel do + @resp1 = conn.public_send(http_method, '/', @payload1) + @resp2 = conn.public_send(http_method, '/', @payload2) + + expect(conn.in_parallel?).to be_truthy + expect(@resp1.body).to be_nil + expect(@resp2.body).to be_nil + end + + expect(conn.in_parallel?).to be_falsey + end + + it 'handles parallel requests status' do + expect(@resp1&.status).to eq(200) + expect(@resp2&.status).to eq(200) + end + + unless http_method == :head && feature?(:skip_response_body_on_head) + it 'handles parallel requests body' do + expect(@resp1&.body).to eq(@payload1.to_json) + expect(@resp2&.body).to eq(@payload2.to_json) + end + end + end + end + + context 'when a proxy is provided as option' do + before do + conn_options[:proxy] = 'http://env-proxy.com:80' + end + + include_examples 'proxy examples' + end + + context 'when http_proxy env variable is set' do + let(:proxy_url) { 'http://env-proxy.com:80' } + + around do |example| + with_env 'http_proxy' => proxy_url do + example.run + end + end + + include_examples 'proxy examples' + + context 'when the env proxy is ignored' do + around do |example| + with_env_proxy_disabled(&example) + end + + include_examples 'proxy examples' + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/support/streaming_response_checker.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/support/streaming_response_checker.rb new file mode 100644 index 0000000..8ef2599 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-2.14.0/spec/support/streaming_response_checker.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +module Faraday + module StreamingResponseChecker + def check_streaming_response(streamed, options = {}) + opts = { + prefix: '', + streaming?: true + }.merge(options) + + expected_response = opts[:prefix] + big_string + + chunks, sizes = streamed.transpose + + # Check that the total size of the chunks (via the last size returned) + # is the same size as the expected_response + expect(sizes.last).to eq(expected_response.bytesize) + + start_index = 0 + expected_chunks = [] + chunks.each do |actual_chunk| + expected_chunk = expected_response[start_index..((start_index + actual_chunk.bytesize) - 1)] + expected_chunks << expected_chunk + start_index += expected_chunk.bytesize + end + + # it's easier to read a smaller portion, so we check that first + expect(expected_chunks[0][0..255]).to eq(chunks[0][0..255]) + + [expected_chunks, chunks].transpose.each do |expected, actual| + expect(actual).to eq(expected) + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-net_http-3.4.2/LICENSE.md b/vendor/bundle/ruby/3.3.0/gems/faraday-net_http-3.4.2/LICENSE.md new file mode 100644 index 0000000..b7aabc5 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-net_http-3.4.2/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2020 Jan van der Pas + +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/vendor/bundle/ruby/3.3.0/gems/faraday-net_http-3.4.2/README.md b/vendor/bundle/ruby/3.3.0/gems/faraday-net_http-3.4.2/README.md new file mode 100644 index 0000000..6d510f4 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-net_http-3.4.2/README.md @@ -0,0 +1,57 @@ +# Faraday Net::HTTP adapter + +This gem is a [Faraday][faraday] adapter for the [Net::HTTP][net-http] library. Faraday is an HTTP client library that provides a common interface over many adapters. Every adapter is defined into it's own gem. This gem defines the adapter for `Net::HTTP` the HTTP library that's included into the standard library of Ruby. + +## Installation + +Add this line to your application's Gemfile: + +```ruby +gem 'faraday-net_http' +``` + +And then execute: + + $ bundle install + +Or install it yourself as: + + $ gem install faraday-net_http + +## Usage + +```ruby +conn = Faraday.new(...) do |f| + f.adapter :net_http do |http| + # yields Net::HTTP + http.verify_callback = lambda do |preverify, cert_store| + # do something here... + end + end +end +``` + +## Development + +After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. + +To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](rubygems). + +## Contributing + +Bug reports and pull requests are welcome on [GitHub][repo]. + +## License + +The gem is available as open source under the terms of the [license][license]. + +## Code of Conduct + +Everyone interacting in the Faraday Net::HTTP adapter project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct][code-of-conduct]. + +[faraday]: https://github.com/lostisland/faraday +[net-http]: https://ruby-doc.org/stdlib-2.7.0/libdoc/net/http/rdoc/Net/HTTP.html +[rubygems]: https://rubygems.org +[repo]: https://github.com/lostisland/faraday-net_http +[license]: https://github.com/lostisland/faraday-net_http/blob/main/LICENSE.md +[code-of-conduct]: https://github.com/lostisland/faraday-net_http/blob/main/CODE_OF_CONDUCT.md diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-net_http-3.4.2/lib/faraday/adapter/net_http.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-net_http-3.4.2/lib/faraday/adapter/net_http.rb new file mode 100644 index 0000000..871bd86 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-net_http-3.4.2/lib/faraday/adapter/net_http.rb @@ -0,0 +1,206 @@ +# frozen_string_literal: true + +begin + require 'net/https' +rescue LoadError + warn 'Warning: no such file to load -- net/https. ' \ + 'Make sure openssl is installed if you want ssl support' + require 'net/http' +end +require 'zlib' + +module Faraday + class Adapter + class NetHttp < Faraday::Adapter + exceptions = [ + IOError, + Errno::EADDRNOTAVAIL, + Errno::EALREADY, + Errno::ECONNABORTED, + Errno::ECONNREFUSED, + Errno::ECONNRESET, + Errno::EHOSTUNREACH, + Errno::EINVAL, + Errno::ENETUNREACH, + Errno::EPIPE, + Net::HTTPBadResponse, + Net::HTTPHeaderSyntaxError, + Net::ProtocolError, + SocketError, + Zlib::GzipFile::Error + ] + + exceptions << ::OpenSSL::SSL::SSLError if defined?(::OpenSSL::SSL::SSLError) + exceptions << ::Net::OpenTimeout if defined?(::Net::OpenTimeout) + + NET_HTTP_EXCEPTIONS = exceptions.freeze + + def initialize(app = nil, opts = {}, &block) + @ssl_cert_store = nil + super(app, opts, &block) + end + + def build_connection(env) + net_http_connection(env).tap do |http| + configure_ssl(http, env[:ssl]) if env[:url].scheme == 'https' && env[:ssl] + configure_request(http, env[:request]) + end + end + + def net_http_connection(env) + proxy = env[:request][:proxy] + port = env[:url].port || (env[:url].scheme == 'https' ? 443 : 80) + if proxy + Net::HTTP.new(env[:url].hostname, port, + proxy[:uri].hostname, proxy[:uri].port, + proxy[:user], proxy[:password], + nil, proxy[:uri].scheme == 'https') + else + Net::HTTP.new(env[:url].hostname, port, nil) + end + end + + def call(env) + super + connection(env) do |http| + perform_request(http, env) + rescue *NET_HTTP_EXCEPTIONS => e + raise Faraday::SSLError, e if defined?(OpenSSL) && e.is_a?(OpenSSL::SSL::SSLError) + + raise Faraday::ConnectionFailed, e + end + @app.call env + rescue Timeout::Error, Errno::ETIMEDOUT => e + raise Faraday::TimeoutError, e + end + + private + + def create_request(env) + request = Net::HTTPGenericRequest.new \ + env[:method].to_s.upcase, # request method + !!env[:body], # is there request body + env[:method] != :head, # is there response body + env[:url].request_uri, # request uri path + env[:request_headers] # request headers + + if env[:body].respond_to?(:read) + request.body_stream = env[:body] + else + request.body = env[:body] + end + request + end + + def perform_request(http, env) + if env.stream_response? + http_response = env.stream_response do |&on_data| + request_with_wrapped_block(http, env, &on_data) + end + http_response.body = nil + else + http_response = request_with_wrapped_block(http, env) + end + env.response_body = encoded_body(http_response) + env.response.finish(env) + http_response + end + + def request_with_wrapped_block(http, env, &block) + # Must use Net::HTTP#start and pass it a block otherwise the server's + # TCP socket does not close correctly. + http.start do |opened_http| + opened_http.request create_request(env) do |response| + save_http_response(env, response) + + response.read_body(&block) if block_given? + end + end + end + + def save_http_response(env, http_response) + save_response( + env, http_response.code.to_i, nil, nil, http_response.message, finished: false + ) do |response_headers| + http_response.each_header do |key, value| + response_headers[key] = value + end + end + end + + def configure_ssl(http, ssl) + http.use_ssl = true if http.respond_to?(:use_ssl=) + + http.verify_mode = ssl_verify_mode(ssl) + http.cert_store = ssl_cert_store(ssl) + + cert, *extra_chain_cert = ssl[:client_cert] + http.cert = cert if cert + http.extra_chain_cert = extra_chain_cert if extra_chain_cert.any? + + http.key = ssl[:client_key] if ssl[:client_key] + http.ca_file = ssl[:ca_file] if ssl[:ca_file] + http.ca_path = ssl[:ca_path] if ssl[:ca_path] + http.verify_depth = ssl[:verify_depth] if ssl[:verify_depth] + http.ssl_version = ssl[:version] if ssl[:version] + http.min_version = ssl[:min_version] if ssl[:min_version] + http.max_version = ssl[:max_version] if ssl[:max_version] + http.verify_hostname = ssl[:verify_hostname] if verify_hostname_enabled?(http, ssl) + http.ciphers = ssl[:ciphers] if ssl[:ciphers] + end + + def configure_request(http, req) + if (sec = request_timeout(:read, req)) + http.read_timeout = sec + end + + if (sec = http.respond_to?(:write_timeout=) && + request_timeout(:write, req)) + http.write_timeout = sec + end + + if (sec = request_timeout(:open, req)) + http.open_timeout = sec + end + + # Only set if Net::Http supports it, since Ruby 2.5. + http.max_retries = 0 if http.respond_to?(:max_retries=) + + @config_block&.call(http) + end + + def ssl_cert_store(ssl) + return ssl[:cert_store] if ssl[:cert_store] + + # Use the default cert store by default, i.e. system ca certs + @ssl_cert_store ||= OpenSSL::X509::Store.new.tap(&:set_default_paths) + end + + def ssl_verify_mode(ssl) + ssl[:verify_mode] || begin + if ssl.fetch(:verify, true) + OpenSSL::SSL::VERIFY_PEER + else + OpenSSL::SSL::VERIFY_NONE + end + end + end + + def encoded_body(http_response) + body = http_response.body || +'' + /\bcharset=([^;]+)/.match(http_response['Content-Type']) do |match| + content_charset = ::Encoding.find(match[1].strip) + body = body.dup if body.frozen? + body.force_encoding(content_charset) + rescue ArgumentError + nil + end + body + end + + def verify_hostname_enabled?(http, ssl) + http.respond_to?(:verify_hostname=) && ssl.key?(:verify_hostname) + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-net_http-3.4.2/lib/faraday/net_http.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-net_http-3.4.2/lib/faraday/net_http.rb new file mode 100644 index 0000000..e4048e8 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-net_http-3.4.2/lib/faraday/net_http.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +require 'faraday/adapter/net_http' +require 'faraday/net_http/version' + +module Faraday + module NetHttp + Faraday::Adapter.register_middleware(net_http: Faraday::Adapter::NetHttp) + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/faraday-net_http-3.4.2/lib/faraday/net_http/version.rb b/vendor/bundle/ruby/3.3.0/gems/faraday-net_http-3.4.2/lib/faraday/net_http/version.rb new file mode 100644 index 0000000..534fbb8 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/faraday-net_http-3.4.2/lib/faraday/net_http/version.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +module Faraday + module NetHttp + VERSION = '3.4.2' + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/.github/workflows/ci.yml b/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/.github/workflows/ci.yml new file mode 100644 index 0000000..fc3d489 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/.github/workflows/ci.yml @@ -0,0 +1,27 @@ +name: ci + +on: + - pull_request + - push + +jobs: + build: + strategy: + fail-fast: false + matrix: + ruby: + - 2.7 + - '3.0' + - 3.1 + - 3.2 + - 3.3 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true # 'bundle install' and cache gems + - name: Run rake + run: bundle exec rake diff --git a/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/.gitignore b/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/.gitignore new file mode 100644 index 0000000..134a870 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/.gitignore @@ -0,0 +1,15 @@ +# See http://help.github.com/ignore-files/ for more about ignoring files. +# +# If you find yourself ignoring temporary files generated by your text editor +# or operating system, you probably want to add a global ignore instead: +# git config --global core.excludesfile ~/.gitignore_global + +# Ignore bundler config +/.bundle +/doc +/.yardoc +/Gemfile.lock + +*.swp +*.bak +*.gem diff --git a/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/.rspec b/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/.rspec new file mode 100644 index 0000000..4e1e0d2 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/.rspec @@ -0,0 +1 @@ +--color diff --git a/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/.rubocop.yml b/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/.rubocop.yml new file mode 100644 index 0000000..7668a83 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/.rubocop.yml @@ -0,0 +1,42 @@ +require: rubocop-rspec +AllCops: + TargetRubyVersion: 2.0 # always the lowest version we support +Metrics/PerceivedComplexity: + Enabled: false +Metrics/CyclomaticComplexity: + Enabled: false +Metrics/MethodLength: + Enabled: false +Metrics/AbcSize: + Enabled: false +Layout/LineLength: + Enabled: false +Metrics/ClassLength: + Enabled: false +Metrics/BlockLength: + Enabled: false +Metrics/ModuleLength: + Enabled: false +Style/CaseLikeIf: + Enabled: false +Style/Documentation: + Enabled: false +Style/FrozenStringLiteralComment: + Enabled: true + EnforcedStyle: always +Style/OptionalBooleanParameter: + Enabled: false +Style/NumericPredicate: + Enabled: false +Style/RedundantFreeze: + Enabled: false +Style/RedundantReturn: + Enabled: false +RSpec/ExampleLength: + Enabled: false +RSpec/DescribeClass: + Enabled: false +RSpec/SpecFilePathFormat: + Enabled: false +RSpec/NoExpectationExample: + Enabled: false diff --git a/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/.travis.yml b/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/.travis.yml new file mode 100644 index 0000000..3386da2 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/.travis.yml @@ -0,0 +1,11 @@ +sudo: false +language: ruby +cache: bundler +rvm: + - 2.0.0 + - 2.1.10 + - 2.2.8 + - 2.3.4 + - 2.4.2 + - 2.5.3 + - 2.6.0 diff --git a/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/.yardopts b/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/.yardopts new file mode 100644 index 0000000..22ce944 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/.yardopts @@ -0,0 +1 @@ +--no-private diff --git a/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/Gemfile b/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/Gemfile new file mode 100644 index 0000000..0c4b9b6 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/Gemfile @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +source 'https://rubygems.org' +gemspec + +group :test do + gem 'rake' +end diff --git a/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/LICENSE b/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/LICENSE new file mode 100644 index 0000000..9adfb9d --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2012 Liu Fengyun + +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/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/README.md b/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/README.md new file mode 100644 index 0000000..b0ac072 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/README.md @@ -0,0 +1,323 @@ +# Hashdiff [![Build Status](https://github.com/liufengyun/hashdiff/workflows/ci/badge.svg)](https://github.com/liufengyun/hashdiff/actions?query=workflow%3Aci) [![Gem Version](https://badge.fury.io/rb/hashdiff.svg)](http://badge.fury.io/rb/hashdiff) + +Hashdiff is a ruby library to compute the smallest difference between two hashes. + +It also supports comparing two arrays. + +Hashdiff does not monkey-patch any existing class. All features are contained inside the `Hashdiff` module. + +**Docs**: [Documentation](http://rubydoc.info/gems/hashdiff) + + +__WARNING__: Don't use the library for comparing large arrays, say ~10K (see #49). + +## Why Hashdiff? + +Given two Hashes A and B, sometimes you face the question: what's the smallest modification that can be made to change A into B? + +An algorithm that responds to this question has to do following: + +* Generate a list of additions, deletions and changes, so that `A + ChangeSet = B` and `B - ChangeSet = A`. +* Compute recursively -- Arrays and Hashes may be nested arbitrarily in A or B. +* Compute the smallest change -- it should recognize similar child Hashes or child Arrays between A and B. + +Hashdiff answers the question above using an opinionated approach: + +* Hash can be represented as a list of (dot-syntax-path, value) pairs. For example, `{a:[{c:2}]}` can be represented as `["a[0].c", 2]`. +* The change set can be represented using the dot-syntax representation. For example, `[['-', 'b.x', 3], ['~', 'b.z', 45, 30], ['+', 'b.y', 3]]`. +* It compares Arrays using the [LCS(longest common subsequence)](http://en.wikipedia.org/wiki/Longest_common_subsequence_problem) algorithm. +* It recognizes similar Hashes in an Array using a similarity value (0 < similarity <= 1). + +## Usage + +To use the gem, add the following to your Gemfile: + +```Ruby +gem 'hashdiff' +``` + +## Quick Start + +### Diff + +Two simple hashes: + +```ruby +a = {a:3, b:2} +b = {} + +diff = Hashdiff.diff(a, b) +diff.should == [['-', 'a', 3], ['-', 'b', 2]] +``` + +More complex hashes: + +```ruby +a = {a:{x:2, y:3, z:4}, b:{x:3, z:45}} +b = {a:{y:3}, b:{y:3, z:30}} + +diff = Hashdiff.diff(a, b) +diff.should == [['-', 'a.x', 2], ['-', 'a.z', 4], ['-', 'b.x', 3], ['~', 'b.z', 45, 30], ['+', 'b.y', 3]] +``` + +Arrays in hashes: + +```ruby +a = {a:[{x:2, y:3, z:4}, {x:11, y:22, z:33}], b:{x:3, z:45}} +b = {a:[{y:3}, {x:11, z:33}], b:{y:22}} + +diff = Hashdiff.best_diff(a, b) +diff.should == [['-', 'a[0].x', 2], ['-', 'a[0].z', 4], ['-', 'a[1].y', 22], ['-', 'b.x', 3], ['-', 'b.z', 45], ['+', 'b.y', 22]] +``` + +### Patch + +patch example: + +```ruby +a = {'a' => 3} +b = {'a' => {'a1' => 1, 'a2' => 2}} + +diff = Hashdiff.diff(a, b) +Hashdiff.patch!(a, diff).should == b +``` + +unpatch example: + +```ruby +a = [{'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5}, {'x' => 5, 'y' => 6, 'z' => 3}, 1] +b = [1, {'a' => 1, 'b' => 2, 'c' => 3, 'e' => 5}] + +diff = Hashdiff.diff(a, b) # diff two array is OK +Hashdiff.unpatch!(b, diff).should == a +``` + +### Options + +The following options are available: `:delimiter`, `:similarity`, `:strict`, `:ignore_keys`, +`:indifferent`, `:numeric_tolerance`, `:strip`, `:case_insensitive`, `:array_path`, +`:use_lcs`, and `:preserve_key_order` + +#### `:delimiter` + +You can specify `:delimiter` to be something other than the default dot. For example: + +```ruby +a = {a:{x:2, y:3, z:4}, b:{x:3, z:45}} +b = {a:{y:3}, b:{y:3, z:30}} + +diff = Hashdiff.diff(a, b, delimiter: '\t') +diff.should == [['-', 'a\tx', 2], ['-', 'a\tz', 4], ['-', 'b\tx', 3], ['~', 'b\tz', 45, 30], ['+', 'b\ty', 3]] +``` + +#### `:similarity` + +In cases where you have similar hash objects in arrays, you can pass a custom value for `:similarity` instead of the default `0.8`. This is interpreted as a ratio of similarity (default is 80% similar, whereas `:similarity => 0.5` would look for at least a 50% similarity). + +#### `:strict` + +The `:strict` option, which defaults to `true`, specifies whether numeric types are compared on type as well as value. By default, an Integer will never be equal to a Float (e.g. 4 != 4.0). Setting `:strict` to false makes the comparison looser (e.g. 4 == 4.0). + +#### `:ignore_keys` + +The `:ignore_keys` option allows you to specify one or more keys to ignore, which defaults to `[]` (none). Ignored keys are ignored at all levels in both hashes. For example: + +```ruby +a = { a: 4, g: 0, b: { a: 5, c: 6, e: 1 } } +b = { b: { a: 7, c: 3, f: 1 }, d: 8 } +diff = Hashdiff.diff(a, b, ignore_keys: %i[a f]) +diff.should == [['-', 'g', 0], ['-', 'b.e', 1], ['~', 'b.c', 6, 3], ['+', 'd', 8]] +``` +If you wish instead to ignore keys at a particlar level you should +use a [custom comparison method](https://github.com/liufengyun/hashdiff#specifying-a-custom-comparison-method) instead. For example to diff only at the 2nd level of both hashes: + +```ruby +a = { a: 4, g: 0, b: { a: 5, c: 6, e: 1 } } +b = { b: { a: 7, c: 3, f: 1 }, d: 8 } +diff = Hashdiff.diff(a, b) do |path, _e, _a| + arr = path.split('.') + true if %w[a f].include?(arr.last) && arr.size == 2 # note '.' is the default delimiter +end +diff.should == [['-', 'a', 4], ['-', 'g', 0], ['-', 'b.e', 1], ['~', 'b.c', 6, 3], ['+', 'd', 8]] +``` + +#### `:indifferent` + +The `:indifferent` option, which defaults to `false`, specifies whether to treat hash keys indifferently. Setting `:indifferent` to true has the effect of ignoring differences between symbol keys (ie. {a: 1} ~= {'a' => 1}) + +#### `:numeric_tolerance` + +The :numeric_tolerance option allows for a small numeric tolerance. + +```ruby +a = {x:5, y:3.75, z:7} +b = {x:6, y:3.76, z:7} + +diff = Hashdiff.diff(a, b, numeric_tolerance: 0.1) +diff.should == [["~", "x", 5, 6]] +``` + +#### `:strip` + +The :strip option strips all strings before comparing. + +```ruby +a = {x:5, s:'foo '} +b = {x:6, s:'foo'} + +diff = Hashdiff.diff(a, b, numeric_tolerance: 0.1, strip: true) +diff.should == [["~", "x", 5, 6]] +``` + +#### `:case_insensitive` + +The :case_insensitive option makes string comparisons ignore case. + +```ruby +a = {x:5, s:'FooBar'} +b = {x:6, s:'foobar'} + +diff = Hashdiff.diff(a, b, numeric_tolerance: 0.1, case_insensitive: true) +diff.should == [["~", "x", 5, 6]] +``` + +#### `:array_path` + +The :array_path option represents the path of the diff in an array rather than +a string. This can be used to show differences in between hash key types and +is useful for `patch!` when used on hashes without string keys. + +```ruby +a = {x:5} +b = {'x'=>6} + +diff = Hashdiff.diff(a, b, array_path: true) +diff.should == [['-', [:x], 5], ['+', ['x'], 6]] +``` + +For cases where there are arrays in paths their index will be added to the path. +```ruby +a = {x:[0,1]} +b = {x:[0,2]} + +diff = Hashdiff.diff(a, b, array_path: true) +diff.should == [["-", [:x, 1], 1], ["+", [:x, 1], 2]] +``` + +This shouldn't cause problems if you are comparing an array with a hash: + +```ruby +a = {x:{0=>1}} +b = {x:[1]} + +diff = Hashdiff.diff(a, b, array_path: true) +diff.should == [["~", [:x], {0=>1}, [1]]] +``` + +#### `:use_lcs` + +The :use_lcs option is used to specify whether a +[Longest common subsequence](https://en.wikipedia.org/wiki/Longest_common_subsequence_problem) +(LCS) algorithm is used to determine differences in arrays. This defaults to +`true` but can be changed to `false` for significantly faster array comparisons +(O(n) complexity rather than O(n2) for LCS). + +When :use_lcs is false the results of array comparisons have a tendency to +show changes at indexes rather than additions and subtractions when :use_lcs is +true. + +Note, currently the :similarity option has no effect when :use_lcs is false. + +```ruby +a = {x: [0, 1, 2]} +b = {x: [0, 2, 2, 3]} + +diff = Hashdiff.diff(a, b, use_lcs: false) +diff.should == [["~", "x[1]", 1, 2], ["+", "x[3]", 3]] +``` + +#### `:preserve_key_order` + +By default, the change set is ordered by operation type: deletions (-) first, then updates (~), and finally additions (+). +Within each operation group, keys are sorted alphabetically: + +```ruby +a = {d: 1, c: 1, a: 1} +b = {d: 2, b: 2, a: 2} + +diff = Hashdiff.diff(a, b) +diff.should == [["-", "c", 1], ["~", "a", 1, 2], ["~", "d", 1, 2], ["+", "b", 2]] +``` + +Setting :preserve_key_order to true processes keys in the order they appear in the first hash. +Keys that only exist in the second hash are appended in their original order: + +```ruby +a = {d: 1, c: 1, a: 1} +b = {d: 2, b: 2, a: 2} + +diff = Hashdiff.diff(a, b, preserve_key_order: true) +diff.should == [["~", "d", 1, 2], ["-", "c", 1], ["~", "a", 1, 2], ["+", "b", 2]] +``` + +#### Specifying a custom comparison method + +It's possible to specify how the values of a key should be compared. + +```ruby +a = {a:'car', b:'boat', c:'plane'} +b = {a:'bus', b:'truck', c:' plan'} + +diff = Hashdiff.diff(a, b) do |path, obj1, obj2| + case path + when /a|b|c/ + obj1.length == obj2.length + end +end + +diff.should == [['~', 'b', 'boat', 'truck']] +``` + +The yielded params of the comparison block is `|path, obj1, obj2|`, in which path is the key (or delimited compound key) to the value being compared. When comparing elements in array, the path is with the format `array[*]`. For example: + +```ruby +a = {a:'car', b:['boat', 'plane'] } +b = {a:'bus', b:['truck', ' plan'] } + +diff = Hashdiff.diff(a, b) do |path, obj1, obj2| + case path + when 'b[*]' + obj1.length == obj2.length + end +end + +diff.should == [["~", "a", "car", "bus"], ["~", "b[1]", "plane", " plan"], ["-", "b[0]", "boat"], ["+", "b[0]", "truck"]] +``` + +When a comparison block is given, it'll be given priority over other specified options. If the block returns value other than `true` or `false`, then the two values will be compared with other specified options. + +When used in conjunction with the `array_path` option, the path passed in as an argument will be an array. When determining the ordering of an array a key of `"*"` will be used in place of the `key[*]` field. It is possible, if you have hashes with integer or `"*"` keys, to have problems distinguishing between arrays and hashes - although this shouldn't be an issue unless your data is very difficult to predict and/or your custom rules are very specific. + +#### Sorting arrays before comparison + +An order difference alone between two arrays can create too many diffs to be useful. Consider sorting them prior to diffing. + +```ruby +a = {a:'car', b:['boat', 'plane'] } +b = {a:'car', b:['plane', 'boat'] } + +Hashdiff.diff(a, b).should == [["+", "b[0]", "plane"], ["-", "b[2]", "plane"]] + +b[:b].sort! + +Hashdiff.diff(a, b).should == [] +``` + +## Maintainers + +- Krzysztof Rybka ([@krzysiek1507](https://github.com/krzysiek1507)) +- Fengyun Liu ([@liufengyun](https://github.com/liufengyun)) + +## License + +Hashdiff is distributed under the MIT-LICENSE. diff --git a/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/Rakefile b/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/Rakefile new file mode 100644 index 0000000..b78b4a2 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/Rakefile @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +$LOAD_PATH.push File.expand_path('lib', __dir__) + +require 'rubocop/rake_task' + +require 'bundler' +Bundler::GemHelper.install_tasks + +require 'rspec/core/rake_task' + +RuboCop::RakeTask.new + +task default: %w[spec rubocop] + +RSpec::Core::RakeTask.new(:spec) do |spec| + spec.pattern = './spec/**/*_spec.rb' +end diff --git a/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/changelog.md b/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/changelog.md new file mode 100644 index 0000000..bd2d4ea --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/changelog.md @@ -0,0 +1,127 @@ +# Change Log + +## v1.2.1 2025-09-06 + +* Use HTTPS for the source in the Gemfile [#101](https://github.com/liufengyun/hashdiff/issues/101) ([@krzysiek1507](https://github.com/krzysiek1507)) + +## v1.2.0 2025-5-20 + +* Added :preserve_key_order option to maintain original hash key order [#99](https://github.com/liufengyun/hashdiff/issues/99) ([@robkiessling](https://github.com/robkiessling)) + +## v1.1.2 2024-11-12 + +* Fix bundler cache [#96](https://github.com/liufengyun/hashdiff/issues/96) ([@olleolleolle](https://github.com/olleolleolle)) +* Quote the '3.0' in YAML [#95](https://github.com/liufengyun/hashdiff/issues/95) ([@olleolleolle](https://github.com/olleolleolle)) +* Fix version in source code [#97](https://github.com/liufengyun/hashdiff/issues/97) ([@liufengyun](https://github.com/liufengyun)) + +## v1.1.1 2024-08-02 + +* Fix bug in ignore_keys option [#88](https://github.com/liufengyun/hashdiff/issues/88) ([@Matzfan](https://github.com/Matzfan)) +* Exclude spec files from gem package [#94](https://github.com/liufengyun/hashdiff/issues/94) ([@amatsuda](https://github.com/amatsuda)) + +## v1.1.0 2023-12-14 + +* Add ignore_keys option ([#86](https://github.com/liufengyun/hashdiff/issues/86) [@Matzfan](https://github.com/Matzfan)) +* Remove pinned version of rake < 11 +* Bump rspec dep ~> 3.5 +* Bump rubocop dep >= 1.52.1 +* Bump rubocop-rspec dep > 1.16.0 + +## v1.0.1 2020-02-25 + +* Add indifferent option + +## v1.0.0 2019-06-06 + +* Fix typo in readme ([#72](https://github.com/liufengyun/hashdiff/issues/72) [@koic](https://github.com/koic)) +* Fix Rubocop offence ([#73](https://github.com/liufengyun/hashdiff/issues/73) [@koic](https://github.com/koic)) +* Bumps version to v1.0.0 ([#74](https://github.com/liufengyun/hashdiff/issues/74) [@jfelchner](https://github.com/jfelchner)) + +## v1.0.0.beta1 2019-06-06 + +* fix warnings in ci ([#69](https://github.com/liufengyun/hashdiff/issues/69) [@y-yagi](https://github.com/y-yagi)) +* drop warnings of the constant change ([#70](https://github.com/liufengyun/hashdiff/issues/70) [@jfelchner](https://github.com/jfelchner)) + +## v0.4.0 2019-05-28 + +* refactoring ([#56](https://github.com/liufengyun/hashdiff/issues/56) [#57](https://github.com/liufengyun/hashdiff/issues/57) [#59](https://github.com/liufengyun/hashdiff/issues/59) [#61](https://github.com/liufengyun/hashdiff/issues/61) [@krzysiek1507](https://github.com/krzysiek1507)) +* fix typo in README ([#64](https://github.com/liufengyun/hashdiff/issues/64) [@pboling](https://github.com/pboling)) +* change HashDiff to Hashdiff ([#65](https://github.com/liufengyun/hashdiff/issues/65) [@jfelchner](https://github.com/jfelchner)) + +## v0.3.9 2019-04-22 + +* Performance tweak (thanks [@krzysiek1507](https://github.com/krzysiek1507): [#51](https://github.com/liufengyun/hashdiff/issues/51) [#52](https://github.com/liufengyun/hashdiff/issues/52) [#53](https://github.com/liufengyun/hashdiff/issues/53)) + +## v0.3.8 2018-12-30 + +* Add Rubocop and drops Ruby 1.9 support [#47](https://github.com/liufengyun/hashdiff/issues/47) + +## v0.3.7 2017-10-08 + +* remove 1.8.7 support from gemspec [#39](https://github.com/liufengyun/hashdiff/issues/39) + +## v0.3.6 2017-08-22 + +* add option `use_lcs` [#35](https://github.com/liufengyun/hashdiff/issues/35) + +## v0.3.5 2017-08-06 + +* add option `array_path` [#34](https://github.com/liufengyun/hashdiff/issues/34) + +## v0.3.4 2017-05-01 + +* performance improvement of `#similar?` [#31](https://github.com/liufengyun/hashdiff/issues/31) + +## v0.3.2 2016-12-27 + +* replace `Fixnum` by `Integer` [#28](https://github.com/liufengyun/hashdiff/issues/28) + +## v0.3.1 2016-11-24 + +* fix an error when a hash has mixed types [#26](https://github.com/liufengyun/hashdiff/issues/26) + +## v0.3.0 2016-2-11 + +* support `:case_insensitive` option + +## v0.2.3 2015-11-5 + +* improve performance of LCS algorithm [#12](https://github.com/liufengyun/hashdiff/issues/12) + +## v0.2.2 2014-10-6 + +* make library 1.8.7 compatible + +## v0.2.1 2014-7-13 + +* yield added/deleted keys for custom comparison + +## v0.2.0 2014-3-29 + +* support custom comparison blocks +* support `:strip`, `:numeric_tolerance` and `:strict` options + +## v0.1.0 2013-8-25 + +* use options for parameters `:delimiter` and `:similarity` in interfaces + +## v0.0.6 2013-3-2 + +* Add parameter for custom property-path delimiter. + +## v0.0.5 2012-7-1 + +* fix a bug in judging whehter two objects are similiar. +* add more spec test for `.best_diff` + +## v0.0.4 2012-6-24 + +Main changes in this version is to output the whole object in addition & deletion, instead of recursely add/deletes the object. + +For example, `diff({a:2, c:[4, 5]}, {a:2}) will generate following output: + + [['-', 'c', [4, 5]]] + +instead of following: + + [['-', 'c[0]', 4], ['-', 'c[1]', 5], ['-', 'c', []]] diff --git a/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/hashdiff.gemspec b/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/hashdiff.gemspec new file mode 100644 index 0000000..001af53 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/hashdiff.gemspec @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +$LOAD_PATH << File.expand_path('lib', __dir__) +require 'hashdiff/version' + +Gem::Specification.new do |s| + s.name = 'hashdiff' + s.version = Hashdiff::VERSION + s.license = 'MIT' + s.summary = ' Hashdiff is a diff lib to compute the smallest difference between two hashes. ' + s.description = ' Hashdiff is a diff lib to compute the smallest difference between two hashes. ' + + s.files = `git ls-files`.split("\n").grep_v(%r{^spec/}) + s.test_files = `git ls-files -- Appraisals {spec}/*`.split("\n") + + s.require_paths = ['lib'] + s.required_ruby_version = Gem::Requirement.new('>= 2.0.0') + + s.authors = ['Liu Fengyun'] + s.email = ['liufengyunchina@gmail.com'] + + s.homepage = 'https://github.com/liufengyun/hashdiff' + + s.add_development_dependency('bluecloth') + s.add_development_dependency('rspec', '~> 3.5') + s.add_development_dependency('rubocop', '>= 1.52.1') # earliest version that works with Ruby 3.3 + s.add_development_dependency('rubocop-rspec', '> 1.16.0') # https://github.com/rubocop/rubocop-rspec/issues/461 + s.add_development_dependency('yard') + + if s.respond_to?(:metadata) + s.metadata = { + 'bug_tracker_uri' => 'https://github.com/liufengyun/hashdiff/issues', + 'changelog_uri' => 'https://github.com/liufengyun/hashdiff/blob/master/changelog.md', + 'documentation_uri' => 'https://www.rubydoc.info/gems/hashdiff', + 'homepage_uri' => 'https://github.com/liufengyun/hashdiff', + 'source_code_uri' => 'https://github.com/liufengyun/hashdiff' + } + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/lib/hashdiff.rb b/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/lib/hashdiff.rb new file mode 100644 index 0000000..d44deb3 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/lib/hashdiff.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +require 'hashdiff/util' +require 'hashdiff/compare_hashes' +require 'hashdiff/lcs' +require 'hashdiff/lcs_compare_arrays' +require 'hashdiff/linear_compare_array' +require 'hashdiff/diff' +require 'hashdiff/patch' +require 'hashdiff/version' diff --git a/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/lib/hashdiff/compare_hashes.rb b/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/lib/hashdiff/compare_hashes.rb new file mode 100644 index 0000000..6422895 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/lib/hashdiff/compare_hashes.rb @@ -0,0 +1,101 @@ +# frozen_string_literal: true + +module Hashdiff + # @private + # Used to compare hashes + class CompareHashes + class << self + def call(obj1, obj2, opts = {}) + return [] if obj1.empty? && obj2.empty? + + obj1_keys = obj1.keys + obj2_keys = obj2.keys + obj1_lookup = {} + obj2_lookup = {} + + if opts[:indifferent] + obj1_lookup = obj1_keys.each_with_object({}) { |k, h| h[k.to_s] = k } + obj2_lookup = obj2_keys.each_with_object({}) { |k, h| h[k.to_s] = k } + obj1_keys = obj1_keys.map { |k| k.is_a?(Symbol) ? k.to_s : k } + obj2_keys = obj2_keys.map { |k| k.is_a?(Symbol) ? k.to_s : k } + end + + added_keys = obj2_keys - obj1_keys + common_keys = obj1_keys & obj2_keys + deleted_keys = obj1_keys - obj2_keys + + result = [] + + opts[:ignore_keys].each do |k| + added_keys.delete k + common_keys.delete k + deleted_keys.delete k + end + + handle_key = lambda do |k, type| + case type + when :deleted + # add deleted properties + k = opts[:indifferent] ? obj1_lookup[k] : k + change_key = Hashdiff.prefix_append_key(opts[:prefix], k, opts) + custom_result = Hashdiff.custom_compare(opts[:comparison], change_key, obj1[k], nil) + + if custom_result + result.concat(custom_result) + else + result << ['-', change_key, obj1[k]] + end + when :common + # recursive comparison for common keys + prefix = Hashdiff.prefix_append_key(opts[:prefix], k, opts) + + k1 = opts[:indifferent] ? obj1_lookup[k] : k + k2 = opts[:indifferent] ? obj2_lookup[k] : k + result.concat(Hashdiff.diff(obj1[k1], obj2[k2], opts.merge(prefix: prefix))) + when :added + # added properties + change_key = Hashdiff.prefix_append_key(opts[:prefix], k, opts) + + k = opts[:indifferent] ? obj2_lookup[k] : k + custom_result = Hashdiff.custom_compare(opts[:comparison], change_key, nil, obj2[k]) + + if custom_result + result.concat(custom_result) + else + result << ['+', change_key, obj2[k]] + end + else + raise "Invalid type: #{type}" + end + end + + if opts[:preserve_key_order] + # Building lookups to speed up key classification + added_keys_lookup = added_keys.each_with_object({}) { |k, h| h[k] = true } + common_keys_lookup = common_keys.each_with_object({}) { |k, h| h[k] = true } + deleted_keys_lookup = deleted_keys.each_with_object({}) { |k, h| h[k] = true } + + # Iterate through all keys, preserving obj1's key order and appending any new keys from obj2. Shared keys + # (found in both obj1 and obj2) follow obj1's order since uniq only keeps the first occurrence. + (obj1_keys + obj2_keys).uniq.each do |k| + if added_keys_lookup[k] + handle_key.call(k, :added) + elsif common_keys_lookup[k] + handle_key.call(k, :common) + elsif deleted_keys_lookup[k] + handle_key.call(k, :deleted) + end + end + else + # Keys are first grouped by operation type (deletions first, then changes, then additions), and then sorted + # alphabetically within each group. + deleted_keys.sort_by(&:to_s).each { |k| handle_key.call(k, :deleted) } + common_keys.sort_by(&:to_s).each { |k| handle_key.call(k, :common) } + added_keys.sort_by(&:to_s).each { |k| handle_key.call(k, :added) } + end + + result + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/lib/hashdiff/diff.rb b/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/lib/hashdiff/diff.rb new file mode 100644 index 0000000..8e24d16 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/lib/hashdiff/diff.rb @@ -0,0 +1,185 @@ +# frozen_string_literal: true + +module Hashdiff + # Best diff two objects, which tries to generate the smallest change set using different similarity values. + # + # Hashdiff.best_diff is useful in case of comparing two objects which include similar hashes in arrays. + # + # @param [Array, Hash] obj1 + # @param [Array, Hash] obj2 + # @param [Hash] options the options to use when comparing + # * :strict (Boolean) [true] whether numeric values will be compared on type as well as value. Set to false to allow comparing Integer, Float, BigDecimal to each other + # * :ignore_keys (Symbol, String or Array) [[]] a list of keys to ignore. No comparison is made for the specified key(s) in either hash + # * :indifferent (Boolean) [false] whether to treat hash keys indifferently. Set to true to ignore differences between symbol keys (ie. {a: 1} ~= {'a' => 1}) + # * :delimiter (String) ['.'] the delimiter used when returning nested key references + # * :numeric_tolerance (Numeric) [0] should be a positive numeric value. Value by which numeric differences must be greater than. By default, numeric values are compared exactly; with the :tolerance option, the difference between numeric values must be greater than the given value. + # * :strip (Boolean) [false] whether or not to call #strip on strings before comparing + # * :array_path (Boolean) [false] whether to return the path references for nested values in an array, can be used for patch compatibility with non string keys. + # * :use_lcs (Boolean) [true] whether or not to use an implementation of the Longest common subsequence algorithm for comparing arrays, produces better diffs but is slower. + # * :preserve_key_order (Boolean) [false] If false, operations are grouped by type (-, ~, then +) then by hash key alphabetically. If true, preserves the original key order from the first hash and appends new keys from the second hash in order. + # + # @yield [path, value1, value2] Optional block is used to compare each value, instead of default #==. If the block returns value other than true of false, then other specified comparison options will be used to do the comparison. + # + # @return [Array] an array of changes. + # e.g. [[ '+', 'a.b', '45' ], [ '-', 'a.c', '5' ], [ '~', 'a.x', '45', '63']] + # + # @example + # a = {'x' => [{'a' => 1, 'c' => 3, 'e' => 5}, {'y' => 3}]} + # b = {'x' => [{'a' => 1, 'b' => 2, 'e' => 5}] } + # diff = Hashdiff.best_diff(a, b) + # diff.should == [['-', 'x[0].c', 3], ['+', 'x[0].b', 2], ['-', 'x[1].y', 3], ['-', 'x[1]', {}]] + # + # @since 0.0.1 + def self.best_diff(obj1, obj2, options = {}, &block) + options[:comparison] = block if block_given? + + opts = { similarity: 0.3 }.merge!(options) + diffs1 = diff(obj1, obj2, opts) + count1 = count_diff diffs1 + + opts = { similarity: 0.5 }.merge!(options) + diffs2 = diff(obj1, obj2, opts) + count2 = count_diff diffs2 + + opts = { similarity: 0.8 }.merge!(options) + diffs3 = diff(obj1, obj2, opts) + count3 = count_diff diffs3 + + count, diffs = count1 < count2 ? [count1, diffs1] : [count2, diffs2] + count < count3 ? diffs : diffs3 + end + + # Compute the diff of two hashes or arrays + # + # @param [Array, Hash] obj1 + # @param [Array, Hash] obj2 + # @param [Hash] options the options to use when comparing + # * :strict (Boolean) [true] whether numeric values will be compared on type as well as value. Set to false to allow comparing Integer, Float, BigDecimal to each other + # * :ignore_keys (Symbol, String or Array) [[]] a list of keys to ignore. No comparison is made for the specified key(s) in either hash + # * :indifferent (Boolean) [false] whether to treat hash keys indifferently. Set to true to ignore differences between symbol keys (ie. {a: 1} ~= {'a' => 1}) + # * :similarity (Numeric) [0.8] should be between (0, 1]. Meaningful if there are similar hashes in arrays. See {best_diff}. + # * :delimiter (String) ['.'] the delimiter used when returning nested key references + # * :numeric_tolerance (Numeric) [0] should be a positive numeric value. Value by which numeric differences must be greater than. By default, numeric values are compared exactly; with the :tolerance option, the difference between numeric values must be greater than the given value. + # * :strip (Boolean) [false] whether or not to call #strip on strings before comparing + # * :array_path (Boolean) [false] whether to return the path references for nested values in an array, can be used for patch compatibility with non string keys. + # * :use_lcs (Boolean) [true] whether or not to use an implementation of the Longest common subsequence algorithm for comparing arrays, produces better diffs but is slower. + # * :preserve_key_order (Boolean) [false] If false, operations are grouped by type (-, ~, then +) then by hash key alphabetically. If true, preserves the original key order from the first hash and appends new keys from the second hash in order. + # + # + # @yield [path, value1, value2] Optional block is used to compare each value, instead of default #==. If the block returns value other than true of false, then other specified comparison options will be used to do the comparison. + # + # @return [Array] an array of changes. + # e.g. [[ '+', 'a.b', '45' ], [ '-', 'a.c', '5' ], [ '~', 'a.x', '45', '63']] + # + # @example + # a = {"a" => 1, "b" => {"b1" => 1, "b2" =>2}} + # b = {"a" => 1, "b" => {}} + # + # diff = Hashdiff.diff(a, b) + # diff.should == [['-', 'b.b1', 1], ['-', 'b.b2', 2]] + # + # @since 0.0.1 + def self.diff(obj1, obj2, options = {}, &block) + opts = { + prefix: '', + similarity: 0.8, + delimiter: '.', + strict: true, + ignore_keys: [], + indifferent: false, + strip: false, + numeric_tolerance: 0, + array_path: false, + use_lcs: true, + preserve_key_order: false + }.merge!(options) + + opts[:prefix] = [] if opts[:array_path] && opts[:prefix] == '' + + opts[:ignore_keys] = [*opts[:ignore_keys]] + + opts[:comparison] = block if block_given? + + # prefer to compare with provided block + result = custom_compare(opts[:comparison], opts[:prefix], obj1, obj2) + return result if result + + return [] if obj1.nil? && obj2.nil? + + return [['~', opts[:prefix], obj1, obj2]] if obj1.nil? || obj2.nil? + + return [['~', opts[:prefix], obj1, obj2]] unless comparable?(obj1, obj2, opts[:strict]) + + return LcsCompareArrays.call(obj1, obj2, opts) if obj1.is_a?(Array) && opts[:use_lcs] + + return LinearCompareArray.call(obj1, obj2, opts) if obj1.is_a?(Array) && !opts[:use_lcs] + + return CompareHashes.call(obj1, obj2, opts) if obj1.is_a?(Hash) + + return [] if compare_values(obj1, obj2, opts) + + [['~', opts[:prefix], obj1, obj2]] + end + + # @private + # + # diff array using LCS algorithm + def self.diff_array_lcs(arraya, arrayb, options = {}) + return [] if arraya.empty? && arrayb.empty? + + change_set = [] + + if arraya.empty? + arrayb.each_index do |index| + change_set << ['+', index, arrayb[index]] + end + + return change_set + end + + if arrayb.empty? + arraya.each_index do |index| + i = arraya.size - index - 1 + change_set << ['-', i, arraya[i]] + end + + return change_set + end + + opts = { + prefix: '', + similarity: 0.8, + delimiter: '.' + }.merge!(options) + + links = lcs(arraya, arrayb, opts) + + # yield common + yield links if block_given? + + # padding the end + links << [arraya.size, arrayb.size] + + last_x = -1 + last_y = -1 + links.each do |pair| + x, y = pair + + # remove from a, beginning from the end + (x > last_x + 1) && (x - last_x - 2).downto(0).each do |i| + change_set << ['-', last_y + i + 1, arraya[i + last_x + 1]] + end + + # add from b, beginning from the head + (y > last_y + 1) && 0.upto(y - last_y - 2).each do |i| + change_set << ['+', last_y + i + 1, arrayb[i + last_y + 1]] + end + + # update flags + last_x = x + last_y = y + end + + change_set + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/lib/hashdiff/lcs.rb b/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/lib/hashdiff/lcs.rb new file mode 100644 index 0000000..b23c3e8 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/lib/hashdiff/lcs.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +module Hashdiff + # @private + # + # caculate array difference using LCS algorithm + # http://en.wikipedia.org/wiki/Longest_common_subsequence_problem + def self.lcs(arraya, arrayb, options = {}) + return [] if arraya.empty? || arrayb.empty? + + opts = { similarity: 0.8 }.merge!(options) + + opts[:prefix] = prefix_append_array_index(opts[:prefix], '*', opts) + + a_start = b_start = 0 + a_finish = arraya.size - 1 + b_finish = arrayb.size - 1 + vector = [] + + lcs = [] + (b_start..b_finish).each do |bi| + lcs[bi] = [] + (a_start..a_finish).each do |ai| + if similar?(arraya[ai], arrayb[bi], opts) + topleft = (ai > 0) && (bi > 0) ? lcs[bi - 1][ai - 1][1] : 0 + lcs[bi][ai] = [:topleft, topleft + 1] + elsif (top = bi > 0 ? lcs[bi - 1][ai][1] : 0) + left = ai > 0 ? lcs[bi][ai - 1][1] : 0 + count = top > left ? top : left + + direction = if top > left + :top + elsif top < left + :left + elsif bi.zero? + :top + elsif ai.zero? + :left + else + :both + end + + lcs[bi][ai] = [direction, count] + end + end + end + + x = a_finish + y = b_finish + while (x >= 0) && (y >= 0) && (lcs[y][x][1] > 0) + if lcs[y][x][0] == :both + x -= 1 + elsif lcs[y][x][0] == :topleft + vector.insert(0, [x, y]) + x -= 1 + y -= 1 + elsif lcs[y][x][0] == :top + y -= 1 + elsif lcs[y][x][0] == :left + x -= 1 + end + end + + vector + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/lib/hashdiff/lcs_compare_arrays.rb b/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/lib/hashdiff/lcs_compare_arrays.rb new file mode 100644 index 0000000..1bae912 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/lib/hashdiff/lcs_compare_arrays.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module Hashdiff + # @private + # Used to compare arrays using the lcs algorithm + class LcsCompareArrays + class << self + def call(obj1, obj2, opts = {}) + result = [] + + changeset = Hashdiff.diff_array_lcs(obj1, obj2, opts) do |lcs| + # use a's index for similarity + lcs.each do |pair| + prefix = Hashdiff.prefix_append_array_index(opts[:prefix], pair[0], opts) + + result.concat(Hashdiff.diff(obj1[pair[0]], obj2[pair[1]], opts.merge(prefix: prefix))) + end + end + + changeset.each do |change| + next if change[0] != '-' && change[0] != '+' + + change_key = Hashdiff.prefix_append_array_index(opts[:prefix], change[1], opts) + + result << [change[0], change_key, change[2]] + end + + result + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/lib/hashdiff/linear_compare_array.rb b/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/lib/hashdiff/linear_compare_array.rb new file mode 100644 index 0000000..0b9623c --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/lib/hashdiff/linear_compare_array.rb @@ -0,0 +1,159 @@ +# frozen_string_literal: true + +module Hashdiff + # @private + # + # Used to compare arrays in a linear complexity, which produces longer diffs + # than using the lcs algorithm but is considerably faster + class LinearCompareArray + def self.call(old_array, new_array, options = {}) + instance = new(old_array, new_array, options) + instance.call + end + + def call + return [] if old_array.empty? && new_array.empty? + + self.old_index = 0 + self.new_index = 0 + # by comparing the array lengths we can expect that a number of items + # are either added or removed + self.expected_additions = new_array.length - old_array.length + + loop do + if extra_items_in_old_array? + append_deletion(old_array[old_index], old_index) + elsif extra_items_in_new_array? + append_addition(new_array[new_index], new_index) + else + compare_at_index + end + + self.old_index = old_index + 1 + self.new_index = new_index + 1 + break if iterated_through_both_arrays? + end + + changes + end + + private + + attr_reader :old_array, :new_array, :options, :additions, :deletions, :differences + attr_accessor :old_index, :new_index, :expected_additions + + def initialize(old_array, new_array, options) + @old_array = old_array + @new_array = new_array + @options = { prefix: '' }.merge!(options) + + @additions = [] + @deletions = [] + @differences = [] + end + + def extra_items_in_old_array? + old_index < old_array.length && new_index >= new_array.length + end + + def extra_items_in_new_array? + new_index < new_array.length && old_index >= old_array.length + end + + def iterated_through_both_arrays? + old_index >= old_array.length && new_index >= new_array.length + end + + def compare_at_index + difference = item_difference(old_array[old_index], new_array[new_index], old_index) + return if difference.empty? + + index_after_additions = index_of_match_after_additions + append_addititions_before_match(index_after_additions) + + index_after_deletions = index_of_match_after_deletions + append_deletions_before_match(index_after_deletions) + + match = index_after_additions || index_after_deletions + + append_differences(difference) unless match + end + + def item_difference(old_item, new_item, item_index) + prefix = Hashdiff.prefix_append_array_index(options[:prefix], item_index, options) + Hashdiff.diff(old_item, new_item, options.merge(prefix: prefix)) + end + + # look ahead in the new array to see if the current item appears later + # thereby having new items added + def index_of_match_after_additions + return unless expected_additions > 0 + + (1..expected_additions).each do |i| + next_difference = item_difference( + old_array[old_index], + new_array[new_index + i], + old_index + ) + + return new_index + i if next_difference.empty? + end + + nil + end + + # look ahead in the old array to see if the current item appears later + # thereby having items removed + def index_of_match_after_deletions + return unless expected_additions < 0 + + (1..(expected_additions.abs)).each do |i| + next_difference = item_difference( + old_array[old_index + i], + new_array[new_index], + old_index + ) + + return old_index + i if next_difference.empty? + end + + nil + end + + def append_addititions_before_match(match_index) + return unless match_index + + (new_index...match_index).each { |i| append_addition(new_array[i], i) } + self.expected_additions = expected_additions - (match_index - new_index) + self.new_index = match_index + end + + def append_deletions_before_match(match_index) + return unless match_index + + (old_index...match_index).each { |i| append_deletion(old_array[i], i) } + self.expected_additions = expected_additions + (match_index - new_index) + self.old_index = match_index + end + + def append_addition(item, index) + key = Hashdiff.prefix_append_array_index(options[:prefix], index, options) + additions << ['+', key, item] + end + + def append_deletion(item, index) + key = Hashdiff.prefix_append_array_index(options[:prefix], index, options) + deletions << ['-', key, item] + end + + def append_differences(difference) + differences.concat(difference) + end + + def changes + # this algorithm only allows there to be additions or deletions + # deletions are reverse so they don't change the index of earlier items + differences + additions + deletions.reverse + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/lib/hashdiff/patch.rb b/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/lib/hashdiff/patch.rb new file mode 100644 index 0000000..31541f3 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/lib/hashdiff/patch.rb @@ -0,0 +1,88 @@ +# frozen_string_literal: true + +# +# This module provides methods to diff two hash, patch and unpatch hash +# +module Hashdiff + # Apply patch to object + # + # @param [Hash, Array] obj the object to be patched, can be an Array or a Hash + # @param [Array] changes e.g. [[ '+', 'a.b', '45' ], [ '-', 'a.c', '5' ], [ '~', 'a.x', '45', '63']] + # @param [Hash] options supports following keys: + # * :delimiter (String) ['.'] delimiter string for representing nested keys in changes array + # + # @return the object after patch + # + # @since 0.0.1 + def self.patch!(obj, changes, options = {}) + delimiter = options[:delimiter] || '.' + + changes.each do |change| + parts = change[1] + parts = decode_property_path(parts, delimiter) unless parts.is_a?(Array) + + last_part = parts.last + + parent_node = node(obj, parts[0, parts.size - 1]) + + if change[0] == '+' + if parent_node.is_a?(Array) + parent_node.insert(last_part, change[2]) + else + parent_node[last_part] = change[2] + end + elsif change[0] == '-' + if parent_node.is_a?(Array) + parent_node.delete_at(last_part) + else + parent_node.delete(last_part) + end + elsif change[0] == '~' + parent_node[last_part] = change[3] + end + end + + obj + end + + # Unpatch an object + # + # @param [Hash, Array] obj the object to be unpatched, can be an Array or a Hash + # @param [Array] changes e.g. [[ '+', 'a.b', '45' ], [ '-', 'a.c', '5' ], [ '~', 'a.x', '45', '63']] + # @param [Hash] options supports following keys: + # * :delimiter (String) ['.'] delimiter string for representing nested keys in changes array + # + # @return the object after unpatch + # + # @since 0.0.1 + def self.unpatch!(obj, changes, options = {}) + delimiter = options[:delimiter] || '.' + + changes.reverse_each do |change| + parts = change[1] + parts = decode_property_path(parts, delimiter) unless parts.is_a?(Array) + + last_part = parts.last + + parent_node = node(obj, parts[0, parts.size - 1]) + + if change[0] == '+' + if parent_node.is_a?(Array) + parent_node.delete_at(last_part) + else + parent_node.delete(last_part) + end + elsif change[0] == '-' + if parent_node.is_a?(Array) + parent_node.insert(last_part, change[2]) + else + parent_node[last_part] = change[2] + end + elsif change[0] == '~' + parent_node[last_part] = change[2] + end + end + + obj + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/lib/hashdiff/util.rb b/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/lib/hashdiff/util.rb new file mode 100644 index 0000000..09ed84b --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/lib/hashdiff/util.rb @@ -0,0 +1,155 @@ +# frozen_string_literal: true + +module Hashdiff + # @private + # + # judge whether two objects are similar + def self.similar?(obja, objb, options = {}) + return compare_values(obja, objb, options) if !options[:comparison] && !any_hash_or_array?(obja, objb) + + count_a = count_nodes(obja) + count_b = count_nodes(objb) + + return true if (count_a + count_b).zero? + + opts = { similarity: 0.8 }.merge!(options) + + diffs = count_diff diff(obja, objb, opts) + + (1 - diffs / (count_a + count_b).to_f) >= opts[:similarity] + end + + # @private + # + # count node differences + def self.count_diff(diffs) + diffs.inject(0) do |sum, item| + old_change_count = count_nodes(item[2]) + new_change_count = count_nodes(item[3]) + sum + (old_change_count + new_change_count) + end + end + + # @private + # + # count total nodes for an object + def self.count_nodes(obj) + return 0 unless obj + + count = 0 + if obj.is_a?(Array) + obj.each { |e| count += count_nodes(e) } + elsif obj.is_a?(Hash) + obj.each_value { |v| count += count_nodes(v) } + else + return 1 + end + + count + end + + # @private + # + # decode property path into an array + # @param [String] path Property-string + # @param [String] delimiter Property-string delimiter + # + # e.g. "a.b[3].c" => ['a', 'b', 3, 'c'] + def self.decode_property_path(path, delimiter = '.') + path.split(delimiter).inject([]) do |memo, part| + if part =~ /^(.*)\[(\d+)\]$/ + if !Regexp.last_match(1).empty? + memo + [Regexp.last_match(1), Regexp.last_match(2).to_i] + else + memo + [Regexp.last_match(2).to_i] + end + else + memo + [part] + end + end + end + + # @private + # + # get the node of hash by given path parts + def self.node(hash, parts) + temp = hash + parts.each do |part| + temp = temp[part] + end + temp + end + + # @private + # + # check for equality or "closeness" within given tolerance + def self.compare_values(obj1, obj2, options = {}) + if options[:numeric_tolerance].is_a?(Numeric) && + obj1.is_a?(Numeric) && obj2.is_a?(Numeric) + return (obj1 - obj2).abs <= options[:numeric_tolerance] + end + + if options[:strip] == true + obj1 = obj1.strip if obj1.respond_to?(:strip) + obj2 = obj2.strip if obj2.respond_to?(:strip) + end + + if options[:case_insensitive] == true + obj1 = obj1.downcase if obj1.respond_to?(:downcase) + obj2 = obj2.downcase if obj2.respond_to?(:downcase) + end + + obj1 == obj2 + end + + # @private + # + # check if objects are comparable + def self.comparable?(obj1, obj2, strict = true) + return true if (obj1.is_a?(Array) || obj1.is_a?(Hash)) && obj2.is_a?(obj1.class) + return true if (obj2.is_a?(Array) || obj2.is_a?(Hash)) && obj1.is_a?(obj2.class) + return true if !strict && obj1.is_a?(Numeric) && obj2.is_a?(Numeric) + + obj1.is_a?(obj2.class) && obj2.is_a?(obj1.class) + end + + # @private + # + # try custom comparison + def self.custom_compare(method, key, obj1, obj2) + return unless method + + res = method.call(key, obj1, obj2) + + # nil != false here + return [['~', key, obj1, obj2]] if res == false + return [] if res == true + end + + def self.prefix_append_key(prefix, key, opts) + if opts[:array_path] + prefix + [key] + else + prefix.empty? ? key.to_s : "#{prefix}#{opts[:delimiter]}#{key}" + end + end + + def self.prefix_append_array_index(prefix, array_index, opts) + if opts[:array_path] + prefix + [array_index] + else + "#{prefix}[#{array_index}]" + end + end + + class << self + private + + # @private + # + # checks if both objects are Arrays or Hashes + def any_hash_or_array?(obja, objb) + obja.is_a?(Array) || obja.is_a?(Hash) || objb.is_a?(Array) || objb.is_a?(Hash) + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/lib/hashdiff/version.rb b/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/lib/hashdiff/version.rb new file mode 100644 index 0000000..b8151a4 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/hashdiff-1.2.1/lib/hashdiff/version.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +module Hashdiff + VERSION = '1.2.1'.freeze +end diff --git a/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/BSDL b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/BSDL new file mode 100644 index 0000000..66d9359 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/BSDL @@ -0,0 +1,22 @@ +Copyright (C) 1993-2013 Yukihiro Matsumoto. 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. diff --git a/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/CHANGES.md b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/CHANGES.md new file mode 100644 index 0000000..41264de --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/CHANGES.md @@ -0,0 +1,728 @@ +# Changes + +### Unreleased + +### 2025-12-11 (2.18.0) + +* Add `:allow_control_characters` parser options, to allow JSON strings containing unescaped ASCII control characters (e.g. newlines). + +### 2025-12-04 (2.17.1) + +* Fix a regression in parsing of unicode surogate pairs (`\uXX\uXX`) that could cause an invalid string to be returned. + +### 2025-12-03 (2.17.0) + +* Improve `JSON.load` and `JSON.unsafe_load` to allow passing options as second argument. +* Fix the parser to no longer ignore invalid escapes in strings. + Only `\"`, `\\`, `\b`, `\f`, `\n`, `\r`, `\t` and `\u` are valid JSON escapes. +* Fixed `JSON::Coder` to use the depth it was initialized with. +* On TruffleRuby, fix the generator to not call `to_json` on the return value of `as_json` for `Float::NAN`. +* Fixed handling of `state.depth`: when `to_json` changes `state.depth` but does not restore it, it is reset + automatically to its initial value. + In particular, when a `NestingError` is raised, `depth` is no longer equal to `max_nesting` after the call to + generate, and is reset to its initial value. Similarly when `to_json` raises an exception. + +### 2025-11-07 (2.16.0) + +* Deprecate `JSON::State#[]` and `JSON::State#[]=`. Consider using `JSON::Coder` instead. +* `JSON::Coder` now also yields to the block when encountering strings with invalid encoding. +* Fix GeneratorError messages to be UTF-8 encoded. +* Fix memory leak when `Exception` is raised, or `throw` is used during JSON generation. +* Optimized floating point number parsing by integrating the ryu algorithm (thanks to Josef Šimánek). +* Optimized numbers parsing using SWAR (thanks to Scott Myron). +* Optimized parsing of pretty printed documents using SWAR (thanks to Scott Myron). + +### 2025-10-25 (2.15.2) + +* Fix `JSON::Coder` to have one dedicated depth counter per invocation. + After encountering a circular reference in `JSON::Coder#dump`, any further `#dump` call would raise `JSON::NestingError`. + +### 2025-10-07 (2.15.1) + +* Fix incorrect escaping in the JRuby extension when encoding shared strings. + +### 2025-09-22 (2.15.0) + +* `JSON::Coder` callback now receive a second argument to convey whether the object is a hash key. +* Tuned the floating point number generator to not use scientific notation as aggressively. + +### 2025-09-18 (2.14.1) + +* Fix `IndexOutOfBoundsException` in the JRuby extension when encoding shared strings. + +### 2025-09-18 (2.14.0) + +* Add new `allow_duplicate_key` generator options. By default a warning is now emitted when a duplicated key is encountered. + In `json 3.0` an error will be raised. + ```ruby + >> Warning[:deprecated] = true + >> puts JSON.generate({ foo: 1, "foo" => 2 }) + (irb):2: warning: detected duplicate key "foo" in {foo: 1, "foo" => 2}. + This will raise an error in json 3.0 unless enabled via `allow_duplicate_key: true` + {"foo":1,"foo":2} + >> JSON.generate({ foo: 1, "foo" => 2 }, allow_duplicate_key: false) + detected duplicate key "foo" in {foo: 1, "foo" => 2} (JSON::GeneratorError) + ``` +* Fix `JSON.generate` `strict: true` mode to also restrict hash keys. +* Fix `JSON::Coder` to also invoke block for hash keys that aren't strings nor symbols. +* Fix `JSON.unsafe_load` usage with proc +* Fix the parser to more consistently reject invalid UTF-16 surogate pairs. +* Stop defining `String.json_create`, `String#to_json_raw`, `String#to_json_raw_object` when `json/add` isn't loaded. + +### 2025-07-28 (2.13.2) + +* Improve duplicate key warning and errors to include the key name and point to the right caller. + +### 2025-07-24 (2.13.1) + +* Fix support for older compilers without `__builtin_cpu_supports`. + +### 2025-07-17 (2.13.0) + +* Add new `allow_duplicate_key` parsing options. By default a warning is now emitted when a duplicated key is encountered. + In `json 3.0` an error will be raised. +* Optimize parsing further using SIMD to scan strings. + +### 2025-05-23 (2.12.2) + +* Fix compiler optimization level. + +### 2025-05-23 (2.12.1) + +* Fix a potential crash in large negative floating point number generation. +* Fix for JSON.pretty_generate to use passed state object's generate instead of state class as the required parameters aren't available. + +### 2025-05-12 (2.12.0) + +* Improve floating point generation to not use scientific notation as much. +* Include line and column in parser errors. Both in the message and as exception attributes. +* Handle non-string hash keys with broken `to_s` implementations. +* `JSON.generate` now uses SSE2 (x86) or NEON (arm64) instructions when available to escape strings. + +### 2025-04-25 (2.11.3) + +* Fix a regression in `JSON.pretty_generate` that could cause indentation to be off once some `#to_json` has been called. + +### 2025-04-24 (2.11.2) + +* Add back `JSON::PRETTY_STATE_PROTOTYPE`. This constant was private API but is used by popular gems like `multi_json`. + It now emits a deprecation warning. + +### 2025-04-24 (2.11.1) + +* Add back `JSON.restore`, `JSON.unparse`, `JSON.fast_unparse` and `JSON.pretty_unparse`. + These were deprecated 16 years ago, but never emitted warnings, only undocumented, so are + still used by a few gems. + +### 2025-04-24 (2.11.0) + +* Optimize Integer generation to be ~1.8x faster. +* Optimize Float generation to be ~10x faster. +* Fix `JSON.load` proc argument to substitute the parsed object with the return value. + This better match `Marshal.load` behavior. +* Deprecate `JSON.fast_generate` (it's not any faster, so pointless). +* Deprecate `JSON.load_default_options`. +* Deprecate `JSON.unsafe_load_default_options`. +* Deprecate `JSON.dump_default_options`. +* Deprecate `Kernel#j` +* Deprecate `Kernel#jj` +* Remove outdated `JSON.iconv`. +* Remove `Class#json_creatable?` monkey patch. +* Remove deprecated `JSON.restore` method. +* Remove deprecated `JSON.unparse` method. +* Remove deprecated `JSON.fast_unparse` method. +* Remove deprecated `JSON.pretty_unparse` method. +* Remove deprecated `JSON::UnparserError` constant. +* Remove outdated `JSON::MissingUnicodeSupport` constant. + +### 2025-03-12 (2.10.2) + +* Fix a potential crash in the C extension parser. +* Raise a ParserError on all incomplete unicode escape sequence. This was the behavior until `2.10.0` inadvertently changed it. +* Ensure document snippets that are included in parser errors don't include truncated multibyte characters. +* Ensure parser error snippets are valid UTF-8. +* Fix `JSON::GeneratorError#detailed_message` on Ruby < 3.2 + +### 2025-02-10 (2.10.1) + +* Fix a compatibility issue with `MultiJson.dump(obj, pretty: true)`: `no implicit conversion of false into Proc (TypeError)`. + +### 2025-02-10 (2.10.0) + +* `strict: true` now accept symbols as values. Previously they'd only be accepted as hash keys. +* The C extension Parser has been entirely reimplemented from scratch. +* Introduced `JSON::Coder` as a new API allowing to customize how non native types are serialized in a non-global way. +* Introduced `JSON::Fragment` to allow assembling cached fragments in a safe way. +* The Java implementation of the generator received many optimizations. + +### 2024-12-18 (2.9.1) + +* Fix support for Solaris 10. + +### 2024-12-03 (2.9.0) + +* Fix C implementation of `script_safe` escaping to not confuse some other 3 wide characters with `\u2028` and `\u2029`. + e.g. `JSON.generate(["倩", "瀨"], script_safe: true)` would generate the wrong JSON. +* `JSON.dump(object, some_io)` now write into the IO in chunks while previously it would buffer the entire JSON before writing. +* `JSON::GeneratorError` now has a `#invalid_object` attribute, making it easier to understand why an object tree cannot be serialized. +* Numerous improvements to the JRuby extension. + +### 2024-11-14 (2.8.2) + +* `JSON.load_file` explicitly read the file as UTF-8. + +### 2024-11-06 (2.8.1) + +* Fix the java packages to include the extension. + +### 2024-11-06 (2.8.0) + +* Emit a deprecation warning when `JSON.load` create custom types without the `create_additions` option being explicitly enabled. + * Prefer to use `JSON.unsafe_load(string)` or `JSON.load(string, create_additions: true)`. +* Emit a deprecation warning when serializing valid UTF-8 strings encoded in `ASCII_8BIT` aka `BINARY`. +* Bump required Ruby version to 2.7. +* Add support for optionally parsing trailing commas, via `allow_trailing_comma: true`, which in cunjunction with the + pre-existing support for comments, make it suitable to parse `jsonc` documents. +* Many performance improvements to `JSON.parse` and `JSON.load`, up to `1.7x` faster on real world documents. +* Some minor performance improvements to `JSON.dump` and `JSON.generate`. +* `JSON.pretty_generate` no longer includes newlines inside empty object and arrays. + +### 2024-11-04 (2.7.6) + +* Fix a regression in JSON.generate when dealing with Hash keys that are string subclasses, call `to_json` on them. + +### 2024-10-25 (2.7.5) + +* Fix a memory leak when `#to_json` methods raise an exception. +* Gracefully handle formatting configs being set to `nil` instead of `""`. +* Workaround another issue caused by conflicting versions of both `json_pure` and `json` being loaded. + +### 2024-10-25 (2.7.4) + +* Workaround a bug in 3.4.8 and older https://github.com/rubygems/rubygems/pull/6490. + This bug would cause some gems with native extension to fail during compilation. +* Workaround different versions of `json` and `json_pure` being loaded (not officially supported). +* Make `json_pure` Ractor compatible. + +### 2024-10-24 (2.7.3) + +* Numerous performance optimizations in `JSON.generate` and `JSON.dump` (up to 2 times faster). +* Limit the size of ParserError exception messages, only include up to 32 bytes of the unparsable source. +* Fix json-pure's `Object#to_json` to accept non-state arguments. +* Fix multiline comment support in `json-pure`. +* Fix `JSON.parse` to no longer mutate the argument encoding when passed an ASCII-8BIT string. +* Fix `String#to_json` to raise on invalid encoding in `json-pure`. +* Delete code that was based on CVTUTF. +* Use the pure-Ruby generator on TruffleRuby. +* Fix `strict` mode in `json-pure` to not break on Integer. + +### 2024-04-04 (2.7.2) + +* Use rb_sym2str instead of SYM2ID #561 +* Fix memory leak when exception is raised during JSON generation #574 +* Remove references to "19" methods in JRuby #576 +* Make OpenStruct support as optional by @hsbt in #565 +* Autoload JSON::GenericObject to avoid require ostruct warning in Ruby 3.4 #577 +* Warn to install ostruct if json couldn't load it by @hsbt #578 + +### 2023-12-05 (2.7.1) + +* JSON.dump: handle unenclosed hashes regression #554 +* Overload kwargs in JSON.dump #556 +* [DOC] RDoc for additions #557 +* Fix JSON.dump overload combination #558 + +### 2023-12-01 (2.7.0) + +* Add a strict option to Generator #519 +* `escape_slash` option was renamed as `script_safe` and now also escape U+2028 and U+2029. `escape_slash` is now an alias of `script_safe` #525 +* Remove unnecessary initialization of create_id in JSON.parse() #454 +* Improvements to Hash#to_json in pure implementation generator #203 +* Use ruby_xfree to free buffers #518 +* Fix "unexpected token" offset for Infinity #507 +* Avoid using deprecated BigDecimal.new on JRuby #546 +* Removed code for Ruby 1.8 #540 +* Rename JSON::ParseError to JSON:ParserError #530 +* Call super in included hook #486 +* JRuby requires a minimum of Java 8 #516 +* Always indent even if empty #517 + +### 2022-11-30 (2.6.3) + +* bugfix json/pure mixing escaped with literal unicode raises Encoding::CompatibilityError #483 +* Stop including the parser source __LINE__ in exceptions #470 + +### 2022-11-17 (2.6.2) + +* Remove unknown keyword arg from DateTime.parse #488 +* Ignore java artifacts by @hsbt #489 +* Fix parser bug for empty string allocation #496 + +### 2021-10-24 (2.6.1) + +* Restore version.rb with 2.6.1 + +### 2021-10-14 (2.6.0) + +* Use `rb_enc_interned_str` if available to reduce allocations in `freeze: true` mode. #451. +* Bump required_ruby_version to 2.3. +* Fix compatibility with `GC.compact`. +* Fix some compilation warnings. #469 + +## 2020-12-22 (2.5.1) + +* Restore the compatibility for constants of JSON class. + +## 2020-12-22 (2.5.0) + +* Ready to Ractor-safe at Ruby 3.0. + +## 2020-12-17 (2.4.1) + +* Restore version.rb with 2.4.1 + +## 2020-12-15 (2.4.0) + +* Implement a freeze: parser option #447 +* Fix an issue with generate_pretty and empty objects in the Ruby and Java implementations #449 +* Fix JSON.load_file doc #448 +* Fix pure parser with unclosed arrays / objects #425 +* bundle the LICENSE file in the gem #444 +* Add an option to escape forward slash character #405 +* RDoc for JSON #439 #446 #442 #434 #433 #430 + +## 2020-06-30 (2.3.1) + +* Spelling and grammar fixes for comments. Pull request #191 by Josh + Kline. +* Enhance generic JSON and #generate docs. Pull request #347 by Victor + Shepelev. +* Add :nodoc: for GeneratorMethods. Pull request #349 by Victor Shepelev. +* Baseline changes to help (JRuby) development. Pull request #371 by Karol + Bucek. +* Add metadata for rubygems.org. Pull request #379 by Alexandre ZANNI. +* Remove invalid JSON.generate description from JSON module rdoc. Pull + request #384 by Jeremy Evans. +* Test with TruffleRuby in CI. Pull request #402 by Benoit Daloze. +* Rdoc enhancements. Pull request #413 by Burdette Lamar. +* Fixtures/ are not being tested... Pull request #416 by Marc-André + Lafortune. +* Use frozen string for hash key. Pull request #420 by Marc-André + Lafortune. +* Added :call-seq: to RDoc for some methods. Pull request #422 by Burdette + Lamar. +* Small typo fix. Pull request #423 by Marc-André Lafortune. + +## 2019-12-11 (2.3.0) + * Fix default of `create_additions` to always be `false` for `JSON(user_input)` + and `JSON.parse(user_input, nil)`. + Note that `JSON.load` remains with default `true` and is meant for internal + serialization of trusted data. [CVE-2020-10663] + * Fix passing args all #to_json in json/add/*. + * Fix encoding issues + * Fix issues of keyword vs positional parameter + * Fix JSON::Parser against bigdecimal updates + * Bug fixes to JRuby port + +## 2019-02-21 (2.2.0) + * Adds support for 2.6 BigDecimal and ruby standard library Set datetype. + +## 2017-04-18 (2.1.0) + * Allow passing of `decimal_class` option to specify a class as which to parse + JSON float numbers. +## 2017-03-23 (2.0.4) + * Raise exception for incomplete unicode surrogates/character escape + sequences. This problem was reported by Daniel Gollahon (dgollahon). + * Fix arbitrary heap exposure problem. This problem was reported by Ahmad + Sherif (ahmadsherif). + +## 2017-01-12 (2.0.3) + * Set `required_ruby_version` to 1.9 + * Some small fixes + +## 2016-07-26 (2.0.2) + * Specify `required_ruby_version` for json\_pure. + * Fix issue #295 failure when parsing frozen strings. + +## 2016-07-01 (2.0.1) + * Fix problem when requiring json\_pure and Parser constant was defined top + level. + * Add `RB_GC_GUARD` to avoid possible GC problem via Pete Johns. + * Store `current_nesting` on stack by Aaron Patterson. + +## 2015-09-11 (2.0.0) + * Now complies to newest JSON RFC 7159. + * Implements compatibility to ruby 2.4 integer unification. + * Removed support for `quirks_mode` option. + * Drops support for old rubies whose life has ended, that is rubies < 2.0. + Also see https://www.ruby-lang.org/en/news/2014/07/01/eol-for-1-8-7-and-1-9-2/ + * There were still some mentions of dual GPL licensing in the source, but JSON + has just the Ruby license that itself includes an explicit dual-licensing + clause that allows covered software to be distributed under the terms of + the Simplified BSD License instead for all ruby versions >= 1.9.3. This is + however a GPL compatible license according to the Free Software Foundation. + I changed these mentions to be consistent with the Ruby license setting in + the gemspec files which were already correct now. + +## 2017-01-13 (1.8.6) + * Be compatible with ancient ruby 1.8 (maybe?) + +## 2015-09-11 (1.8.5) + * Be compatible with ruby 2.4.0 + * There were still some mentions of dual GPL licensing in the source, but JSON + has just the Ruby license that itself includes an explicit dual-licensing + clause that allows covered software to be distributed under the terms of + the Simplified BSD License instead for all ruby versions >= 1.9.3. This is + however a GPL compatible license according to the Free Software Foundation. + I changed these mentions to be consistent with the Ruby license setting in + the gemspec files which were already correct now. + +## 2015-06-01 (1.8.3) + * Fix potential memory leak, thx to nobu. + +## 2015-01-08 (1.8.2) + * Some performance improvements by Vipul A M . + * Fix by Jason R. Clark to avoid mutation of + `JSON.dump_default_options`. + * More tests by Michael Mac-Vicar and fixing + `space_before` accessor in generator. + * Performance on Jruby improved by Ben Browning . + * Some fixes to be compatible with the new Ruby 2.2 by Zachary Scott + and SHIBATA Hiroshi . + +## 2013-05-13 (1.8.1) + * Remove Rubinius exception since transcoding should be working now. + +## 2013-05-13 (1.8.0) + * Fix https://github.com/ruby/json/issues/162 reported by Marc-Andre + Lafortune . Thanks! + * Applied patches by Yui NARUSE to suppress warning with + -Wchar-subscripts and better validate UTF-8 strings. + * Applied patch by ginriki@github to remove unnecessary if. + * Add load/dump interface to `JSON::GenericObject` to make + serialize :some_attribute, `JSON::GenericObject` + work in Rails active models for convenient `SomeModel#some_attribute.foo.bar` + access to serialised JSON data. + +## 2013-02-04 (1.7.7) + * Security fix for JSON create_additions default value and + `JSON::GenericObject`. It should not be possible to create additions unless + explicitly requested by setting the create_additions argument to true or + using the JSON.load/dump interface. If `JSON::GenericObject` is supposed to + be automatically deserialised, this has to be explicitly enabled by + setting + JSON::GenericObject.json_creatable = true + as well. + * Remove useless assert in fbuffer implementation. + * Apply patch attached to https://github.com/ruby/json/issues#issue/155 + provided by John Shahid , Thx! + * Add license information to rubygems spec data, reported by Jordi Massaguer Pla . + * Improve documentation, thx to Zachary Scott . + +## 2012-11-29 (1.7.6) + * Add `GeneratorState#merge` alias for JRuby, fix state accessor methods. Thx to + jvshahid@github. + * Increase hash likeness of state objects. + +## 2012-08-17 (1.7.5) + * Fix compilation of extension on older rubies. + +## 2012-07-26 (1.7.4) + * Fix compilation problem on AIX, see https://github.com/ruby/json/issues/142 + +## 2012-05-12 (1.7.3) + * Work around Rubinius encoding issues using iconv for conversion instead. + +## 2012-05-11 (1.7.2) + * Fix some encoding issues, that cause problems for the pure and the + extension variant in jruby 1.9 mode. + +## 2012-04-28 (1.7.1) + * Some small fixes for building + +## 2012-04-28 (1.7.0) + * Add `JSON::GenericObject` for method access to objects transmitted via JSON. + +## 2012-04-27 (1.6.7) + * Fix possible crash when trying to parse nil value. + +## 2012-02-11 (1.6.6) + * Propagate src encoding to values made from it (fixes 1.9 mode converting + everything to ascii-8bit; harmless for 1.8 mode too) (Thomas E. Enebo + ), should fix + https://github.com/ruby/json/issues#issue/119. + * Fix https://github.com/ruby/json/issues#issue/124 Thx to Jason Hutchens. + * Fix https://github.com/ruby/json/issues#issue/117 + +## 2012-01-15 (1.6.5) + * Vit Ondruch reported a bug that shows up when using + optimisation under GCC 4.7. Thx to him, Bohuslav Kabrda + and Yui NARUSE for debugging and + developing a patch fix. + +## 2011-12-24 (1.6.4) + * Patches that improve speed on JRuby contributed by Charles Oliver Nutter + . + * Support `object_class`/`array_class` with duck typed hash/array. + +## 2011-12-01 (1.6.3) + * Let `JSON.load('')` return nil as well to make mysql text columns (default to + `''`) work better for serialization. + +## 2011-11-21 (1.6.2) + * Add support for OpenStruct and BigDecimal. + * Fix bug when parsing nil in `quirks_mode`. + * Make JSON.dump and JSON.load methods better cooperate with Rails' serialize + method. Just use: + serialize :value, JSON + * Fix bug with time serialization concerning nanoseconds. Thanks for the + patch go to Josh Partlow (jpartlow@github). + * Improve parsing speed for JSON numbers (integers and floats) in a similar way to + what Evan Phoenix suggested in: + https://github.com/ruby/json/pull/103 + +## 2011-09-18 (1.6.1) + * Using -target 1.5 to force Java bits to compile with 1.5. + +## 2011-09-12 (1.6.0) + * Extract utilities (prettifier and GUI-editor) in its own gem json-utils. + * Split json/add/core into different files for classes to be serialised. + +## 2011-08-31 (1.5.4) + * Fix memory leak when used from multiple JRuby. (Patch by + jfirebaugh@github). + * Apply patch by Eric Wong that fixes garbage collection problem + reported in https://github.com/ruby/json/issues/46. + * Add :quirks_mode option to parser and generator. + * Add support for Rational and Complex number additions via json/add/complex + and json/add/rational requires. + +## 2011-06-20 (1.5.3) + * Alias State#configure method as State#merge to increase duck type synonymy with Hash. + * Add `as_json` methods in json/add/core, so rails can create its json objects the new way. + +## 2011-05-11 (1.5.2) + * Apply documentation patch by Cory Monty . + * Add gemspecs for json and json\_pure. + * Fix bug in jruby pretty printing. + * Fix bug in `object_class` and `array_class` when inheriting from Hash or + Array. + +## 2011-01-24 (1.5.1) + * Made rake-compiler build a fat binary gem. This should fix issue + https://github.com/ruby/json/issues#issue/54. + +## 2011-01-22 (1.5.0) + * Included Java source codes for the Jruby extension made by Daniel Luz + . + * Output full exception message of `deep_const_get` to aid debugging. + * Fixed an issue with ruby 1.9 `Module#const_defined?` method, that was + reported by Riley Goodside. + +## 2010-08-09 (1.4.6) + * Fixed oversight reported in http://github.com/ruby/json/issues/closed#issue/23, + always create a new object from the state prototype. + * Made pure and ext api more similar again. + +## 2010-08-07 (1.4.5) + * Manage data structure nesting depth in state object during generation. This + should reduce problems with `to_json` method definіtions that only have one + argument. + * Some fixes in the state objects and additional tests. +## 2010-08-06 (1.4.4) + * Fixes build problem for rubinius under OS X, http://github.com/ruby/json/issues/closed#issue/25 + * Fixes crashes described in http://github.com/ruby/json/issues/closed#issue/21 and + http://github.com/ruby/json/issues/closed#issue/23 +## 2010-05-05 (1.4.3) + * Fixed some test assertions, from Ruby r27587 and r27590, patch by nobu. + * Fixed issue http://github.com/ruby/json/issues/#issue/20 reported by + electronicwhisper@github. Thx! + +## 2010-04-26 (1.4.2) + * Applied patch from naruse Yui NARUSE to make building with + Microsoft Visual C possible again. + * Applied patch from devrandom in order to allow building of + json_pure if extensiontask is not present. + * Thanks to Dustin Schneider , who reported a memory + leak, which is fixed in this release. + * Applied 993f261ccb8f911d2ae57e9db48ec7acd0187283 patch from josh@github. + +## 2010-04-25 (1.4.1) + * Fix for a bug reported by Dan DeLeo , caused by T_FIXNUM + being different on 32bit/64bit architectures. + +## 2010-04-23 (1.4.0) + * Major speed improvements and building with simplified + directory/file-structure. + * Extension should at least be compatible with MRI, YARV and Rubinius. + +## 2010-04-07 (1.2.4) + * Trigger const_missing callback to make Rails' dynamic class loading work. + +## 2010-03-11 (1.2.3) + * Added a `State#[]` method which returns an attribute's value in order to + increase duck type compatibility to Hash. + +## 2010-02-27 (1.2.2) + * Made some changes to make the building of the parser/generator compatible + to Rubinius. + +## 2009-11-25 (1.2.1) + * Added `:symbolize_names` option to Parser, which returns symbols instead of + strings in object names/keys. + +## 2009-10-01 (1.2.0) + * `fast_generate` now raises an exception for nan and infinite floats. + * On Ruby 1.8 json supports parsing of UTF-8, UTF-16BE, UTF-16LE, UTF-32BE, + and UTF-32LE JSON documents now. Under Ruby 1.9 the M17n conversion + functions are used to convert from all supported encodings. ASCII-8BIT + encoded strings are handled like all strings under Ruby 1.8 were. + * Better documentation + +## 2009-08-23 (1.1.9) + * Added forgotten main doc file `extra_rdoc_files`. + +## 2009-08-23 (1.1.8) + * Applied a patch by OZAWA Sakuro to make json/pure + work in environments that don't provide iconv. + * Applied patch by okkez_ in order to fix Ruby Bug #1768: + http://redmine.ruby-lang.org/issues/show/1768. + * Finally got around to avoid the rather paranoid escaping of ?/ characters + in the generator's output. The parsers aren't affected by this change. + Thanks to Rich Apodaca for the suggestion. + +## 2009-06-29 (1.1.7) + * Security Fix for JSON::Pure::Parser. A specially designed string could + cause catastrophic backtracking in one of the parser's regular expressions + in earlier 1.1.x versions. JSON::Ext::Parser isn't affected by this issue. + Thanks to Bartosz Blimke for reporting this + problem. + * This release also uses a less strict ruby version requirement for the + creation of the mswin32 native gem. + +## 2009-05-10 (1.1.6) + * No changes. І tested native linux gems in the last release and they don't + play well with different ruby versions other than the one the gem was built + with. This release is just to bump the version number in order to skip the + native gem on rubyforge. + +## 2009-05-10 (1.1.5) + * Started to build gems with rake-compiler gem. + * Applied patch object/array class patch from Brian Candler + and fixes. + +## 2009-04-01 (1.1.4) + * Fixed a bug in the creation of serialized generic rails objects reported by + Friedrich Graeter . + * Deleted tests/runner.rb, we're using testrb instead. + * Editor supports Infinity in numbers now. + * Made some changes in order to get the library to compile/run under Ruby + 1.9. + * Improved speed of the code path for the fast_generate method in the pure + variant. + +## 2008-07-10 (1.1.3) + * Wesley Beary reported a bug in json/add/core's DateTime + handling: If the nominator and denominator of the offset were divisible by + each other Ruby's Rational#to_s returns them as an integer not a fraction + with '/'. This caused a ZeroDivisionError during parsing. + * Use Date#start and DateTime#start instead of sg method, while + remaining backwards compatible. + * Supports ragel >= 6.0 now. + * Corrected some tests. + * Some minor changes. + +## 2007-11-27 (1.1.2) + * Remember default dir (last used directory) in editor. + * JSON::Editor.edit method added, the editor can now receive json texts from + the clipboard via C-v. + * Load json texts from an URL pasted via middle button press. + * Added :create_additions option to Parser. This makes it possible to disable + the creation of additions by force, in order to treat json texts as data + while having additions loaded. + * Jacob Maine reported, that JSON(:foo) outputs a JSON + object if the rails addition is enabled, which is wrong. It now outputs a + JSON string "foo" instead, like suggested by Jacob Maine. + * Discovered a bug in the Ruby Bugs Tracker on rubyforge, that was reported + by John Evans lgastako@gmail.com. He could produce a crash in the JSON + generator by returning something other than a String instance from a + to_json method. I now guard against this by doing a rather crude type + check, which raises an exception instead of crashing. + +## 2007-07-06 (1.1.1) + * Yui NARUSE sent some patches to fix tests for Ruby + 1.9. I applied them and adapted some of them a bit to run both on 1.8 and + 1.9. + * Introduced a `JSON.parse!` method without depth checking for people who + like danger. + * Made generate and `pretty_generate` methods configurable by an options hash. + * Added :allow_nan option to parser and generator in order to handle NaN, + Infinity, and -Infinity correctly - if requested. Floats, which aren't numbers, + aren't valid JSON according to RFC4627, so by default an exception will be + raised if any of these symbols are encountered. Thanks to Andrea Censi + for his hint about this. + * Fixed some more tests for Ruby 1.9. + * Implemented dump/load interface of Marshal as suggested in ruby-core:11405 + by murphy . + * Implemented the `max_nesting` feature for generate methods, too. + * Added some implementations for ruby core's custom objects for + serialisation/deserialisation purposes. + +## 2007-05-21 (1.1.0) + * Implemented max_nesting feature for parser to avoid stack overflows for + data from untrusted sources. If you trust the source, you can disable it + with the option max_nesting => false. + * Piers Cawley reported a bug, that not every + character can be escaped by `\` as required by RFC4627. There's a + contradiction between David Crockford's JSON checker test vectors (in + tests/fixtures) and RFC4627, though. I decided to stick to the RFC, because + the JSON checker seems to be a bit older than the RFC. + * Extended license to Ruby License, which includes the GPL. + * Added keyboard shortcuts, and 'Open location' menu item to edit_json.rb. + +## 2007-05-09 (1.0.4) + * Applied a patch from Yui NARUSE to make JSON compile + under Ruby 1.9. Thank you very much for mailing it to me! + * Made binary variants of JSON fail early, instead of falling back to the + pure version. This should avoid overshadowing of eventual problems while + loading of the binary. + +## 2007-03-24 (1.0.3) + * Improved performance of pure variant a bit. + * The ext variant of this release supports the mswin32 platform. Ugh! + +## 2007-03-24 (1.0.2) + * Ext Parser didn't parse 0e0 correctly into 0.0: Fixed! + +## 2007-03-24 (1.0.1) + * Forgot some object files in the build dir. I really like that - not! + +## 2007-03-24 (1.0.0) + * Added C implementations for the JSON generator and a ragel based JSON + parser in C. + * Much more tests, especially fixtures from json.org. + * Further improved conformance to RFC4627. + +## 2007-02-09 (0.4.3) + * Conform more to RFC4627 for JSON: This means JSON strings + now always must contain exactly one object `"{ ... }"` or array `"[ ... ]"` in + order to be parsed without raising an exception. The definition of what + constitutes a whitespace is narrower in JSON than in Ruby ([ \t\r\n]), and + there are differences in floats and integers (no octals or hexadecimals) as + well. + * Added aliases generate and `pretty_generate` of unparse and `pretty_unparse`. + * Fixed a test case. + * Catch an `Iconv::InvalidEncoding` exception, that seems to occur on some Sun + boxes with SunOS 5.8, if iconv doesn't support utf16 conversions. This was + reported by Andrew R Jackson , thanks a bunch! + +## 2006-08-25 (0.4.2) + * Fixed a bug in handling solidi (/-characters), that was reported by + Kevin Gilpin . + +## 2006-02-06 (0.4.1) + * Fixed a bug related to escaping with backslashes. Thanks for the report go + to Florian Munz . + +## 2005-09-23 (0.4.0) + * Initial Rubyforge Version diff --git a/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/COPYING b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/COPYING new file mode 100644 index 0000000..426810a --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/COPYING @@ -0,0 +1,56 @@ +Ruby is copyrighted free software by Yukihiro Matsumoto . +You can redistribute it and/or modify it under either the terms of the +2-clause BSDL (see the file BSDL), or the conditions below: + + 1. You may make and give away verbatim copies of the source form of the + software without restriction, provided that you duplicate all of the + original copyright notices and associated disclaimers. + + 2. You may modify your copy of the software in any way, provided that + you do at least ONE of the following: + + a) place your modifications in the Public Domain or otherwise + make them Freely Available, such as by posting said + modifications to Usenet or an equivalent medium, or by allowing + the author to include your modifications in the software. + + b) use the modified software only within your corporation or + organization. + + c) give non-standard binaries non-standard names, with + instructions on where to get the original software distribution. + + d) make other distribution arrangements with the author. + + 3. You may distribute the software in object code or binary form, + provided that you do at least ONE of the following: + + a) distribute the binaries and library files of the software, + together with instructions (in the manual page or equivalent) + on where to get the original distribution. + + b) accompany the distribution with the machine-readable source of + the software. + + c) give non-standard binaries non-standard names, with + instructions on where to get the original software distribution. + + d) make other distribution arrangements with the author. + + 4. You may modify and include the part of the software into any other + software (possibly commercial). But some files in the distribution + are not written by the author, so that they are not under these terms. + + For the list of those files and their copying conditions, see the + file LEGAL. + + 5. The scripts and library files supplied as input to or produced as + output from the software do not automatically fall under the + copyright of the software, but belong to whomever generated them, + and may be sold commercially, and may be aggregated with this + software. + + 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE. diff --git a/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/LEGAL b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/LEGAL new file mode 100644 index 0000000..dfc7189 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/LEGAL @@ -0,0 +1,20 @@ +# -*- rdoc -*- + += LEGAL NOTICE INFORMATION +-------------------------- + +All the files in this distribution are covered under either the Ruby's +license (see the file COPYING) or public-domain except some files +mentioned below. + +ext/json/ext/vendor/fpconv.h:: + This file is adapted from https://github.com/night-shift/fpconv + It is licensed under Boost Software License 1.0. + +ext/json/ext/vendor/jeaiii-ltoa.h:: + This file is adapted from https://github.com/jeaiii/itoa + It is licensed under the MIT License + +ext/json/ext/vendor/ryu.h:: + This file is adapted from the Ryu algorithm by Ulf Adams https://github.com/ulfjack/ryu. + It is dual-licensed under Apache License 2.0 OR Boost Software License 1.0. diff --git a/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/README.md b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/README.md new file mode 100644 index 0000000..fee9930 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/README.md @@ -0,0 +1,299 @@ +# JSON implementation for Ruby + +[![CI](https://github.com/ruby/json/actions/workflows/ci.yml/badge.svg)](https://github.com/ruby/json/actions/workflows/ci.yml) + +## Description + +This is an implementation of the JSON specification according to RFC 7159 +http://www.ietf.org/rfc/rfc7159.txt . + +The JSON generator generate UTF-8 character sequences by default. +If an :ascii\_only option with a true value is given, they escape all +non-ASCII and control characters with \uXXXX escape sequences, and support +UTF-16 surrogate pairs in order to be able to generate the whole range of +unicode code points. + +All strings, that are to be encoded as JSON strings, should be UTF-8 byte +sequences on the Ruby side. To encode raw binary strings, that aren't UTF-8 +encoded, please use the to\_json\_raw\_object method of String (which produces +an object, that contains a byte array) and decode the result on the receiving +endpoint. + +## Installation + +Install the gem and add to the application's Gemfile by executing: + + $ bundle add json + +If bundler is not being used to manage dependencies, install the gem by executing: + + $ gem install json + +## Basic Usage + +To use JSON you can + +```ruby +require 'json' +``` + +Now you can parse a JSON document into a ruby data structure by calling + +```ruby +JSON.parse(document) +``` + +If you want to generate a JSON document from a ruby data structure call +```ruby +JSON.generate(data) +``` + +You can also use the `pretty_generate` method (which formats the output more +verbosely and nicely) or `fast_generate` (which doesn't do any of the security +checks generate performs, e. g. nesting deepness checks). + +## Casting non native types + +JSON documents can only support Hashes, Arrays, Strings, Integers and Floats. + +By default if you attempt to serialize something else, `JSON.generate` will +search for a `#to_json` method on that object: + +```ruby +Position = Struct.new(:latitude, :longitude) do + def to_json(state = nil, *) + JSON::State.from_state(state).generate({ + latitude: latitude, + longitude: longitude, + }) + end +end + +JSON.generate([ + Position.new(12323.234, 435345.233), + Position.new(23434.676, 159435.324), +]) # => [{"latitude":12323.234,"longitude":435345.233},{"latitude":23434.676,"longitude":159435.324}] +``` + +If a `#to_json` method isn't defined on the object, `JSON.generate` will fallback to call `#to_s`: + +```ruby +JSON.generate(Object.new) # => "#" +``` + +Both of these behavior can be disabled using the `strict: true` option: + +```ruby +JSON.generate(Object.new, strict: true) # => Object not allowed in JSON (JSON::GeneratorError) +JSON.generate(Position.new(1, 2)) # => Position not allowed in JSON (JSON::GeneratorError) +``` + +## JSON::Coder + +Since `#to_json` methods are global, it can sometimes be problematic if you need a given type to be +serialized in different ways in different locations. + +Instead it is recommended to use the newer `JSON::Coder` API: + +```ruby +module MyApp + API_JSON_CODER = JSON::Coder.new do |object, is_object_key| + case object + when Time + object.iso8601(3) + else + object + end + end +end + +puts MyApp::API_JSON_CODER.dump(Time.now.utc) # => "2025-01-21T08:41:44.286Z" +``` + +The provided block is called for all objects that don't have a native JSON equivalent, and +must return a Ruby object that has a native JSON equivalent. + +It is also called for objects that do have a JSON equivalent, but are used as Hash keys, for instance `{ 1 => 2}`, +as well as for strings that aren't valid UTF-8: + +```ruby +coder = JSON::Combining.new do |object, is_object_key| + case object + when String + if !string.valid_encoding? || string.encoding != Encoding::UTF_8 + Base64.encode64(string) + else + string + end + else + object + end +end +``` + +## Combining JSON fragments + +To combine JSON fragments into a bigger JSON document, you can use `JSON::Fragment`: + +```ruby +posts_json = cache.fetch_multi(post_ids) do |post_id| + JSON.generate(Post.find(post_id)) +end +posts_json.map! { |post_json| JSON::Fragment.new(post_json) } +JSON.generate({ posts: posts_json, count: posts_json.count }) +``` + +## Round-tripping arbitrary types + +> [!CAUTION] +> You should never use `JSON.unsafe_load` nor `JSON.parse(str, create_additions: true)` to parse untrusted user input, +> as it can lead to remote code execution vulnerabilities. + +To create a JSON document from a ruby data structure, you can call +`JSON.generate` like that: + +```ruby +json = JSON.generate [1, 2, {"a"=>3.141}, false, true, nil, 4..10] +# => "[1,2,{\"a\":3.141},false,true,null,\"4..10\"]" +``` + +To get back a ruby data structure from a JSON document, you have to call +JSON.parse on it: + +```ruby +JSON.parse json +# => [1, 2, {"a"=>3.141}, false, true, nil, "4..10"] +``` + +Note, that the range from the original data structure is a simple +string now. The reason for this is, that JSON doesn't support ranges +or arbitrary classes. In this case the json library falls back to call +`Object#to_json`, which is the same as `#to_s.to_json`. + +It's possible to add JSON support serialization to arbitrary classes by +simply implementing a more specialized version of the `#to_json method`, that +should return a JSON object (a hash converted to JSON with `#to_json`) like +this (don't forget the `*a` for all the arguments): + +```ruby +class Range + def to_json(*a) + { + 'json_class' => self.class.name, # = 'Range' + 'data' => [ first, last, exclude_end? ] + }.to_json(*a) + end +end +``` + +The hash key `json_class` is the class, that will be asked to deserialise the +JSON representation later. In this case it's `Range`, but any namespace of +the form `A::B` or `::A::B` will do. All other keys are arbitrary and can be +used to store the necessary data to configure the object to be deserialised. + +If the key `json_class` is found in a JSON object, the JSON parser checks +if the given class responds to the `json_create` class method. If so, it is +called with the JSON object converted to a Ruby hash. So a range can +be deserialised by implementing `Range.json_create` like this: + +```ruby +class Range + def self.json_create(o) + new(*o['data']) + end +end +``` + +Now it possible to serialise/deserialise ranges as well: + +```ruby +json = JSON.generate [1, 2, {"a"=>3.141}, false, true, nil, 4..10] +# => "[1,2,{\"a\":3.141},false,true,null,{\"json_class\":\"Range\",\"data\":[4,10,false]}]" +JSON.parse json +# => [1, 2, {"a"=>3.141}, false, true, nil, 4..10] +json = JSON.generate [1, 2, {"a"=>3.141}, false, true, nil, 4..10] +# => "[1,2,{\"a\":3.141},false,true,null,{\"json_class\":\"Range\",\"data\":[4,10,false]}]" +JSON.unsafe_load json +# => [1, 2, {"a"=>3.141}, false, true, nil, 4..10] +``` + +`JSON.generate` always creates the shortest possible string representation of a +ruby data structure in one line. This is good for data storage or network +protocols, but not so good for humans to read. Fortunately there's also +`JSON.pretty_generate` (or `JSON.pretty_generate`) that creates a more readable +output: + +```ruby + puts JSON.pretty_generate([1, 2, {"a"=>3.141}, false, true, nil, 4..10]) + [ + 1, + 2, + { + "a": 3.141 + }, + false, + true, + null, + { + "json_class": "Range", + "data": [ + 4, + 10, + false + ] + } + ] +``` + +There are also the methods `Kernel#j` for generate, and `Kernel#jj` for +`pretty_generate` output to the console, that work analogous to Core Ruby's `p` and +the `pp` library's `pp` methods. + +## Development + +### Prerequisites + +1. Clone the repository +2. Install dependencies with `bundle install` + +### Testing + +The full test suite can be run with: + +```bash +bundle exec rake test +``` + +### Release + +Update the `lib/json/version.rb` file. + +``` +rbenv shell 2.6.5 +rake build +gem push pkg/json-2.3.0.gem + +rbenv shell jruby-9.2.9.0 +rake build +gem push pkg/json-2.3.0-java.gem +``` + +## Author + +Florian Frank + +## License + +Ruby License, see https://www.ruby-lang.org/en/about/license.txt. + +## Download + +The latest version of this library can be downloaded at + +* https://rubygems.org/gems/json + +Online Documentation should be located at + +* https://www.rubydoc.info/gems/json + +[Ragel]: http://www.colm.net/open-source/ragel/ diff --git a/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/ext/json/ext/fbuffer/fbuffer.h b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/ext/json/ext/fbuffer/fbuffer.h new file mode 100644 index 0000000..752d153 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/ext/json/ext/fbuffer/fbuffer.h @@ -0,0 +1,247 @@ +#ifndef _FBUFFER_H_ +#define _FBUFFER_H_ + +#include "../json.h" +#include "../vendor/jeaiii-ltoa.h" + +enum fbuffer_type { + FBUFFER_HEAP_ALLOCATED = 0, + FBUFFER_STACK_ALLOCATED = 1, +}; + +typedef struct FBufferStruct { + enum fbuffer_type type; + unsigned long initial_length; + unsigned long len; + unsigned long capa; +#if JSON_DEBUG + unsigned long requested; +#endif + char *ptr; + VALUE io; +} FBuffer; + +#define FBUFFER_STACK_SIZE 512 +#define FBUFFER_IO_BUFFER_SIZE (16384 - 1) +#define FBUFFER_INITIAL_LENGTH_DEFAULT 1024 + +#define FBUFFER_PTR(fb) ((fb)->ptr) +#define FBUFFER_LEN(fb) ((fb)->len) +#define FBUFFER_CAPA(fb) ((fb)->capa) +#define FBUFFER_PAIR(fb) FBUFFER_PTR(fb), FBUFFER_LEN(fb) + +static void fbuffer_free(FBuffer *fb); +static void fbuffer_clear(FBuffer *fb); +static void fbuffer_append(FBuffer *fb, const char *newstr, unsigned long len); +static void fbuffer_append_long(FBuffer *fb, long number); +static inline void fbuffer_append_char(FBuffer *fb, char newchr); +static VALUE fbuffer_finalize(FBuffer *fb); + +static void fbuffer_stack_init(FBuffer *fb, unsigned long initial_length, char *stack_buffer, long stack_buffer_size) +{ + fb->initial_length = (initial_length > 0) ? initial_length : FBUFFER_INITIAL_LENGTH_DEFAULT; + if (stack_buffer) { + fb->type = FBUFFER_STACK_ALLOCATED; + fb->ptr = stack_buffer; + fb->capa = stack_buffer_size; + } +#if JSON_DEBUG + fb->requested = 0; +#endif +} + +static inline void fbuffer_consumed(FBuffer *fb, unsigned long consumed) +{ +#if JSON_DEBUG + if (consumed > fb->requested) { + rb_bug("fbuffer: Out of bound write"); + } + fb->requested = 0; +#endif + fb->len += consumed; +} + +static void fbuffer_free(FBuffer *fb) +{ + if (fb->ptr && fb->type == FBUFFER_HEAP_ALLOCATED) { + ruby_xfree(fb->ptr); + } +} + +static void fbuffer_clear(FBuffer *fb) +{ + fb->len = 0; +} + +static void fbuffer_flush(FBuffer *fb) +{ + rb_io_write(fb->io, rb_utf8_str_new(fb->ptr, fb->len)); + fbuffer_clear(fb); +} + +static void fbuffer_realloc(FBuffer *fb, unsigned long required) +{ + if (required > fb->capa) { + if (fb->type == FBUFFER_STACK_ALLOCATED) { + const char *old_buffer = fb->ptr; + fb->ptr = ALLOC_N(char, required); + fb->type = FBUFFER_HEAP_ALLOCATED; + MEMCPY(fb->ptr, old_buffer, char, fb->len); + } else { + REALLOC_N(fb->ptr, char, required); + } + fb->capa = required; + } +} + +static void fbuffer_do_inc_capa(FBuffer *fb, unsigned long requested) +{ + if (RB_UNLIKELY(fb->io)) { + if (fb->capa < FBUFFER_IO_BUFFER_SIZE) { + fbuffer_realloc(fb, FBUFFER_IO_BUFFER_SIZE); + } else { + fbuffer_flush(fb); + } + + if (RB_LIKELY(requested < fb->capa)) { + return; + } + } + + unsigned long required; + + if (RB_UNLIKELY(!fb->ptr)) { + fb->ptr = ALLOC_N(char, fb->initial_length); + fb->capa = fb->initial_length; + } + + for (required = fb->capa; requested > required - fb->len; required <<= 1); + + fbuffer_realloc(fb, required); +} + +static inline void fbuffer_inc_capa(FBuffer *fb, unsigned long requested) +{ +#if JSON_DEBUG + fb->requested = requested; +#endif + + if (RB_UNLIKELY(requested > fb->capa - fb->len)) { + fbuffer_do_inc_capa(fb, requested); + } +} + +static inline void fbuffer_append_reserved(FBuffer *fb, const char *newstr, unsigned long len) +{ + MEMCPY(fb->ptr + fb->len, newstr, char, len); + fbuffer_consumed(fb, len); +} + +static inline void fbuffer_append(FBuffer *fb, const char *newstr, unsigned long len) +{ + if (len > 0) { + fbuffer_inc_capa(fb, len); + fbuffer_append_reserved(fb, newstr, len); + } +} + +/* Appends a character into a buffer. The buffer needs to have sufficient capacity, via fbuffer_inc_capa(...). */ +static inline void fbuffer_append_reserved_char(FBuffer *fb, char chr) +{ +#if JSON_DEBUG + if (fb->requested < 1) { + rb_bug("fbuffer: unreserved write"); + } + fb->requested--; +#endif + + fb->ptr[fb->len] = chr; + fb->len++; +} + +static void fbuffer_append_str(FBuffer *fb, VALUE str) +{ + const char *newstr = StringValuePtr(str); + unsigned long len = RSTRING_LEN(str); + + fbuffer_append(fb, newstr, len); +} + +static void fbuffer_append_str_repeat(FBuffer *fb, VALUE str, size_t repeat) +{ + const char *newstr = StringValuePtr(str); + unsigned long len = RSTRING_LEN(str); + + fbuffer_inc_capa(fb, repeat * len); + while (repeat) { +#if JSON_DEBUG + fb->requested = len; +#endif + fbuffer_append_reserved(fb, newstr, len); + repeat--; + } +} + +static inline void fbuffer_append_char(FBuffer *fb, char newchr) +{ + fbuffer_inc_capa(fb, 1); + *(fb->ptr + fb->len) = newchr; + fbuffer_consumed(fb, 1); +} + +static inline char *fbuffer_cursor(FBuffer *fb) +{ + return fb->ptr + fb->len; +} + +static inline void fbuffer_advance_to(FBuffer *fb, char *end) +{ + fbuffer_consumed(fb, (end - fb->ptr) - fb->len); +} + +/* + * Appends the decimal string representation of \a number into the buffer. + */ +static void fbuffer_append_long(FBuffer *fb, long number) +{ + /* + * The jeaiii_ultoa() function produces digits left-to-right, + * allowing us to write directly into the buffer, but we don't know + * the number of resulting characters. + * + * We do know, however, that the `number` argument is always in the + * range 0xc000000000000000 to 0x3fffffffffffffff, or, in decimal, + * -4611686018427387904 to 4611686018427387903. The max number of chars + * generated is therefore 20 (including a potential sign character). + */ + + static const int MAX_CHARS_FOR_LONG = 20; + + fbuffer_inc_capa(fb, MAX_CHARS_FOR_LONG); + + if (number < 0) { + fbuffer_append_reserved_char(fb, '-'); + + /* + * Since number is always > LONG_MIN, `-number` will not overflow + * and is always the positive abs() value. + */ + number = -number; + } + + char *end = jeaiii_ultoa(fbuffer_cursor(fb), number); + fbuffer_advance_to(fb, end); +} + +static VALUE fbuffer_finalize(FBuffer *fb) +{ + if (fb->io) { + fbuffer_flush(fb); + rb_io_flush(fb->io); + return fb->io; + } else { + return rb_utf8_str_new(FBUFFER_PTR(fb), FBUFFER_LEN(fb)); + } +} + +#endif // _FBUFFER_H_ diff --git a/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/ext/json/ext/generator/Makefile b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/ext/json/ext/generator/Makefile new file mode 100644 index 0000000..cb04c3a --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/ext/json/ext/generator/Makefile @@ -0,0 +1,269 @@ + +SHELL = /bin/sh + +# V=0 quiet, V=1 verbose. other values don't work. +V = 0 +V0 = $(V:0=) +Q1 = $(V:1=) +Q = $(Q1:0=@) +ECHO1 = $(V:1=@ :) +ECHO = $(ECHO1:0=@ echo) +NULLCMD = : + +#### Start of system configuration section. #### + +srcdir = . +topdir = /opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0 +hdrdir = $(topdir) +arch_hdrdir = /opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/x86_64-linux +PATH_SEPARATOR = : +VPATH = $(srcdir):$(arch_hdrdir)/ruby:$(hdrdir)/ruby +prefix = $(DESTDIR)/opt/hostedtoolcache/Ruby/3.3.10/x64 +rubysitearchprefix = $(rubylibprefix)/$(sitearch) +rubyarchprefix = $(rubylibprefix)/$(arch) +rubylibprefix = $(libdir)/$(RUBY_BASE_NAME) +exec_prefix = $(prefix) +vendorarchhdrdir = $(vendorhdrdir)/$(sitearch) +sitearchhdrdir = $(sitehdrdir)/$(sitearch) +rubyarchhdrdir = $(rubyhdrdir)/$(arch) +vendorhdrdir = $(rubyhdrdir)/vendor_ruby +sitehdrdir = $(rubyhdrdir)/site_ruby +rubyhdrdir = $(includedir)/$(RUBY_VERSION_NAME) +vendorarchdir = $(vendorlibdir)/$(sitearch) +vendorlibdir = $(vendordir)/$(ruby_version) +vendordir = $(rubylibprefix)/vendor_ruby +sitearchdir = $(sitelibdir)/$(sitearch) +sitelibdir = $(sitedir)/$(ruby_version) +sitedir = $(rubylibprefix)/site_ruby +rubyarchdir = $(rubylibdir)/$(arch) +rubylibdir = $(rubylibprefix)/$(ruby_version) +sitearchincludedir = $(includedir)/$(sitearch) +archincludedir = $(includedir)/$(arch) +sitearchlibdir = $(libdir)/$(sitearch) +archlibdir = $(libdir)/$(arch) +ridir = $(datarootdir)/$(RI_BASE_NAME) +mandir = $(datarootdir)/man +localedir = $(datarootdir)/locale +libdir = $(exec_prefix)/lib +psdir = $(docdir) +pdfdir = $(docdir) +dvidir = $(docdir) +htmldir = $(docdir) +infodir = $(datarootdir)/info +docdir = $(datarootdir)/doc/$(PACKAGE) +oldincludedir = $(DESTDIR)/usr/include +includedir = $(prefix)/include +runstatedir = $(localstatedir)/run +localstatedir = $(prefix)/var +sharedstatedir = $(prefix)/com +sysconfdir = $(prefix)/etc +datadir = $(datarootdir) +datarootdir = $(prefix)/share +libexecdir = $(exec_prefix)/libexec +sbindir = $(exec_prefix)/sbin +bindir = $(exec_prefix)/bin +archdir = $(rubyarchdir) + + +CC_WRAPPER = +CC = gcc +CXX = g++ +LIBRUBY = $(LIBRUBY_SO) +LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a +LIBRUBYARG_SHARED = -Wl,-rpath,$(libdir) -L$(libdir) -l$(RUBY_SO_NAME) +LIBRUBYARG_STATIC = -Wl,-rpath,$(libdir) -L$(libdir) -l$(RUBY_SO_NAME)-static $(MAINLIBS) +empty = +OUTFLAG = -o $(empty) +COUTFLAG = -o $(empty) +CSRCFLAG = $(empty) + +RUBY_EXTCONF_H = +cflags = $(optflags) $(debugflags) $(warnflags) +cxxflags = +optflags = -O3 -fno-fast-math +debugflags = -ggdb3 +warnflags = -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef +cppflags = +CCDLFLAGS = -fPIC +CFLAGS = $(CCDLFLAGS) $(cflags) -fPIC -std=c99 $(ARCH_FLAG) +INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir)/ruby/backward -I$(hdrdir) -I$(srcdir) +DEFS = +CPPFLAGS = -DJSON_GENERATOR -DHAVE_X86INTRIN_H -DJSON_ENABLE_SIMD -DHAVE_CPUID_H -DENABLE_PATH_CHECK=0 $(DEFS) $(cppflags) +CXXFLAGS = $(CCDLFLAGS) $(ARCH_FLAG) +ldflags = -L. -fstack-protector-strong -rdynamic -Wl,-export-dynamic -Wl,--no-as-needed +dldflags = -Wl,--compress-debug-sections=zlib +ARCH_FLAG = +DLDFLAGS = $(ldflags) $(dldflags) $(ARCH_FLAG) +LDSHARED = $(CC) -shared +LDSHAREDXX = $(CXX) -shared +AR = gcc-ar +EXEEXT = + +RUBY_INSTALL_NAME = $(RUBY_BASE_NAME) +RUBY_SO_NAME = ruby +RUBYW_INSTALL_NAME = +RUBY_VERSION_NAME = $(RUBY_BASE_NAME)-$(ruby_version) +RUBYW_BASE_NAME = rubyw +RUBY_BASE_NAME = ruby + +arch = x86_64-linux +sitearch = $(arch) +ruby_version = 3.3.0 +ruby = $(bindir)/$(RUBY_BASE_NAME) +RUBY = $(ruby) +BUILTRUBY = $(bindir)/$(RUBY_BASE_NAME) +ruby_headers = $(hdrdir)/ruby.h $(hdrdir)/ruby/backward.h $(hdrdir)/ruby/ruby.h $(hdrdir)/ruby/defines.h $(hdrdir)/ruby/missing.h $(hdrdir)/ruby/intern.h $(hdrdir)/ruby/st.h $(hdrdir)/ruby/subst.h $(arch_hdrdir)/ruby/config.h + +RM = rm -f +RM_RF = rm -fr +RMDIRS = rmdir --ignore-fail-on-non-empty -p +MAKEDIRS = /usr/bin/mkdir -p +INSTALL = /usr/bin/install -c +INSTALL_PROG = $(INSTALL) -m 0755 +INSTALL_DATA = $(INSTALL) -m 644 +COPY = cp +TOUCH = exit > + +#### End of system configuration section. #### + +preload = +libpath = . $(libdir) +LIBPATH = -L. -L$(libdir) -Wl,-rpath,$(libdir) +DEFFILE = + +CLEANFILES = mkmf.log +DISTCLEANFILES = +DISTCLEANDIRS = + +extout = +extout_prefix = +target_prefix = /json/ext +LOCAL_LIBS = +LIBS = $(LIBRUBYARG_SHARED) -lm -lpthread -lc +ORIG_SRCS = generator.c +SRCS = $(ORIG_SRCS) +OBJS = generator.o +HDRS = +LOCAL_HDRS = +TARGET = generator +TARGET_NAME = generator +TARGET_ENTRY = Init_$(TARGET_NAME) +DLLIB = $(TARGET).so +EXTSTATIC = +STATIC_LIB = + +TIMESTAMP_DIR = . +BINDIR = $(bindir) +RUBYCOMMONDIR = $(sitedir)$(target_prefix) +RUBYLIBDIR = $(sitelibdir)$(target_prefix) +RUBYARCHDIR = $(sitearchdir)$(target_prefix) +HDRDIR = $(sitehdrdir)$(target_prefix) +ARCHHDRDIR = $(sitearchhdrdir)$(target_prefix) +TARGET_SO_DIR = +TARGET_SO = $(TARGET_SO_DIR)$(DLLIB) +CLEANLIBS = $(TARGET_SO) false +CLEANOBJS = $(OBJS) *.bak +TARGET_SO_DIR_TIMESTAMP = $(TIMESTAMP_DIR)/.sitearchdir.-.json.-.ext.time + +all: $(DLLIB) +static: $(STATIC_LIB) +.PHONY: all install static install-so install-rb +.PHONY: clean clean-so clean-static clean-rb + +clean-static:: +clean-rb-default:: +clean-rb:: +clean-so:: +clean: clean-so clean-static clean-rb-default clean-rb + -$(Q)$(RM_RF) $(CLEANLIBS) $(CLEANOBJS) $(CLEANFILES) .*.time + +distclean-rb-default:: +distclean-rb:: +distclean-so:: +distclean-static:: +distclean: clean distclean-so distclean-static distclean-rb-default distclean-rb + -$(Q)$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log + -$(Q)$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES) + -$(Q)$(RMDIRS) $(DISTCLEANDIRS) 2> /dev/null || true + +realclean: distclean +install: install-so install-rb + +install-so: $(DLLIB) $(TARGET_SO_DIR_TIMESTAMP) + $(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR) +clean-static:: + -$(Q)$(RM) $(STATIC_LIB) +install-rb: pre-install-rb do-install-rb install-rb-default +install-rb-default: pre-install-rb-default do-install-rb-default +pre-install-rb: Makefile +pre-install-rb-default: Makefile +do-install-rb: +do-install-rb-default: +pre-install-rb-default: + @$(NULLCMD) +$(TARGET_SO_DIR_TIMESTAMP): + $(Q) $(MAKEDIRS) $(@D) $(RUBYARCHDIR) + $(Q) $(TOUCH) $@ + +site-install: site-install-so site-install-rb +site-install-so: install-so +site-install-rb: install-rb + +.SUFFIXES: .c .m .cc .mm .cxx .cpp .o .S + +.cc.o: + $(ECHO) compiling $(<) + $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$< + +.cc.S: + $(ECHO) translating $(<) + $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$< + +.mm.o: + $(ECHO) compiling $(<) + $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$< + +.mm.S: + $(ECHO) translating $(<) + $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$< + +.cxx.o: + $(ECHO) compiling $(<) + $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$< + +.cxx.S: + $(ECHO) translating $(<) + $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$< + +.cpp.o: + $(ECHO) compiling $(<) + $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$< + +.cpp.S: + $(ECHO) translating $(<) + $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$< + +.c.o: + $(ECHO) compiling $(<) + $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$< + +.c.S: + $(ECHO) translating $(<) + $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$< + +.m.o: + $(ECHO) compiling $(<) + $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$< + +.m.S: + $(ECHO) translating $(<) + $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$< + +$(TARGET_SO): $(OBJS) Makefile + $(ECHO) linking shared-object json/ext/$(DLLIB) + -$(Q)$(RM) $(@) + $(Q) $(LDSHARED) -o $@ $(OBJS) $(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS) + + + +$(OBJS): $(HDRS) $(ruby_headers) diff --git a/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/ext/json/ext/generator/extconf.rb b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/ext/json/ext/generator/extconf.rb new file mode 100644 index 0000000..ee1bbea --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/ext/json/ext/generator/extconf.rb @@ -0,0 +1,16 @@ +require 'mkmf' + +if RUBY_ENGINE == 'truffleruby' + # The pure-Ruby generator is faster on TruffleRuby, so skip compiling the generator extension + File.write('Makefile', dummy_makefile("").join) +else + append_cflags("-std=c99") + $defs << "-DJSON_GENERATOR" + $defs << "-DJSON_DEBUG" if ENV.fetch("JSON_DEBUG", "0") != "0" + + if enable_config('generator-use-simd', default=!ENV["JSON_DISABLE_SIMD"]) + load __dir__ + "/../simd/conf.rb" + end + + create_makefile 'json/ext/generator' +end diff --git a/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/ext/json/ext/generator/generator.c b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/ext/json/ext/generator/generator.c new file mode 100644 index 0000000..d202e97 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/ext/json/ext/generator/generator.c @@ -0,0 +1,2202 @@ +#include "../json.h" +#include "../fbuffer/fbuffer.h" +#include "../vendor/fpconv.c" + +#include +#include + +#include "../simd/simd.h" + +/* ruby api and some helpers */ + +enum duplicate_key_action { + JSON_DEPRECATED = 0, + JSON_IGNORE, + JSON_RAISE, +}; + +typedef struct JSON_Generator_StateStruct { + VALUE indent; + VALUE space; + VALUE space_before; + VALUE object_nl; + VALUE array_nl; + VALUE as_json; + + long max_nesting; + long depth; + long buffer_initial_length; + + enum duplicate_key_action on_duplicate_key; + + bool as_json_single_arg; + bool allow_nan; + bool ascii_only; + bool script_safe; + bool strict; +} JSON_Generator_State; + +static VALUE mJSON, cState, cFragment, eGeneratorError, eNestingError, Encoding_UTF_8; + +static ID i_to_s, i_to_json, i_new, i_encode; +static VALUE sym_indent, sym_space, sym_space_before, sym_object_nl, sym_array_nl, sym_max_nesting, sym_allow_nan, sym_allow_duplicate_key, + sym_ascii_only, sym_depth, sym_buffer_initial_length, sym_script_safe, sym_escape_slash, sym_strict, sym_as_json; + + +#define GET_STATE_TO(self, state) \ + TypedData_Get_Struct(self, JSON_Generator_State, &JSON_Generator_State_type, state) + +#define GET_STATE(self) \ + JSON_Generator_State *state; \ + GET_STATE_TO(self, state) + +struct generate_json_data; + +typedef void (*generator_func)(FBuffer *buffer, struct generate_json_data *data, VALUE obj); + +struct generate_json_data { + FBuffer *buffer; + VALUE vstate; + JSON_Generator_State *state; + VALUE obj; + generator_func func; + long depth; +}; + +static VALUE cState_from_state_s(VALUE self, VALUE opts); +static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func, VALUE io); +static void generate_json(FBuffer *buffer, struct generate_json_data *data, VALUE obj); +static void generate_json_object(FBuffer *buffer, struct generate_json_data *data, VALUE obj); +static void generate_json_array(FBuffer *buffer, struct generate_json_data *data, VALUE obj); +static void generate_json_string(FBuffer *buffer, struct generate_json_data *data, VALUE obj); +static void generate_json_null(FBuffer *buffer, struct generate_json_data *data, VALUE obj); +static void generate_json_false(FBuffer *buffer, struct generate_json_data *data, VALUE obj); +static void generate_json_true(FBuffer *buffer, struct generate_json_data *data, VALUE obj); +#ifdef RUBY_INTEGER_UNIFICATION +static void generate_json_integer(FBuffer *buffer, struct generate_json_data *data, VALUE obj); +#endif +static void generate_json_fixnum(FBuffer *buffer, struct generate_json_data *data, VALUE obj); +static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *data, VALUE obj); +static void generate_json_float(FBuffer *buffer, struct generate_json_data *data, VALUE obj); +static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *data, VALUE obj); + +static int usascii_encindex, utf8_encindex, binary_encindex; + +NORETURN(static void) raise_generator_error_str(VALUE invalid_object, VALUE str) +{ + rb_enc_associate_index(str, utf8_encindex); + VALUE exc = rb_exc_new_str(eGeneratorError, str); + rb_ivar_set(exc, rb_intern("@invalid_object"), invalid_object); + rb_exc_raise(exc); +} + +#ifdef RBIMPL_ATTR_FORMAT +RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 2, 3) +#endif +NORETURN(static void) raise_generator_error(VALUE invalid_object, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + VALUE str = rb_vsprintf(fmt, args); + va_end(args); + raise_generator_error_str(invalid_object, str); +} + +// 0 - single byte char that don't need to be escaped. +// (x | 8) - char that needs to be escaped. +static const unsigned char CHAR_LENGTH_MASK = 7; +static const unsigned char ESCAPE_MASK = 8; + +typedef struct _search_state { + const char *ptr; + const char *end; + const char *cursor; + FBuffer *buffer; + +#ifdef HAVE_SIMD + const char *chunk_base; + const char *chunk_end; + bool has_matches; + +#if defined(HAVE_SIMD_NEON) + uint64_t matches_mask; +#elif defined(HAVE_SIMD_SSE2) + int matches_mask; +#else +#error "Unknown SIMD Implementation." +#endif /* HAVE_SIMD_NEON */ +#endif /* HAVE_SIMD */ +} search_state; + +ALWAYS_INLINE(static) void search_flush(search_state *search) +{ + // Do not remove this conditional without profiling, specifically escape-heavy text. + // escape_UTF8_char_basic will advance search->ptr and search->cursor (effectively a search_flush). + // For back-to-back characters that need to be escaped, specifically for the SIMD code paths, this method + // will be called just before calling escape_UTF8_char_basic. There will be no characters to append for the + // consecutive characters that need to be escaped. While the fbuffer_append is a no-op if + // nothing needs to be flushed, we can save a few memory references with this conditional. + if (search->ptr > search->cursor) { + fbuffer_append(search->buffer, search->cursor, search->ptr - search->cursor); + search->cursor = search->ptr; + } +} + +static const unsigned char escape_table_basic[256] = { + // ASCII Control Characters + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + // ASCII Characters + 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // '"' + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, // '\\' + 0, 0, 0, 0, 0, 0, 0, 0, 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 unsigned char (*search_escape_basic_impl)(search_state *); + +static inline unsigned char search_escape_basic(search_state *search) +{ + while (search->ptr < search->end) { + if (RB_UNLIKELY(escape_table_basic[(const unsigned char)*search->ptr])) { + search_flush(search); + return 1; + } else { + search->ptr++; + } + } + search_flush(search); + return 0; +} + +ALWAYS_INLINE(static) void escape_UTF8_char_basic(search_state *search) +{ + const unsigned char ch = (unsigned char)*search->ptr; + switch (ch) { + case '"': fbuffer_append(search->buffer, "\\\"", 2); break; + case '\\': fbuffer_append(search->buffer, "\\\\", 2); break; + case '/': fbuffer_append(search->buffer, "\\/", 2); break; + case '\b': fbuffer_append(search->buffer, "\\b", 2); break; + case '\f': fbuffer_append(search->buffer, "\\f", 2); break; + case '\n': fbuffer_append(search->buffer, "\\n", 2); break; + case '\r': fbuffer_append(search->buffer, "\\r", 2); break; + case '\t': fbuffer_append(search->buffer, "\\t", 2); break; + default: { + const char *hexdig = "0123456789abcdef"; + char scratch[6] = { '\\', 'u', '0', '0', 0, 0 }; + scratch[4] = hexdig[(ch >> 4) & 0xf]; + scratch[5] = hexdig[ch & 0xf]; + fbuffer_append(search->buffer, scratch, 6); + break; + } + } + search->ptr++; + search->cursor = search->ptr; +} + +/* Converts in_string to a JSON string (without the wrapping '"' + * characters) in FBuffer out_buffer. + * + * Character are JSON-escaped according to: + * + * - Always: ASCII control characters (0x00-0x1F), dquote, and + * backslash. + * + * - If out_ascii_only: non-ASCII characters (>0x7F) + * + * - If script_safe: forwardslash (/), line separator (U+2028), and + * paragraph separator (U+2029) + * + * Everything else (should be UTF-8) is just passed through and + * appended to the result. + */ +static inline void convert_UTF8_to_JSON(search_state *search) +{ + while (search_escape_basic_impl(search)) { + escape_UTF8_char_basic(search); + } +} + +static inline void escape_UTF8_char(search_state *search, unsigned char ch_len) +{ + const unsigned char ch = (unsigned char)*search->ptr; + switch (ch_len) { + case 1: { + switch (ch) { + case '"': fbuffer_append(search->buffer, "\\\"", 2); break; + case '\\': fbuffer_append(search->buffer, "\\\\", 2); break; + case '/': fbuffer_append(search->buffer, "\\/", 2); break; + case '\b': fbuffer_append(search->buffer, "\\b", 2); break; + case '\f': fbuffer_append(search->buffer, "\\f", 2); break; + case '\n': fbuffer_append(search->buffer, "\\n", 2); break; + case '\r': fbuffer_append(search->buffer, "\\r", 2); break; + case '\t': fbuffer_append(search->buffer, "\\t", 2); break; + default: { + const char *hexdig = "0123456789abcdef"; + char scratch[6] = { '\\', 'u', '0', '0', 0, 0 }; + scratch[4] = hexdig[(ch >> 4) & 0xf]; + scratch[5] = hexdig[ch & 0xf]; + fbuffer_append(search->buffer, scratch, 6); + break; + } + } + break; + } + case 3: { + if (search->ptr[2] & 1) { + fbuffer_append(search->buffer, "\\u2029", 6); + } else { + fbuffer_append(search->buffer, "\\u2028", 6); + } + break; + } + } + search->cursor = (search->ptr += ch_len); +} + +#ifdef HAVE_SIMD + +ALWAYS_INLINE(static) char *copy_remaining_bytes(search_state *search, unsigned long vec_len, unsigned long len) +{ + // Flush the buffer so everything up until the last 'len' characters are unflushed. + search_flush(search); + + FBuffer *buf = search->buffer; + fbuffer_inc_capa(buf, vec_len); + + char *s = (buf->ptr + buf->len); + + // Pad the buffer with dummy characters that won't need escaping. + // This seem wateful at first sight, but memset of vector length is very fast. + memset(s, 'X', vec_len); + + // Optimistically copy the remaining 'len' characters to the output FBuffer. If there are no characters + // to escape, then everything ends up in the correct spot. Otherwise it was convenient temporary storage. + MEMCPY(s, search->ptr, char, len); + + return s; +} + +#ifdef HAVE_SIMD_NEON + +ALWAYS_INLINE(static) unsigned char neon_next_match(search_state *search) +{ + uint64_t mask = search->matches_mask; + uint32_t index = trailing_zeros64(mask) >> 2; + + // It is assumed escape_UTF8_char_basic will only ever increase search->ptr by at most one character. + // If we want to use a similar approach for full escaping we'll need to ensure: + // search->chunk_base + index >= search->ptr + // However, since we know escape_UTF8_char_basic only increases search->ptr by one, if the next match + // is one byte after the previous match then: + // search->chunk_base + index == search->ptr + search->ptr = search->chunk_base + index; + mask &= mask - 1; + search->matches_mask = mask; + search_flush(search); + return 1; +} + +static inline unsigned char search_escape_basic_neon(search_state *search) +{ + if (RB_UNLIKELY(search->has_matches)) { + // There are more matches if search->matches_mask > 0. + if (search->matches_mask > 0) { + return neon_next_match(search); + } else { + // neon_next_match will only advance search->ptr up to the last matching character. + // Skip over any characters in the last chunk that occur after the last match. + search->has_matches = false; + search->ptr = search->chunk_end; + } + } + + /* + * The code below implements an SIMD-based algorithm to determine if N bytes at a time + * need to be escaped. + * + * Assume the ptr = "Te\sting!" (the double quotes are included in the string) + * + * The explanation will be limited to the first 8 bytes of the string for simplicity. However + * the vector insructions may work on larger vectors. + * + * First, we load three constants 'lower_bound', 'backslash' and 'dblquote" in vector registers. + * + * lower_bound: [20 20 20 20 20 20 20 20] + * backslash: [5C 5C 5C 5C 5C 5C 5C 5C] + * dblquote: [22 22 22 22 22 22 22 22] + * + * Next we load the first chunk of the ptr: + * [22 54 65 5C 73 74 69 6E] (" T e \ s t i n) + * + * First we check if any byte in chunk is less than 32 (0x20). This returns the following vector + * as no bytes are less than 32 (0x20): + * [0 0 0 0 0 0 0 0] + * + * Next, we check if any byte in chunk is equal to a backslash: + * [0 0 0 FF 0 0 0 0] + * + * Finally we check if any byte in chunk is equal to a double quote: + * [FF 0 0 0 0 0 0 0] + * + * Now we have three vectors where each byte indicates if the corresponding byte in chunk + * needs to be escaped. We combine these vectors with a series of logical OR instructions. + * This is the needs_escape vector and it is equal to: + * [FF 0 0 FF 0 0 0 0] + * + * Next we compute the bitwise AND between each byte and 0x1 and compute the horizontal sum of + * the values in the vector. This computes how many bytes need to be escaped within this chunk. + * + * Finally we compute a mask that indicates which bytes need to be escaped. If the mask is 0 then, + * no bytes need to be escaped and we can continue to the next chunk. If the mask is not 0 then we + * have at least one byte that needs to be escaped. + */ + + if (string_scan_simd_neon(&search->ptr, search->end, &search->matches_mask)) { + search->has_matches = true; + search->chunk_base = search->ptr; + search->chunk_end = search->ptr + sizeof(uint8x16_t); + return neon_next_match(search); + } + + // There are fewer than 16 bytes left. + unsigned long remaining = (search->end - search->ptr); + if (remaining >= SIMD_MINIMUM_THRESHOLD) { + char *s = copy_remaining_bytes(search, sizeof(uint8x16_t), remaining); + + uint64_t mask = compute_chunk_mask_neon(s); + + if (!mask) { + // Nothing to escape, ensure search_flush doesn't do anything by setting + // search->cursor to search->ptr. + fbuffer_consumed(search->buffer, remaining); + search->ptr = search->end; + search->cursor = search->end; + return 0; + } + + search->matches_mask = mask; + search->has_matches = true; + search->chunk_end = search->end; + search->chunk_base = search->ptr; + return neon_next_match(search); + } + + if (search->ptr < search->end) { + return search_escape_basic(search); + } + + search_flush(search); + return 0; +} +#endif /* HAVE_SIMD_NEON */ + +#ifdef HAVE_SIMD_SSE2 + +ALWAYS_INLINE(static) unsigned char sse2_next_match(search_state *search) +{ + int mask = search->matches_mask; + int index = trailing_zeros(mask); + + // It is assumed escape_UTF8_char_basic will only ever increase search->ptr by at most one character. + // If we want to use a similar approach for full escaping we'll need to ensure: + // search->chunk_base + index >= search->ptr + // However, since we know escape_UTF8_char_basic only increases search->ptr by one, if the next match + // is one byte after the previous match then: + // search->chunk_base + index == search->ptr + search->ptr = search->chunk_base + index; + mask &= mask - 1; + search->matches_mask = mask; + search_flush(search); + return 1; +} + +#if defined(__clang__) || defined(__GNUC__) +#define TARGET_SSE2 __attribute__((target("sse2"))) +#else +#define TARGET_SSE2 +#endif + +ALWAYS_INLINE(static) TARGET_SSE2 unsigned char search_escape_basic_sse2(search_state *search) +{ + if (RB_UNLIKELY(search->has_matches)) { + // There are more matches if search->matches_mask > 0. + if (search->matches_mask > 0) { + return sse2_next_match(search); + } else { + // sse2_next_match will only advance search->ptr up to the last matching character. + // Skip over any characters in the last chunk that occur after the last match. + search->has_matches = false; + if (RB_UNLIKELY(search->chunk_base + sizeof(__m128i) >= search->end)) { + search->ptr = search->end; + } else { + search->ptr = search->chunk_base + sizeof(__m128i); + } + } + } + + if (string_scan_simd_sse2(&search->ptr, search->end, &search->matches_mask)) { + search->has_matches = true; + search->chunk_base = search->ptr; + search->chunk_end = search->ptr + sizeof(__m128i); + return sse2_next_match(search); + } + + // There are fewer than 16 bytes left. + unsigned long remaining = (search->end - search->ptr); + if (remaining >= SIMD_MINIMUM_THRESHOLD) { + char *s = copy_remaining_bytes(search, sizeof(__m128i), remaining); + + int needs_escape_mask = compute_chunk_mask_sse2(s); + + if (needs_escape_mask == 0) { + // Nothing to escape, ensure search_flush doesn't do anything by setting + // search->cursor to search->ptr. + fbuffer_consumed(search->buffer, remaining); + search->ptr = search->end; + search->cursor = search->end; + return 0; + } + + search->has_matches = true; + search->matches_mask = needs_escape_mask; + search->chunk_base = search->ptr; + return sse2_next_match(search); + } + + if (search->ptr < search->end) { + return search_escape_basic(search); + } + + search_flush(search); + return 0; +} + +#endif /* HAVE_SIMD_SSE2 */ + +#endif /* HAVE_SIMD */ + +static const unsigned char script_safe_escape_table[256] = { + // ASCII Control Characters + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + // ASCII Characters + 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, // '"' and '/' + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, // '\\' + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // Continuation byte + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + // First byte of a 2-byte code point + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + // First byte of a 3-byte code point + 3, 3,11, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xE2 is the start of \u2028 and \u2029 + //First byte of a 4+ byte code point + 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 9, 9, +}; + +static inline unsigned char search_script_safe_escape(search_state *search) +{ + while (search->ptr < search->end) { + unsigned char ch = (unsigned char)*search->ptr; + unsigned char ch_len = script_safe_escape_table[ch]; + + if (RB_UNLIKELY(ch_len)) { + if (ch_len & ESCAPE_MASK) { + if (RB_UNLIKELY(ch_len == 11)) { + const unsigned char *uptr = (const unsigned char *)search->ptr; + if (!(uptr[1] == 0x80 && (uptr[2] >> 1) == 0x54)) { + search->ptr += 3; + continue; + } + } + search_flush(search); + return ch_len & CHAR_LENGTH_MASK; + } else { + search->ptr += ch_len; + } + } else { + search->ptr++; + } + } + search_flush(search); + return 0; +} + +static void convert_UTF8_to_script_safe_JSON(search_state *search) +{ + unsigned char ch_len; + while ((ch_len = search_script_safe_escape(search))) { + escape_UTF8_char(search, ch_len); + } +} + +static const unsigned char ascii_only_escape_table[256] = { + // ASCII Control Characters + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + // ASCII Characters + 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // '"' + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, // '\\' + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // Continuation byte + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + // First byte of a 2-byte code point + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + // First byte of a 3-byte code point + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + //First byte of a 4+ byte code point + 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 9, 9, +}; + +static inline unsigned char search_ascii_only_escape(search_state *search, const unsigned char escape_table[256]) +{ + while (search->ptr < search->end) { + unsigned char ch = (unsigned char)*search->ptr; + unsigned char ch_len = escape_table[ch]; + + if (RB_UNLIKELY(ch_len)) { + search_flush(search); + return ch_len & CHAR_LENGTH_MASK; + } else { + search->ptr++; + } + } + search_flush(search); + return 0; +} + +static inline void full_escape_UTF8_char(search_state *search, unsigned char ch_len) +{ + const unsigned char ch = (unsigned char)*search->ptr; + switch (ch_len) { + case 1: { + switch (ch) { + case '"': fbuffer_append(search->buffer, "\\\"", 2); break; + case '\\': fbuffer_append(search->buffer, "\\\\", 2); break; + case '/': fbuffer_append(search->buffer, "\\/", 2); break; + case '\b': fbuffer_append(search->buffer, "\\b", 2); break; + case '\f': fbuffer_append(search->buffer, "\\f", 2); break; + case '\n': fbuffer_append(search->buffer, "\\n", 2); break; + case '\r': fbuffer_append(search->buffer, "\\r", 2); break; + case '\t': fbuffer_append(search->buffer, "\\t", 2); break; + default: { + const char *hexdig = "0123456789abcdef"; + char scratch[6] = { '\\', 'u', '0', '0', 0, 0 }; + scratch[4] = hexdig[(ch >> 4) & 0xf]; + scratch[5] = hexdig[ch & 0xf]; + fbuffer_append(search->buffer, scratch, 6); + break; + } + } + break; + } + default: { + const char *hexdig = "0123456789abcdef"; + char scratch[12] = { '\\', 'u', 0, 0, 0, 0, '\\', 'u' }; + + uint32_t wchar = 0; + + switch (ch_len) { + case 2: + wchar = ch & 0x1F; + break; + case 3: + wchar = ch & 0x0F; + break; + case 4: + wchar = ch & 0x07; + break; + } + + for (short i = 1; i < ch_len; i++) { + wchar = (wchar << 6) | (search->ptr[i] & 0x3F); + } + + if (wchar <= 0xFFFF) { + scratch[2] = hexdig[wchar >> 12]; + scratch[3] = hexdig[(wchar >> 8) & 0xf]; + scratch[4] = hexdig[(wchar >> 4) & 0xf]; + scratch[5] = hexdig[wchar & 0xf]; + fbuffer_append(search->buffer, scratch, 6); + } else { + uint16_t hi, lo; + wchar -= 0x10000; + hi = 0xD800 + (uint16_t)(wchar >> 10); + lo = 0xDC00 + (uint16_t)(wchar & 0x3FF); + + scratch[2] = hexdig[hi >> 12]; + scratch[3] = hexdig[(hi >> 8) & 0xf]; + scratch[4] = hexdig[(hi >> 4) & 0xf]; + scratch[5] = hexdig[hi & 0xf]; + + scratch[8] = hexdig[lo >> 12]; + scratch[9] = hexdig[(lo >> 8) & 0xf]; + scratch[10] = hexdig[(lo >> 4) & 0xf]; + scratch[11] = hexdig[lo & 0xf]; + + fbuffer_append(search->buffer, scratch, 12); + } + + break; + } + } + search->cursor = (search->ptr += ch_len); +} + +static void convert_UTF8_to_ASCII_only_JSON(search_state *search, const unsigned char escape_table[256]) +{ + unsigned char ch_len; + while ((ch_len = search_ascii_only_escape(search, escape_table))) { + full_escape_UTF8_char(search, ch_len); + } +} + +/* + * Document-module: JSON::Ext::Generator + * + * This is the JSON generator implemented as a C extension. It can be + * configured to be used by setting + * + * JSON.generator = JSON::Ext::Generator + * + * with the method generator= in JSON. + * + */ + +/* Explanation of the following: that's the only way to not pollute + * standard library's docs with GeneratorMethods:: which + * are uninformative and take a large place in a list of classes + */ + +/* + * Document-module: JSON::Ext::Generator::GeneratorMethods + * :nodoc: + */ + +/* + * Document-module: JSON::Ext::Generator::GeneratorMethods::Array + * :nodoc: + */ + +/* + * Document-module: JSON::Ext::Generator::GeneratorMethods::Bignum + * :nodoc: + */ + +/* + * Document-module: JSON::Ext::Generator::GeneratorMethods::FalseClass + * :nodoc: + */ + +/* + * Document-module: JSON::Ext::Generator::GeneratorMethods::Fixnum + * :nodoc: + */ + +/* + * Document-module: JSON::Ext::Generator::GeneratorMethods::Float + * :nodoc: + */ + +/* + * Document-module: JSON::Ext::Generator::GeneratorMethods::Hash + * :nodoc: + */ + +/* + * Document-module: JSON::Ext::Generator::GeneratorMethods::Integer + * :nodoc: + */ + +/* + * Document-module: JSON::Ext::Generator::GeneratorMethods::NilClass + * :nodoc: + */ + +/* + * Document-module: JSON::Ext::Generator::GeneratorMethods::Object + * :nodoc: + */ + +/* + * Document-module: JSON::Ext::Generator::GeneratorMethods::String + * :nodoc: + */ + +/* + * Document-module: JSON::Ext::Generator::GeneratorMethods::String::Extend + * :nodoc: + */ + +/* + * Document-module: JSON::Ext::Generator::GeneratorMethods::TrueClass + * :nodoc: + */ + +/* + * call-seq: to_json(state = nil) + * + * Returns a JSON string containing a JSON object, that is generated from + * this Hash instance. + * _state_ is a JSON::State object, that can also be used to configure the + * produced JSON string output further. + */ +static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self) +{ + rb_check_arity(argc, 0, 1); + VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil); + return cState_partial_generate(Vstate, self, generate_json_object, Qfalse); +} + +/* + * call-seq: to_json(state = nil) + * + * Returns a JSON string containing a JSON array, that is generated from + * this Array instance. + * _state_ is a JSON::State object, that can also be used to configure the + * produced JSON string output further. + */ +static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self) +{ + rb_check_arity(argc, 0, 1); + VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil); + return cState_partial_generate(Vstate, self, generate_json_array, Qfalse); +} + +#ifdef RUBY_INTEGER_UNIFICATION +/* + * call-seq: to_json(*) + * + * Returns a JSON string representation for this Integer number. + */ +static VALUE mInteger_to_json(int argc, VALUE *argv, VALUE self) +{ + rb_check_arity(argc, 0, 1); + VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil); + return cState_partial_generate(Vstate, self, generate_json_integer, Qfalse); +} + +#else +/* + * call-seq: to_json(*) + * + * Returns a JSON string representation for this Integer number. + */ +static VALUE mFixnum_to_json(int argc, VALUE *argv, VALUE self) +{ + rb_check_arity(argc, 0, 1); + VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil); + return cState_partial_generate(Vstate, self, generate_json_fixnum, Qfalse); +} + +/* + * call-seq: to_json(*) + * + * Returns a JSON string representation for this Integer number. + */ +static VALUE mBignum_to_json(int argc, VALUE *argv, VALUE self) +{ + rb_check_arity(argc, 0, 1); + VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil); + return cState_partial_generate(Vstate, self, generate_json_bignum, Qfalse); +} +#endif + +/* + * call-seq: to_json(*) + * + * Returns a JSON string representation for this Float number. + */ +static VALUE mFloat_to_json(int argc, VALUE *argv, VALUE self) +{ + rb_check_arity(argc, 0, 1); + VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil); + return cState_partial_generate(Vstate, self, generate_json_float, Qfalse); +} + +/* + * call-seq: to_json(*) + * + * This string should be encoded with UTF-8 A call to this method + * returns a JSON string encoded with UTF16 big endian characters as + * \u????. + */ +static VALUE mString_to_json(int argc, VALUE *argv, VALUE self) +{ + rb_check_arity(argc, 0, 1); + VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil); + return cState_partial_generate(Vstate, self, generate_json_string, Qfalse); +} + +/* + * call-seq: to_json(*) + * + * Returns a JSON string for true: 'true'. + */ +static VALUE mTrueClass_to_json(int argc, VALUE *argv, VALUE self) +{ + rb_check_arity(argc, 0, 1); + return rb_utf8_str_new("true", 4); +} + +/* + * call-seq: to_json(*) + * + * Returns a JSON string for false: 'false'. + */ +static VALUE mFalseClass_to_json(int argc, VALUE *argv, VALUE self) +{ + rb_check_arity(argc, 0, 1); + return rb_utf8_str_new("false", 5); +} + +/* + * call-seq: to_json(*) + * + * Returns a JSON string for nil: 'null'. + */ +static VALUE mNilClass_to_json(int argc, VALUE *argv, VALUE self) +{ + rb_check_arity(argc, 0, 1); + return rb_utf8_str_new("null", 4); +} + +/* + * call-seq: to_json(*) + * + * Converts this object to a string (calling #to_s), converts + * it to a JSON string, and returns the result. This is a fallback, if no + * special method #to_json was defined for some object. + */ +static VALUE mObject_to_json(int argc, VALUE *argv, VALUE self) +{ + VALUE state; + VALUE string = rb_funcall(self, i_to_s, 0); + rb_scan_args(argc, argv, "01", &state); + Check_Type(string, T_STRING); + state = cState_from_state_s(cState, state); + return cState_partial_generate(state, string, generate_json_string, Qfalse); +} + +static void State_mark(void *ptr) +{ + JSON_Generator_State *state = ptr; + rb_gc_mark_movable(state->indent); + rb_gc_mark_movable(state->space); + rb_gc_mark_movable(state->space_before); + rb_gc_mark_movable(state->object_nl); + rb_gc_mark_movable(state->array_nl); + rb_gc_mark_movable(state->as_json); +} + +static void State_compact(void *ptr) +{ + JSON_Generator_State *state = ptr; + state->indent = rb_gc_location(state->indent); + state->space = rb_gc_location(state->space); + state->space_before = rb_gc_location(state->space_before); + state->object_nl = rb_gc_location(state->object_nl); + state->array_nl = rb_gc_location(state->array_nl); + state->as_json = rb_gc_location(state->as_json); +} + +static void State_free(void *ptr) +{ + JSON_Generator_State *state = ptr; + ruby_xfree(state); +} + +static size_t State_memsize(const void *ptr) +{ + return sizeof(JSON_Generator_State); +} + +static const rb_data_type_t JSON_Generator_State_type = { + "JSON/Generator/State", + { + .dmark = State_mark, + .dfree = State_free, + .dsize = State_memsize, + .dcompact = State_compact, + }, + 0, 0, + RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE, +}; + +static void state_init(JSON_Generator_State *state) +{ + state->max_nesting = 100; + state->buffer_initial_length = FBUFFER_INITIAL_LENGTH_DEFAULT; +} + +static VALUE cState_s_allocate(VALUE klass) +{ + JSON_Generator_State *state; + VALUE obj = TypedData_Make_Struct(klass, JSON_Generator_State, &JSON_Generator_State_type, state); + state_init(state); + return obj; +} + +static void vstate_spill(struct generate_json_data *data) +{ + VALUE vstate = cState_s_allocate(cState); + GET_STATE(vstate); + MEMCPY(state, data->state, JSON_Generator_State, 1); + data->state = state; + data->vstate = vstate; + RB_OBJ_WRITTEN(vstate, Qundef, state->indent); + RB_OBJ_WRITTEN(vstate, Qundef, state->space); + RB_OBJ_WRITTEN(vstate, Qundef, state->space_before); + RB_OBJ_WRITTEN(vstate, Qundef, state->object_nl); + RB_OBJ_WRITTEN(vstate, Qundef, state->array_nl); + RB_OBJ_WRITTEN(vstate, Qundef, state->as_json); +} + +static inline VALUE json_call_to_json(struct generate_json_data *data, VALUE obj) +{ + if (RB_UNLIKELY(!data->vstate)) { + vstate_spill(data); + } + GET_STATE(data->vstate); + state->depth = data->depth; + VALUE tmp = rb_funcall(obj, i_to_json, 1, data->vstate); + // no need to restore state->depth, vstate is just a temporary State + return tmp; +} + +static VALUE +json_call_as_json(JSON_Generator_State *state, VALUE object, VALUE is_key) +{ + VALUE proc_args[2] = {object, is_key}; + return rb_proc_call_with_block(state->as_json, 2, proc_args, Qnil); +} + +static VALUE +convert_string_subclass(VALUE key) +{ + VALUE key_to_s = rb_funcall(key, i_to_s, 0); + + if (RB_UNLIKELY(!RB_TYPE_P(key_to_s, T_STRING))) { + VALUE cname = rb_obj_class(key); + rb_raise(rb_eTypeError, + "can't convert %"PRIsVALUE" to %s (%"PRIsVALUE"#%s gives %"PRIsVALUE")", + cname, "String", cname, "to_s", rb_obj_class(key_to_s)); + } + + return key_to_s; +} + +static bool enc_utf8_compatible_p(int enc_idx) +{ + if (enc_idx == usascii_encindex) return true; + if (enc_idx == utf8_encindex) return true; + return false; +} + +static VALUE encode_json_string_try(VALUE str) +{ + return rb_funcall(str, i_encode, 1, Encoding_UTF_8); +} + +static VALUE encode_json_string_rescue(VALUE str, VALUE exception) +{ + raise_generator_error_str(str, rb_funcall(exception, rb_intern("message"), 0)); + return Qundef; +} + +static inline bool valid_json_string_p(VALUE str) +{ + int coderange = rb_enc_str_coderange(str); + + if (RB_LIKELY(coderange == ENC_CODERANGE_7BIT)) { + return true; + } + + if (RB_LIKELY(coderange == ENC_CODERANGE_VALID)) { + return enc_utf8_compatible_p(RB_ENCODING_GET_INLINED(str)); + } + + return false; +} + +static inline VALUE ensure_valid_encoding(struct generate_json_data *data, VALUE str, bool as_json_called, bool is_key) +{ + if (RB_LIKELY(valid_json_string_p(str))) { + return str; + } + + if (!as_json_called && data->state->strict && RTEST(data->state->as_json)) { + VALUE coerced_str = json_call_as_json(data->state, str, Qfalse); + if (coerced_str != str) { + if (RB_TYPE_P(coerced_str, T_STRING)) { + if (!valid_json_string_p(coerced_str)) { + raise_generator_error(str, "source sequence is illegal/malformed utf-8"); + } + } else { + // as_json could return another type than T_STRING + if (is_key) { + raise_generator_error(coerced_str, "%"PRIsVALUE" not allowed as object key in JSON", CLASS_OF(coerced_str)); + } + } + + return coerced_str; + } + } + + if (RB_ENCODING_GET_INLINED(str) == binary_encindex) { + VALUE utf8_string = rb_enc_associate_index(rb_str_dup(str), utf8_encindex); + switch (rb_enc_str_coderange(utf8_string)) { + case ENC_CODERANGE_7BIT: + return utf8_string; + case ENC_CODERANGE_VALID: + // For historical reason, we silently reinterpret binary strings as UTF-8 if it would work. + // TODO: Raise in 3.0.0 + rb_warn("JSON.generate: UTF-8 string passed as BINARY, this will raise an encoding error in json 3.0"); + return utf8_string; + break; + } + } + + return rb_rescue(encode_json_string_try, str, encode_json_string_rescue, str); +} + +static void raw_generate_json_string(FBuffer *buffer, struct generate_json_data *data, VALUE obj) +{ + fbuffer_append_char(buffer, '"'); + + long len; + search_state search; + search.buffer = buffer; + RSTRING_GETMEM(obj, search.ptr, len); + search.cursor = search.ptr; + search.end = search.ptr + len; + +#ifdef HAVE_SIMD + search.matches_mask = 0; + search.has_matches = false; + search.chunk_base = NULL; +#endif /* HAVE_SIMD */ + + switch (rb_enc_str_coderange(obj)) { + case ENC_CODERANGE_7BIT: + case ENC_CODERANGE_VALID: + if (RB_UNLIKELY(data->state->ascii_only)) { + convert_UTF8_to_ASCII_only_JSON(&search, data->state->script_safe ? script_safe_escape_table : ascii_only_escape_table); + } else if (RB_UNLIKELY(data->state->script_safe)) { + convert_UTF8_to_script_safe_JSON(&search); + } else { + convert_UTF8_to_JSON(&search); + } + break; + default: + raise_generator_error(obj, "source sequence is illegal/malformed utf-8"); + break; + } + fbuffer_append_char(buffer, '"'); +} + +static void generate_json_string(FBuffer *buffer, struct generate_json_data *data, VALUE obj) +{ + obj = ensure_valid_encoding(data, obj, false, false); + raw_generate_json_string(buffer, data, obj); +} + +struct hash_foreach_arg { + VALUE hash; + struct generate_json_data *data; + int first_key_type; + bool first; + bool mixed_keys_encountered; +}; + +NOINLINE(static) void +json_inspect_hash_with_mixed_keys(struct hash_foreach_arg *arg) +{ + if (arg->mixed_keys_encountered) { + return; + } + arg->mixed_keys_encountered = true; + + JSON_Generator_State *state = arg->data->state; + if (state->on_duplicate_key != JSON_IGNORE) { + VALUE do_raise = state->on_duplicate_key == JSON_RAISE ? Qtrue : Qfalse; + rb_funcall(mJSON, rb_intern("on_mixed_keys_hash"), 2, arg->hash, do_raise); + } +} + +static int +json_object_i(VALUE key, VALUE val, VALUE _arg) +{ + struct hash_foreach_arg *arg = (struct hash_foreach_arg *)_arg; + struct generate_json_data *data = arg->data; + + FBuffer *buffer = data->buffer; + JSON_Generator_State *state = data->state; + + long depth = data->depth; + int key_type = rb_type(key); + + if (arg->first) { + arg->first = false; + arg->first_key_type = key_type; + } + else { + fbuffer_append_char(buffer, ','); + } + + if (RB_UNLIKELY(data->state->object_nl)) { + fbuffer_append_str(buffer, data->state->object_nl); + } + if (RB_UNLIKELY(data->state->indent)) { + fbuffer_append_str_repeat(buffer, data->state->indent, depth); + } + + VALUE key_to_s; + bool as_json_called = false; + + start: + switch (key_type) { + case T_STRING: + if (RB_UNLIKELY(arg->first_key_type != T_STRING)) { + json_inspect_hash_with_mixed_keys(arg); + } + + if (RB_LIKELY(RBASIC_CLASS(key) == rb_cString)) { + key_to_s = key; + } else { + key_to_s = convert_string_subclass(key); + } + break; + case T_SYMBOL: + if (RB_UNLIKELY(arg->first_key_type != T_SYMBOL)) { + json_inspect_hash_with_mixed_keys(arg); + } + + key_to_s = rb_sym2str(key); + break; + default: + if (data->state->strict) { + if (RTEST(data->state->as_json) && !as_json_called) { + key = json_call_as_json(data->state, key, Qtrue); + key_type = rb_type(key); + as_json_called = true; + goto start; + } else { + raise_generator_error(key, "%"PRIsVALUE" not allowed as object key in JSON", CLASS_OF(key)); + } + } + key_to_s = rb_convert_type(key, T_STRING, "String", "to_s"); + break; + } + + key_to_s = ensure_valid_encoding(data, key_to_s, as_json_called, true); + + if (RB_LIKELY(RBASIC_CLASS(key_to_s) == rb_cString)) { + raw_generate_json_string(buffer, data, key_to_s); + } else { + generate_json(buffer, data, key_to_s); + } + if (RB_UNLIKELY(state->space_before)) fbuffer_append_str(buffer, data->state->space_before); + fbuffer_append_char(buffer, ':'); + if (RB_UNLIKELY(state->space)) fbuffer_append_str(buffer, data->state->space); + generate_json(buffer, data, val); + + return ST_CONTINUE; +} + +static inline long increase_depth(struct generate_json_data *data) +{ + JSON_Generator_State *state = data->state; + long depth = ++data->depth; + if (RB_UNLIKELY(depth > state->max_nesting && state->max_nesting)) { + rb_raise(eNestingError, "nesting of %ld is too deep. Did you try to serialize objects with circular references?", --data->depth); + } + return depth; +} + +static void generate_json_object(FBuffer *buffer, struct generate_json_data *data, VALUE obj) +{ + long depth = increase_depth(data); + + if (RHASH_SIZE(obj) == 0) { + fbuffer_append(buffer, "{}", 2); + --data->depth; + return; + } + + fbuffer_append_char(buffer, '{'); + + struct hash_foreach_arg arg = { + .hash = obj, + .data = data, + .first = true, + }; + rb_hash_foreach(obj, json_object_i, (VALUE)&arg); + + depth = --data->depth; + if (RB_UNLIKELY(data->state->object_nl)) { + fbuffer_append_str(buffer, data->state->object_nl); + if (RB_UNLIKELY(data->state->indent)) { + fbuffer_append_str_repeat(buffer, data->state->indent, depth); + } + } + fbuffer_append_char(buffer, '}'); +} + +static void generate_json_array(FBuffer *buffer, struct generate_json_data *data, VALUE obj) +{ + long depth = increase_depth(data); + + if (RARRAY_LEN(obj) == 0) { + fbuffer_append(buffer, "[]", 2); + --data->depth; + return; + } + + fbuffer_append_char(buffer, '['); + if (RB_UNLIKELY(data->state->array_nl)) fbuffer_append_str(buffer, data->state->array_nl); + for (int i = 0; i < RARRAY_LEN(obj); i++) { + if (i > 0) { + fbuffer_append_char(buffer, ','); + if (RB_UNLIKELY(data->state->array_nl)) fbuffer_append_str(buffer, data->state->array_nl); + } + if (RB_UNLIKELY(data->state->indent)) { + fbuffer_append_str_repeat(buffer, data->state->indent, depth); + } + generate_json(buffer, data, RARRAY_AREF(obj, i)); + } + data->depth = --depth; + if (RB_UNLIKELY(data->state->array_nl)) { + fbuffer_append_str(buffer, data->state->array_nl); + if (RB_UNLIKELY(data->state->indent)) { + fbuffer_append_str_repeat(buffer, data->state->indent, depth); + } + } + fbuffer_append_char(buffer, ']'); +} + +static void generate_json_fallback(FBuffer *buffer, struct generate_json_data *data, VALUE obj) +{ + VALUE tmp; + if (rb_respond_to(obj, i_to_json)) { + tmp = json_call_to_json(data, obj); + Check_Type(tmp, T_STRING); + fbuffer_append_str(buffer, tmp); + } else { + tmp = rb_funcall(obj, i_to_s, 0); + Check_Type(tmp, T_STRING); + generate_json_string(buffer, data, tmp); + } +} + +static inline void generate_json_symbol(FBuffer *buffer, struct generate_json_data *data, VALUE obj) +{ + if (data->state->strict) { + generate_json_string(buffer, data, rb_sym2str(obj)); + } else { + generate_json_fallback(buffer, data, obj); + } +} + +static void generate_json_null(FBuffer *buffer, struct generate_json_data *data, VALUE obj) +{ + fbuffer_append(buffer, "null", 4); +} + +static void generate_json_false(FBuffer *buffer, struct generate_json_data *data, VALUE obj) +{ + fbuffer_append(buffer, "false", 5); +} + +static void generate_json_true(FBuffer *buffer, struct generate_json_data *data, VALUE obj) +{ + fbuffer_append(buffer, "true", 4); +} + +static void generate_json_fixnum(FBuffer *buffer, struct generate_json_data *data, VALUE obj) +{ + fbuffer_append_long(buffer, FIX2LONG(obj)); +} + +static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *data, VALUE obj) +{ + VALUE tmp = rb_funcall(obj, i_to_s, 0); + fbuffer_append_str(buffer, tmp); +} + +#ifdef RUBY_INTEGER_UNIFICATION +static void generate_json_integer(FBuffer *buffer, struct generate_json_data *data, VALUE obj) +{ + if (FIXNUM_P(obj)) + generate_json_fixnum(buffer, data, obj); + else + generate_json_bignum(buffer, data, obj); +} +#endif + +static void generate_json_float(FBuffer *buffer, struct generate_json_data *data, VALUE obj) +{ + double value = RFLOAT_VALUE(obj); + char allow_nan = data->state->allow_nan; + if (isinf(value) || isnan(value)) { + /* for NaN and Infinity values we either raise an error or rely on Float#to_s. */ + if (!allow_nan) { + if (data->state->strict && data->state->as_json) { + VALUE casted_obj = json_call_as_json(data->state, obj, Qfalse); + if (casted_obj != obj) { + increase_depth(data); + generate_json(buffer, data, casted_obj); + data->depth--; + return; + } + } + raise_generator_error(obj, "%"PRIsVALUE" not allowed in JSON", rb_funcall(obj, i_to_s, 0)); + } + + VALUE tmp = rb_funcall(obj, i_to_s, 0); + fbuffer_append_str(buffer, tmp); + return; + } + + /* This implementation writes directly into the buffer. We reserve + * the 32 characters that fpconv_dtoa states as its maximum. + */ + fbuffer_inc_capa(buffer, 32); + char* d = buffer->ptr + buffer->len; + int len = fpconv_dtoa(value, d); + /* fpconv_dtoa converts a float to its shortest string representation, + * but it adds a ".0" if this is a plain integer. + */ + fbuffer_consumed(buffer, len); +} + +static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *data, VALUE obj) +{ + VALUE fragment = RSTRUCT_GET(obj, 0); + Check_Type(fragment, T_STRING); + fbuffer_append_str(buffer, fragment); +} + +static void generate_json(FBuffer *buffer, struct generate_json_data *data, VALUE obj) +{ + bool as_json_called = false; +start: + if (obj == Qnil) { + generate_json_null(buffer, data, obj); + } else if (obj == Qfalse) { + generate_json_false(buffer, data, obj); + } else if (obj == Qtrue) { + generate_json_true(buffer, data, obj); + } else if (RB_SPECIAL_CONST_P(obj)) { + if (RB_FIXNUM_P(obj)) { + generate_json_fixnum(buffer, data, obj); + } else if (RB_FLONUM_P(obj)) { + generate_json_float(buffer, data, obj); + } else if (RB_STATIC_SYM_P(obj)) { + generate_json_symbol(buffer, data, obj); + } else { + goto general; + } + } else { + VALUE klass = RBASIC_CLASS(obj); + switch (RB_BUILTIN_TYPE(obj)) { + case T_BIGNUM: + generate_json_bignum(buffer, data, obj); + break; + case T_HASH: + if (klass != rb_cHash) goto general; + generate_json_object(buffer, data, obj); + break; + case T_ARRAY: + if (klass != rb_cArray) goto general; + generate_json_array(buffer, data, obj); + break; + case T_STRING: + if (klass != rb_cString) goto general; + + if (RB_LIKELY(valid_json_string_p(obj))) { + raw_generate_json_string(buffer, data, obj); + } else if (as_json_called) { + raise_generator_error(obj, "source sequence is illegal/malformed utf-8"); + } else { + obj = ensure_valid_encoding(data, obj, false, false); + as_json_called = true; + goto start; + } + break; + case T_SYMBOL: + generate_json_symbol(buffer, data, obj); + break; + case T_FLOAT: + if (klass != rb_cFloat) goto general; + generate_json_float(buffer, data, obj); + break; + case T_STRUCT: + if (klass != cFragment) goto general; + generate_json_fragment(buffer, data, obj); + break; + default: + general: + if (data->state->strict) { + if (RTEST(data->state->as_json) && !as_json_called) { + obj = json_call_as_json(data->state, obj, Qfalse); + as_json_called = true; + goto start; + } else { + raise_generator_error(obj, "%"PRIsVALUE" not allowed in JSON", CLASS_OF(obj)); + } + } else { + generate_json_fallback(buffer, data, obj); + } + } + } +} + +static VALUE generate_json_try(VALUE d) +{ + struct generate_json_data *data = (struct generate_json_data *)d; + + data->func(data->buffer, data, data->obj); + + return fbuffer_finalize(data->buffer); +} + +static VALUE generate_json_ensure(VALUE d) +{ + struct generate_json_data *data = (struct generate_json_data *)d; + fbuffer_free(data->buffer); + + return Qundef; +} + +static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func func, VALUE io) +{ + GET_STATE(self); + + char stack_buffer[FBUFFER_STACK_SIZE]; + FBuffer buffer = { + .io = RTEST(io) ? io : Qfalse, + }; + fbuffer_stack_init(&buffer, state->buffer_initial_length, stack_buffer, FBUFFER_STACK_SIZE); + + struct generate_json_data data = { + .buffer = &buffer, + .vstate = Qfalse, // don't use self as it may be frozen and its depth is mutated when calling to_json + .state = state, + .depth = state->depth, + .obj = obj, + .func = func + }; + return rb_ensure(generate_json_try, (VALUE)&data, generate_json_ensure, (VALUE)&data); +} + +/* call-seq: + * generate(obj) -> String + * generate(obj, anIO) -> anIO + * + * Generates a valid JSON document from object +obj+ and returns the + * result. If no valid JSON document can be created this method raises a + * GeneratorError exception. + */ +static VALUE cState_generate(int argc, VALUE *argv, VALUE self) +{ + rb_check_arity(argc, 1, 2); + VALUE obj = argv[0]; + VALUE io = argc > 1 ? argv[1] : Qnil; + return cState_partial_generate(self, obj, generate_json, io); +} + +static VALUE cState_initialize(int argc, VALUE *argv, VALUE self) +{ + rb_warn("The json gem extension was loaded with the stdlib ruby code. You should upgrade rubygems with `gem update --system`"); + return self; +} + +/* + * call-seq: initialize_copy(orig) + * + * Initializes this object from orig if it can be duplicated/cloned and returns + * it. +*/ +static VALUE cState_init_copy(VALUE obj, VALUE orig) +{ + JSON_Generator_State *objState, *origState; + + if (obj == orig) return obj; + GET_STATE_TO(obj, objState); + GET_STATE_TO(orig, origState); + if (!objState) rb_raise(rb_eArgError, "unallocated JSON::State"); + + MEMCPY(objState, origState, JSON_Generator_State, 1); + objState->indent = origState->indent; + objState->space = origState->space; + objState->space_before = origState->space_before; + objState->object_nl = origState->object_nl; + objState->array_nl = origState->array_nl; + objState->as_json = origState->as_json; + return obj; +} + +/* + * call-seq: from_state(opts) + * + * Creates a State object from _opts_, which ought to be Hash to create a + * new State instance configured by _opts_, something else to create an + * unconfigured instance. If _opts_ is a State object, it is just returned. + */ +static VALUE cState_from_state_s(VALUE self, VALUE opts) +{ + if (rb_obj_is_kind_of(opts, self)) { + return opts; + } else if (rb_obj_is_kind_of(opts, rb_cHash)) { + return rb_funcall(self, i_new, 1, opts); + } else { + return rb_class_new_instance(0, NULL, cState); + } +} + +/* + * call-seq: indent() + * + * Returns the string that is used to indent levels in the JSON text. + */ +static VALUE cState_indent(VALUE self) +{ + GET_STATE(self); + return state->indent ? state->indent : rb_str_freeze(rb_utf8_str_new("", 0)); +} + +static VALUE string_config(VALUE config) +{ + if (RTEST(config)) { + Check_Type(config, T_STRING); + if (RSTRING_LEN(config)) { + return rb_str_new_frozen(config); + } + } + return Qfalse; +} + +/* + * call-seq: indent=(indent) + * + * Sets the string that is used to indent levels in the JSON text. + */ +static VALUE cState_indent_set(VALUE self, VALUE indent) +{ + rb_check_frozen(self); + GET_STATE(self); + RB_OBJ_WRITE(self, &state->indent, string_config(indent)); + return Qnil; +} + +/* + * call-seq: space() + * + * Returns the string that is used to insert a space between the tokens in a JSON + * string. + */ +static VALUE cState_space(VALUE self) +{ + GET_STATE(self); + return state->space ? state->space : rb_str_freeze(rb_utf8_str_new("", 0)); +} + +/* + * call-seq: space=(space) + * + * Sets _space_ to the string that is used to insert a space between the tokens in a JSON + * string. + */ +static VALUE cState_space_set(VALUE self, VALUE space) +{ + rb_check_frozen(self); + GET_STATE(self); + RB_OBJ_WRITE(self, &state->space, string_config(space)); + return Qnil; +} + +/* + * call-seq: space_before() + * + * Returns the string that is used to insert a space before the ':' in JSON objects. + */ +static VALUE cState_space_before(VALUE self) +{ + GET_STATE(self); + return state->space_before ? state->space_before : rb_str_freeze(rb_utf8_str_new("", 0)); +} + +/* + * call-seq: space_before=(space_before) + * + * Sets the string that is used to insert a space before the ':' in JSON objects. + */ +static VALUE cState_space_before_set(VALUE self, VALUE space_before) +{ + rb_check_frozen(self); + GET_STATE(self); + RB_OBJ_WRITE(self, &state->space_before, string_config(space_before)); + return Qnil; +} + +/* + * call-seq: object_nl() + * + * This string is put at the end of a line that holds a JSON object (or + * Hash). + */ +static VALUE cState_object_nl(VALUE self) +{ + GET_STATE(self); + return state->object_nl ? state->object_nl : rb_str_freeze(rb_utf8_str_new("", 0)); +} + +/* + * call-seq: object_nl=(object_nl) + * + * This string is put at the end of a line that holds a JSON object (or + * Hash). + */ +static VALUE cState_object_nl_set(VALUE self, VALUE object_nl) +{ + rb_check_frozen(self); + GET_STATE(self); + RB_OBJ_WRITE(self, &state->object_nl, string_config(object_nl)); + return Qnil; +} + +/* + * call-seq: array_nl() + * + * This string is put at the end of a line that holds a JSON array. + */ +static VALUE cState_array_nl(VALUE self) +{ + GET_STATE(self); + return state->array_nl ? state->array_nl : rb_str_freeze(rb_utf8_str_new("", 0)); +} + +/* + * call-seq: array_nl=(array_nl) + * + * This string is put at the end of a line that holds a JSON array. + */ +static VALUE cState_array_nl_set(VALUE self, VALUE array_nl) +{ + rb_check_frozen(self); + GET_STATE(self); + RB_OBJ_WRITE(self, &state->array_nl, string_config(array_nl)); + return Qnil; +} + +/* + * call-seq: as_json() + * + * This string is put at the end of a line that holds a JSON array. + */ +static VALUE cState_as_json(VALUE self) +{ + GET_STATE(self); + return state->as_json; +} + +/* + * call-seq: as_json=(as_json) + * + * This string is put at the end of a line that holds a JSON array. + */ +static VALUE cState_as_json_set(VALUE self, VALUE as_json) +{ + rb_check_frozen(self); + GET_STATE(self); + RB_OBJ_WRITE(self, &state->as_json, rb_convert_type(as_json, T_DATA, "Proc", "to_proc")); + return Qnil; +} + +/* +* call-seq: check_circular? +* +* Returns true, if circular data structures should be checked, +* otherwise returns false. +*/ +static VALUE cState_check_circular_p(VALUE self) +{ + GET_STATE(self); + return state->max_nesting ? Qtrue : Qfalse; +} + +/* + * call-seq: max_nesting + * + * This integer returns the maximum level of data structure nesting in + * the generated JSON, max_nesting = 0 if no maximum is checked. + */ +static VALUE cState_max_nesting(VALUE self) +{ + GET_STATE(self); + return LONG2FIX(state->max_nesting); +} + +static long long_config(VALUE num) +{ + return RTEST(num) ? FIX2LONG(num) : 0; +} + +/* + * call-seq: max_nesting=(depth) + * + * This sets the maximum level of data structure nesting in the generated JSON + * to the integer depth, max_nesting = 0 if no maximum should be checked. + */ +static VALUE cState_max_nesting_set(VALUE self, VALUE depth) +{ + rb_check_frozen(self); + GET_STATE(self); + state->max_nesting = long_config(depth); + return Qnil; +} + +/* + * call-seq: script_safe + * + * If this boolean is true, the forward slashes will be escaped in + * the json output. + */ +static VALUE cState_script_safe(VALUE self) +{ + GET_STATE(self); + return state->script_safe ? Qtrue : Qfalse; +} + +/* + * call-seq: script_safe=(enable) + * + * This sets whether or not the forward slashes will be escaped in + * the json output. + */ +static VALUE cState_script_safe_set(VALUE self, VALUE enable) +{ + rb_check_frozen(self); + 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) +{ + rb_check_frozen(self); + GET_STATE(self); + state->strict = RTEST(enable); + return Qnil; +} + +/* + * call-seq: allow_nan? + * + * Returns true, if NaN, Infinity, and -Infinity should be generated, otherwise + * returns false. + */ +static VALUE cState_allow_nan_p(VALUE self) +{ + GET_STATE(self); + return state->allow_nan ? Qtrue : Qfalse; +} + +/* + * call-seq: allow_nan=(enable) + * + * This sets whether or not to serialize NaN, Infinity, and -Infinity + */ +static VALUE cState_allow_nan_set(VALUE self, VALUE enable) +{ + rb_check_frozen(self); + GET_STATE(self); + state->allow_nan = RTEST(enable); + return Qnil; +} + +/* + * call-seq: ascii_only? + * + * Returns true, if only ASCII characters should be generated. Otherwise + * returns false. + */ +static VALUE cState_ascii_only_p(VALUE self) +{ + GET_STATE(self); + return state->ascii_only ? Qtrue : Qfalse; +} + +/* + * call-seq: ascii_only=(enable) + * + * This sets whether only ASCII characters should be generated. + */ +static VALUE cState_ascii_only_set(VALUE self, VALUE enable) +{ + rb_check_frozen(self); + GET_STATE(self); + state->ascii_only = RTEST(enable); + return Qnil; +} + +static VALUE cState_allow_duplicate_key_p(VALUE self) +{ + GET_STATE(self); + switch (state->on_duplicate_key) { + case JSON_IGNORE: + return Qtrue; + case JSON_DEPRECATED: + return Qnil; + default: + return Qfalse; + } +} + +/* + * call-seq: depth + * + * This integer returns the current depth of data structure nesting. + */ +static VALUE cState_depth(VALUE self) +{ + GET_STATE(self); + return LONG2FIX(state->depth); +} + +/* + * call-seq: depth=(depth) + * + * This sets the maximum level of data structure nesting in the generated JSON + * to the integer depth, max_nesting = 0 if no maximum should be checked. + */ +static VALUE cState_depth_set(VALUE self, VALUE depth) +{ + rb_check_frozen(self); + GET_STATE(self); + state->depth = long_config(depth); + return Qnil; +} + +/* + * call-seq: buffer_initial_length + * + * This integer returns the current initial length of the buffer. + */ +static VALUE cState_buffer_initial_length(VALUE self) +{ + GET_STATE(self); + return LONG2FIX(state->buffer_initial_length); +} + +static void buffer_initial_length_set(JSON_Generator_State *state, VALUE buffer_initial_length) +{ + Check_Type(buffer_initial_length, T_FIXNUM); + long initial_length = FIX2LONG(buffer_initial_length); + if (initial_length > 0) { + state->buffer_initial_length = initial_length; + } +} + +/* + * call-seq: buffer_initial_length=(length) + * + * This sets the initial length of the buffer to +length+, if +length+ > 0, + * otherwise its value isn't changed. + */ +static VALUE cState_buffer_initial_length_set(VALUE self, VALUE buffer_initial_length) +{ + rb_check_frozen(self); + GET_STATE(self); + buffer_initial_length_set(state, buffer_initial_length); + return Qnil; +} + +struct configure_state_data { + JSON_Generator_State *state; + VALUE vstate; // Ruby object that owns the state, or Qfalse if stack-allocated +}; + +static inline void state_write_value(struct configure_state_data *data, VALUE *field, VALUE value) +{ + if (RTEST(data->vstate)) { + RB_OBJ_WRITE(data->vstate, field, value); + } else { + *field = value; + } +} + +static int configure_state_i(VALUE key, VALUE val, VALUE _arg) +{ + struct configure_state_data *data = (struct configure_state_data *)_arg; + JSON_Generator_State *state = data->state; + + if (key == sym_indent) { state_write_value(data, &state->indent, string_config(val)); } + else if (key == sym_space) { state_write_value(data, &state->space, string_config(val)); } + else if (key == sym_space_before) { state_write_value(data, &state->space_before, string_config(val)); } + else if (key == sym_object_nl) { state_write_value(data, &state->object_nl, string_config(val)); } + else if (key == sym_array_nl) { state_write_value(data, &state->array_nl, string_config(val)); } + else if (key == sym_max_nesting) { state->max_nesting = long_config(val); } + else if (key == sym_allow_nan) { state->allow_nan = RTEST(val); } + else if (key == sym_ascii_only) { state->ascii_only = RTEST(val); } + else if (key == sym_depth) { state->depth = long_config(val); } + else if (key == sym_buffer_initial_length) { buffer_initial_length_set(state, val); } + else if (key == sym_script_safe) { state->script_safe = RTEST(val); } + else if (key == sym_escape_slash) { state->script_safe = RTEST(val); } + else if (key == sym_strict) { state->strict = RTEST(val); } + else if (key == sym_allow_duplicate_key) { state->on_duplicate_key = RTEST(val) ? JSON_IGNORE : JSON_RAISE; } + else if (key == sym_as_json) { + VALUE proc = RTEST(val) ? rb_convert_type(val, T_DATA, "Proc", "to_proc") : Qfalse; + state->as_json_single_arg = proc && rb_proc_arity(proc) == 1; + state_write_value(data, &state->as_json, proc); + } + return ST_CONTINUE; +} + +static void configure_state(JSON_Generator_State *state, VALUE vstate, VALUE config) +{ + if (!RTEST(config)) return; + + Check_Type(config, T_HASH); + + if (!RHASH_SIZE(config)) return; + + struct configure_state_data data = { + .state = state, + .vstate = vstate + }; + + // We assume in most cases few keys are set so it's faster to go over + // the provided keys than to check all possible keys. + rb_hash_foreach(config, configure_state_i, (VALUE)&data); +} + +static VALUE cState_configure(VALUE self, VALUE opts) +{ + rb_check_frozen(self); + GET_STATE(self); + configure_state(state, self, opts); + return self; +} + +static VALUE cState_m_generate(VALUE klass, VALUE obj, VALUE opts, VALUE io) +{ + JSON_Generator_State state = {0}; + state_init(&state); + configure_state(&state, Qfalse, opts); + + char stack_buffer[FBUFFER_STACK_SIZE]; + FBuffer buffer = { + .io = RTEST(io) ? io : Qfalse, + }; + fbuffer_stack_init(&buffer, state.buffer_initial_length, stack_buffer, FBUFFER_STACK_SIZE); + + struct generate_json_data data = { + .buffer = &buffer, + .vstate = Qfalse, + .state = &state, + .depth = state.depth, + .obj = obj, + .func = generate_json, + }; + return rb_ensure(generate_json_try, (VALUE)&data, generate_json_ensure, (VALUE)&data); +} + +/* + * + */ +void Init_generator(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"); + + rb_global_variable(&cFragment); + cFragment = rb_const_get(mJSON, rb_intern("Fragment")); + + VALUE mExt = rb_define_module_under(mJSON, "Ext"); + VALUE mGenerator = rb_define_module_under(mExt, "Generator"); + + rb_global_variable(&eGeneratorError); + eGeneratorError = rb_path2class("JSON::GeneratorError"); + + rb_global_variable(&eNestingError); + eNestingError = rb_path2class("JSON::NestingError"); + + cState = rb_define_class_under(mGenerator, "State", rb_cObject); + rb_define_alloc_func(cState, cState_s_allocate); + rb_define_singleton_method(cState, "from_state", cState_from_state_s, 1); + rb_define_method(cState, "initialize", cState_initialize, -1); + rb_define_alias(cState, "initialize", "initialize"); // avoid method redefinition warnings + rb_define_private_method(cState, "_configure", cState_configure, 1); + + rb_define_method(cState, "initialize_copy", cState_init_copy, 1); + rb_define_method(cState, "indent", cState_indent, 0); + rb_define_method(cState, "indent=", cState_indent_set, 1); + rb_define_method(cState, "space", cState_space, 0); + rb_define_method(cState, "space=", cState_space_set, 1); + rb_define_method(cState, "space_before", cState_space_before, 0); + rb_define_method(cState, "space_before=", cState_space_before_set, 1); + rb_define_method(cState, "object_nl", cState_object_nl, 0); + rb_define_method(cState, "object_nl=", cState_object_nl_set, 1); + rb_define_method(cState, "array_nl", cState_array_nl, 0); + rb_define_method(cState, "array_nl=", cState_array_nl_set, 1); + rb_define_method(cState, "as_json", cState_as_json, 0); + rb_define_method(cState, "as_json=", cState_as_json_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, "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, "allow_nan=", cState_allow_nan_set, 1); + rb_define_method(cState, "ascii_only?", cState_ascii_only_p, 0); + rb_define_method(cState, "ascii_only=", cState_ascii_only_set, 1); + rb_define_method(cState, "depth", cState_depth, 0); + rb_define_method(cState, "depth=", cState_depth_set, 1); + rb_define_method(cState, "buffer_initial_length", cState_buffer_initial_length, 0); + rb_define_method(cState, "buffer_initial_length=", cState_buffer_initial_length_set, 1); + rb_define_method(cState, "generate", cState_generate, -1); + + rb_define_private_method(cState, "allow_duplicate_key?", cState_allow_duplicate_key_p, 0); + + rb_define_singleton_method(cState, "generate", cState_m_generate, 3); + + VALUE mGeneratorMethods = rb_define_module_under(mGenerator, "GeneratorMethods"); + + VALUE mObject = rb_define_module_under(mGeneratorMethods, "Object"); + rb_define_method(mObject, "to_json", mObject_to_json, -1); + + VALUE mHash = rb_define_module_under(mGeneratorMethods, "Hash"); + rb_define_method(mHash, "to_json", mHash_to_json, -1); + + VALUE mArray = rb_define_module_under(mGeneratorMethods, "Array"); + rb_define_method(mArray, "to_json", mArray_to_json, -1); + +#ifdef RUBY_INTEGER_UNIFICATION + VALUE mInteger = rb_define_module_under(mGeneratorMethods, "Integer"); + rb_define_method(mInteger, "to_json", mInteger_to_json, -1); +#else + VALUE mFixnum = rb_define_module_under(mGeneratorMethods, "Fixnum"); + rb_define_method(mFixnum, "to_json", mFixnum_to_json, -1); + + VALUE mBignum = rb_define_module_under(mGeneratorMethods, "Bignum"); + rb_define_method(mBignum, "to_json", mBignum_to_json, -1); +#endif + VALUE mFloat = rb_define_module_under(mGeneratorMethods, "Float"); + rb_define_method(mFloat, "to_json", mFloat_to_json, -1); + + VALUE mString = rb_define_module_under(mGeneratorMethods, "String"); + rb_define_method(mString, "to_json", mString_to_json, -1); + + VALUE mTrueClass = rb_define_module_under(mGeneratorMethods, "TrueClass"); + rb_define_method(mTrueClass, "to_json", mTrueClass_to_json, -1); + + VALUE mFalseClass = rb_define_module_under(mGeneratorMethods, "FalseClass"); + rb_define_method(mFalseClass, "to_json", mFalseClass_to_json, -1); + + VALUE mNilClass = rb_define_module_under(mGeneratorMethods, "NilClass"); + rb_define_method(mNilClass, "to_json", mNilClass_to_json, -1); + + rb_global_variable(&Encoding_UTF_8); + Encoding_UTF_8 = rb_const_get(rb_path2class("Encoding"), rb_intern("UTF_8")); + + i_to_s = rb_intern("to_s"); + i_to_json = rb_intern("to_json"); + i_new = rb_intern("new"); + i_encode = rb_intern("encode"); + + sym_indent = ID2SYM(rb_intern("indent")); + sym_space = ID2SYM(rb_intern("space")); + sym_space_before = ID2SYM(rb_intern("space_before")); + sym_object_nl = ID2SYM(rb_intern("object_nl")); + sym_array_nl = ID2SYM(rb_intern("array_nl")); + sym_max_nesting = ID2SYM(rb_intern("max_nesting")); + sym_allow_nan = ID2SYM(rb_intern("allow_nan")); + sym_ascii_only = ID2SYM(rb_intern("ascii_only")); + sym_depth = ID2SYM(rb_intern("depth")); + sym_buffer_initial_length = ID2SYM(rb_intern("buffer_initial_length")); + sym_script_safe = ID2SYM(rb_intern("script_safe")); + sym_escape_slash = ID2SYM(rb_intern("escape_slash")); + sym_strict = ID2SYM(rb_intern("strict")); + sym_as_json = ID2SYM(rb_intern("as_json")); + sym_allow_duplicate_key = ID2SYM(rb_intern("allow_duplicate_key")); + + usascii_encindex = rb_usascii_encindex(); + utf8_encindex = rb_utf8_encindex(); + binary_encindex = rb_ascii8bit_encindex(); + + rb_require("json/ext/generator/state"); + + + switch (find_simd_implementation()) { +#ifdef HAVE_SIMD +#ifdef HAVE_SIMD_NEON + case SIMD_NEON: + search_escape_basic_impl = search_escape_basic_neon; + break; +#endif /* HAVE_SIMD_NEON */ +#ifdef HAVE_SIMD_SSE2 + case SIMD_SSE2: + search_escape_basic_impl = search_escape_basic_sse2; + break; +#endif /* HAVE_SIMD_SSE2 */ +#endif /* HAVE_SIMD */ + default: + search_escape_basic_impl = search_escape_basic; + break; + } +} diff --git a/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/ext/json/ext/json.h b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/ext/json/ext/json.h new file mode 100644 index 0000000..28efa04 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/ext/json/ext/json.h @@ -0,0 +1,97 @@ +#ifndef _JSON_H_ +#define _JSON_H_ + +#include "ruby.h" +#include "ruby/encoding.h" +#include + +#if defined(RUBY_DEBUG) && RUBY_DEBUG +# define JSON_ASSERT RUBY_ASSERT +#else +# ifdef JSON_DEBUG +# include +# define JSON_ASSERT(x) assert(x) +# else +# define JSON_ASSERT(x) +# endif +#endif + +/* shims */ + +#if SIZEOF_UINT64_T == SIZEOF_LONG_LONG +# define INT64T2NUM(x) LL2NUM(x) +# define UINT64T2NUM(x) ULL2NUM(x) +#elif SIZEOF_UINT64_T == SIZEOF_LONG +# define INT64T2NUM(x) LONG2NUM(x) +# define UINT64T2NUM(x) ULONG2NUM(x) +#else +# error No uint64_t conversion +#endif + +/* This is the fallback definition from Ruby 3.4 */ +#ifndef RBIMPL_STDBOOL_H +#if defined(__cplusplus) +# if defined(HAVE_STDBOOL_H) && (__cplusplus >= 201103L) +# include +# endif +#elif defined(HAVE_STDBOOL_H) +# include +#elif !defined(HAVE__BOOL) +typedef unsigned char _Bool; +# define bool _Bool +# define true ((_Bool)+1) +# define false ((_Bool)+0) +# define __bool_true_false_are_defined +#endif +#endif + +#ifndef HAVE_RB_EXT_RACTOR_SAFE +# undef RUBY_TYPED_FROZEN_SHAREABLE +# define RUBY_TYPED_FROZEN_SHAREABLE 0 +#endif + +#ifndef NORETURN +#define NORETURN(x) x +#endif + +#ifndef NOINLINE +#if defined(__has_attribute) && __has_attribute(noinline) +#define NOINLINE(x) __attribute__((noinline)) x +#else +#define NOINLINE(x) x +#endif +#endif + +#ifndef ALWAYS_INLINE +#if defined(__has_attribute) && __has_attribute(always_inline) +#define ALWAYS_INLINE(x) inline __attribute__((always_inline)) x +#else +#define ALWAYS_INLINE(x) inline x +#endif +#endif + +#ifndef RB_UNLIKELY +#define RB_UNLIKELY(expr) expr +#endif + +#ifndef RB_LIKELY +#define RB_LIKELY(expr) expr +#endif + +#ifndef MAYBE_UNUSED +# define MAYBE_UNUSED(x) x +#endif + +#ifdef RUBY_DEBUG +#ifndef JSON_DEBUG +#define JSON_DEBUG RUBY_DEBUG +#endif +#endif + +#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ && INTPTR_MAX == INT64_MAX +#define JSON_CPU_LITTLE_ENDIAN_64BITS 1 +#else +#define JSON_CPU_LITTLE_ENDIAN_64BITS 0 +#endif + +#endif // _JSON_H_ diff --git a/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/ext/json/ext/parser/Makefile b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/ext/json/ext/parser/Makefile new file mode 100644 index 0000000..c9fbd3c --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/ext/json/ext/parser/Makefile @@ -0,0 +1,269 @@ + +SHELL = /bin/sh + +# V=0 quiet, V=1 verbose. other values don't work. +V = 0 +V0 = $(V:0=) +Q1 = $(V:1=) +Q = $(Q1:0=@) +ECHO1 = $(V:1=@ :) +ECHO = $(ECHO1:0=@ echo) +NULLCMD = : + +#### Start of system configuration section. #### + +srcdir = . +topdir = /opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0 +hdrdir = $(topdir) +arch_hdrdir = /opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/x86_64-linux +PATH_SEPARATOR = : +VPATH = $(srcdir):$(arch_hdrdir)/ruby:$(hdrdir)/ruby +prefix = $(DESTDIR)/opt/hostedtoolcache/Ruby/3.3.10/x64 +rubysitearchprefix = $(rubylibprefix)/$(sitearch) +rubyarchprefix = $(rubylibprefix)/$(arch) +rubylibprefix = $(libdir)/$(RUBY_BASE_NAME) +exec_prefix = $(prefix) +vendorarchhdrdir = $(vendorhdrdir)/$(sitearch) +sitearchhdrdir = $(sitehdrdir)/$(sitearch) +rubyarchhdrdir = $(rubyhdrdir)/$(arch) +vendorhdrdir = $(rubyhdrdir)/vendor_ruby +sitehdrdir = $(rubyhdrdir)/site_ruby +rubyhdrdir = $(includedir)/$(RUBY_VERSION_NAME) +vendorarchdir = $(vendorlibdir)/$(sitearch) +vendorlibdir = $(vendordir)/$(ruby_version) +vendordir = $(rubylibprefix)/vendor_ruby +sitearchdir = $(sitelibdir)/$(sitearch) +sitelibdir = $(sitedir)/$(ruby_version) +sitedir = $(rubylibprefix)/site_ruby +rubyarchdir = $(rubylibdir)/$(arch) +rubylibdir = $(rubylibprefix)/$(ruby_version) +sitearchincludedir = $(includedir)/$(sitearch) +archincludedir = $(includedir)/$(arch) +sitearchlibdir = $(libdir)/$(sitearch) +archlibdir = $(libdir)/$(arch) +ridir = $(datarootdir)/$(RI_BASE_NAME) +mandir = $(datarootdir)/man +localedir = $(datarootdir)/locale +libdir = $(exec_prefix)/lib +psdir = $(docdir) +pdfdir = $(docdir) +dvidir = $(docdir) +htmldir = $(docdir) +infodir = $(datarootdir)/info +docdir = $(datarootdir)/doc/$(PACKAGE) +oldincludedir = $(DESTDIR)/usr/include +includedir = $(prefix)/include +runstatedir = $(localstatedir)/run +localstatedir = $(prefix)/var +sharedstatedir = $(prefix)/com +sysconfdir = $(prefix)/etc +datadir = $(datarootdir) +datarootdir = $(prefix)/share +libexecdir = $(exec_prefix)/libexec +sbindir = $(exec_prefix)/sbin +bindir = $(exec_prefix)/bin +archdir = $(rubyarchdir) + + +CC_WRAPPER = +CC = gcc +CXX = g++ +LIBRUBY = $(LIBRUBY_SO) +LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a +LIBRUBYARG_SHARED = -Wl,-rpath,$(libdir) -L$(libdir) -l$(RUBY_SO_NAME) +LIBRUBYARG_STATIC = -Wl,-rpath,$(libdir) -L$(libdir) -l$(RUBY_SO_NAME)-static $(MAINLIBS) +empty = +OUTFLAG = -o $(empty) +COUTFLAG = -o $(empty) +CSRCFLAG = $(empty) + +RUBY_EXTCONF_H = +cflags = $(optflags) $(debugflags) $(warnflags) +cxxflags = +optflags = -O3 -fno-fast-math +debugflags = -ggdb3 +warnflags = -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef +cppflags = +CCDLFLAGS = -fPIC +CFLAGS = $(CCDLFLAGS) $(cflags) -fPIC -std=c99 $(ARCH_FLAG) +INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir)/ruby/backward -I$(hdrdir) -I$(srcdir) +DEFS = +CPPFLAGS = -DHAVE_RB_ENC_INTERNED_STR -DHAVE_RB_STR_TO_INTERNED_STR -DHAVE_RB_HASH_NEW_CAPA -DHAVE_RB_HASH_BULK_INSERT -DHAVE_X86INTRIN_H -DJSON_ENABLE_SIMD -DHAVE_CPUID_H -DENABLE_PATH_CHECK=0 $(DEFS) $(cppflags) +CXXFLAGS = $(CCDLFLAGS) $(ARCH_FLAG) +ldflags = -L. -fstack-protector-strong -rdynamic -Wl,-export-dynamic -Wl,--no-as-needed +dldflags = -Wl,--compress-debug-sections=zlib +ARCH_FLAG = +DLDFLAGS = $(ldflags) $(dldflags) $(ARCH_FLAG) +LDSHARED = $(CC) -shared +LDSHAREDXX = $(CXX) -shared +AR = gcc-ar +EXEEXT = + +RUBY_INSTALL_NAME = $(RUBY_BASE_NAME) +RUBY_SO_NAME = ruby +RUBYW_INSTALL_NAME = +RUBY_VERSION_NAME = $(RUBY_BASE_NAME)-$(ruby_version) +RUBYW_BASE_NAME = rubyw +RUBY_BASE_NAME = ruby + +arch = x86_64-linux +sitearch = $(arch) +ruby_version = 3.3.0 +ruby = $(bindir)/$(RUBY_BASE_NAME) +RUBY = $(ruby) +BUILTRUBY = $(bindir)/$(RUBY_BASE_NAME) +ruby_headers = $(hdrdir)/ruby.h $(hdrdir)/ruby/backward.h $(hdrdir)/ruby/ruby.h $(hdrdir)/ruby/defines.h $(hdrdir)/ruby/missing.h $(hdrdir)/ruby/intern.h $(hdrdir)/ruby/st.h $(hdrdir)/ruby/subst.h $(arch_hdrdir)/ruby/config.h + +RM = rm -f +RM_RF = rm -fr +RMDIRS = rmdir --ignore-fail-on-non-empty -p +MAKEDIRS = /usr/bin/mkdir -p +INSTALL = /usr/bin/install -c +INSTALL_PROG = $(INSTALL) -m 0755 +INSTALL_DATA = $(INSTALL) -m 644 +COPY = cp +TOUCH = exit > + +#### End of system configuration section. #### + +preload = +libpath = . $(libdir) +LIBPATH = -L. -L$(libdir) -Wl,-rpath,$(libdir) +DEFFILE = + +CLEANFILES = mkmf.log +DISTCLEANFILES = +DISTCLEANDIRS = + +extout = +extout_prefix = +target_prefix = /json/ext +LOCAL_LIBS = +LIBS = $(LIBRUBYARG_SHARED) -lm -lpthread -lc +ORIG_SRCS = parser.c +SRCS = $(ORIG_SRCS) +OBJS = parser.o +HDRS = +LOCAL_HDRS = +TARGET = parser +TARGET_NAME = parser +TARGET_ENTRY = Init_$(TARGET_NAME) +DLLIB = $(TARGET).so +EXTSTATIC = +STATIC_LIB = + +TIMESTAMP_DIR = . +BINDIR = $(bindir) +RUBYCOMMONDIR = $(sitedir)$(target_prefix) +RUBYLIBDIR = $(sitelibdir)$(target_prefix) +RUBYARCHDIR = $(sitearchdir)$(target_prefix) +HDRDIR = $(sitehdrdir)$(target_prefix) +ARCHHDRDIR = $(sitearchhdrdir)$(target_prefix) +TARGET_SO_DIR = +TARGET_SO = $(TARGET_SO_DIR)$(DLLIB) +CLEANLIBS = $(TARGET_SO) false +CLEANOBJS = $(OBJS) *.bak +TARGET_SO_DIR_TIMESTAMP = $(TIMESTAMP_DIR)/.sitearchdir.-.json.-.ext.time + +all: $(DLLIB) +static: $(STATIC_LIB) +.PHONY: all install static install-so install-rb +.PHONY: clean clean-so clean-static clean-rb + +clean-static:: +clean-rb-default:: +clean-rb:: +clean-so:: +clean: clean-so clean-static clean-rb-default clean-rb + -$(Q)$(RM_RF) $(CLEANLIBS) $(CLEANOBJS) $(CLEANFILES) .*.time + +distclean-rb-default:: +distclean-rb:: +distclean-so:: +distclean-static:: +distclean: clean distclean-so distclean-static distclean-rb-default distclean-rb + -$(Q)$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log + -$(Q)$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES) + -$(Q)$(RMDIRS) $(DISTCLEANDIRS) 2> /dev/null || true + +realclean: distclean +install: install-so install-rb + +install-so: $(DLLIB) $(TARGET_SO_DIR_TIMESTAMP) + $(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR) +clean-static:: + -$(Q)$(RM) $(STATIC_LIB) +install-rb: pre-install-rb do-install-rb install-rb-default +install-rb-default: pre-install-rb-default do-install-rb-default +pre-install-rb: Makefile +pre-install-rb-default: Makefile +do-install-rb: +do-install-rb-default: +pre-install-rb-default: + @$(NULLCMD) +$(TARGET_SO_DIR_TIMESTAMP): + $(Q) $(MAKEDIRS) $(@D) $(RUBYARCHDIR) + $(Q) $(TOUCH) $@ + +site-install: site-install-so site-install-rb +site-install-so: install-so +site-install-rb: install-rb + +.SUFFIXES: .c .m .cc .mm .cxx .cpp .o .S + +.cc.o: + $(ECHO) compiling $(<) + $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$< + +.cc.S: + $(ECHO) translating $(<) + $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$< + +.mm.o: + $(ECHO) compiling $(<) + $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$< + +.mm.S: + $(ECHO) translating $(<) + $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$< + +.cxx.o: + $(ECHO) compiling $(<) + $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$< + +.cxx.S: + $(ECHO) translating $(<) + $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$< + +.cpp.o: + $(ECHO) compiling $(<) + $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$< + +.cpp.S: + $(ECHO) translating $(<) + $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$< + +.c.o: + $(ECHO) compiling $(<) + $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$< + +.c.S: + $(ECHO) translating $(<) + $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$< + +.m.o: + $(ECHO) compiling $(<) + $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$< + +.m.S: + $(ECHO) translating $(<) + $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$< + +$(TARGET_SO): $(OBJS) Makefile + $(ECHO) linking shared-object json/ext/$(DLLIB) + -$(Q)$(RM) $(@) + $(Q) $(LDSHARED) -o $@ $(OBJS) $(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS) + + + +$(OBJS): $(HDRS) $(ruby_headers) diff --git a/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/ext/json/ext/parser/extconf.rb b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/ext/json/ext/parser/extconf.rb new file mode 100644 index 0000000..2440e66 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/ext/json/ext/parser/extconf.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true +require 'mkmf' + +$defs << "-DJSON_DEBUG" if ENV.fetch("JSON_DEBUG", "0") != "0" +have_func("rb_enc_interned_str", "ruby/encoding.h") # RUBY_VERSION >= 3.0 +have_func("rb_str_to_interned_str", "ruby.h") # RUBY_VERSION >= 3.0 +have_func("rb_hash_new_capa", "ruby.h") # RUBY_VERSION >= 3.2 +have_func("rb_hash_bulk_insert", "ruby.h") # Missing on TruffleRuby + +append_cflags("-std=c99") + +if enable_config('parser-use-simd', default=!ENV["JSON_DISABLE_SIMD"]) + load __dir__ + "/../simd/conf.rb" +end + +create_makefile 'json/ext/parser' diff --git a/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/ext/json/ext/parser/parser.c b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/ext/json/ext/parser/parser.c new file mode 100644 index 0000000..8f9729e --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/ext/json/ext/parser/parser.c @@ -0,0 +1,1679 @@ +#include "../json.h" +#include "../vendor/ryu.h" +#include "../simd/simd.h" + +static VALUE mJSON, eNestingError, Encoding_UTF_8; +static VALUE CNaN, CInfinity, CMinusInfinity; + +static ID i_new, i_try_convert, i_uminus, i_encode; + +static VALUE sym_max_nesting, sym_allow_nan, sym_allow_trailing_comma, sym_allow_control_characters, sym_symbolize_names, sym_freeze, + sym_decimal_class, sym_on_load, sym_allow_duplicate_key; + +static int binary_encindex; +static int utf8_encindex; + +#ifndef HAVE_RB_HASH_BULK_INSERT +// For TruffleRuby +static void +rb_hash_bulk_insert(long count, const VALUE *pairs, VALUE hash) +{ + long index = 0; + while (index < count) { + VALUE name = pairs[index++]; + VALUE value = pairs[index++]; + rb_hash_aset(hash, name, value); + } + RB_GC_GUARD(hash); +} +#endif + +#ifndef HAVE_RB_HASH_NEW_CAPA +#define rb_hash_new_capa(n) rb_hash_new() +#endif + +#ifndef HAVE_RB_STR_TO_INTERNED_STR +static VALUE rb_str_to_interned_str(VALUE str) +{ + return rb_funcall(rb_str_freeze(str), i_uminus, 0); +} +#endif + +/* name cache */ + +#include +#include + +// Object names are likely to be repeated, and are frozen. +// As such we can re-use them if we keep a cache of the ones we've seen so far, +// and save much more expensive lookups into the global fstring table. +// This cache implementation is deliberately simple, as we're optimizing for compactness, +// to be able to fit safely on the stack. +// As such, binary search into a sorted array gives a good tradeoff between compactness and +// performance. +#define JSON_RVALUE_CACHE_CAPA 63 +typedef struct rvalue_cache_struct { + int length; + VALUE entries[JSON_RVALUE_CACHE_CAPA]; +} rvalue_cache; + +static rb_encoding *enc_utf8; + +#define JSON_RVALUE_CACHE_MAX_ENTRY_LENGTH 55 + +static inline VALUE build_interned_string(const char *str, const long length) +{ +# ifdef HAVE_RB_ENC_INTERNED_STR + return rb_enc_interned_str(str, length, enc_utf8); +# else + VALUE rstring = rb_utf8_str_new(str, length); + return rb_funcall(rb_str_freeze(rstring), i_uminus, 0); +# endif +} + +static inline VALUE build_symbol(const char *str, const long length) +{ + return rb_str_intern(build_interned_string(str, length)); +} + +static void rvalue_cache_insert_at(rvalue_cache *cache, int index, VALUE rstring) +{ + MEMMOVE(&cache->entries[index + 1], &cache->entries[index], VALUE, cache->length - index); + cache->length++; + cache->entries[index] = rstring; +} + +#define rstring_cache_memcmp memcmp + +#if JSON_CPU_LITTLE_ENDIAN_64BITS +#if __has_builtin(__builtin_bswap64) +#undef rstring_cache_memcmp +ALWAYS_INLINE(static) int rstring_cache_memcmp(const char *str, const char *rptr, const long length) +{ + // The libc memcmp has numerous complex optimizations, but in this particular case, + // we know the string is small (JSON_RVALUE_CACHE_MAX_ENTRY_LENGTH), so being able to + // inline a simpler memcmp outperforms calling the libc version. + long i = 0; + + for (; i + 8 <= length; i += 8) { + uint64_t a, b; + memcpy(&a, str + i, 8); + memcpy(&b, rptr + i, 8); + if (a != b) { + a = __builtin_bswap64(a); + b = __builtin_bswap64(b); + return (a < b) ? -1 : 1; + } + } + + for (; i < length; i++) { + if (str[i] != rptr[i]) { + return (str[i] < rptr[i]) ? -1 : 1; + } + } + + return 0; +} +#endif +#endif + +ALWAYS_INLINE(static) int rstring_cache_cmp(const char *str, const long length, VALUE rstring) +{ + const char *rstring_ptr; + long rstring_length; + + RSTRING_GETMEM(rstring, rstring_ptr, rstring_length); + + if (length == rstring_length) { + return rstring_cache_memcmp(str, rstring_ptr, length); + } else { + return (int)(length - rstring_length); + } +} + +ALWAYS_INLINE(static) VALUE rstring_cache_fetch(rvalue_cache *cache, const char *str, const long length) +{ + int low = 0; + int high = cache->length - 1; + + while (low <= high) { + int mid = (high + low) >> 1; + VALUE entry = cache->entries[mid]; + int cmp = rstring_cache_cmp(str, length, entry); + + if (cmp == 0) { + return entry; + } else if (cmp > 0) { + low = mid + 1; + } else { + high = mid - 1; + } + } + + VALUE rstring = build_interned_string(str, length); + + if (cache->length < JSON_RVALUE_CACHE_CAPA) { + rvalue_cache_insert_at(cache, low, rstring); + } + return rstring; +} + +static VALUE rsymbol_cache_fetch(rvalue_cache *cache, const char *str, const long length) +{ + int low = 0; + int high = cache->length - 1; + + while (low <= high) { + int mid = (high + low) >> 1; + VALUE entry = cache->entries[mid]; + int cmp = rstring_cache_cmp(str, length, rb_sym2str(entry)); + + if (cmp == 0) { + return entry; + } else if (cmp > 0) { + low = mid + 1; + } else { + high = mid - 1; + } + } + + VALUE rsymbol = build_symbol(str, length); + + if (cache->length < JSON_RVALUE_CACHE_CAPA) { + rvalue_cache_insert_at(cache, low, rsymbol); + } + return rsymbol; +} + +/* rvalue stack */ + +#define RVALUE_STACK_INITIAL_CAPA 128 + +enum rvalue_stack_type { + RVALUE_STACK_HEAP_ALLOCATED = 0, + RVALUE_STACK_STACK_ALLOCATED = 1, +}; + +typedef struct rvalue_stack_struct { + enum rvalue_stack_type type; + long capa; + long head; + VALUE *ptr; +} rvalue_stack; + +static rvalue_stack *rvalue_stack_spill(rvalue_stack *old_stack, VALUE *handle, rvalue_stack **stack_ref); + +static rvalue_stack *rvalue_stack_grow(rvalue_stack *stack, VALUE *handle, rvalue_stack **stack_ref) +{ + long required = stack->capa * 2; + + if (stack->type == RVALUE_STACK_STACK_ALLOCATED) { + stack = rvalue_stack_spill(stack, handle, stack_ref); + } else { + REALLOC_N(stack->ptr, VALUE, required); + stack->capa = required; + } + return stack; +} + +static VALUE rvalue_stack_push(rvalue_stack *stack, VALUE value, VALUE *handle, rvalue_stack **stack_ref) +{ + if (RB_UNLIKELY(stack->head >= stack->capa)) { + stack = rvalue_stack_grow(stack, handle, stack_ref); + } + stack->ptr[stack->head] = value; + stack->head++; + return value; +} + +static inline VALUE *rvalue_stack_peek(rvalue_stack *stack, long count) +{ + return stack->ptr + (stack->head - count); +} + +static inline void rvalue_stack_pop(rvalue_stack *stack, long count) +{ + stack->head -= count; +} + +static void rvalue_stack_mark(void *ptr) +{ + rvalue_stack *stack = (rvalue_stack *)ptr; + long index; + for (index = 0; index < stack->head; index++) { + rb_gc_mark(stack->ptr[index]); + } +} + +static void rvalue_stack_free(void *ptr) +{ + rvalue_stack *stack = (rvalue_stack *)ptr; + if (stack) { + ruby_xfree(stack->ptr); + ruby_xfree(stack); + } +} + +static size_t rvalue_stack_memsize(const void *ptr) +{ + const rvalue_stack *stack = (const rvalue_stack *)ptr; + return sizeof(rvalue_stack) + sizeof(VALUE) * stack->capa; +} + +static const rb_data_type_t JSON_Parser_rvalue_stack_type = { + "JSON::Ext::Parser/rvalue_stack", + { + .dmark = rvalue_stack_mark, + .dfree = rvalue_stack_free, + .dsize = rvalue_stack_memsize, + }, + 0, 0, + RUBY_TYPED_FREE_IMMEDIATELY, +}; + +static rvalue_stack *rvalue_stack_spill(rvalue_stack *old_stack, VALUE *handle, rvalue_stack **stack_ref) +{ + rvalue_stack *stack; + *handle = TypedData_Make_Struct(0, rvalue_stack, &JSON_Parser_rvalue_stack_type, stack); + *stack_ref = stack; + MEMCPY(stack, old_stack, rvalue_stack, 1); + + stack->capa = old_stack->capa << 1; + stack->ptr = ALLOC_N(VALUE, stack->capa); + stack->type = RVALUE_STACK_HEAP_ALLOCATED; + MEMCPY(stack->ptr, old_stack->ptr, VALUE, old_stack->head); + return stack; +} + +static void rvalue_stack_eagerly_release(VALUE handle) +{ + if (handle) { + rvalue_stack *stack; + TypedData_Get_Struct(handle, rvalue_stack, &JSON_Parser_rvalue_stack_type, stack); + RTYPEDDATA_DATA(handle) = NULL; + rvalue_stack_free(stack); + } +} + +static int convert_UTF32_to_UTF8(char *buf, uint32_t 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; +} + +enum duplicate_key_action { + JSON_DEPRECATED = 0, + JSON_IGNORE, + JSON_RAISE, +}; + +typedef struct JSON_ParserStruct { + VALUE on_load_proc; + VALUE decimal_class; + ID decimal_method_id; + enum duplicate_key_action on_duplicate_key; + int max_nesting; + bool allow_nan; + bool allow_trailing_comma; + bool allow_control_characters; + bool symbolize_names; + bool freeze; +} JSON_ParserConfig; + +typedef struct JSON_ParserStateStruct { + VALUE stack_handle; + const char *start; + const char *cursor; + const char *end; + rvalue_stack *stack; + rvalue_cache name_cache; + int in_array; + int current_nesting; +} JSON_ParserState; + +static inline size_t rest(JSON_ParserState *state) { + return state->end - state->cursor; +} + +static inline bool eos(JSON_ParserState *state) { + return state->cursor >= state->end; +} + +static inline char peek(JSON_ParserState *state) +{ + if (RB_UNLIKELY(eos(state))) { + return 0; + } + return *state->cursor; +} + +static void cursor_position(JSON_ParserState *state, long *line_out, long *column_out) +{ + const char *cursor = state->cursor; + long column = 0; + long line = 1; + + while (cursor >= state->start) { + if (*cursor-- == '\n') { + break; + } + column++; + } + + while (cursor >= state->start) { + if (*cursor-- == '\n') { + line++; + } + } + *line_out = line; + *column_out = column; +} + +static void emit_parse_warning(const char *message, JSON_ParserState *state) +{ + long line, column; + cursor_position(state, &line, &column); + + VALUE warning = rb_sprintf("%s at line %ld column %ld", message, line, column); + rb_funcall(mJSON, rb_intern("deprecation_warning"), 1, warning); +} + +#define PARSE_ERROR_FRAGMENT_LEN 32 + +#ifdef RBIMPL_ATTR_NORETURN +RBIMPL_ATTR_NORETURN() +#endif +static void raise_parse_error(const char *format, JSON_ParserState *state) +{ + unsigned char buffer[PARSE_ERROR_FRAGMENT_LEN + 3]; + long line, column; + cursor_position(state, &line, &column); + + const char *ptr = "EOF"; + if (state->cursor && state->cursor < state->end) { + ptr = state->cursor; + size_t len = 0; + while (len < PARSE_ERROR_FRAGMENT_LEN) { + char ch = ptr[len]; + if (!ch || ch == '\n' || ch == ' ' || ch == '\t' || ch == '\r') { + break; + } + len++; + } + + if (len) { + buffer[0] = '\''; + MEMCPY(buffer + 1, ptr, char, len); + + while (buffer[len] >= 0x80 && buffer[len] < 0xC0) { // Is continuation byte + len--; + } + + if (buffer[len] >= 0xC0) { // multibyte character start + len--; + } + + buffer[len + 1] = '\''; + buffer[len + 2] = '\0'; + ptr = (const char *)buffer; + } + } + + VALUE msg = rb_sprintf(format, ptr); + VALUE message = rb_enc_sprintf(enc_utf8, "%s at line %ld column %ld", RSTRING_PTR(msg), line, column); + RB_GC_GUARD(msg); + + VALUE exc = rb_exc_new_str(rb_path2class("JSON::ParserError"), message); + rb_ivar_set(exc, rb_intern("@line"), LONG2NUM(line)); + rb_ivar_set(exc, rb_intern("@column"), LONG2NUM(column)); + rb_exc_raise(exc); +} + +#ifdef RBIMPL_ATTR_NORETURN +RBIMPL_ATTR_NORETURN() +#endif +static void raise_parse_error_at(const char *format, JSON_ParserState *state, const char *at) +{ + state->cursor = at; + raise_parse_error(format, state); +} + +/* 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 +}; + +static uint32_t unescape_unicode(JSON_ParserState *state, const unsigned char *p) +{ + signed char b; + uint32_t result = 0; + b = digit_values[p[0]]; + if (b < 0) raise_parse_error_at("incomplete unicode character escape sequence at %s", state, (char *)p - 2); + result = (result << 4) | (unsigned char)b; + b = digit_values[p[1]]; + if (b < 0) raise_parse_error_at("incomplete unicode character escape sequence at %s", state, (char *)p - 2); + result = (result << 4) | (unsigned char)b; + b = digit_values[p[2]]; + if (b < 0) raise_parse_error_at("incomplete unicode character escape sequence at %s", state, (char *)p - 2); + result = (result << 4) | (unsigned char)b; + b = digit_values[p[3]]; + if (b < 0) raise_parse_error_at("incomplete unicode character escape sequence at %s", state, (char *)p - 2); + result = (result << 4) | (unsigned char)b; + return result; +} + +#define GET_PARSER_CONFIG \ + JSON_ParserConfig *config; \ + TypedData_Get_Struct(self, JSON_ParserConfig, &JSON_ParserConfig_type, config) + +static const rb_data_type_t JSON_ParserConfig_type; + +static void +json_eat_comments(JSON_ParserState *state) +{ + const char *start = state->cursor; + state->cursor++; + + switch (peek(state)) { + case '/': { + state->cursor = memchr(state->cursor, '\n', state->end - state->cursor); + if (!state->cursor) { + state->cursor = state->end; + } else { + state->cursor++; + } + break; + } + case '*': { + state->cursor++; + + while (true) { + const char *next_match = memchr(state->cursor, '*', state->end - state->cursor); + if (!next_match) { + raise_parse_error_at("unterminated comment, expected closing '*/'", state, start); + } + + state->cursor = next_match + 1; + if (peek(state) == '/') { + state->cursor++; + break; + } + } + break; + } + default: + raise_parse_error_at("unexpected token %s", state, start); + break; + } +} + +ALWAYS_INLINE(static) void +json_eat_whitespace(JSON_ParserState *state) +{ + while (true) { + switch (peek(state)) { + case ' ': + state->cursor++; + break; + case '\n': + state->cursor++; + + // Heuristic: if we see a newline, there is likely consecutive spaces after it. +#if JSON_CPU_LITTLE_ENDIAN_64BITS + while (rest(state) > 8) { + uint64_t chunk; + memcpy(&chunk, state->cursor, sizeof(uint64_t)); + if (chunk == 0x2020202020202020) { + state->cursor += 8; + continue; + } + + uint32_t consecutive_spaces = trailing_zeros64(chunk ^ 0x2020202020202020) / CHAR_BIT; + state->cursor += consecutive_spaces; + break; + } +#endif + break; + case '\t': + case '\r': + state->cursor++; + break; + case '/': + json_eat_comments(state); + break; + + default: + return; + } + } +} + +static inline VALUE build_string(const char *start, const char *end, bool intern, bool symbolize) +{ + if (symbolize) { + intern = true; + } + VALUE result; +# ifdef HAVE_RB_ENC_INTERNED_STR + if (intern) { + result = rb_enc_interned_str(start, (long)(end - start), enc_utf8); + } else { + result = rb_utf8_str_new(start, (long)(end - start)); + } +# else + result = rb_utf8_str_new(start, (long)(end - start)); + if (intern) { + result = rb_funcall(rb_str_freeze(result), i_uminus, 0); + } +# endif + + if (symbolize) { + result = rb_str_intern(result); + } + + return result; +} + +static inline bool json_string_cacheable_p(const char *string, size_t length) +{ + // We mostly want to cache strings that are likely to be repeated. + // Simple heuristics: + // - Common names aren't likely to be very long. So we just don't cache names above an arbitrary threshold. + // - If the first character isn't a letter, we're much less likely to see this string again. + return length <= JSON_RVALUE_CACHE_MAX_ENTRY_LENGTH && rb_isalpha(string[0]); +} + +static inline VALUE json_string_fastpath(JSON_ParserState *state, JSON_ParserConfig *config, const char *string, const char *stringEnd, bool is_name) +{ + bool intern = is_name || config->freeze; + bool symbolize = is_name && config->symbolize_names; + size_t bufferSize = stringEnd - string; + + if (is_name && state->in_array && RB_LIKELY(json_string_cacheable_p(string, bufferSize))) { + VALUE cached_key; + if (RB_UNLIKELY(symbolize)) { + cached_key = rsymbol_cache_fetch(&state->name_cache, string, bufferSize); + } else { + cached_key = rstring_cache_fetch(&state->name_cache, string, bufferSize); + } + + if (RB_LIKELY(cached_key)) { + return cached_key; + } + } + + return build_string(string, stringEnd, intern, symbolize); +} + +#define JSON_MAX_UNESCAPE_POSITIONS 16 +typedef struct _json_unescape_positions { + long size; + const char **positions; + bool has_more; +} JSON_UnescapePositions; + +static inline const char *json_next_backslash(const char *pe, const char *stringEnd, JSON_UnescapePositions *positions) +{ + while (positions->size) { + positions->size--; + const char *next_position = positions->positions[0]; + positions->positions++; + if (next_position >= pe) { + return next_position; + } + } + + if (positions->has_more) { + return memchr(pe, '\\', stringEnd - pe); + } + + return NULL; +} + +NOINLINE(static) VALUE json_string_unescape(JSON_ParserState *state, JSON_ParserConfig *config, const char *string, const char *stringEnd, bool is_name, JSON_UnescapePositions *positions) +{ + bool intern = is_name || config->freeze; + bool symbolize = is_name && config->symbolize_names; + size_t bufferSize = stringEnd - string; + const char *p = string, *pe = string, *bufferStart; + char *buffer; + + VALUE result = rb_str_buf_new(bufferSize); + rb_enc_associate_index(result, utf8_encindex); + buffer = RSTRING_PTR(result); + bufferStart = buffer; + +#define APPEND_CHAR(chr) *buffer++ = chr; p = ++pe; + + while (pe < stringEnd && (pe = json_next_backslash(pe, stringEnd, positions))) { + if (pe > p) { + MEMCPY(buffer, p, char, pe - p); + buffer += pe - p; + } + switch (*++pe) { + case '"': + case '/': + p = pe; // nothing to unescape just need to skip the backslash + break; + case '\\': + APPEND_CHAR('\\'); + break; + case 'n': + APPEND_CHAR('\n'); + break; + case 'r': + APPEND_CHAR('\r'); + break; + case 't': + APPEND_CHAR('\t'); + break; + case 'b': + APPEND_CHAR('\b'); + break; + case 'f': + APPEND_CHAR('\f'); + break; + case 'u': + if (pe > stringEnd - 5) { + raise_parse_error_at("incomplete unicode character escape sequence at %s", state, p); + } else { + uint32_t ch = unescape_unicode(state, (unsigned char *) ++pe); + pe += 3; + /* To handle values above U+FFFF, we take a sequence of + * \uXXXX escapes in the U+D800..U+DBFF then + * U+DC00..U+DFFF ranges, take the low 10 bits from each + * to make a 20-bit number, then add 0x10000 to get the + * final codepoint. + * + * See Unicode 15: 3.8 "Surrogates", 5.3 "Handling + * Surrogate Pairs in UTF-16", and 23.6 "Surrogates + * Area". + */ + if ((ch & 0xFC00) == 0xD800) { + pe++; + if (pe > stringEnd - 6) { + raise_parse_error_at("incomplete surrogate pair at %s", state, p); + } + if (pe[0] == '\\' && pe[1] == 'u') { + uint32_t sur = unescape_unicode(state, (unsigned char *) pe + 2); + + if ((sur & 0xFC00) != 0xDC00) { + raise_parse_error_at("invalid surrogate pair at %s", state, p); + } + + ch = (((ch & 0x3F) << 10) | ((((ch >> 6) & 0xF) + 1) << 16) + | (sur & 0x3FF)); + pe += 5; + } else { + raise_parse_error_at("incomplete surrogate pair at %s", state, p); + break; + } + } + + char buf[4]; + int unescape_len = convert_UTF32_to_UTF8(buf, ch); + MEMCPY(buffer, buf, char, unescape_len); + buffer += unescape_len; + p = ++pe; + } + break; + default: + if ((unsigned char)*pe < 0x20) { + if (!config->allow_control_characters) { + if (*pe == '\n') { + raise_parse_error_at("Invalid unescaped newline character (\\n) in string: %s", state, pe - 1); + } + raise_parse_error_at("invalid ASCII control character in string: %s", state, pe - 1); + } + } else { + raise_parse_error_at("invalid escape character in string: %s", state, pe - 1); + } + break; + } + } +#undef APPEND_CHAR + + if (stringEnd > p) { + MEMCPY(buffer, p, char, stringEnd - p); + buffer += stringEnd - p; + } + rb_str_set_len(result, buffer - bufferStart); + + if (symbolize) { + result = rb_str_intern(result); + } else if (intern) { + result = rb_str_to_interned_str(result); + } + + return result; +} + +#define MAX_FAST_INTEGER_SIZE 18 + +static VALUE json_decode_large_integer(const char *start, long len) +{ + VALUE buffer_v; + char *buffer = RB_ALLOCV_N(char, buffer_v, len + 1); + MEMCPY(buffer, start, char, len); + buffer[len] = '\0'; + VALUE number = rb_cstr2inum(buffer, 10); + RB_ALLOCV_END(buffer_v); + return number; +} + +static inline VALUE +json_decode_integer(uint64_t mantissa, int mantissa_digits, bool negative, const char *start, const char *end) +{ + if (RB_LIKELY(mantissa_digits < MAX_FAST_INTEGER_SIZE)) { + if (negative) { + return INT64T2NUM(-((int64_t)mantissa)); + } + return UINT64T2NUM(mantissa); + } + + return json_decode_large_integer(start, end - start); +} + +static VALUE json_decode_large_float(const char *start, long len) +{ + if (RB_LIKELY(len < 64)) { + char buffer[64]; + MEMCPY(buffer, start, char, len); + buffer[len] = '\0'; + return DBL2NUM(rb_cstr_to_dbl(buffer, 1)); + } + + VALUE buffer_v; + char *buffer = RB_ALLOCV_N(char, buffer_v, len + 1); + MEMCPY(buffer, start, char, len); + buffer[len] = '\0'; + VALUE number = DBL2NUM(rb_cstr_to_dbl(buffer, 1)); + RB_ALLOCV_END(buffer_v); + return number; +} + +/* Ruby JSON optimized float decoder using vendored Ryu algorithm + * Accepts pre-extracted mantissa and exponent from first-pass validation + */ +static inline VALUE json_decode_float(JSON_ParserConfig *config, uint64_t mantissa, int mantissa_digits, int32_t exponent, bool negative, + const char *start, const char *end) +{ + if (RB_UNLIKELY(config->decimal_class)) { + VALUE text = rb_str_new(start, end - start); + return rb_funcallv(config->decimal_class, config->decimal_method_id, 1, &text); + } + + // Fall back to rb_cstr_to_dbl for potential subnormals (rare edge case) + // Ryu has rounding issues with subnormals around 1e-310 (< 2.225e-308) + if (RB_UNLIKELY(mantissa_digits > 17 || mantissa_digits + exponent < -307)) { + return json_decode_large_float(start, end - start); + } + + return DBL2NUM(ryu_s2d_from_parts(mantissa, mantissa_digits, exponent, negative)); +} + +static inline VALUE json_decode_array(JSON_ParserState *state, JSON_ParserConfig *config, long count) +{ + VALUE array = rb_ary_new_from_values(count, rvalue_stack_peek(state->stack, count)); + rvalue_stack_pop(state->stack, count); + + if (config->freeze) { + RB_OBJ_FREEZE(array); + } + + return array; +} + +static VALUE json_find_duplicated_key(size_t count, const VALUE *pairs) +{ + VALUE set = rb_hash_new_capa(count / 2); + for (size_t index = 0; index < count; index += 2) { + size_t before = RHASH_SIZE(set); + VALUE key = pairs[index]; + rb_hash_aset(set, key, Qtrue); + if (RHASH_SIZE(set) == before) { + if (RB_SYMBOL_P(key)) { + return rb_sym2str(key); + } + return key; + } + } + return Qfalse; +} + +static void emit_duplicate_key_warning(JSON_ParserState *state, VALUE duplicate_key) +{ + VALUE message = rb_sprintf( + "detected duplicate key %"PRIsVALUE" in JSON object. This will raise an error in json 3.0 unless enabled via `allow_duplicate_key: true`", + rb_inspect(duplicate_key) + ); + + emit_parse_warning(RSTRING_PTR(message), state); + RB_GC_GUARD(message); +} + +#ifdef RBIMPL_ATTR_NORETURN +RBIMPL_ATTR_NORETURN() +#endif +static void raise_duplicate_key_error(JSON_ParserState *state, VALUE duplicate_key) +{ + VALUE message = rb_sprintf( + "duplicate key %"PRIsVALUE, + rb_inspect(duplicate_key) + ); + + raise_parse_error(RSTRING_PTR(message), state); + RB_GC_GUARD(message); +} + +static inline VALUE json_decode_object(JSON_ParserState *state, JSON_ParserConfig *config, size_t count) +{ + size_t entries_count = count / 2; + VALUE object = rb_hash_new_capa(entries_count); + const VALUE *pairs = rvalue_stack_peek(state->stack, count); + rb_hash_bulk_insert(count, pairs, object); + + if (RB_UNLIKELY(RHASH_SIZE(object) < entries_count)) { + switch (config->on_duplicate_key) { + case JSON_IGNORE: + break; + case JSON_DEPRECATED: + emit_duplicate_key_warning(state, json_find_duplicated_key(count, pairs)); + break; + case JSON_RAISE: + raise_duplicate_key_error(state, json_find_duplicated_key(count, pairs)); + break; + } + } + + rvalue_stack_pop(state->stack, count); + + if (config->freeze) { + RB_OBJ_FREEZE(object); + } + + return object; +} + +static inline VALUE json_push_value(JSON_ParserState *state, JSON_ParserConfig *config, VALUE value) +{ + if (RB_UNLIKELY(config->on_load_proc)) { + value = rb_proc_call_with_block(config->on_load_proc, 1, &value, Qnil); + } + rvalue_stack_push(state->stack, value, &state->stack_handle, &state->stack); + return value; +} + +static const bool string_scan_table[256] = { + // ASCII Control Characters + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + // ASCII Characters + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // '"' + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, // '\\' + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +#ifdef HAVE_SIMD +static SIMD_Implementation simd_impl = SIMD_NONE; +#endif /* HAVE_SIMD */ + +ALWAYS_INLINE(static) bool string_scan(JSON_ParserState *state) +{ +#ifdef HAVE_SIMD +#if defined(HAVE_SIMD_NEON) + + uint64_t mask = 0; + if (string_scan_simd_neon(&state->cursor, state->end, &mask)) { + state->cursor += trailing_zeros64(mask) >> 2; + return true; + } + +#elif defined(HAVE_SIMD_SSE2) + if (simd_impl == SIMD_SSE2) { + int mask = 0; + if (string_scan_simd_sse2(&state->cursor, state->end, &mask)) { + state->cursor += trailing_zeros(mask); + return true; + } + } +#endif /* HAVE_SIMD_NEON or HAVE_SIMD_SSE2 */ +#endif /* HAVE_SIMD */ + + while (!eos(state)) { + if (RB_UNLIKELY(string_scan_table[(unsigned char)*state->cursor])) { + return true; + } + state->cursor++; + } + return false; +} + +static VALUE json_parse_escaped_string(JSON_ParserState *state, JSON_ParserConfig *config, bool is_name, const char *start) +{ + const char *backslashes[JSON_MAX_UNESCAPE_POSITIONS]; + JSON_UnescapePositions positions = { + .size = 0, + .positions = backslashes, + .has_more = false, + }; + + do { + switch (*state->cursor) { + case '"': { + VALUE string = json_string_unescape(state, config, start, state->cursor, is_name, &positions); + state->cursor++; + return json_push_value(state, config, string); + } + case '\\': { + if (RB_LIKELY(positions.size < JSON_MAX_UNESCAPE_POSITIONS)) { + backslashes[positions.size] = state->cursor; + positions.size++; + } else { + positions.has_more = true; + } + state->cursor++; + break; + } + default: + if (!config->allow_control_characters) { + raise_parse_error("invalid ASCII control character in string: %s", state); + } + break; + } + + state->cursor++; + } while (string_scan(state)); + + raise_parse_error("unexpected end of input, expected closing \"", state); + return Qfalse; +} + +ALWAYS_INLINE(static) VALUE json_parse_string(JSON_ParserState *state, JSON_ParserConfig *config, bool is_name) +{ + state->cursor++; + const char *start = state->cursor; + + if (RB_UNLIKELY(!string_scan(state))) { + raise_parse_error("unexpected end of input, expected closing \"", state); + } + + if (RB_LIKELY(*state->cursor == '"')) { + VALUE string = json_string_fastpath(state, config, start, state->cursor, is_name); + state->cursor++; + return json_push_value(state, config, string); + } + return json_parse_escaped_string(state, config, is_name, start); +} + +#if JSON_CPU_LITTLE_ENDIAN_64BITS +// From: https://lemire.me/blog/2022/01/21/swar-explained-parsing-eight-digits/ +// Additional References: +// https://johnnylee-sde.github.io/Fast-numeric-string-to-int/ +// http://0x80.pl/notesen/2014-10-12-parsing-decimal-numbers-part-1-swar.html +static inline uint64_t decode_8digits_unrolled(uint64_t val) { + const uint64_t mask = 0x000000FF000000FF; + const uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32) + const uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 32) + val -= 0x3030303030303030; + val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8; + val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32; + return val; +} + +static inline uint64_t decode_4digits_unrolled(uint32_t val) { + const uint32_t mask = 0x000000FF; + const uint32_t mul1 = 100; + val -= 0x30303030; + val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8; + val = ((val & mask) * mul1) + (((val >> 16) & mask)); + return val; +} +#endif + +static inline int json_parse_digits(JSON_ParserState *state, uint64_t *accumulator) +{ + const char *start = state->cursor; + +#if JSON_CPU_LITTLE_ENDIAN_64BITS + while (rest(state) >= sizeof(uint64_t)) { + uint64_t next_8bytes; + memcpy(&next_8bytes, state->cursor, sizeof(uint64_t)); + + // From: https://github.com/simdjson/simdjson/blob/32b301893c13d058095a07d9868edaaa42ee07aa/include/simdjson/generic/numberparsing.h#L333 + // Branchless version of: http://0x80.pl/articles/swar-digits-validate.html + uint64_t match = (next_8bytes & 0xF0F0F0F0F0F0F0F0) | (((next_8bytes + 0x0606060606060606) & 0xF0F0F0F0F0F0F0F0) >> 4); + + if (match == 0x3333333333333333) { // 8 consecutive digits + *accumulator = (*accumulator * 100000000) + decode_8digits_unrolled(next_8bytes); + state->cursor += 8; + continue; + } + + uint32_t consecutive_digits = trailing_zeros64(match ^ 0x3333333333333333) / CHAR_BIT; + + if (consecutive_digits >= 4) { + *accumulator = (*accumulator * 10000) + decode_4digits_unrolled((uint32_t)next_8bytes); + state->cursor += 4; + consecutive_digits -= 4; + } + + while (consecutive_digits) { + *accumulator = *accumulator * 10 + (*state->cursor - '0'); + consecutive_digits--; + state->cursor++; + } + + return (int)(state->cursor - start); + } +#endif + + char next_char; + while (rb_isdigit(next_char = peek(state))) { + *accumulator = *accumulator * 10 + (next_char - '0'); + state->cursor++; + } + return (int)(state->cursor - start); +} + +static inline VALUE json_parse_number(JSON_ParserState *state, JSON_ParserConfig *config, bool negative, const char *start) +{ + bool integer = true; + const char first_digit = *state->cursor; + + // Variables for Ryu optimization - extract digits during parsing + int32_t exponent = 0; + int decimal_point_pos = -1; + uint64_t mantissa = 0; + + // Parse integer part and extract mantissa digits + int mantissa_digits = json_parse_digits(state, &mantissa); + + if (RB_UNLIKELY((first_digit == '0' && mantissa_digits > 1) || (negative && mantissa_digits == 0))) { + raise_parse_error_at("invalid number: %s", state, start); + } + + // Parse fractional part + if (peek(state) == '.') { + integer = false; + decimal_point_pos = mantissa_digits; // Remember position of decimal point + state->cursor++; + + int fractional_digits = json_parse_digits(state, &mantissa); + mantissa_digits += fractional_digits; + + if (RB_UNLIKELY(!fractional_digits)) { + raise_parse_error_at("invalid number: %s", state, start); + } + } + + // Parse exponent + if (rb_tolower(peek(state)) == 'e') { + integer = false; + state->cursor++; + + bool negative_exponent = false; + const char next_char = peek(state); + if (next_char == '-' || next_char == '+') { + negative_exponent = next_char == '-'; + state->cursor++; + } + + uint64_t abs_exponent = 0; + int exponent_digits = json_parse_digits(state, &abs_exponent); + + if (RB_UNLIKELY(!exponent_digits)) { + raise_parse_error_at("invalid number: %s", state, start); + } + + exponent = negative_exponent ? -((int32_t)abs_exponent) : ((int32_t)abs_exponent); + } + + if (integer) { + return json_decode_integer(mantissa, mantissa_digits, negative, start, state->cursor); + } + + // Adjust exponent based on decimal point position + if (decimal_point_pos >= 0) { + exponent -= (mantissa_digits - decimal_point_pos); + } + + return json_decode_float(config, mantissa, mantissa_digits, exponent, negative, start, state->cursor); +} + +static inline VALUE json_parse_positive_number(JSON_ParserState *state, JSON_ParserConfig *config) +{ + return json_parse_number(state, config, false, state->cursor); +} + +static inline VALUE json_parse_negative_number(JSON_ParserState *state, JSON_ParserConfig *config) +{ + const char *start = state->cursor; + state->cursor++; + return json_parse_number(state, config, true, start); +} + +static VALUE json_parse_any(JSON_ParserState *state, JSON_ParserConfig *config) +{ + json_eat_whitespace(state); + + switch (peek(state)) { + case 'n': + if (rest(state) >= 4 && (memcmp(state->cursor, "null", 4) == 0)) { + state->cursor += 4; + return json_push_value(state, config, Qnil); + } + + raise_parse_error("unexpected token %s", state); + break; + case 't': + if (rest(state) >= 4 && (memcmp(state->cursor, "true", 4) == 0)) { + state->cursor += 4; + return json_push_value(state, config, Qtrue); + } + + raise_parse_error("unexpected token %s", state); + break; + case 'f': + // Note: memcmp with a small power of two compile to an integer comparison + if (rest(state) >= 5 && (memcmp(state->cursor + 1, "alse", 4) == 0)) { + state->cursor += 5; + return json_push_value(state, config, Qfalse); + } + + raise_parse_error("unexpected token %s", state); + break; + case 'N': + // Note: memcmp with a small power of two compile to an integer comparison + if (config->allow_nan && rest(state) >= 3 && (memcmp(state->cursor + 1, "aN", 2) == 0)) { + state->cursor += 3; + return json_push_value(state, config, CNaN); + } + + raise_parse_error("unexpected token %s", state); + break; + case 'I': + if (config->allow_nan && rest(state) >= 8 && (memcmp(state->cursor, "Infinity", 8) == 0)) { + state->cursor += 8; + return json_push_value(state, config, CInfinity); + } + + raise_parse_error("unexpected token %s", state); + break; + case '-': { + // Note: memcmp with a small power of two compile to an integer comparison + if (rest(state) >= 9 && (memcmp(state->cursor + 1, "Infinity", 8) == 0)) { + if (config->allow_nan) { + state->cursor += 9; + return json_push_value(state, config, CMinusInfinity); + } else { + raise_parse_error("unexpected token %s", state); + } + } + return json_push_value(state, config, json_parse_negative_number(state, config)); + break; + } + case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': + return json_push_value(state, config, json_parse_positive_number(state, config)); + break; + case '"': { + // %r{\A"[^"\\\t\n\x00]*(?:\\[bfnrtu\\/"][^"\\]*)*"} + return json_parse_string(state, config, false); + break; + } + case '[': { + state->cursor++; + json_eat_whitespace(state); + long stack_head = state->stack->head; + + if (peek(state) == ']') { + state->cursor++; + return json_push_value(state, config, json_decode_array(state, config, 0)); + } else { + state->current_nesting++; + if (RB_UNLIKELY(config->max_nesting && (config->max_nesting < state->current_nesting))) { + rb_raise(eNestingError, "nesting of %d is too deep", state->current_nesting); + } + state->in_array++; + json_parse_any(state, config); + } + + while (true) { + json_eat_whitespace(state); + + const char next_char = peek(state); + + if (RB_LIKELY(next_char == ',')) { + state->cursor++; + if (config->allow_trailing_comma) { + json_eat_whitespace(state); + if (peek(state) == ']') { + continue; + } + } + json_parse_any(state, config); + continue; + } + + if (next_char == ']') { + state->cursor++; + long count = state->stack->head - stack_head; + state->current_nesting--; + state->in_array--; + return json_push_value(state, config, json_decode_array(state, config, count)); + } + + raise_parse_error("expected ',' or ']' after array value", state); + } + break; + } + case '{': { + const char *object_start_cursor = state->cursor; + + state->cursor++; + json_eat_whitespace(state); + long stack_head = state->stack->head; + + if (peek(state) == '}') { + state->cursor++; + return json_push_value(state, config, json_decode_object(state, config, 0)); + } else { + state->current_nesting++; + if (RB_UNLIKELY(config->max_nesting && (config->max_nesting < state->current_nesting))) { + rb_raise(eNestingError, "nesting of %d is too deep", state->current_nesting); + } + + if (peek(state) != '"') { + raise_parse_error("expected object key, got %s", state); + } + json_parse_string(state, config, true); + + json_eat_whitespace(state); + if (peek(state) != ':') { + raise_parse_error("expected ':' after object key", state); + } + state->cursor++; + + json_parse_any(state, config); + } + + while (true) { + json_eat_whitespace(state); + + const char next_char = peek(state); + if (next_char == '}') { + state->cursor++; + state->current_nesting--; + size_t count = state->stack->head - stack_head; + + // Temporary rewind cursor in case an error is raised + const char *final_cursor = state->cursor; + state->cursor = object_start_cursor; + VALUE object = json_decode_object(state, config, count); + state->cursor = final_cursor; + + return json_push_value(state, config, object); + } + + if (next_char == ',') { + state->cursor++; + json_eat_whitespace(state); + + if (config->allow_trailing_comma) { + if (peek(state) == '}') { + continue; + } + } + + if (RB_UNLIKELY(peek(state) != '"')) { + raise_parse_error("expected object key, got: %s", state); + } + json_parse_string(state, config, true); + + json_eat_whitespace(state); + if (RB_UNLIKELY(peek(state) != ':')) { + raise_parse_error("expected ':' after object key, got: %s", state); + } + state->cursor++; + + json_parse_any(state, config); + + continue; + } + + raise_parse_error("expected ',' or '}' after object value, got: %s", state); + } + break; + } + + case 0: + raise_parse_error("unexpected end of input", state); + break; + + default: + raise_parse_error("unexpected character: %s", state); + break; + } + + raise_parse_error("unreachable: %s", state); + return Qundef; +} + +static void json_ensure_eof(JSON_ParserState *state) +{ + json_eat_whitespace(state); + if (!eos(state)) { + raise_parse_error("unexpected token at end of stream %s", state); + } +} + +/* + * 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) +{ + int encindex = RB_ENCODING_GET(source); + + if (RB_LIKELY(encindex == utf8_encindex)) { + return source; + } + + if (encindex == binary_encindex) { + // For historical reason, we silently reinterpret binary strings as UTF-8 + return rb_enc_associate_index(rb_str_dup(source), utf8_encindex); + } + + return rb_funcall(source, i_encode, 1, Encoding_UTF_8); +} + +static int parser_config_init_i(VALUE key, VALUE val, VALUE data) +{ + JSON_ParserConfig *config = (JSON_ParserConfig *)data; + + if (key == sym_max_nesting) { config->max_nesting = RTEST(val) ? FIX2INT(val) : 0; } + else if (key == sym_allow_nan) { config->allow_nan = RTEST(val); } + else if (key == sym_allow_trailing_comma) { config->allow_trailing_comma = RTEST(val); } + else if (key == sym_allow_control_characters) { config->allow_control_characters = RTEST(val); } + else if (key == sym_symbolize_names) { config->symbolize_names = RTEST(val); } + else if (key == sym_freeze) { config->freeze = RTEST(val); } + else if (key == sym_on_load) { config->on_load_proc = RTEST(val) ? val : Qfalse; } + else if (key == sym_allow_duplicate_key) { config->on_duplicate_key = RTEST(val) ? JSON_IGNORE : JSON_RAISE; } + else if (key == sym_decimal_class) { + if (RTEST(val)) { + if (rb_respond_to(val, i_try_convert)) { + config->decimal_class = val; + config->decimal_method_id = i_try_convert; + } else if (rb_respond_to(val, i_new)) { + config->decimal_class = val; + config->decimal_method_id = i_new; + } else if (RB_TYPE_P(val, T_CLASS)) { + VALUE name = rb_class_name(val); + 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); + config->decimal_class = 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); + config->decimal_method_id = SYM2ID(rb_str_intern(method_name)); + } else { + config->decimal_class = rb_mKernel; + config->decimal_method_id = SYM2ID(rb_str_intern(name)); + } + } + } + } + + return ST_CONTINUE; +} + +static void parser_config_init(JSON_ParserConfig *config, VALUE opts) +{ + config->max_nesting = 100; + + if (!NIL_P(opts)) { + Check_Type(opts, T_HASH); + if (RHASH_SIZE(opts) > 0) { + // We assume in most cases few keys are set so it's faster to go over + // the provided keys than to check all possible keys. + rb_hash_foreach(opts, parser_config_init_i, (VALUE)config); + } + + } +} + +/* + * call-seq: new(opts => {}) + * + * Creates a new JSON::Ext::ParserConfig instance. + * + * 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. + * * *decimal_class*: Specifies which class to use instead of the default + * (Float) when parsing decimal numbers. This class must accept a single + * string argument in its constructor. + */ +static VALUE cParserConfig_initialize(VALUE self, VALUE opts) +{ + rb_check_frozen(self); + GET_PARSER_CONFIG; + + parser_config_init(config, opts); + + RB_OBJ_WRITTEN(self, Qundef, config->decimal_class); + + return self; +} + +static VALUE cParser_parse(JSON_ParserConfig *config, VALUE Vsource) +{ + Vsource = convert_encoding(StringValue(Vsource)); + StringValue(Vsource); + + VALUE rvalue_stack_buffer[RVALUE_STACK_INITIAL_CAPA]; + rvalue_stack stack = { + .type = RVALUE_STACK_STACK_ALLOCATED, + .ptr = rvalue_stack_buffer, + .capa = RVALUE_STACK_INITIAL_CAPA, + }; + + long len; + const char *start; + RSTRING_GETMEM(Vsource, start, len); + + JSON_ParserState _state = { + .start = start, + .cursor = start, + .end = start + len, + .stack = &stack, + }; + JSON_ParserState *state = &_state; + + VALUE result = json_parse_any(state, config); + + // This may be skipped in case of exception, but + // it won't cause a leak. + rvalue_stack_eagerly_release(state->stack_handle); + + json_ensure_eof(state); + + return result; +} + +/* + * call-seq: parse(source) + * + * 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 cParserConfig_parse(VALUE self, VALUE Vsource) +{ + GET_PARSER_CONFIG; + return cParser_parse(config, Vsource); +} + +static VALUE cParser_m_parse(VALUE klass, VALUE Vsource, VALUE opts) +{ + Vsource = convert_encoding(StringValue(Vsource)); + StringValue(Vsource); + + JSON_ParserConfig _config = {0}; + JSON_ParserConfig *config = &_config; + parser_config_init(config, opts); + + return cParser_parse(config, Vsource); +} + +static void JSON_ParserConfig_mark(void *ptr) +{ + JSON_ParserConfig *config = ptr; + rb_gc_mark(config->on_load_proc); + rb_gc_mark(config->decimal_class); +} + +static void JSON_ParserConfig_free(void *ptr) +{ + JSON_ParserConfig *config = ptr; + ruby_xfree(config); +} + +static size_t JSON_ParserConfig_memsize(const void *ptr) +{ + return sizeof(JSON_ParserConfig); +} + +static const rb_data_type_t JSON_ParserConfig_type = { + "JSON::Ext::Parser/ParserConfig", + { + JSON_ParserConfig_mark, + JSON_ParserConfig_free, + JSON_ParserConfig_memsize, + }, + 0, 0, + RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FROZEN_SHAREABLE, +}; + +static VALUE cJSON_parser_s_allocate(VALUE klass) +{ + JSON_ParserConfig *config; + return TypedData_Make_Struct(klass, JSON_ParserConfig, &JSON_ParserConfig_type, config); +} + +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"); + VALUE mExt = rb_define_module_under(mJSON, "Ext"); + VALUE cParserConfig = rb_define_class_under(mExt, "ParserConfig", rb_cObject); + eNestingError = rb_path2class("JSON::NestingError"); + rb_gc_register_mark_object(eNestingError); + rb_define_alloc_func(cParserConfig, cJSON_parser_s_allocate); + rb_define_method(cParserConfig, "initialize", cParserConfig_initialize, 1); + rb_define_method(cParserConfig, "parse", cParserConfig_parse, 1); + + VALUE cParser = rb_define_class_under(mExt, "Parser", rb_cObject); + rb_define_singleton_method(cParser, "parse", cParser_m_parse, 2); + + 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); + + rb_global_variable(&Encoding_UTF_8); + Encoding_UTF_8 = rb_const_get(rb_path2class("Encoding"), rb_intern("UTF_8")); + + sym_max_nesting = ID2SYM(rb_intern("max_nesting")); + sym_allow_nan = ID2SYM(rb_intern("allow_nan")); + sym_allow_trailing_comma = ID2SYM(rb_intern("allow_trailing_comma")); + sym_allow_control_characters = ID2SYM(rb_intern("allow_control_characters")); + sym_symbolize_names = ID2SYM(rb_intern("symbolize_names")); + sym_freeze = ID2SYM(rb_intern("freeze")); + sym_on_load = ID2SYM(rb_intern("on_load")); + sym_decimal_class = ID2SYM(rb_intern("decimal_class")); + sym_allow_duplicate_key = ID2SYM(rb_intern("allow_duplicate_key")); + + i_new = rb_intern("new"); + i_try_convert = rb_intern("try_convert"); + i_uminus = rb_intern("-@"); + i_encode = rb_intern("encode"); + + binary_encindex = rb_ascii8bit_encindex(); + utf8_encindex = rb_utf8_encindex(); + enc_utf8 = rb_utf8_encoding(); + +#ifdef HAVE_SIMD + simd_impl = find_simd_implementation(); +#endif +} diff --git a/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/ext/json/ext/simd/conf.rb b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/ext/json/ext/simd/conf.rb new file mode 100644 index 0000000..76f774b --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/ext/json/ext/simd/conf.rb @@ -0,0 +1,24 @@ +case RbConfig::CONFIG['host_cpu'] +when /^(arm|aarch64)/ + # Try to compile a small program using NEON instructions + header, type, init, extra = 'arm_neon.h', 'uint8x16_t', 'vdupq_n_u8(32)', nil +when /^(x86_64|x64)/ + header, type, init, extra = 'x86intrin.h', '__m128i', '_mm_set1_epi8(32)', 'if (__builtin_cpu_supports("sse2")) { printf("OK"); }' +end +if header + if have_header(header) && try_compile(<<~SRC, '-Werror=implicit-function-declaration') + #{cpp_include(header)} + int main(int argc, char **argv) { + #{type} test = #{init}; + #{extra} + if (argc > 100000) printf("%p", &test); + return 0; + } + SRC + $defs.push("-DJSON_ENABLE_SIMD") + else + puts "Disable SIMD" + end +end + +have_header('cpuid.h') diff --git a/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/ext/json/ext/simd/simd.h b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/ext/json/ext/simd/simd.h new file mode 100644 index 0000000..f8e5ee1 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/ext/json/ext/simd/simd.h @@ -0,0 +1,191 @@ +#include "../json.h" + +typedef enum { + SIMD_NONE, + SIMD_NEON, + SIMD_SSE2 +} SIMD_Implementation; + +#ifndef __has_builtin // Optional of course. + #define __has_builtin(x) 0 // Compatibility with non-clang compilers. +#endif + +#ifdef __clang__ +# if __has_builtin(__builtin_ctzll) +# define HAVE_BUILTIN_CTZLL 1 +# else +# define HAVE_BUILTIN_CTZLL 0 +# endif +#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) +# define HAVE_BUILTIN_CTZLL 1 +#else +# define HAVE_BUILTIN_CTZLL 0 +#endif + +static inline uint32_t trailing_zeros64(uint64_t input) +{ + JSON_ASSERT(input > 0); // __builtin_ctz(0) is undefined behavior + +#if HAVE_BUILTIN_CTZLL + return __builtin_ctzll(input); +#else + uint32_t trailing_zeros = 0; + uint64_t temp = input; + while ((temp & 1) == 0 && temp > 0) { + trailing_zeros++; + temp >>= 1; + } + return trailing_zeros; +#endif +} + +static inline int trailing_zeros(int input) +{ + JSON_ASSERT(input > 0); // __builtin_ctz(0) is undefined behavior + +#if HAVE_BUILTIN_CTZLL + return __builtin_ctz(input); +#else + int trailing_zeros = 0; + int temp = input; + while ((temp & 1) == 0 && temp > 0) { + trailing_zeros++; + temp >>= 1; + } + return trailing_zeros; +#endif +} + +#ifdef JSON_ENABLE_SIMD + +#define SIMD_MINIMUM_THRESHOLD 6 + +#if defined(__ARM_NEON) || defined(__ARM_NEON__) || defined(__aarch64__) || defined(_M_ARM64) +#include + +#define FIND_SIMD_IMPLEMENTATION_DEFINED 1 +static inline SIMD_Implementation find_simd_implementation(void) +{ + return SIMD_NEON; +} + +#define HAVE_SIMD 1 +#define HAVE_SIMD_NEON 1 + +// See: https://community.arm.com/arm-community-blogs/b/servers-and-cloud-computing-blog/posts/porting-x86-vector-bitmask-optimizations-to-arm-neon +ALWAYS_INLINE(static) uint64_t neon_match_mask(uint8x16_t matches) +{ + const uint8x8_t res = vshrn_n_u16(vreinterpretq_u16_u8(matches), 4); + const uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(res), 0); + return mask & 0x8888888888888888ull; +} + +ALWAYS_INLINE(static) uint64_t compute_chunk_mask_neon(const char *ptr) +{ + uint8x16_t chunk = vld1q_u8((const unsigned char *)ptr); + + // Trick: c < 32 || c == 34 can be factored as c ^ 2 < 33 + // https://lemire.me/blog/2025/04/13/detect-control-characters-quotes-and-backslashes-efficiently-using-swar/ + const uint8x16_t too_low_or_dbl_quote = vcltq_u8(veorq_u8(chunk, vdupq_n_u8(2)), vdupq_n_u8(33)); + + uint8x16_t has_backslash = vceqq_u8(chunk, vdupq_n_u8('\\')); + uint8x16_t needs_escape = vorrq_u8(too_low_or_dbl_quote, has_backslash); + return neon_match_mask(needs_escape); +} + +ALWAYS_INLINE(static) int string_scan_simd_neon(const char **ptr, const char *end, uint64_t *mask) +{ + while (*ptr + sizeof(uint8x16_t) <= end) { + uint64_t chunk_mask = compute_chunk_mask_neon(*ptr); + if (chunk_mask) { + *mask = chunk_mask; + return 1; + } + *ptr += sizeof(uint8x16_t); + } + return 0; +} + +static inline uint8x16x4_t load_uint8x16_4(const unsigned char *table) +{ + uint8x16x4_t tab; + tab.val[0] = vld1q_u8(table); + tab.val[1] = vld1q_u8(table+16); + tab.val[2] = vld1q_u8(table+32); + tab.val[3] = vld1q_u8(table+48); + return tab; +} + +#endif /* ARM Neon Support.*/ + +#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64) + +#ifdef HAVE_X86INTRIN_H +#include + +#define HAVE_SIMD 1 +#define HAVE_SIMD_SSE2 1 + +#ifdef HAVE_CPUID_H +#define FIND_SIMD_IMPLEMENTATION_DEFINED 1 + +#if defined(__clang__) || defined(__GNUC__) +#define TARGET_SSE2 __attribute__((target("sse2"))) +#else +#define TARGET_SSE2 +#endif + +#define _mm_cmpge_epu8(a, b) _mm_cmpeq_epi8(_mm_max_epu8(a, b), a) +#define _mm_cmple_epu8(a, b) _mm_cmpge_epu8(b, a) +#define _mm_cmpgt_epu8(a, b) _mm_xor_si128(_mm_cmple_epu8(a, b), _mm_set1_epi8(-1)) +#define _mm_cmplt_epu8(a, b) _mm_cmpgt_epu8(b, a) + +ALWAYS_INLINE(static) TARGET_SSE2 int compute_chunk_mask_sse2(const char *ptr) +{ + __m128i chunk = _mm_loadu_si128((__m128i const*)ptr); + // Trick: c < 32 || c == 34 can be factored as c ^ 2 < 33 + // https://lemire.me/blog/2025/04/13/detect-control-characters-quotes-and-backslashes-efficiently-using-swar/ + __m128i too_low_or_dbl_quote = _mm_cmplt_epu8(_mm_xor_si128(chunk, _mm_set1_epi8(2)), _mm_set1_epi8(33)); + __m128i has_backslash = _mm_cmpeq_epi8(chunk, _mm_set1_epi8('\\')); + __m128i needs_escape = _mm_or_si128(too_low_or_dbl_quote, has_backslash); + return _mm_movemask_epi8(needs_escape); +} + +ALWAYS_INLINE(static) TARGET_SSE2 int string_scan_simd_sse2(const char **ptr, const char *end, int *mask) +{ + while (*ptr + sizeof(__m128i) <= end) { + int chunk_mask = compute_chunk_mask_sse2(*ptr); + if (chunk_mask) { + *mask = chunk_mask; + return 1; + } + *ptr += sizeof(__m128i); + } + + return 0; +} + +#include +#endif /* HAVE_CPUID_H */ + +static inline SIMD_Implementation find_simd_implementation(void) +{ + // TODO Revisit. I think the SSE version now only uses SSE2 instructions. + if (__builtin_cpu_supports("sse2")) { + return SIMD_SSE2; + } + + return SIMD_NONE; +} + +#endif /* HAVE_X86INTRIN_H */ +#endif /* X86_64 Support */ + +#endif /* JSON_ENABLE_SIMD */ + +#ifndef FIND_SIMD_IMPLEMENTATION_DEFINED +static inline SIMD_Implementation find_simd_implementation(void) +{ + return SIMD_NONE; +} +#endif diff --git a/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/ext/json/ext/vendor/fpconv.c b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/ext/json/ext/vendor/fpconv.c new file mode 100644 index 0000000..4888ab4 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/ext/json/ext/vendor/fpconv.c @@ -0,0 +1,480 @@ +// Boost Software License - Version 1.0 - August 17th, 2003 +// +// Permission is hereby granted, free of charge, to any person or organization +// obtaining a copy of the software and accompanying documentation covered by +// this license (the "Software") to use, reproduce, display, distribute, +// execute, and transmit the Software, and to prepare derivative works of the +// Software, and to permit third-parties to whom the Software is furnished to +// do so, all subject to the following: +// +// The copyright notices in the Software and this entire statement, including +// the above license grant, this restriction and the following disclaimer, +// must be included in all copies of the Software, in whole or in part, and +// all derivative works of the Software, unless such copies or derivative +// works are solely in the form of machine-executable object code generated by +// a source language processor. +// +// 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT +// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +// The contents of this file is extracted from https://github.com/night-shift/fpconv +// It was slightly modified to append ".0" to plain floats, for use with the https://github.com/ruby/json package. + +#include +#include +#include + +#if JSON_DEBUG +#include +#endif + +#define npowers 87 +#define steppowers 8 +#define firstpower -348 /* 10 ^ -348 */ + +#define expmax -32 +#define expmin -60 + +typedef struct Fp { + uint64_t frac; + int exp; +} Fp; + +static const Fp powers_ten[] = { + { 18054884314459144840U, -1220 }, { 13451937075301367670U, -1193 }, + { 10022474136428063862U, -1166 }, { 14934650266808366570U, -1140 }, + { 11127181549972568877U, -1113 }, { 16580792590934885855U, -1087 }, + { 12353653155963782858U, -1060 }, { 18408377700990114895U, -1034 }, + { 13715310171984221708U, -1007 }, { 10218702384817765436U, -980 }, + { 15227053142812498563U, -954 }, { 11345038669416679861U, -927 }, + { 16905424996341287883U, -901 }, { 12595523146049147757U, -874 }, + { 9384396036005875287U, -847 }, { 13983839803942852151U, -821 }, + { 10418772551374772303U, -794 }, { 15525180923007089351U, -768 }, + { 11567161174868858868U, -741 }, { 17236413322193710309U, -715 }, + { 12842128665889583758U, -688 }, { 9568131466127621947U, -661 }, + { 14257626930069360058U, -635 }, { 10622759856335341974U, -608 }, + { 15829145694278690180U, -582 }, { 11793632577567316726U, -555 }, + { 17573882009934360870U, -529 }, { 13093562431584567480U, -502 }, + { 9755464219737475723U, -475 }, { 14536774485912137811U, -449 }, + { 10830740992659433045U, -422 }, { 16139061738043178685U, -396 }, + { 12024538023802026127U, -369 }, { 17917957937422433684U, -343 }, + { 13349918974505688015U, -316 }, { 9946464728195732843U, -289 }, + { 14821387422376473014U, -263 }, { 11042794154864902060U, -236 }, + { 16455045573212060422U, -210 }, { 12259964326927110867U, -183 }, + { 18268770466636286478U, -157 }, { 13611294676837538539U, -130 }, + { 10141204801825835212U, -103 }, { 15111572745182864684U, -77 }, + { 11258999068426240000U, -50 }, { 16777216000000000000U, -24 }, + { 12500000000000000000U, 3 }, { 9313225746154785156U, 30 }, + { 13877787807814456755U, 56 }, { 10339757656912845936U, 83 }, + { 15407439555097886824U, 109 }, { 11479437019748901445U, 136 }, + { 17105694144590052135U, 162 }, { 12744735289059618216U, 189 }, + { 9495567745759798747U, 216 }, { 14149498560666738074U, 242 }, + { 10542197943230523224U, 269 }, { 15709099088952724970U, 295 }, + { 11704190886730495818U, 322 }, { 17440603504673385349U, 348 }, + { 12994262207056124023U, 375 }, { 9681479787123295682U, 402 }, + { 14426529090290212157U, 428 }, { 10748601772107342003U, 455 }, + { 16016664761464807395U, 481 }, { 11933345169920330789U, 508 }, + { 17782069995880619868U, 534 }, { 13248674568444952270U, 561 }, + { 9871031767461413346U, 588 }, { 14708983551653345445U, 614 }, + { 10959046745042015199U, 641 }, { 16330252207878254650U, 667 }, + { 12166986024289022870U, 694 }, { 18130221999122236476U, 720 }, + { 13508068024458167312U, 747 }, { 10064294952495520794U, 774 }, + { 14996968138956309548U, 800 }, { 11173611982879273257U, 827 }, + { 16649979327439178909U, 853 }, { 12405201291620119593U, 880 }, + { 9242595204427927429U, 907 }, { 13772540099066387757U, 933 }, + { 10261342003245940623U, 960 }, { 15290591125556738113U, 986 }, + { 11392378155556871081U, 1013 }, { 16975966327722178521U, 1039 }, + { 12648080533535911531U, 1066 } +}; + +static Fp find_cachedpow10(int exp, int* k) +{ + const double one_log_ten = 0.30102999566398114; + + int approx = (int)(-(exp + npowers) * one_log_ten); + int idx = (approx - firstpower) / steppowers; + + while(1) { + int current = exp + powers_ten[idx].exp + 64; + + if(current < expmin) { + idx++; + continue; + } + + if(current > expmax) { + idx--; + continue; + } + + *k = (firstpower + idx * steppowers); + + return powers_ten[idx]; + } +} + +#define fracmask 0x000FFFFFFFFFFFFFU +#define expmask 0x7FF0000000000000U +#define hiddenbit 0x0010000000000000U +#define signmask 0x8000000000000000U +#define expbias (1023 + 52) + +#define absv(n) ((n) < 0 ? -(n) : (n)) +#define minv(a, b) ((a) < (b) ? (a) : (b)) + +static const uint64_t tens[] = { + 10000000000000000000U, 1000000000000000000U, 100000000000000000U, + 10000000000000000U, 1000000000000000U, 100000000000000U, + 10000000000000U, 1000000000000U, 100000000000U, + 10000000000U, 1000000000U, 100000000U, + 10000000U, 1000000U, 100000U, + 10000U, 1000U, 100U, + 10U, 1U +}; + +static inline uint64_t get_dbits(double d) +{ + union { + double dbl; + uint64_t i; + } dbl_bits = { d }; + + return dbl_bits.i; +} + +static Fp build_fp(double d) +{ + uint64_t bits = get_dbits(d); + + Fp fp; + fp.frac = bits & fracmask; + fp.exp = (bits & expmask) >> 52; + + if(fp.exp) { + fp.frac += hiddenbit; + fp.exp -= expbias; + + } else { + fp.exp = -expbias + 1; + } + + return fp; +} + +static void normalize(Fp* fp) +{ + while ((fp->frac & hiddenbit) == 0) { + fp->frac <<= 1; + fp->exp--; + } + + int shift = 64 - 52 - 1; + fp->frac <<= shift; + fp->exp -= shift; +} + +static void get_normalized_boundaries(Fp* fp, Fp* lower, Fp* upper) +{ + upper->frac = (fp->frac << 1) + 1; + upper->exp = fp->exp - 1; + + while ((upper->frac & (hiddenbit << 1)) == 0) { + upper->frac <<= 1; + upper->exp--; + } + + int u_shift = 64 - 52 - 2; + + upper->frac <<= u_shift; + upper->exp = upper->exp - u_shift; + + + int l_shift = fp->frac == hiddenbit ? 2 : 1; + + lower->frac = (fp->frac << l_shift) - 1; + lower->exp = fp->exp - l_shift; + + + lower->frac <<= lower->exp - upper->exp; + lower->exp = upper->exp; +} + +static Fp multiply(Fp* a, Fp* b) +{ + const uint64_t lomask = 0x00000000FFFFFFFF; + + uint64_t ah_bl = (a->frac >> 32) * (b->frac & lomask); + uint64_t al_bh = (a->frac & lomask) * (b->frac >> 32); + uint64_t al_bl = (a->frac & lomask) * (b->frac & lomask); + uint64_t ah_bh = (a->frac >> 32) * (b->frac >> 32); + + uint64_t tmp = (ah_bl & lomask) + (al_bh & lomask) + (al_bl >> 32); + /* round up */ + tmp += 1U << 31; + + Fp fp = { + ah_bh + (ah_bl >> 32) + (al_bh >> 32) + (tmp >> 32), + a->exp + b->exp + 64 + }; + + return fp; +} + +static void round_digit(char* digits, int ndigits, uint64_t delta, uint64_t rem, uint64_t kappa, uint64_t frac) +{ + while (rem < frac && delta - rem >= kappa && + (rem + kappa < frac || frac - rem > rem + kappa - frac)) { + + digits[ndigits - 1]--; + rem += kappa; + } +} + +static int generate_digits(Fp* fp, Fp* upper, Fp* lower, char* digits, int* K) +{ + uint64_t wfrac = upper->frac - fp->frac; + uint64_t delta = upper->frac - lower->frac; + + Fp one; + one.frac = 1ULL << -upper->exp; + one.exp = upper->exp; + + uint64_t part1 = upper->frac >> -one.exp; + uint64_t part2 = upper->frac & (one.frac - 1); + + int idx = 0, kappa = 10; + const uint64_t* divp; + /* 1000000000 */ + for(divp = tens + 10; kappa > 0; divp++) { + + uint64_t div = *divp; + unsigned digit = (unsigned) (part1 / div); + + if (digit || idx) { + digits[idx++] = digit + '0'; + } + + part1 -= digit * div; + kappa--; + + uint64_t tmp = (part1 <<-one.exp) + part2; + if (tmp <= delta) { + *K += kappa; + round_digit(digits, idx, delta, tmp, div << -one.exp, wfrac); + + return idx; + } + } + + /* 10 */ + const uint64_t* unit = tens + 18; + + while(true) { + part2 *= 10; + delta *= 10; + kappa--; + + unsigned digit = (unsigned) (part2 >> -one.exp); + if (digit || idx) { + digits[idx++] = digit + '0'; + } + + part2 &= one.frac - 1; + if (part2 < delta) { + *K += kappa; + round_digit(digits, idx, delta, part2, one.frac, wfrac * *unit); + + return idx; + } + + unit--; + } +} + +static int grisu2(double d, char* digits, int* K) +{ + Fp w = build_fp(d); + + Fp lower, upper; + get_normalized_boundaries(&w, &lower, &upper); + + normalize(&w); + + int k; + Fp cp = find_cachedpow10(upper.exp, &k); + + w = multiply(&w, &cp); + upper = multiply(&upper, &cp); + lower = multiply(&lower, &cp); + + lower.frac++; + upper.frac--; + + *K = -k; + + return generate_digits(&w, &upper, &lower, digits, K); +} + +static int emit_digits(char* digits, int ndigits, char* dest, int K, bool neg) +{ + int exp = absv(K + ndigits - 1); + + if(K >= 0 && exp < 15) { + memcpy(dest, digits, ndigits); + memset(dest + ndigits, '0', K); + + /* add a .0 to mark this as a float. */ + dest[ndigits + K] = '.'; + dest[ndigits + K + 1] = '0'; + + return ndigits + K + 2; + } + + /* write decimal w/o scientific notation */ + if(K < 0 && (K > -7 || exp < 10)) { + int offset = ndigits - absv(K); + /* fp < 1.0 -> write leading zero */ + if(offset <= 0) { + offset = -offset; + dest[0] = '0'; + dest[1] = '.'; + memset(dest + 2, '0', offset); + memcpy(dest + offset + 2, digits, ndigits); + + return ndigits + 2 + offset; + + /* fp > 1.0 */ + } else { + memcpy(dest, digits, offset); + dest[offset] = '.'; + memcpy(dest + offset + 1, digits + offset, ndigits - offset); + + return ndigits + 1; + } + } + + /* write decimal w/ scientific notation */ + ndigits = minv(ndigits, 18 - neg); + + int idx = 0; + dest[idx++] = digits[0]; + + if(ndigits > 1) { + dest[idx++] = '.'; + memcpy(dest + idx, digits + 1, ndigits - 1); + idx += ndigits - 1; + } + + dest[idx++] = 'e'; + + char sign = K + ndigits - 1 < 0 ? '-' : '+'; + dest[idx++] = sign; + + int cent = 0; + + if(exp > 99) { + cent = exp / 100; + dest[idx++] = cent + '0'; + exp -= cent * 100; + } + if(exp > 9) { + int dec = exp / 10; + dest[idx++] = dec + '0'; + exp -= dec * 10; + + } else if(cent) { + dest[idx++] = '0'; + } + + dest[idx++] = exp % 10 + '0'; + + return idx; +} + +static int filter_special(double fp, char* dest) +{ + if(fp == 0.0) { + dest[0] = '0'; + dest[1] = '.'; + dest[2] = '0'; + return 3; + } + + uint64_t bits = get_dbits(fp); + + bool nan = (bits & expmask) == expmask; + + if(!nan) { + return 0; + } + + if(bits & fracmask) { + dest[0] = 'n'; dest[1] = 'a'; dest[2] = 'n'; + + } else { + dest[0] = 'i'; dest[1] = 'n'; dest[2] = 'f'; + } + + return 3; +} + +/* Fast and accurate double to string conversion based on Florian Loitsch's + * Grisu-algorithm[1]. + * + * Input: + * fp -> the double to convert, dest -> destination buffer. + * The generated string will never be longer than 32 characters. + * Make sure to pass a pointer to at least 32 bytes of memory. + * The emitted string will not be null terminated. + * + * + * + * Output: + * The number of written characters. + * + * Exemplary usage: + * + * void print(double d) + * { + * char buf[28 + 1] // plus null terminator + * int str_len = fpconv_dtoa(d, buf); + * + * buf[str_len] = '\0'; + * printf("%s", buf); + * } + * + */ +static int fpconv_dtoa(double d, char dest[28]) +{ + char digits[18]; + + int str_len = 0; + bool neg = false; + + if(get_dbits(d) & signmask) { + dest[0] = '-'; + str_len++; + neg = true; + } + + int spec = filter_special(d, dest + str_len); + + if(spec) { + return str_len + spec; + } + + int K = 0; + int ndigits = grisu2(d, digits, &K); + + str_len += emit_digits(digits, ndigits, dest + str_len, K, neg); +#if JSON_DEBUG + assert(str_len <= 32); +#endif + + return str_len; +} diff --git a/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/ext/json/ext/vendor/jeaiii-ltoa.h b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/ext/json/ext/vendor/jeaiii-ltoa.h new file mode 100644 index 0000000..ba4f497 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/ext/json/ext/vendor/jeaiii-ltoa.h @@ -0,0 +1,267 @@ +/* + +This file is released under the terms of the MIT License. It is based on the +work of James Edward Anhalt III, with the original license listed below. + +MIT License + +Copyright (c) 2024,2025 Enrico Thierbach - https://github.com/radiospiel +Copyright (c) 2022 James Edward Anhalt III - https://github.com/jeaiii/itoa + +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. +*/ + +#ifndef JEAIII_TO_TEXT_H_ +#define JEAIII_TO_TEXT_H_ + +#include + +typedef uint_fast32_t u32_t; +typedef uint_fast64_t u64_t; + +#define u32(x) ((u32_t)(x)) +#define u64(x) ((u64_t)(x)) + +struct digit_pair +{ + char dd[2]; +}; + +static const struct digit_pair *digits_dd = (struct digit_pair *)( + "00" "01" "02" "03" "04" "05" "06" "07" "08" "09" + "10" "11" "12" "13" "14" "15" "16" "17" "18" "19" + "20" "21" "22" "23" "24" "25" "26" "27" "28" "29" + "30" "31" "32" "33" "34" "35" "36" "37" "38" "39" + "40" "41" "42" "43" "44" "45" "46" "47" "48" "49" + "50" "51" "52" "53" "54" "55" "56" "57" "58" "59" + "60" "61" "62" "63" "64" "65" "66" "67" "68" "69" + "70" "71" "72" "73" "74" "75" "76" "77" "78" "79" + "80" "81" "82" "83" "84" "85" "86" "87" "88" "89" + "90" "91" "92" "93" "94" "95" "96" "97" "98" "99" +); + +static const struct digit_pair *digits_fd = (struct digit_pair *)( + "0_" "1_" "2_" "3_" "4_" "5_" "6_" "7_" "8_" "9_" + "10" "11" "12" "13" "14" "15" "16" "17" "18" "19" + "20" "21" "22" "23" "24" "25" "26" "27" "28" "29" + "30" "31" "32" "33" "34" "35" "36" "37" "38" "39" + "40" "41" "42" "43" "44" "45" "46" "47" "48" "49" + "50" "51" "52" "53" "54" "55" "56" "57" "58" "59" + "60" "61" "62" "63" "64" "65" "66" "67" "68" "69" + "70" "71" "72" "73" "74" "75" "76" "77" "78" "79" + "80" "81" "82" "83" "84" "85" "86" "87" "88" "89" + "90" "91" "92" "93" "94" "95" "96" "97" "98" "99" +); + +static const u64_t mask24 = (u64(1) << 24) - 1; +static const u64_t mask32 = (u64(1) << 32) - 1; +static const u64_t mask57 = (u64(1) << 57) - 1; + +#define COPY(buffer, digits) memcpy(buffer, &(digits), sizeof(struct digit_pair)) + +static char * +jeaiii_ultoa(char *b, u64_t n) +{ + if (n < u32(1e2)) { + COPY(b, digits_fd[n]); + return n < 10 ? b + 1 : b + 2; + } + + if (n < u32(1e6)) { + if (n < u32(1e4)) { + u32_t f0 = u32((10 * (1 << 24) / 1e3 + 1) * n); + COPY(b, digits_fd[f0 >> 24]); + + b -= n < u32(1e3); + u32_t f2 = (f0 & mask24) * 100; + COPY(b + 2, digits_dd[f2 >> 24]); + + return b + 4; + } + + u64_t f0 = u64(10 * (1ull << 32ull)/ 1e5 + 1) * n; + COPY(b, digits_fd[f0 >> 32]); + + b -= n < u32(1e5); + u64_t f2 = (f0 & mask32) * 100; + COPY(b + 2, digits_dd[f2 >> 32]); + + u64_t f4 = (f2 & mask32) * 100; + COPY(b + 4, digits_dd[f4 >> 32]); + return b + 6; + } + + if (n < u64(1ull << 32ull)) { + if (n < u32(1e8)) { + u64_t f0 = u64(10 * (1ull << 48ull) / 1e7 + 1) * n >> 16; + COPY(b, digits_fd[f0 >> 32]); + + b -= n < u32(1e7); + u64_t f2 = (f0 & mask32) * 100; + COPY(b + 2, digits_dd[f2 >> 32]); + + u64_t f4 = (f2 & mask32) * 100; + COPY(b + 4, digits_dd[f4 >> 32]); + + u64_t f6 = (f4 & mask32) * 100; + COPY(b + 6, digits_dd[f6 >> 32]); + + return b + 8; + } + + u64_t f0 = u64(10 * (1ull << 57ull) / 1e9 + 1) * n; + COPY(b, digits_fd[f0 >> 57]); + + b -= n < u32(1e9); + u64_t f2 = (f0 & mask57) * 100; + COPY(b + 2, digits_dd[f2 >> 57]); + + u64_t f4 = (f2 & mask57) * 100; + COPY(b + 4, digits_dd[f4 >> 57]); + + u64_t f6 = (f4 & mask57) * 100; + COPY(b + 6, digits_dd[f6 >> 57]); + + u64_t f8 = (f6 & mask57) * 100; + COPY(b + 8, digits_dd[f8 >> 57]); + + return b + 10; + } + + // if we get here U must be u64 but some compilers don't know that, so reassign n to a u64 to avoid warnings + u32_t z = n % u32(1e8); + u64_t u = n / u32(1e8); + + if (u < u32(1e2)) { + // u can't be 1 digit (if u < 10 it would have been handled above as a 9 digit 32bit number) + COPY(b, digits_dd[u]); + b += 2; + } + else if (u < u32(1e6)) { + if (u < u32(1e4)) { + u32_t f0 = u32((10 * (1 << 24) / 1e3 + 1) * u); + COPY(b, digits_fd[f0 >> 24]); + + b -= u < u32(1e3); + u32_t f2 = (f0 & mask24) * 100; + COPY(b + 2, digits_dd[f2 >> 24]); + b += 4; + } + else { + u64_t f0 = u64(10 * (1ull << 32ull) / 1e5 + 1) * u; + COPY(b, digits_fd[f0 >> 32]); + + b -= u < u32(1e5); + u64_t f2 = (f0 & mask32) * 100; + COPY(b + 2, digits_dd[f2 >> 32]); + + u64_t f4 = (f2 & mask32) * 100; + COPY(b + 4, digits_dd[f4 >> 32]); + b += 6; + } + } + else if (u < u32(1e8)) { + u64_t f0 = u64(10 * (1ull << 48ull) / 1e7 + 1) * u >> 16; + COPY(b, digits_fd[f0 >> 32]); + + b -= u < u32(1e7); + u64_t f2 = (f0 & mask32) * 100; + COPY(b + 2, digits_dd[f2 >> 32]); + + u64_t f4 = (f2 & mask32) * 100; + COPY(b + 4, digits_dd[f4 >> 32]); + + u64_t f6 = (f4 & mask32) * 100; + COPY(b + 6, digits_dd[f6 >> 32]); + + b += 8; + } + else if (u < u64(1ull << 32ull)) { + u64_t f0 = u64(10 * (1ull << 57ull) / 1e9 + 1) * u; + COPY(b, digits_fd[f0 >> 57]); + + b -= u < u32(1e9); + u64_t f2 = (f0 & mask57) * 100; + COPY(b + 2, digits_dd[f2 >> 57]); + + u64_t f4 = (f2 & mask57) * 100; + COPY(b + 4, digits_dd[f4 >> 57]); + + u64_t f6 = (f4 & mask57) * 100; + COPY(b + 6, digits_dd[f6 >> 57]); + + u64_t f8 = (f6 & mask57) * 100; + COPY(b + 8, digits_dd[f8 >> 57]); + b += 10; + } + else { + u32_t y = u % u32(1e8); + u /= u32(1e8); + + // u is 2, 3, or 4 digits (if u < 10 it would have been handled above) + if (u < u32(1e2)) { + COPY(b, digits_dd[u]); + b += 2; + } + else { + u32_t f0 = u32((10 * (1 << 24) / 1e3 + 1) * u); + COPY(b, digits_fd[f0 >> 24]); + + b -= u < u32(1e3); + u32_t f2 = (f0 & mask24) * 100; + COPY(b + 2, digits_dd[f2 >> 24]); + + b += 4; + } + // do 8 digits + u64_t f0 = (u64((1ull << 48ull) / 1e6 + 1) * y >> 16) + 1; + COPY(b, digits_dd[f0 >> 32]); + + u64_t f2 = (f0 & mask32) * 100; + COPY(b + 2, digits_dd[f2 >> 32]); + + u64_t f4 = (f2 & mask32) * 100; + COPY(b + 4, digits_dd[f4 >> 32]); + + u64_t f6 = (f4 & mask32) * 100; + COPY(b + 6, digits_dd[f6 >> 32]); + b += 8; + } + + // do 8 digits + u64_t f0 = (u64((1ull << 48ull) / 1e6 + 1) * z >> 16) + 1; + COPY(b, digits_dd[f0 >> 32]); + + u64_t f2 = (f0 & mask32) * 100; + COPY(b + 2, digits_dd[f2 >> 32]); + + u64_t f4 = (f2 & mask32) * 100; + COPY(b + 4, digits_dd[f4 >> 32]); + + u64_t f6 = (f4 & mask32) * 100; + COPY(b + 6, digits_dd[f6 >> 32]); + + return b + 8; +} + +#undef u32 +#undef u64 +#undef COPY + +#endif // JEAIII_TO_TEXT_H_ diff --git a/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/ext/json/ext/vendor/ryu.h b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/ext/json/ext/vendor/ryu.h new file mode 100644 index 0000000..f06ec81 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/ext/json/ext/vendor/ryu.h @@ -0,0 +1,819 @@ +// Copyright 2018 Ulf Adams +// +// The contents of this file may be used under the terms of the Apache License, +// Version 2.0. +// +// Alternatively, the contents of this file may be used under the terms of +// the Boost Software License, Version 1.0. +// +// Unless required by applicable law or agreed to in writing, this software +// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. +// +// --- +// +// Apache License +// Version 2.0, January 2004 +// http://www.apache.org/licenses/ +// +// TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +// +// 1. Definitions. +// +// "License" shall mean the terms and conditions for use, reproduction, +// and distribution as defined by Sections 1 through 9 of this document. +// +// "Licensor" shall mean the copyright owner or entity authorized by +// the copyright owner that is granting the License. +// +// "Legal Entity" shall mean the union of the acting entity and all +// other entities that control, are controlled by, or are under common +// control with that entity. For the purposes of this definition, +// "control" means (i) the power, direct or indirect, to cause the +// direction or management of such entity, whether by contract or +// otherwise, or (ii) ownership of fifty percent (50%) or more of the +// outstanding shares, or (iii) beneficial ownership of such entity. +// +// "You" (or "Your") shall mean an individual or Legal Entity +// exercising permissions granted by this License. +// +// "Source" form shall mean the preferred form for making modifications, +// including but not limited to software source code, documentation +// source, and configuration files. +// +// "Object" form shall mean any form resulting from mechanical +// transformation or translation of a Source form, including but +// not limited to compiled object code, generated documentation, +// and conversions to other media types. +// +// "Work" shall mean the work of authorship, whether in Source or +// Object form, made available under the License, as indicated by a +// copyright notice that is included in or attached to the work +// (an example is provided in the Appendix below). +// +// "Derivative Works" shall mean any work, whether in Source or Object +// form, that is based on (or derived from) the Work and for which the +// editorial revisions, annotations, elaborations, or other modifications +// represent, as a whole, an original work of authorship. For the purposes +// of this License, Derivative Works shall not include works that remain +// separable from, or merely link (or bind by name) to the interfaces of, +// the Work and Derivative Works thereof. +// +// "Contribution" shall mean any work of authorship, including +// the original version of the Work and any modifications or additions +// to that Work or Derivative Works thereof, that is intentionally +// submitted to Licensor for inclusion in the Work by the copyright owner +// or by an individual or Legal Entity authorized to submit on behalf of +// the copyright owner. For the purposes of this definition, "submitted" +// means any form of electronic, verbal, or written communication sent +// to the Licensor or its representatives, including but not limited to +// communication on electronic mailing lists, source code control systems, +// and issue tracking systems that are managed by, or on behalf of, the +// Licensor for the purpose of discussing and improving the Work, but +// excluding communication that is conspicuously marked or otherwise +// designated in writing by the copyright owner as "Not a Contribution." +// +// "Contributor" shall mean Licensor and any individual or Legal Entity +// on behalf of whom a Contribution has been received by Licensor and +// subsequently incorporated within the Work. +// +// 2. Grant of Copyright License. Subject to the terms and conditions of +// this License, each Contributor hereby grants to You a perpetual, +// worldwide, non-exclusive, no-charge, royalty-free, irrevocable +// copyright license to reproduce, prepare Derivative Works of, +// publicly display, publicly perform, sublicense, and distribute the +// Work and such Derivative Works in Source or Object form. +// +// 3. Grant of Patent License. Subject to the terms and conditions of +// this License, each Contributor hereby grants to You a perpetual, +// worldwide, non-exclusive, no-charge, royalty-free, irrevocable +// (except as stated in this section) patent license to make, have made, +// use, offer to sell, sell, import, and otherwise transfer the Work, +// where such license applies only to those patent claims licensable +// by such Contributor that are necessarily infringed by their +// Contribution(s) alone or by combination of their Contribution(s) +// with the Work to which such Contribution(s) was submitted. If You +// institute patent litigation against any entity (including a +// cross-claim or counterclaim in a lawsuit) alleging that the Work +// or a Contribution incorporated within the Work constitutes direct +// or contributory patent infringement, then any patent licenses +// granted to You under this License for that Work shall terminate +// as of the date such litigation is filed. +// +// 4. Redistribution. You may reproduce and distribute copies of the +// Work or Derivative Works thereof in any medium, with or without +// modifications, and in Source or Object form, provided that You +// meet the following conditions: +// +// (a) You must give any other recipients of the Work or +// Derivative Works a copy of this License; and +// +// (b) You must cause any modified files to carry prominent notices +// stating that You changed the files; and +// +// (c) You must retain, in the Source form of any Derivative Works +// that You distribute, all copyright, patent, trademark, and +// attribution notices from the Source form of the Work, +// excluding those notices that do not pertain to any part of +// the Derivative Works; and +// +// (d) If the Work includes a "NOTICE" text file as part of its +// distribution, then any Derivative Works that You distribute must +// include a readable copy of the attribution notices contained +// within such NOTICE file, excluding those notices that do not +// pertain to any part of the Derivative Works, in at least one +// of the following places: within a NOTICE text file distributed +// as part of the Derivative Works; within the Source form or +// documentation, if provided along with the Derivative Works; or, +// within a display generated by the Derivative Works, if and +// wherever such third-party notices normally appear. The contents +// of the NOTICE file are for informational purposes only and +// do not modify the License. You may add Your own attribution +// notices within Derivative Works that You distribute, alongside +// or as an addendum to the NOTICE text from the Work, provided +// that such additional attribution notices cannot be construed +// as modifying the License. +// +// You may add Your own copyright statement to Your modifications and +// may provide additional or different license terms and conditions +// for use, reproduction, or distribution of Your modifications, or +// for any such Derivative Works as a whole, provided Your use, +// reproduction, and distribution of the Work otherwise complies with +// the conditions stated in this License. +// +// 5. Submission of Contributions. Unless You explicitly state otherwise, +// any Contribution intentionally submitted for inclusion in the Work +// by You to the Licensor shall be under the terms and conditions of +// this License, without any additional terms or conditions. +// Notwithstanding the above, nothing herein shall supersede or modify +// the terms of any separate license agreement you may have executed +// with Licensor regarding such Contributions. +// +// 6. Trademarks. This License does not grant permission to use the trade +// names, trademarks, service marks, or product names of the Licensor, +// except as required for reasonable and customary use in describing the +// origin of the Work and reproducing the content of the NOTICE file. +// +// 7. Disclaimer of Warranty. Unless required by applicable law or +// agreed to in writing, Licensor provides the Work (and each +// Contributor provides its Contributions) on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +// implied, including, without limitation, any warranties or conditions +// of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +// PARTICULAR PURPOSE. You are solely responsible for determining the +// appropriateness of using or redistributing the Work and assume any +// risks associated with Your exercise of permissions under this License. +// +// 8. Limitation of Liability. In no event and under no legal theory, +// whether in tort (including negligence), contract, or otherwise, +// unless required by applicable law (such as deliberate and grossly +// negligent acts) or agreed to in writing, shall any Contributor be +// liable to You for damages, including any direct, indirect, special, +// incidental, or consequential damages of any character arising as a +// result of this License or out of the use or inability to use the +// Work (including but not limited to damages for loss of goodwill, +// work stoppage, computer failure or malfunction, or any and all +// other commercial damages or losses), even if such Contributor +// has been advised of the possibility of such damages. +// +// 9. Accepting Warranty or Additional Liability. While redistributing +// the Work or Derivative Works thereof, You may choose to offer, +// and charge a fee for, acceptance of support, warranty, indemnity, +// or other liability obligations and/or rights consistent with this +// License. However, in accepting such obligations, You may act only +// on Your own behalf and on Your sole responsibility, not on behalf +// of any other Contributor, and only if You agree to indemnify, +// defend, and hold each Contributor harmless for any liability +// incurred by, or claims asserted against, such Contributor by reason +// of your accepting any such warranty or additional liability. +// +// END OF TERMS AND CONDITIONS +// +// APPENDIX: How to apply the Apache License to your work. +// +// To apply the Apache License to your work, attach the following +// boilerplate notice, with the fields enclosed by brackets "[]" +// replaced with your own identifying information. (Don't include +// the brackets!) The text should be enclosed in the appropriate +// comment syntax for the file format. We also recommend that a +// file or class name and description of purpose be included on the +// same "printed page" as the copyright notice for easier +// identification within third-party archives. +// +// Copyright [yyyy] [name of copyright owner] +// +// 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. +// +// --- +// +// Boost Software License - Version 1.0 - August 17th, 2003 +// +// Permission is hereby granted, free of charge, to any person or organization +// obtaining a copy of the software and accompanying documentation covered by +// this license (the "Software") to use, reproduce, display, distribute, +// execute, and transmit the Software, and to prepare derivative works of the +// Software, and to permit third-parties to whom the Software is furnished to +// do so, all subject to the following: +// +// The copyright notices in the Software and this entire statement, including +// the above license grant, this restriction and the following disclaimer, +// must be included in all copies of the Software, in whole or in part, and +// all derivative works of the Software, unless such copies or derivative +// works are solely in the form of machine-executable object code generated by +// a source language processor. +// +// 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT +// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// --- +// Minimal Ryu implementation adapted for Ruby JSON gem by Josef Šimánek +// Optimized for pre-extracted mantissa/exponent from JSON parsing +// This is a stripped-down version containing only what's needed for +// converting decimal mantissa+exponent to IEEE 754 double precision. + +#ifndef RYU_H +#define RYU_H + +#include +#include +#include + +// Detect __builtin_clzll availability (for floor_log2) +// Note: MSVC doesn't have __builtin_clzll, so we provide a fallback +#ifdef __clang__ + #if __has_builtin(__builtin_clzll) + #define RYU_HAVE_BUILTIN_CLZLL 1 + #else + #define RYU_HAVE_BUILTIN_CLZLL 0 + #endif +#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) + #define RYU_HAVE_BUILTIN_CLZLL 1 +#else + #define RYU_HAVE_BUILTIN_CLZLL 0 +#endif + +// Count leading zeros (for floor_log2) +static inline uint32_t ryu_leading_zeros64(uint64_t input) +{ +#if RYU_HAVE_BUILTIN_CLZLL + return __builtin_clzll(input); +#else + // Fallback: binary search for the highest set bit + // This works on MSVC and other compilers without __builtin_clzll + if (input == 0) return 64; + uint32_t n = 0; + if (input <= 0x00000000FFFFFFFFULL) { n += 32; input <<= 32; } + if (input <= 0x0000FFFFFFFFFFFFULL) { n += 16; input <<= 16; } + if (input <= 0x00FFFFFFFFFFFFFFULL) { n += 8; input <<= 8; } + if (input <= 0x0FFFFFFFFFFFFFFFULL) { n += 4; input <<= 4; } + if (input <= 0x3FFFFFFFFFFFFFFFULL) { n += 2; input <<= 2; } + if (input <= 0x7FFFFFFFFFFFFFFFULL) { n += 1; } + return n; +#endif +} + +// These tables are generated by PrintDoubleLookupTable. +#define DOUBLE_POW5_INV_BITCOUNT 125 +#define DOUBLE_POW5_BITCOUNT 125 + +#define DOUBLE_POW5_INV_TABLE_SIZE 342 +#define DOUBLE_POW5_TABLE_SIZE 326 + +static const uint64_t DOUBLE_POW5_INV_SPLIT[DOUBLE_POW5_INV_TABLE_SIZE][2] = { + { 1u, 2305843009213693952u }, { 11068046444225730970u, 1844674407370955161u }, + { 5165088340638674453u, 1475739525896764129u }, { 7821419487252849886u, 1180591620717411303u }, + { 8824922364862649494u, 1888946593147858085u }, { 7059937891890119595u, 1511157274518286468u }, + { 13026647942995916322u, 1208925819614629174u }, { 9774590264567735146u, 1934281311383406679u }, + { 11509021026396098440u, 1547425049106725343u }, { 16585914450600699399u, 1237940039285380274u }, + { 15469416676735388068u, 1980704062856608439u }, { 16064882156130220778u, 1584563250285286751u }, + { 9162556910162266299u, 1267650600228229401u }, { 7281393426775805432u, 2028240960365167042u }, + { 16893161185646375315u, 1622592768292133633u }, { 2446482504291369283u, 1298074214633706907u }, + { 7603720821608101175u, 2076918743413931051u }, { 2393627842544570617u, 1661534994731144841u }, + { 16672297533003297786u, 1329227995784915872u }, { 11918280793837635165u, 2126764793255865396u }, + { 5845275820328197809u, 1701411834604692317u }, { 15744267100488289217u, 1361129467683753853u }, + { 3054734472329800808u, 2177807148294006166u }, { 17201182836831481939u, 1742245718635204932u }, + { 6382248639981364905u, 1393796574908163946u }, { 2832900194486363201u, 2230074519853062314u }, + { 5955668970331000884u, 1784059615882449851u }, { 1075186361522890384u, 1427247692705959881u }, + { 12788344622662355584u, 2283596308329535809u }, { 13920024512871794791u, 1826877046663628647u }, + { 3757321980813615186u, 1461501637330902918u }, { 10384555214134712795u, 1169201309864722334u }, + { 5547241898389809503u, 1870722095783555735u }, { 4437793518711847602u, 1496577676626844588u }, + { 10928932444453298728u, 1197262141301475670u }, { 17486291911125277965u, 1915619426082361072u }, + { 6610335899416401726u, 1532495540865888858u }, { 12666966349016942027u, 1225996432692711086u }, + { 12888448528943286597u, 1961594292308337738u }, { 17689456452638449924u, 1569275433846670190u }, + { 14151565162110759939u, 1255420347077336152u }, { 7885109000409574610u, 2008672555323737844u }, + { 9997436015069570011u, 1606938044258990275u }, { 7997948812055656009u, 1285550435407192220u }, + { 12796718099289049614u, 2056880696651507552u }, { 2858676849947419045u, 1645504557321206042u }, + { 13354987924183666206u, 1316403645856964833u }, { 17678631863951955605u, 2106245833371143733u }, + { 3074859046935833515u, 1684996666696914987u }, { 13527933681774397782u, 1347997333357531989u }, + { 10576647446613305481u, 2156795733372051183u }, { 15840015586774465031u, 1725436586697640946u }, + { 8982663654677661702u, 1380349269358112757u }, { 18061610662226169046u, 2208558830972980411u }, + { 10759939715039024913u, 1766847064778384329u }, { 12297300586773130254u, 1413477651822707463u }, + { 15986332124095098083u, 2261564242916331941u }, { 9099716884534168143u, 1809251394333065553u }, + { 14658471137111155161u, 1447401115466452442u }, { 4348079280205103483u, 1157920892373161954u }, + { 14335624477811986218u, 1852673427797059126u }, { 7779150767507678651u, 1482138742237647301u }, + { 2533971799264232598u, 1185710993790117841u }, { 15122401323048503126u, 1897137590064188545u }, + { 12097921058438802501u, 1517710072051350836u }, { 5988988032009131678u, 1214168057641080669u }, + { 16961078480698431330u, 1942668892225729070u }, { 13568862784558745064u, 1554135113780583256u }, + { 7165741412905085728u, 1243308091024466605u }, { 11465186260648137165u, 1989292945639146568u }, + { 16550846638002330379u, 1591434356511317254u }, { 16930026125143774626u, 1273147485209053803u }, + { 4951948911778577463u, 2037035976334486086u }, { 272210314680951647u, 1629628781067588869u }, + { 3907117066486671641u, 1303703024854071095u }, { 6251387306378674625u, 2085924839766513752u }, + { 16069156289328670670u, 1668739871813211001u }, { 9165976216721026213u, 1334991897450568801u }, + { 7286864317269821294u, 2135987035920910082u }, { 16897537898041588005u, 1708789628736728065u }, + { 13518030318433270404u, 1367031702989382452u }, { 6871453250525591353u, 2187250724783011924u }, + { 9186511415162383406u, 1749800579826409539u }, { 11038557946871817048u, 1399840463861127631u }, + { 10282995085511086630u, 2239744742177804210u }, { 8226396068408869304u, 1791795793742243368u }, + { 13959814484210916090u, 1433436634993794694u }, { 11267656730511734774u, 2293498615990071511u }, + { 5324776569667477496u, 1834798892792057209u }, { 7949170070475892320u, 1467839114233645767u }, + { 17427382500606444826u, 1174271291386916613u }, { 5747719112518849781u, 1878834066219066582u }, + { 15666221734240810795u, 1503067252975253265u }, { 12532977387392648636u, 1202453802380202612u }, + { 5295368560860596524u, 1923926083808324180u }, { 4236294848688477220u, 1539140867046659344u }, + { 7078384693692692099u, 1231312693637327475u }, { 11325415509908307358u, 1970100309819723960u }, + { 9060332407926645887u, 1576080247855779168u }, { 14626963555825137356u, 1260864198284623334u }, + { 12335095245094488799u, 2017382717255397335u }, { 9868076196075591040u, 1613906173804317868u }, + { 15273158586344293478u, 1291124939043454294u }, { 13369007293925138595u, 2065799902469526871u }, + { 7005857020398200553u, 1652639921975621497u }, { 16672732060544291412u, 1322111937580497197u }, + { 11918976037903224966u, 2115379100128795516u }, { 5845832015580669650u, 1692303280103036413u }, + { 12055363241948356366u, 1353842624082429130u }, { 841837113407818570u, 2166148198531886609u }, + { 4362818505468165179u, 1732918558825509287u }, { 14558301248600263113u, 1386334847060407429u }, + { 12225235553534690011u, 2218135755296651887u }, { 2401490813343931363u, 1774508604237321510u }, + { 1921192650675145090u, 1419606883389857208u }, { 17831303500047873437u, 2271371013423771532u }, + { 6886345170554478103u, 1817096810739017226u }, { 1819727321701672159u, 1453677448591213781u }, + { 16213177116328979020u, 1162941958872971024u }, { 14873036941900635463u, 1860707134196753639u }, + { 15587778368262418694u, 1488565707357402911u }, { 8780873879868024632u, 1190852565885922329u }, + { 2981351763563108441u, 1905364105417475727u }, { 13453127855076217722u, 1524291284333980581u }, + { 7073153469319063855u, 1219433027467184465u }, { 11317045550910502167u, 1951092843947495144u }, + { 12742985255470312057u, 1560874275157996115u }, { 10194388204376249646u, 1248699420126396892u }, + { 1553625868034358140u, 1997919072202235028u }, { 8621598323911307159u, 1598335257761788022u }, + { 17965325103354776697u, 1278668206209430417u }, { 13987124906400001422u, 2045869129935088668u }, + { 121653480894270168u, 1636695303948070935u }, { 97322784715416134u, 1309356243158456748u }, + { 14913111714512307107u, 2094969989053530796u }, { 8241140556867935363u, 1675975991242824637u }, + { 17660958889720079260u, 1340780792994259709u }, { 17189487779326395846u, 2145249268790815535u }, + { 13751590223461116677u, 1716199415032652428u }, { 18379969808252713988u, 1372959532026121942u }, + { 14650556434236701088u, 2196735251241795108u }, { 652398703163629901u, 1757388200993436087u }, + { 11589965406756634890u, 1405910560794748869u }, { 7475898206584884855u, 2249456897271598191u }, + { 2291369750525997561u, 1799565517817278553u }, { 9211793429904618695u, 1439652414253822842u }, + { 18428218302589300235u, 2303443862806116547u }, { 7363877012587619542u, 1842755090244893238u }, + { 13269799239553916280u, 1474204072195914590u }, { 10615839391643133024u, 1179363257756731672u }, + { 2227947767661371545u, 1886981212410770676u }, { 16539753473096738529u, 1509584969928616540u }, + { 13231802778477390823u, 1207667975942893232u }, { 6413489186596184024u, 1932268761508629172u }, + { 16198837793502678189u, 1545815009206903337u }, { 5580372605318321905u, 1236652007365522670u }, + { 8928596168509315048u, 1978643211784836272u }, { 18210923379033183008u, 1582914569427869017u }, + { 7190041073742725760u, 1266331655542295214u }, { 436019273762630246u, 2026130648867672343u }, + { 7727513048493924843u, 1620904519094137874u }, { 9871359253537050198u, 1296723615275310299u }, + { 4726128361433549347u, 2074757784440496479u }, { 7470251503888749801u, 1659806227552397183u }, + { 13354898832594820487u, 1327844982041917746u }, { 13989140502667892133u, 2124551971267068394u }, + { 14880661216876224029u, 1699641577013654715u }, { 11904528973500979224u, 1359713261610923772u }, + { 4289851098633925465u, 2175541218577478036u }, { 18189276137874781665u, 1740432974861982428u }, + { 3483374466074094362u, 1392346379889585943u }, { 1884050330976640656u, 2227754207823337509u }, + { 5196589079523222848u, 1782203366258670007u }, { 15225317707844309248u, 1425762693006936005u }, + { 5913764258841343181u, 2281220308811097609u }, { 8420360221814984868u, 1824976247048878087u }, + { 17804334621677718864u, 1459980997639102469u }, { 17932816512084085415u, 1167984798111281975u }, + { 10245762345624985047u, 1868775676978051161u }, { 4507261061758077715u, 1495020541582440929u }, + { 7295157664148372495u, 1196016433265952743u }, { 7982903447895485668u, 1913626293225524389u }, + { 10075671573058298858u, 1530901034580419511u }, { 4371188443704728763u, 1224720827664335609u }, + { 14372599139411386667u, 1959553324262936974u }, { 15187428126271019657u, 1567642659410349579u }, + { 15839291315758726049u, 1254114127528279663u }, { 3206773216762499739u, 2006582604045247462u }, + { 13633465017635730761u, 1605266083236197969u }, { 14596120828850494932u, 1284212866588958375u }, + { 4907049252451240275u, 2054740586542333401u }, { 236290587219081897u, 1643792469233866721u }, + { 14946427728742906810u, 1315033975387093376u }, { 16535586736504830250u, 2104054360619349402u }, + { 5849771759720043554u, 1683243488495479522u }, { 15747863852001765813u, 1346594790796383617u }, + { 10439186904235184007u, 2154551665274213788u }, { 15730047152871967852u, 1723641332219371030u }, + { 12584037722297574282u, 1378913065775496824u }, { 9066413911450387881u, 2206260905240794919u }, + { 10942479943902220628u, 1765008724192635935u }, { 8753983955121776503u, 1412006979354108748u }, + { 10317025513452932081u, 2259211166966573997u }, { 874922781278525018u, 1807368933573259198u }, + { 8078635854506640661u, 1445895146858607358u }, { 13841606313089133175u, 1156716117486885886u }, + { 14767872471458792434u, 1850745787979017418u }, { 746251532941302978u, 1480596630383213935u }, + { 597001226353042382u, 1184477304306571148u }, { 15712597221132509104u, 1895163686890513836u }, + { 8880728962164096960u, 1516130949512411069u }, { 10793931984473187891u, 1212904759609928855u }, + { 17270291175157100626u, 1940647615375886168u }, { 2748186495899949531u, 1552518092300708935u }, + { 2198549196719959625u, 1242014473840567148u }, { 18275073973719576693u, 1987223158144907436u }, + { 10930710364233751031u, 1589778526515925949u }, { 12433917106128911148u, 1271822821212740759u }, + { 8826220925580526867u, 2034916513940385215u }, { 7060976740464421494u, 1627933211152308172u }, + { 16716827836597268165u, 1302346568921846537u }, { 11989529279587987770u, 2083754510274954460u }, + { 9591623423670390216u, 1667003608219963568u }, { 15051996368420132820u, 1333602886575970854u }, + { 13015147745246481542u, 2133764618521553367u }, { 3033420566713364587u, 1707011694817242694u }, + { 6116085268112601993u, 1365609355853794155u }, { 9785736428980163188u, 2184974969366070648u }, + { 15207286772667951197u, 1747979975492856518u }, { 1097782973908629988u, 1398383980394285215u }, + { 1756452758253807981u, 2237414368630856344u }, { 5094511021344956708u, 1789931494904685075u }, + { 4075608817075965366u, 1431945195923748060u }, { 6520974107321544586u, 2291112313477996896u }, + { 1527430471115325346u, 1832889850782397517u }, { 12289990821117991246u, 1466311880625918013u }, + { 17210690286378213644u, 1173049504500734410u }, { 9090360384495590213u, 1876879207201175057u }, + { 18340334751822203140u, 1501503365760940045u }, { 14672267801457762512u, 1201202692608752036u }, + { 16096930852848599373u, 1921924308174003258u }, { 1809498238053148529u, 1537539446539202607u }, + { 12515645034668249793u, 1230031557231362085u }, { 1578287981759648052u, 1968050491570179337u }, + { 12330676829633449412u, 1574440393256143469u }, { 13553890278448669853u, 1259552314604914775u }, + { 3239480371808320148u, 2015283703367863641u }, { 17348979556414297411u, 1612226962694290912u }, + { 6500486015647617283u, 1289781570155432730u }, { 10400777625036187652u, 2063650512248692368u }, + { 15699319729512770768u, 1650920409798953894u }, { 16248804598352126938u, 1320736327839163115u }, + { 7551343283653851484u, 2113178124542660985u }, { 6041074626923081187u, 1690542499634128788u }, + { 12211557331022285596u, 1352433999707303030u }, { 1091747655926105338u, 2163894399531684849u }, + { 4562746939482794594u, 1731115519625347879u }, { 7339546366328145998u, 1384892415700278303u }, + { 8053925371383123274u, 2215827865120445285u }, { 6443140297106498619u, 1772662292096356228u }, + { 12533209867169019542u, 1418129833677084982u }, { 5295740528502789974u, 2269007733883335972u }, + { 15304638867027962949u, 1815206187106668777u }, { 4865013464138549713u, 1452164949685335022u }, + { 14960057215536570740u, 1161731959748268017u }, { 9178696285890871890u, 1858771135597228828u }, + { 14721654658196518159u, 1487016908477783062u }, { 4398626097073393881u, 1189613526782226450u }, + { 7037801755317430209u, 1903381642851562320u }, { 5630241404253944167u, 1522705314281249856u }, + { 814844308661245011u, 1218164251424999885u }, { 1303750893857992017u, 1949062802279999816u }, + { 15800395974054034906u, 1559250241823999852u }, { 5261619149759407279u, 1247400193459199882u }, + { 12107939454356961969u, 1995840309534719811u }, { 5997002748743659252u, 1596672247627775849u }, + { 8486951013736837725u, 1277337798102220679u }, { 2511075177753209390u, 2043740476963553087u }, + { 13076906586428298482u, 1634992381570842469u }, { 14150874083884549109u, 1307993905256673975u }, + { 4194654460505726958u, 2092790248410678361u }, { 18113118827372222859u, 1674232198728542688u }, + { 3422448617672047318u, 1339385758982834151u }, { 16543964232501006678u, 2143017214372534641u }, + { 9545822571258895019u, 1714413771498027713u }, { 15015355686490936662u, 1371531017198422170u }, + { 5577825024675947042u, 2194449627517475473u }, { 11840957649224578280u, 1755559702013980378u }, + { 16851463748863483271u, 1404447761611184302u }, { 12204946739213931940u, 2247116418577894884u }, + { 13453306206113055875u, 1797693134862315907u }, { 3383947335406624054u, 1438154507889852726u }, + { 16482362180876329456u, 2301047212623764361u }, { 9496540929959153242u, 1840837770099011489u }, + { 11286581558709232917u, 1472670216079209191u }, { 5339916432225476010u, 1178136172863367353u }, + { 4854517476818851293u, 1885017876581387765u }, { 3883613981455081034u, 1508014301265110212u }, + { 14174937629389795797u, 1206411441012088169u }, { 11611853762797942306u, 1930258305619341071u }, + { 5600134195496443521u, 1544206644495472857u }, { 15548153800622885787u, 1235365315596378285u }, + { 6430302007287065643u, 1976584504954205257u }, { 16212288050055383484u, 1581267603963364205u }, + { 12969830440044306787u, 1265014083170691364u }, { 9683682259845159889u, 2024022533073106183u }, + { 15125643437359948558u, 1619218026458484946u }, { 8411165935146048523u, 1295374421166787957u }, + { 17147214310975587960u, 2072599073866860731u }, { 10028422634038560045u, 1658079259093488585u }, + { 8022738107230848036u, 1326463407274790868u }, { 9147032156827446534u, 2122341451639665389u }, + { 11006974540203867551u, 1697873161311732311u }, { 5116230817421183718u, 1358298529049385849u }, + { 15564666937357714594u, 2173277646479017358u }, { 1383687105660440706u, 1738622117183213887u }, + { 12174996128754083534u, 1390897693746571109u }, { 8411947361780802685u, 2225436309994513775u }, + { 6729557889424642148u, 1780349047995611020u }, { 5383646311539713719u, 1424279238396488816u }, + { 1235136468979721303u, 2278846781434382106u }, { 15745504434151418335u, 1823077425147505684u }, + { 16285752362063044992u, 1458461940118004547u }, { 5649904260166615347u, 1166769552094403638u }, + { 5350498001524674232u, 1866831283351045821u }, { 591049586477829062u, 1493465026680836657u }, + { 11540886113407994219u, 1194772021344669325u }, { 18673707743239135u, 1911635234151470921u }, + { 14772334225162232601u, 1529308187321176736u }, { 8128518565387875758u, 1223446549856941389u }, + { 1937583260394870242u, 1957514479771106223u }, { 8928764237799716840u, 1566011583816884978u }, + { 14521709019723594119u, 1252809267053507982u }, { 8477339172590109297u, 2004494827285612772u }, + { 17849917782297818407u, 1603595861828490217u }, { 6901236596354434079u, 1282876689462792174u }, + { 18420676183650915173u, 2052602703140467478u }, { 3668494502695001169u, 1642082162512373983u }, + { 10313493231639821582u, 1313665730009899186u }, { 9122891541139893884u, 2101865168015838698u }, + { 14677010862395735754u, 1681492134412670958u }, { 673562245690857633u, 1345193707530136767u } +}; + +static const uint64_t DOUBLE_POW5_SPLIT[DOUBLE_POW5_TABLE_SIZE][2] = { + { 0u, 1152921504606846976u }, { 0u, 1441151880758558720u }, + { 0u, 1801439850948198400u }, { 0u, 2251799813685248000u }, + { 0u, 1407374883553280000u }, { 0u, 1759218604441600000u }, + { 0u, 2199023255552000000u }, { 0u, 1374389534720000000u }, + { 0u, 1717986918400000000u }, { 0u, 2147483648000000000u }, + { 0u, 1342177280000000000u }, { 0u, 1677721600000000000u }, + { 0u, 2097152000000000000u }, { 0u, 1310720000000000000u }, + { 0u, 1638400000000000000u }, { 0u, 2048000000000000000u }, + { 0u, 1280000000000000000u }, { 0u, 1600000000000000000u }, + { 0u, 2000000000000000000u }, { 0u, 1250000000000000000u }, + { 0u, 1562500000000000000u }, { 0u, 1953125000000000000u }, + { 0u, 1220703125000000000u }, { 0u, 1525878906250000000u }, + { 0u, 1907348632812500000u }, { 0u, 1192092895507812500u }, + { 0u, 1490116119384765625u }, { 4611686018427387904u, 1862645149230957031u }, + { 9799832789158199296u, 1164153218269348144u }, { 12249790986447749120u, 1455191522836685180u }, + { 15312238733059686400u, 1818989403545856475u }, { 14528612397897220096u, 2273736754432320594u }, + { 13692068767113150464u, 1421085471520200371u }, { 12503399940464050176u, 1776356839400250464u }, + { 15629249925580062720u, 2220446049250313080u }, { 9768281203487539200u, 1387778780781445675u }, + { 7598665485932036096u, 1734723475976807094u }, { 274959820560269312u, 2168404344971008868u }, + { 9395221924704944128u, 1355252715606880542u }, { 2520655369026404352u, 1694065894508600678u }, + { 12374191248137781248u, 2117582368135750847u }, { 14651398557727195136u, 1323488980084844279u }, + { 13702562178731606016u, 1654361225106055349u }, { 3293144668132343808u, 2067951531382569187u }, + { 18199116482078572544u, 1292469707114105741u }, { 8913837547316051968u, 1615587133892632177u }, + { 15753982952572452864u, 2019483917365790221u }, { 12152082354571476992u, 1262177448353618888u }, + { 15190102943214346240u, 1577721810442023610u }, { 9764256642163156992u, 1972152263052529513u }, + { 17631875447420442880u, 1232595164407830945u }, { 8204786253993389888u, 1540743955509788682u }, + { 1032610780636961552u, 1925929944387235853u }, { 2951224747111794922u, 1203706215242022408u }, + { 3689030933889743652u, 1504632769052528010u }, { 13834660704216955373u, 1880790961315660012u }, + { 17870034976990372916u, 1175494350822287507u }, { 17725857702810578241u, 1469367938527859384u }, + { 3710578054803671186u, 1836709923159824231u }, { 26536550077201078u, 2295887403949780289u }, + { 11545800389866720434u, 1434929627468612680u }, { 14432250487333400542u, 1793662034335765850u }, + { 8816941072311974870u, 2242077542919707313u }, { 17039803216263454053u, 1401298464324817070u }, + { 12076381983474541759u, 1751623080406021338u }, { 5872105442488401391u, 2189528850507526673u }, + { 15199280947623720629u, 1368455531567204170u }, { 9775729147674874978u, 1710569414459005213u }, + { 16831347453020981627u, 2138211768073756516u }, { 1296220121283337709u, 1336382355046097823u }, + { 15455333206886335848u, 1670477943807622278u }, { 10095794471753144002u, 2088097429759527848u }, + { 6309871544845715001u, 1305060893599704905u }, { 12499025449484531656u, 1631326116999631131u }, + { 11012095793428276666u, 2039157646249538914u }, { 11494245889320060820u, 1274473528905961821u }, + { 532749306367912313u, 1593091911132452277u }, { 5277622651387278295u, 1991364888915565346u }, + { 7910200175544436838u, 1244603055572228341u }, { 14499436237857933952u, 1555753819465285426u }, + { 8900923260467641632u, 1944692274331606783u }, { 12480606065433357876u, 1215432671457254239u }, + { 10989071563364309441u, 1519290839321567799u }, { 9124653435777998898u, 1899113549151959749u }, + { 8008751406574943263u, 1186945968219974843u }, { 5399253239791291175u, 1483682460274968554u }, + { 15972438586593889776u, 1854603075343710692u }, { 759402079766405302u, 1159126922089819183u }, + { 14784310654990170340u, 1448908652612273978u }, { 9257016281882937117u, 1811135815765342473u }, + { 16182956370781059300u, 2263919769706678091u }, { 7808504722524468110u, 1414949856066673807u }, + { 5148944884728197234u, 1768687320083342259u }, { 1824495087482858639u, 2210859150104177824u }, + { 1140309429676786649u, 1381786968815111140u }, { 1425386787095983311u, 1727233711018888925u }, + { 6393419502297367043u, 2159042138773611156u }, { 13219259225790630210u, 1349401336733506972u }, + { 16524074032238287762u, 1686751670916883715u }, { 16043406521870471799u, 2108439588646104644u }, + { 803757039314269066u, 1317774742903815403u }, { 14839754354425000045u, 1647218428629769253u }, + { 4714634887749086344u, 2059023035787211567u }, { 9864175832484260821u, 1286889397367007229u }, + { 16941905809032713930u, 1608611746708759036u }, { 2730638187581340797u, 2010764683385948796u }, + { 10930020904093113806u, 1256727927116217997u }, { 18274212148543780162u, 1570909908895272496u }, + { 4396021111970173586u, 1963637386119090621u }, { 5053356204195052443u, 1227273366324431638u }, + { 15540067292098591362u, 1534091707905539547u }, { 14813398096695851299u, 1917614634881924434u }, + { 13870059828862294966u, 1198509146801202771u }, { 12725888767650480803u, 1498136433501503464u }, + { 15907360959563101004u, 1872670541876879330u }, { 14553786618154326031u, 1170419088673049581u }, + { 4357175217410743827u, 1463023860841311977u }, { 10058155040190817688u, 1828779826051639971u }, + { 7961007781811134206u, 2285974782564549964u }, { 14199001900486734687u, 1428734239102843727u }, + { 13137066357181030455u, 1785917798878554659u }, { 11809646928048900164u, 2232397248598193324u }, + { 16604401366885338411u, 1395248280373870827u }, { 16143815690179285109u, 1744060350467338534u }, + { 10956397575869330579u, 2180075438084173168u }, { 6847748484918331612u, 1362547148802608230u }, + { 17783057643002690323u, 1703183936003260287u }, { 17617136035325974999u, 2128979920004075359u }, + { 17928239049719816230u, 1330612450002547099u }, { 17798612793722382384u, 1663265562503183874u }, + { 13024893955298202172u, 2079081953128979843u }, { 5834715712847682405u, 1299426220705612402u }, + { 16516766677914378815u, 1624282775882015502u }, { 11422586310538197711u, 2030353469852519378u }, + { 11750802462513761473u, 1268970918657824611u }, { 10076817059714813937u, 1586213648322280764u }, + { 12596021324643517422u, 1982767060402850955u }, { 5566670318688504437u, 1239229412751781847u }, + { 2346651879933242642u, 1549036765939727309u }, { 7545000868343941206u, 1936295957424659136u }, + { 4715625542714963254u, 1210184973390411960u }, { 5894531928393704067u, 1512731216738014950u }, + { 16591536947346905892u, 1890914020922518687u }, { 17287239619732898039u, 1181821263076574179u }, + { 16997363506238734644u, 1477276578845717724u }, { 2799960309088866689u, 1846595723557147156u }, + { 10973347230035317489u, 1154122327223216972u }, { 13716684037544146861u, 1442652909029021215u }, + { 12534169028502795672u, 1803316136286276519u }, { 11056025267201106687u, 2254145170357845649u }, + { 18439230838069161439u, 1408840731473653530u }, { 13825666510731675991u, 1761050914342066913u }, + { 3447025083132431277u, 2201313642927583642u }, { 6766076695385157452u, 1375821026829739776u }, + { 8457595869231446815u, 1719776283537174720u }, { 10571994836539308519u, 2149720354421468400u }, + { 6607496772837067824u, 1343575221513417750u }, { 17482743002901110588u, 1679469026891772187u }, + { 17241742735199000331u, 2099336283614715234u }, { 15387775227926763111u, 1312085177259197021u }, + { 5399660979626290177u, 1640106471573996277u }, { 11361262242960250625u, 2050133089467495346u }, + { 11712474920277544544u, 1281333180917184591u }, { 10028907631919542777u, 1601666476146480739u }, + { 7924448521472040567u, 2002083095183100924u }, { 14176152362774801162u, 1251301934489438077u }, + { 3885132398186337741u, 1564127418111797597u }, { 9468101516160310080u, 1955159272639746996u }, + { 15140935484454969608u, 1221974545399841872u }, { 479425281859160394u, 1527468181749802341u }, + { 5210967620751338397u, 1909335227187252926u }, { 17091912818251750210u, 1193334516992033078u }, + { 12141518985959911954u, 1491668146240041348u }, { 15176898732449889943u, 1864585182800051685u }, + { 11791404716994875166u, 1165365739250032303u }, { 10127569877816206054u, 1456707174062540379u }, + { 8047776328842869663u, 1820883967578175474u }, { 836348374198811271u, 2276104959472719343u }, + { 7440246761515338900u, 1422565599670449589u }, { 13911994470321561530u, 1778206999588061986u }, + { 8166621051047176104u, 2222758749485077483u }, { 2798295147690791113u, 1389224218428173427u }, + { 17332926989895652603u, 1736530273035216783u }, { 17054472718942177850u, 2170662841294020979u }, + { 8353202440125167204u, 1356664275808763112u }, { 10441503050156459005u, 1695830344760953890u }, + { 3828506775840797949u, 2119787930951192363u }, { 86973725686804766u, 1324867456844495227u }, + { 13943775212390669669u, 1656084321055619033u }, { 3594660960206173375u, 2070105401319523792u }, + { 2246663100128858359u, 1293815875824702370u }, { 12031700912015848757u, 1617269844780877962u }, + { 5816254103165035138u, 2021587305976097453u }, { 5941001823691840913u, 1263492066235060908u }, + { 7426252279614801142u, 1579365082793826135u }, { 4671129331091113523u, 1974206353492282669u }, + { 5225298841145639904u, 1233878970932676668u }, { 6531623551432049880u, 1542348713665845835u }, + { 3552843420862674446u, 1927935892082307294u }, { 16055585193321335241u, 1204959932551442058u }, + { 10846109454796893243u, 1506199915689302573u }, { 18169322836923504458u, 1882749894611628216u }, + { 11355826773077190286u, 1176718684132267635u }, { 9583097447919099954u, 1470898355165334544u }, + { 11978871809898874942u, 1838622943956668180u }, { 14973589762373593678u, 2298278679945835225u }, + { 2440964573842414192u, 1436424174966147016u }, { 3051205717303017741u, 1795530218707683770u }, + { 13037379183483547984u, 2244412773384604712u }, { 8148361989677217490u, 1402757983365377945u }, + { 14797138505523909766u, 1753447479206722431u }, { 13884737113477499304u, 2191809349008403039u }, + { 15595489723564518921u, 1369880843130251899u }, { 14882676136028260747u, 1712351053912814874u }, + { 9379973133180550126u, 2140438817391018593u }, { 17391698254306313589u, 1337774260869386620u }, + { 3292878744173340370u, 1672217826086733276u }, { 4116098430216675462u, 2090272282608416595u }, + { 266718509671728212u, 1306420176630260372u }, { 333398137089660265u, 1633025220787825465u }, + { 5028433689789463235u, 2041281525984781831u }, { 10060300083759496378u, 1275800953740488644u }, + { 12575375104699370472u, 1594751192175610805u }, { 1884160825592049379u, 1993438990219513507u }, + { 17318501580490888525u, 1245899368887195941u }, { 7813068920331446945u, 1557374211108994927u }, + { 5154650131986920777u, 1946717763886243659u }, { 915813323278131534u, 1216698602428902287u }, + { 14979824709379828129u, 1520873253036127858u }, { 9501408849870009354u, 1901091566295159823u }, + { 12855909558809837702u, 1188182228934474889u }, { 2234828893230133415u, 1485227786168093612u }, + { 2793536116537666769u, 1856534732710117015u }, { 8663489100477123587u, 1160334207943823134u }, + { 1605989338741628675u, 1450417759929778918u }, { 11230858710281811652u, 1813022199912223647u }, + { 9426887369424876662u, 2266277749890279559u }, { 12809333633531629769u, 1416423593681424724u }, + { 16011667041914537212u, 1770529492101780905u }, { 6179525747111007803u, 2213161865127226132u }, + { 13085575628799155685u, 1383226165704516332u }, { 16356969535998944606u, 1729032707130645415u }, + { 15834525901571292854u, 2161290883913306769u }, { 2979049660840976177u, 1350806802445816731u }, + { 17558870131333383934u, 1688508503057270913u }, { 8113529608884566205u, 2110635628821588642u }, + { 9682642023980241782u, 1319147268013492901u }, { 16714988548402690132u, 1648934085016866126u }, + { 11670363648648586857u, 2061167606271082658u }, { 11905663298832754689u, 1288229753919426661u }, + { 1047021068258779650u, 1610287192399283327u }, { 15143834390605638274u, 2012858990499104158u }, + { 4853210475701136017u, 1258036869061940099u }, { 1454827076199032118u, 1572546086327425124u }, + { 1818533845248790147u, 1965682607909281405u }, { 3442426662494187794u, 1228551629943300878u }, + { 13526405364972510550u, 1535689537429126097u }, { 3072948650933474476u, 1919611921786407622u }, + { 15755650962115585259u, 1199757451116504763u }, { 15082877684217093670u, 1499696813895630954u }, + { 9630225068416591280u, 1874621017369538693u }, { 8324733676974063502u, 1171638135855961683u }, + { 5794231077790191473u, 1464547669819952104u }, { 7242788847237739342u, 1830684587274940130u }, + { 18276858095901949986u, 2288355734093675162u }, { 16034722328366106645u, 1430222333808546976u }, + { 1596658836748081690u, 1787777917260683721u }, { 6607509564362490017u, 2234722396575854651u }, + { 1823850468512862308u, 1396701497859909157u }, { 6891499104068465790u, 1745876872324886446u }, + { 17837745916940358045u, 2182346090406108057u }, { 4231062170446641922u, 1363966306503817536u }, + { 5288827713058302403u, 1704957883129771920u }, { 6611034641322878003u, 2131197353912214900u }, + { 13355268687681574560u, 1331998346195134312u }, { 16694085859601968200u, 1664997932743917890u }, + { 11644235287647684442u, 2081247415929897363u }, { 4971804045566108824u, 1300779634956185852u }, + { 6214755056957636030u, 1625974543695232315u }, { 3156757802769657134u, 2032468179619040394u }, + { 6584659645158423613u, 1270292612261900246u }, { 17454196593302805324u, 1587865765327375307u }, + { 17206059723201118751u, 1984832206659219134u }, { 6142101308573311315u, 1240520129162011959u }, + { 3065940617289251240u, 1550650161452514949u }, { 8444111790038951954u, 1938312701815643686u }, + { 665883850346957067u, 1211445438634777304u }, { 832354812933696334u, 1514306798293471630u }, + { 10263815553021896226u, 1892883497866839537u }, { 17944099766707154901u, 1183052186166774710u }, + { 13206752671529167818u, 1478815232708468388u }, { 16508440839411459773u, 1848519040885585485u }, + { 12623618533845856310u, 1155324400553490928u }, { 15779523167307320387u, 1444155500691863660u }, + { 1277659885424598868u, 1805194375864829576u }, { 1597074856780748586u, 2256492969831036970u }, + { 5609857803915355770u, 1410308106144398106u }, { 16235694291748970521u, 1762885132680497632u }, + { 1847873790976661535u, 2203606415850622041u }, { 12684136165428883219u, 1377254009906638775u }, + { 11243484188358716120u, 1721567512383298469u }, { 219297180166231438u, 2151959390479123087u }, + { 7054589765244976505u, 1344974619049451929u }, { 13429923224983608535u, 1681218273811814911u }, + { 12175718012802122765u, 2101522842264768639u }, { 14527352785642408584u, 1313451776415480399u }, + { 13547504963625622826u, 1641814720519350499u }, { 12322695186104640628u, 2052268400649188124u }, + { 16925056528170176201u, 1282667750405742577u }, { 7321262604930556539u, 1603334688007178222u }, + { 18374950293017971482u, 2004168360008972777u }, { 4566814905495150320u, 1252605225005607986u }, + { 14931890668723713708u, 1565756531257009982u }, { 9441491299049866327u, 1957195664071262478u }, + { 1289246043478778550u, 1223247290044539049u }, { 6223243572775861092u, 1529059112555673811u }, + { 3167368447542438461u, 1911323890694592264u }, { 1979605279714024038u, 1194577431684120165u }, + { 7086192618069917952u, 1493221789605150206u }, { 18081112809442173248u, 1866527237006437757u }, + { 13606538515115052232u, 1166579523129023598u }, { 7784801107039039482u, 1458224403911279498u }, + { 507629346944023544u, 1822780504889099373u }, { 5246222702107417334u, 2278475631111374216u }, + { 3278889188817135834u, 1424047269444608885u }, { 8710297504448807696u, 1780059086805761106u } +}; + +// IEEE 754 double precision constants +#define DOUBLE_MANTISSA_BITS 52 +#define DOUBLE_EXPONENT_BITS 11 +#define DOUBLE_EXPONENT_BIAS 1023 + +// Helper: floor(log2(value)) using ryu_leading_zeros64 +static inline uint32_t floor_log2(const uint64_t value) { + return 63 - ryu_leading_zeros64(value); +} + +// Helper: log2(5^e) approximation +static inline int32_t log2pow5(const int32_t e) { + return (int32_t) ((((uint32_t) e) * 1217359) >> 19); +} + +// Helper: ceil(log2(5^e)) +static inline int32_t ceil_log2pow5(const int32_t e) { + return log2pow5(e) + 1; +} + +// Helper: max of two int32 +static inline int32_t max32(int32_t a, int32_t b) { + return a < b ? b : a; +} + +// Helper: convert uint64 bits to double +static inline double int64Bits2Double(uint64_t bits) { + double f; + memcpy(&f, &bits, sizeof(double)); + return f; +} + +// Check if value is multiple of 2^p +static inline bool multipleOfPowerOf2(const uint64_t value, const uint32_t p) { + return (value & ((1ull << p) - 1)) == 0; +} + +// Count how many times value is divisible by 5 +// Uses modular inverse to avoid expensive division +static inline uint32_t pow5Factor(uint64_t value) { + const uint64_t m_inv_5 = 14757395258967641293u; // 5 * m_inv_5 = 1 (mod 2^64) + const uint64_t n_div_5 = 3689348814741910323u; // 2^64 / 5 + uint32_t count = 0; + for (;;) { + value *= m_inv_5; + if (value > n_div_5) + break; + ++count; + } + return count; +} + +// Check if value is multiple of 5^p +// Optimized: uses modular inverse instead of division +static inline bool multipleOfPowerOf5(const uint64_t value, const uint32_t p) { + return pow5Factor(value) >= p; +} + +// 128-bit multiplication with shift +// This is the core operation for converting decimal to binary +#if defined(__SIZEOF_INT128__) +// Use native 128-bit integers if available (GCC/Clang) +static inline uint64_t mulShift64(const uint64_t m, const uint64_t* const mul, const int32_t j) { + const unsigned __int128 b0 = ((unsigned __int128) m) * mul[0]; + const unsigned __int128 b2 = ((unsigned __int128) m) * mul[1]; + return (uint64_t) (((b0 >> 64) + b2) >> (j - 64)); +} +#else +// Fallback for systems without 128-bit integers +static inline uint64_t umul128(const uint64_t a, const uint64_t b, uint64_t* const productHi) { + const uint32_t aLo = (uint32_t)a; + const uint32_t aHi = (uint32_t)(a >> 32); + const uint32_t bLo = (uint32_t)b; + const uint32_t bHi = (uint32_t)(b >> 32); + + const uint64_t b00 = (uint64_t)aLo * bLo; + const uint64_t b01 = (uint64_t)aLo * bHi; + const uint64_t b10 = (uint64_t)aHi * bLo; + const uint64_t b11 = (uint64_t)aHi * bHi; + + const uint32_t b00Lo = (uint32_t)b00; + const uint32_t b00Hi = (uint32_t)(b00 >> 32); + + const uint64_t mid1 = b10 + b00Hi; + const uint32_t mid1Lo = (uint32_t)(mid1); + const uint32_t mid1Hi = (uint32_t)(mid1 >> 32); + + const uint64_t mid2 = b01 + mid1Lo; + const uint32_t mid2Lo = (uint32_t)(mid2); + const uint32_t mid2Hi = (uint32_t)(mid2 >> 32); + + const uint64_t pHi = b11 + mid1Hi + mid2Hi; + const uint64_t pLo = ((uint64_t)mid2Lo << 32) | b00Lo; + + *productHi = pHi; + return pLo; +} + +static inline uint64_t shiftright128(const uint64_t lo, const uint64_t hi, const uint32_t dist) { + return (hi << (64 - dist)) | (lo >> dist); +} + +static inline uint64_t mulShift64(const uint64_t m, const uint64_t* const mul, const int32_t j) { + uint64_t high1; + const uint64_t low1 = umul128(m, mul[1], &high1); + uint64_t high0; + umul128(m, mul[0], &high0); + const uint64_t sum = high0 + low1; + if (sum < high0) { + ++high1; + } + return shiftright128(sum, high1, j - 64); +} +#endif + +// Main conversion function: decimal mantissa+exponent to IEEE 754 double +// Optimized for JSON parsing with fast paths for edge cases +static inline double ryu_s2d_from_parts(uint64_t m10, int m10digits, int32_t e10, bool signedM) { + // Fast path: handle zero explicitly (e.g., "0.0", "0e0") + if (m10 == 0) { + return int64Bits2Double(((uint64_t) signedM) << 63); + } + + // Fast path: handle overflow/underflow early + if (m10digits + e10 <= -324) { + // Underflow to zero + return int64Bits2Double(((uint64_t) signedM) << 63); + } + + if (m10digits + e10 >= 310) { + // Overflow to infinity + return int64Bits2Double((((uint64_t) signedM) << 63) | 0x7ff0000000000000ULL); + } + + // Convert decimal to binary: m10 * 10^e10 = m2 * 2^e2 + int32_t e2; + uint64_t m2; + bool trailingZeros; + + if (e10 >= 0) { + // Positive exponent: multiply by 5^e10 and adjust binary exponent + e2 = floor_log2(m10) + e10 + log2pow5(e10) - (DOUBLE_MANTISSA_BITS + 1); + int j = e2 - e10 - ceil_log2pow5(e10) + DOUBLE_POW5_BITCOUNT; + m2 = mulShift64(m10, DOUBLE_POW5_SPLIT[e10], j); + trailingZeros = e2 < e10 || (e2 - e10 < 64 && multipleOfPowerOf2(m10, e2 - e10)); + } else { + // Negative exponent: divide by 5^(-e10) + e2 = floor_log2(m10) + e10 - ceil_log2pow5(-e10) - (DOUBLE_MANTISSA_BITS + 1); + int j = e2 - e10 + ceil_log2pow5(-e10) - 1 + DOUBLE_POW5_INV_BITCOUNT; + m2 = mulShift64(m10, DOUBLE_POW5_INV_SPLIT[-e10], j); + trailingZeros = multipleOfPowerOf5(m10, -e10); + } + + // Compute IEEE 754 exponent + uint32_t ieee_e2 = (uint32_t) max32(0, e2 + DOUBLE_EXPONENT_BIAS + floor_log2(m2)); + + if (ieee_e2 > 0x7fe) { + // Overflow to infinity + return int64Bits2Double((((uint64_t) signedM) << 63) | 0x7ff0000000000000ULL); + } + + // Compute shift amount for rounding + int32_t shift = (ieee_e2 == 0 ? 1 : ieee_e2) - e2 - DOUBLE_EXPONENT_BIAS - DOUBLE_MANTISSA_BITS; + + // IEEE 754 round-to-even (banker's rounding) + trailingZeros &= (m2 & ((1ull << (shift - 1)) - 1)) == 0; + uint64_t lastRemovedBit = (m2 >> (shift - 1)) & 1; + bool roundUp = (lastRemovedBit != 0) && (!trailingZeros || (((m2 >> shift) & 1) != 0)); + + uint64_t ieee_m2 = (m2 >> shift) + roundUp; + ieee_m2 &= (1ull << DOUBLE_MANTISSA_BITS) - 1; + + if (ieee_m2 == 0 && roundUp) { + ieee_e2++; + } + + // Pack sign, exponent, and mantissa into IEEE 754 format + // Match original Ryu: group sign+exponent, then shift and add mantissa + uint64_t ieee = (((((uint64_t) signedM) << DOUBLE_EXPONENT_BITS) | (uint64_t)ieee_e2) << DOUBLE_MANTISSA_BITS) | ieee_m2; + return int64Bits2Double(ieee); +} + +#endif // RYU_H diff --git a/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/json.gemspec b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/json.gemspec new file mode 100644 index 0000000..5575731 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/json.gemspec @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +version = File.foreach(File.join(__dir__, "lib/json/version.rb")) do |line| + /^\s*VERSION\s*=\s*'(.*)'/ =~ line and break $1 +end rescue nil + +spec = Gem::Specification.new do |s| + java_ext = Gem::Platform === s.platform && s.platform =~ 'java' || RUBY_ENGINE == 'jruby' + + s.name = "json" + s.version = version + + s.summary = "JSON Implementation for Ruby" + s.homepage = "https://github.com/ruby/json" + s.metadata = { + 'bug_tracker_uri' => 'https://github.com/ruby/json/issues', + 'changelog_uri' => 'https://github.com/ruby/json/blob/master/CHANGES.md', + 'documentation_uri' => 'https://docs.ruby-lang.org/en/master/JSON.html', + 'homepage_uri' => s.homepage, + 'source_code_uri' => 'https://github.com/ruby/json', + } + + s.required_ruby_version = Gem::Requirement.new(">= 2.7") + + if java_ext + s.description = "A JSON implementation as a JRuby extension." + s.author = "Daniel Luz" + s.email = "dev+ruby@mernen.com" + else + s.description = "This is a JSON implementation as a Ruby extension in C." + s.authors = ["Florian Frank"] + s.email = "flori@ping.de" + end + + s.licenses = ["Ruby"] + + s.extra_rdoc_files = ["README.md"] + s.rdoc_options = ["--title", "JSON implementation for Ruby", "--main", "README.md"] + + s.files = [ + "CHANGES.md", + "COPYING", + "BSDL", + "LEGAL", + "README.md", + "json.gemspec", + ] + Dir.glob("lib/**/*.rb", base: File.expand_path("..", __FILE__)) + + if java_ext + s.platform = 'java' + s.files += Dir["lib/json/ext/**/*.jar"] + else + s.extensions = Dir["ext/json/**/extconf.rb"] + s.files += Dir["ext/json/**/*.{c,h,rb}"] + end +end + +if RUBY_ENGINE == 'jruby' && $0 == __FILE__ + Gem::Builder.new(spec).build +else + spec +end diff --git a/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json.rb b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json.rb new file mode 100644 index 0000000..43d96af --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json.rb @@ -0,0 +1,642 @@ +# frozen_string_literal: true +require 'json/common' + +## +# = JavaScript \Object Notation (\JSON) +# +# \JSON is a lightweight data-interchange format. +# +# A \JSON value is one of the following: +# - Double-quoted text: "foo". +# - Number: +1+, +1.0+, +2.0e2+. +# - Boolean: +true+, +false+. +# - Null: +null+. +# - \Array: an ordered list of values, enclosed by square brackets: +# ["foo", 1, 1.0, 2.0e2, true, false, null] +# +# - \Object: a collection of name/value pairs, enclosed by curly braces; +# each name is double-quoted text; +# the values may be any \JSON values: +# {"a": "foo", "b": 1, "c": 1.0, "d": 2.0e2, "e": true, "f": false, "g": null} +# +# A \JSON array or object may contain nested arrays, objects, and scalars +# to any depth: +# {"foo": {"bar": 1, "baz": 2}, "bat": [0, 1, 2]} +# [{"foo": 0, "bar": 1}, ["baz", 2]] +# +# == Using \Module \JSON +# +# To make module \JSON available in your code, begin with: +# require 'json' +# +# All examples here assume that this has been done. +# +# === Parsing \JSON +# +# You can parse a \String containing \JSON data using +# either of two methods: +# - JSON.parse(source, opts) +# - JSON.parse!(source, opts) +# +# where +# - +source+ is a Ruby object. +# - +opts+ is a \Hash object containing options +# that control both input allowed and output formatting. +# +# The difference between the two methods +# is that JSON.parse! omits some checks +# and may not be safe for some +source+ data; +# use it only for data from trusted sources. +# Use the safer method JSON.parse for less trusted sources. +# +# ==== Parsing \JSON Arrays +# +# When +source+ is a \JSON array, JSON.parse by default returns a Ruby \Array: +# json = '["foo", 1, 1.0, 2.0e2, true, false, null]' +# ruby = JSON.parse(json) +# ruby # => ["foo", 1, 1.0, 200.0, true, false, nil] +# ruby.class # => Array +# +# The \JSON array may contain nested arrays, objects, and scalars +# to any depth: +# json = '[{"foo": 0, "bar": 1}, ["baz", 2]]' +# JSON.parse(json) # => [{"foo"=>0, "bar"=>1}, ["baz", 2]] +# +# ==== Parsing \JSON \Objects +# +# When the source is a \JSON object, JSON.parse by default returns a Ruby \Hash: +# json = '{"a": "foo", "b": 1, "c": 1.0, "d": 2.0e2, "e": true, "f": false, "g": null}' +# ruby = JSON.parse(json) +# ruby # => {"a"=>"foo", "b"=>1, "c"=>1.0, "d"=>200.0, "e"=>true, "f"=>false, "g"=>nil} +# ruby.class # => Hash +# +# The \JSON object may contain nested arrays, objects, and scalars +# to any depth: +# json = '{"foo": {"bar": 1, "baz": 2}, "bat": [0, 1, 2]}' +# JSON.parse(json) # => {"foo"=>{"bar"=>1, "baz"=>2}, "bat"=>[0, 1, 2]} +# +# ==== Parsing \JSON Scalars +# +# When the source is a \JSON scalar (not an array or object), +# JSON.parse returns a Ruby scalar. +# +# \String: +# ruby = JSON.parse('"foo"') +# ruby # => 'foo' +# ruby.class # => String +# \Integer: +# ruby = JSON.parse('1') +# ruby # => 1 +# ruby.class # => Integer +# \Float: +# ruby = JSON.parse('1.0') +# ruby # => 1.0 +# ruby.class # => Float +# ruby = JSON.parse('2.0e2') +# ruby # => 200 +# ruby.class # => Float +# Boolean: +# ruby = JSON.parse('true') +# ruby # => true +# ruby.class # => TrueClass +# ruby = JSON.parse('false') +# ruby # => false +# ruby.class # => FalseClass +# Null: +# ruby = JSON.parse('null') +# ruby # => nil +# ruby.class # => NilClass +# +# ==== Parsing Options +# +# ====== Input Options +# +# Option +max_nesting+ (\Integer) specifies the maximum nesting depth allowed; +# defaults to +100+; specify +false+ to disable depth checking. +# +# With the default, +false+: +# source = '[0, [1, [2, [3]]]]' +# ruby = JSON.parse(source) +# ruby # => [0, [1, [2, [3]]]] +# Too deep: +# # Raises JSON::NestingError (nesting of 2 is too deep): +# JSON.parse(source, {max_nesting: 1}) +# Bad value: +# # Raises TypeError (wrong argument type Symbol (expected Fixnum)): +# JSON.parse(source, {max_nesting: :foo}) +# +# --- +# +# Option +allow_duplicate_key+ specifies whether duplicate keys in objects +# should be ignored or cause an error to be raised: +# +# When not specified: +# # The last value is used and a deprecation warning emitted. +# JSON.parse('{"a": 1, "a":2}') => {"a" => 2} +# # warning: detected duplicate keys in JSON object. +# # This will raise an error in json 3.0 unless enabled via `allow_duplicate_key: true` +# +# When set to `+true+` +# # The last value is used. +# JSON.parse('{"a": 1, "a":2}') => {"a" => 2} +# +# When set to `+false+`, the future default: +# JSON.parse('{"a": 1, "a":2}') => duplicate key at line 1 column 1 (JSON::ParserError) +# +# --- +# +# Option +allow_nan+ (boolean) specifies whether to allow +# NaN, Infinity, and MinusInfinity in +source+; +# defaults to +false+. +# +# With the default, +false+: +# # Raises JSON::ParserError (225: unexpected token at '[NaN]'): +# JSON.parse('[NaN]') +# # Raises JSON::ParserError (232: unexpected token at '[Infinity]'): +# JSON.parse('[Infinity]') +# # Raises JSON::ParserError (248: unexpected token at '[-Infinity]'): +# JSON.parse('[-Infinity]') +# Allow: +# source = '[NaN, Infinity, -Infinity]' +# ruby = JSON.parse(source, {allow_nan: true}) +# ruby # => [NaN, Infinity, -Infinity] +# +# --- +# +# Option +allow_trailing_comma+ (boolean) specifies whether to allow +# trailing commas in objects and arrays; +# defaults to +false+. +# +# With the default, +false+: +# JSON.parse('[1,]') # unexpected character: ']' at line 1 column 4 (JSON::ParserError) +# +# When enabled: +# JSON.parse('[1,]', allow_trailing_comma: true) # => [1] +# +# ====== Output Options +# +# Option +freeze+ (boolean) specifies whether the returned objects will be frozen; +# defaults to +false+. +# +# Option +symbolize_names+ (boolean) specifies whether returned \Hash keys +# should be Symbols; +# defaults to +false+ (use Strings). +# +# With the default, +false+: +# source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}' +# ruby = JSON.parse(source) +# ruby # => {"a"=>"foo", "b"=>1.0, "c"=>true, "d"=>false, "e"=>nil} +# Use Symbols: +# ruby = JSON.parse(source, {symbolize_names: true}) +# ruby # => {:a=>"foo", :b=>1.0, :c=>true, :d=>false, :e=>nil} +# +# --- +# +# Option +object_class+ (\Class) specifies the Ruby class to be used +# for each \JSON object; +# defaults to \Hash. +# +# With the default, \Hash: +# source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}' +# ruby = JSON.parse(source) +# ruby.class # => Hash +# Use class \OpenStruct: +# ruby = JSON.parse(source, {object_class: OpenStruct}) +# ruby # => # +# +# --- +# +# Option +array_class+ (\Class) specifies the Ruby class to be used +# for each \JSON array; +# defaults to \Array. +# +# With the default, \Array: +# source = '["foo", 1.0, true, false, null]' +# ruby = JSON.parse(source) +# ruby.class # => Array +# Use class \Set: +# ruby = JSON.parse(source, {array_class: Set}) +# ruby # => # +# +# --- +# +# Option +create_additions+ (boolean) specifies whether to use \JSON additions in parsing. +# See {\JSON Additions}[#module-JSON-label-JSON+Additions]. +# +# === Generating \JSON +# +# To generate a Ruby \String containing \JSON data, +# use method JSON.generate(source, opts), where +# - +source+ is a Ruby object. +# - +opts+ is a \Hash object containing options +# that control both input allowed and output formatting. +# +# ==== Generating \JSON from Arrays +# +# When the source is a Ruby \Array, JSON.generate returns +# a \String containing a \JSON array: +# ruby = [0, 's', :foo] +# json = JSON.generate(ruby) +# json # => '[0,"s","foo"]' +# +# The Ruby \Array array may contain nested arrays, hashes, and scalars +# to any depth: +# ruby = [0, [1, 2], {foo: 3, bar: 4}] +# json = JSON.generate(ruby) +# json # => '[0,[1,2],{"foo":3,"bar":4}]' +# +# ==== Generating \JSON from Hashes +# +# When the source is a Ruby \Hash, JSON.generate returns +# a \String containing a \JSON object: +# ruby = {foo: 0, bar: 's', baz: :bat} +# json = JSON.generate(ruby) +# json # => '{"foo":0,"bar":"s","baz":"bat"}' +# +# The Ruby \Hash array may contain nested arrays, hashes, and scalars +# to any depth: +# ruby = {foo: [0, 1], bar: {baz: 2, bat: 3}, bam: :bad} +# json = JSON.generate(ruby) +# json # => '{"foo":[0,1],"bar":{"baz":2,"bat":3},"bam":"bad"}' +# +# ==== Generating \JSON from Other Objects +# +# When the source is neither an \Array nor a \Hash, +# the generated \JSON data depends on the class of the source. +# +# When the source is a Ruby \Integer or \Float, JSON.generate returns +# a \String containing a \JSON number: +# JSON.generate(42) # => '42' +# JSON.generate(0.42) # => '0.42' +# +# When the source is a Ruby \String, JSON.generate returns +# a \String containing a \JSON string (with double-quotes): +# JSON.generate('A string') # => '"A string"' +# +# When the source is +true+, +false+ or +nil+, JSON.generate returns +# a \String containing the corresponding \JSON token: +# JSON.generate(true) # => 'true' +# JSON.generate(false) # => 'false' +# JSON.generate(nil) # => 'null' +# +# When the source is none of the above, JSON.generate returns +# a \String containing a \JSON string representation of the source: +# JSON.generate(:foo) # => '"foo"' +# JSON.generate(Complex(0, 0)) # => '"0+0i"' +# JSON.generate(Dir.new('.')) # => '"#"' +# +# ==== Generating Options +# +# ====== Input Options +# +# Option +allow_nan+ (boolean) specifies whether +# +NaN+, +Infinity+, and -Infinity may be generated; +# defaults to +false+. +# +# With the default, +false+: +# # Raises JSON::GeneratorError (920: NaN not allowed in JSON): +# JSON.generate(JSON::NaN) +# # Raises JSON::GeneratorError (917: Infinity not allowed in JSON): +# JSON.generate(JSON::Infinity) +# # Raises JSON::GeneratorError (917: -Infinity not allowed in JSON): +# JSON.generate(JSON::MinusInfinity) +# +# Allow: +# ruby = [Float::NaN, Float::Infinity, Float::MinusInfinity] +# JSON.generate(ruby, allow_nan: true) # => '[NaN,Infinity,-Infinity]' +# +# --- +# +# Option +allow_duplicate_key+ (boolean) specifies whether +# hashes with duplicate keys should be allowed or produce an error. +# defaults to emit a deprecation warning. +# +# With the default, (not set): +# Warning[:deprecated] = true +# JSON.generate({ foo: 1, "foo" => 2 }) +# # warning: detected duplicate key "foo" in {foo: 1, "foo" => 2}. +# # This will raise an error in json 3.0 unless enabled via `allow_duplicate_key: true` +# # => '{"foo":1,"foo":2}' +# +# With false +# JSON.generate({ foo: 1, "foo" => 2 }, allow_duplicate_key: false) +# # detected duplicate key "foo" in {foo: 1, "foo" => 2} (JSON::GeneratorError) +# +# In version 3.0, false will become the default. +# +# --- +# +# Option +max_nesting+ (\Integer) specifies the maximum nesting depth +# in +obj+; defaults to +100+. +# +# With the default, +100+: +# obj = [[[[[[0]]]]]] +# JSON.generate(obj) # => '[[[[[[0]]]]]]' +# +# Too deep: +# # Raises JSON::NestingError (nesting of 2 is too deep): +# JSON.generate(obj, max_nesting: 2) +# +# ====== Escaping Options +# +# Options +script_safe+ (boolean) specifies wether '\u2028', '\u2029' +# and '/' 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 +# \JSON data, all on one line and with no whitespace. +# +# You can use these formatting options to generate +# \JSON data in a more open format, using whitespace. +# See also JSON.pretty_generate. +# +# - Option +array_nl+ (\String) specifies a string (usually a newline) +# to be inserted after each \JSON array; defaults to the empty \String, ''. +# - Option +object_nl+ (\String) specifies a string (usually a newline) +# to be inserted after each \JSON object; defaults to the empty \String, ''. +# - Option +indent+ (\String) specifies the string (usually spaces) to be +# used for indentation; defaults to the empty \String, ''; +# defaults to the empty \String, ''; +# has no effect unless options +array_nl+ or +object_nl+ specify newlines. +# - Option +space+ (\String) specifies a string (usually a space) to be +# inserted after the colon in each \JSON object's pair; +# defaults to the empty \String, ''. +# - Option +space_before+ (\String) specifies a string (usually a space) to be +# inserted before the colon in each \JSON object's pair; +# defaults to the empty \String, ''. +# +# In this example, +obj+ is used first to generate the shortest +# \JSON data (no whitespace), then again with all formatting options +# specified: +# +# obj = {foo: [:bar, :baz], bat: {bam: 0, bad: 1}} +# json = JSON.generate(obj) +# puts 'Compact:', json +# opts = { +# array_nl: "\n", +# object_nl: "\n", +# indent: ' ', +# space_before: ' ', +# space: ' ' +# } +# puts 'Open:', JSON.generate(obj, opts) +# +# Output: +# Compact: +# {"foo":["bar","baz"],"bat":{"bam":0,"bad":1}} +# Open: +# { +# "foo" : [ +# "bar", +# "baz" +# ], +# "bat" : { +# "bam" : 0, +# "bad" : 1 +# } +# } +# +# == \JSON Additions +# +# Note that JSON Additions must only be used with trusted data, and is +# deprecated. +# +# When you "round trip" a non-\String object from Ruby to \JSON and back, +# you have a new \String, instead of the object you began with: +# ruby0 = Range.new(0, 2) +# json = JSON.generate(ruby0) +# json # => '0..2"' +# ruby1 = JSON.parse(json) +# ruby1 # => '0..2' +# ruby1.class # => String +# +# You can use \JSON _additions_ to preserve the original object. +# The addition is an extension of a ruby class, so that: +# - \JSON.generate stores more information in the \JSON string. +# - \JSON.parse, called with option +create_additions+, +# uses that information to create a proper Ruby object. +# +# This example shows a \Range being generated into \JSON +# and parsed back into Ruby, both without and with +# the addition for \Range: +# ruby = Range.new(0, 2) +# # This passage does not use the addition for Range. +# json0 = JSON.generate(ruby) +# ruby0 = JSON.parse(json0) +# # This passage uses the addition for Range. +# require 'json/add/range' +# json1 = JSON.generate(ruby) +# ruby1 = JSON.parse(json1, create_additions: true) +# # Make a nice display. +# display = <<~EOT +# Generated JSON: +# Without addition: #{json0} (#{json0.class}) +# With addition: #{json1} (#{json1.class}) +# Parsed JSON: +# Without addition: #{ruby0.inspect} (#{ruby0.class}) +# With addition: #{ruby1.inspect} (#{ruby1.class}) +# EOT +# puts display +# +# This output shows the different results: +# Generated JSON: +# Without addition: "0..2" (String) +# With addition: {"json_class":"Range","a":[0,2,false]} (String) +# Parsed JSON: +# Without addition: "0..2" (String) +# With addition: 0..2 (Range) +# +# The \JSON module includes additions for certain classes. +# You can also craft custom additions. +# See {Custom \JSON Additions}[#module-JSON-label-Custom+JSON+Additions]. +# +# === Built-in Additions +# +# The \JSON module includes additions for certain classes. +# To use an addition, +require+ its source: +# - BigDecimal: require 'json/add/bigdecimal' +# - Complex: require 'json/add/complex' +# - Date: require 'json/add/date' +# - DateTime: require 'json/add/date_time' +# - Exception: require 'json/add/exception' +# - OpenStruct: require 'json/add/ostruct' +# - Range: require 'json/add/range' +# - Rational: require 'json/add/rational' +# - Regexp: require 'json/add/regexp' +# - Set: require 'json/add/set' +# - Struct: require 'json/add/struct' +# - Symbol: require 'json/add/symbol' +# - Time: require 'json/add/time' +# +# To reduce punctuation clutter, the examples below +# show the generated \JSON via +puts+, rather than the usual +inspect+, +# +# \BigDecimal: +# require 'json/add/bigdecimal' +# ruby0 = BigDecimal(0) # 0.0 +# json = JSON.generate(ruby0) # {"json_class":"BigDecimal","b":"27:0.0"} +# ruby1 = JSON.parse(json, create_additions: true) # 0.0 +# ruby1.class # => BigDecimal +# +# \Complex: +# require 'json/add/complex' +# ruby0 = Complex(1+0i) # 1+0i +# json = JSON.generate(ruby0) # {"json_class":"Complex","r":1,"i":0} +# ruby1 = JSON.parse(json, create_additions: true) # 1+0i +# ruby1.class # Complex +# +# \Date: +# require 'json/add/date' +# ruby0 = Date.today # 2020-05-02 +# json = JSON.generate(ruby0) # {"json_class":"Date","y":2020,"m":5,"d":2,"sg":2299161.0} +# ruby1 = JSON.parse(json, create_additions: true) # 2020-05-02 +# ruby1.class # Date +# +# \DateTime: +# require 'json/add/date_time' +# ruby0 = DateTime.now # 2020-05-02T10:38:13-05:00 +# json = JSON.generate(ruby0) # {"json_class":"DateTime","y":2020,"m":5,"d":2,"H":10,"M":38,"S":13,"of":"-5/24","sg":2299161.0} +# ruby1 = JSON.parse(json, create_additions: true) # 2020-05-02T10:38:13-05:00 +# ruby1.class # DateTime +# +# \Exception (and its subclasses including \RuntimeError): +# require 'json/add/exception' +# ruby0 = Exception.new('A message') # A message +# json = JSON.generate(ruby0) # {"json_class":"Exception","m":"A message","b":null} +# ruby1 = JSON.parse(json, create_additions: true) # A message +# ruby1.class # Exception +# ruby0 = RuntimeError.new('Another message') # Another message +# json = JSON.generate(ruby0) # {"json_class":"RuntimeError","m":"Another message","b":null} +# ruby1 = JSON.parse(json, create_additions: true) # Another message +# ruby1.class # RuntimeError +# +# \OpenStruct: +# require 'json/add/ostruct' +# ruby0 = OpenStruct.new(name: 'Matz', language: 'Ruby') # # +# json = JSON.generate(ruby0) # {"json_class":"OpenStruct","t":{"name":"Matz","language":"Ruby"}} +# ruby1 = JSON.parse(json, create_additions: true) # # +# ruby1.class # OpenStruct +# +# \Range: +# require 'json/add/range' +# ruby0 = Range.new(0, 2) # 0..2 +# json = JSON.generate(ruby0) # {"json_class":"Range","a":[0,2,false]} +# ruby1 = JSON.parse(json, create_additions: true) # 0..2 +# ruby1.class # Range +# +# \Rational: +# require 'json/add/rational' +# ruby0 = Rational(1, 3) # 1/3 +# json = JSON.generate(ruby0) # {"json_class":"Rational","n":1,"d":3} +# ruby1 = JSON.parse(json, create_additions: true) # 1/3 +# ruby1.class # Rational +# +# \Regexp: +# require 'json/add/regexp' +# ruby0 = Regexp.new('foo') # (?-mix:foo) +# json = JSON.generate(ruby0) # {"json_class":"Regexp","o":0,"s":"foo"} +# ruby1 = JSON.parse(json, create_additions: true) # (?-mix:foo) +# ruby1.class # Regexp +# +# \Set: +# require 'json/add/set' +# ruby0 = Set.new([0, 1, 2]) # # +# json = JSON.generate(ruby0) # {"json_class":"Set","a":[0,1,2]} +# ruby1 = JSON.parse(json, create_additions: true) # # +# ruby1.class # Set +# +# \Struct: +# require 'json/add/struct' +# Customer = Struct.new(:name, :address) # Customer +# ruby0 = Customer.new("Dave", "123 Main") # # +# json = JSON.generate(ruby0) # {"json_class":"Customer","v":["Dave","123 Main"]} +# ruby1 = JSON.parse(json, create_additions: true) # # +# ruby1.class # Customer +# +# \Symbol: +# require 'json/add/symbol' +# ruby0 = :foo # foo +# json = JSON.generate(ruby0) # {"json_class":"Symbol","s":"foo"} +# ruby1 = JSON.parse(json, create_additions: true) # foo +# ruby1.class # Symbol +# +# \Time: +# require 'json/add/time' +# ruby0 = Time.now # 2020-05-02 11:28:26 -0500 +# json = JSON.generate(ruby0) # {"json_class":"Time","s":1588436906,"n":840560000} +# ruby1 = JSON.parse(json, create_additions: true) # 2020-05-02 11:28:26 -0500 +# ruby1.class # Time +# +# +# === Custom \JSON Additions +# +# In addition to the \JSON additions provided, +# you can craft \JSON additions of your own, +# either for Ruby built-in classes or for user-defined classes. +# +# Here's a user-defined class +Foo+: +# class Foo +# attr_accessor :bar, :baz +# def initialize(bar, baz) +# self.bar = bar +# self.baz = baz +# end +# end +# +# Here's the \JSON addition for it: +# # Extend class Foo with JSON addition. +# class Foo +# # Serialize Foo object with its class name and arguments +# def to_json(*args) +# { +# JSON.create_id => self.class.name, +# 'a' => [ bar, baz ] +# }.to_json(*args) +# end +# # Deserialize JSON string by constructing new Foo object with arguments. +# def self.json_create(object) +# new(*object['a']) +# end +# end +# +# Demonstration: +# require 'json' +# # This Foo object has no custom addition. +# foo0 = Foo.new(0, 1) +# json0 = JSON.generate(foo0) +# obj0 = JSON.parse(json0) +# # Lood the custom addition. +# require_relative 'foo_addition' +# # This foo has the custom addition. +# foo1 = Foo.new(0, 1) +# json1 = JSON.generate(foo1) +# obj1 = JSON.parse(json1, create_additions: true) +# # Make a nice display. +# display = <<~EOT +# Generated JSON: +# Without custom addition: #{json0} (#{json0.class}) +# With custom addition: #{json1} (#{json1.class}) +# Parsed JSON: +# Without custom addition: #{obj0.inspect} (#{obj0.class}) +# With custom addition: #{obj1.inspect} (#{obj1.class}) +# EOT +# puts display +# +# Output: +# +# Generated JSON: +# Without custom addition: "#" (String) +# With custom addition: {"json_class":"Foo","a":[0,1]} (String) +# Parsed JSON: +# Without custom addition: "#" (String) +# With custom addition: # (Foo) +# +module JSON + require 'json/version' + require 'json/ext' +end diff --git a/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/bigdecimal.rb b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/bigdecimal.rb new file mode 100644 index 0000000..5dbc12c --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/bigdecimal.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true +unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED + require 'json' +end +begin + require 'bigdecimal' +rescue LoadError +end + +class BigDecimal + + # See #as_json. + def self.json_create(object) + BigDecimal._load object['b'] + end + + # Methods BigDecimal#as_json and +BigDecimal.json_create+ may be used + # to serialize and deserialize a \BigDecimal object; + # see Marshal[https://docs.ruby-lang.org/en/master/Marshal.html]. + # + # \Method BigDecimal#as_json 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 + # + def as_json(*) + { + JSON.create_id => self.class.name, + 'b' => _dump.force_encoding(Encoding::UTF_8), + } + end + + # 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 if defined?(::BigDecimal) diff --git a/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/complex.rb b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/complex.rb new file mode 100644 index 0000000..a69002e --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/complex.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true +unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED + require 'json' +end + +class Complex + + # See #as_json. + def self.json_create(object) + Complex(object['r'], object['i']) + end + + # Methods Complex#as_json and +Complex.json_create+ may be used + # to serialize and deserialize a \Complex object; + # see Marshal[https://docs.ruby-lang.org/en/master/Marshal.html]. + # + # \Method Complex#as_json 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, + 'r' => real, + 'i' => imag, + } + end + + # 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 +end diff --git a/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/core.rb b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/core.rb new file mode 100644 index 0000000..61ff454 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/core.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true +# This file requires the implementations of ruby core's custom objects for +# serialisation/deserialisation. + +require 'json/add/date' +require 'json/add/date_time' +require 'json/add/exception' +require 'json/add/range' +require 'json/add/regexp' +require 'json/add/string' +require 'json/add/struct' +require 'json/add/symbol' +require 'json/add/time' diff --git a/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/date.rb b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/date.rb new file mode 100644 index 0000000..66965d4 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/date.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true +unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED + require 'json' +end +require 'date' + +class 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) + + # Methods Date#as_json and +Date.json_create+ may be used + # to serialize and deserialize a \Date object; + # see Marshal[https://docs.ruby-lang.org/en/master/Marshal.html]. + # + # \Method Date#as_json 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) + # # => # + # + def as_json(*) + { + JSON.create_id => self.class.name, + 'y' => year, + 'm' => month, + 'd' => day, + 'sg' => start, + } + end + + # 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 +end diff --git a/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/date_time.rb b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/date_time.rb new file mode 100644 index 0000000..569f6ec --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/date_time.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true +unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED + require 'json' +end +require 'date' + +class 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('/') + if of_b and of_b != '0' + args << Rational(of_a.to_i, of_b.to_i) + else + args << of_a + end + args << object['sg'] + civil(*args) + end + + alias start sg unless method_defined?(:start) + + # Methods DateTime#as_json and +DateTime.json_create+ may be used + # to serialize and deserialize a \DateTime object; + # see Marshal[https://docs.ruby-lang.org/en/master/Marshal.html]. + # + # \Method DateTime#as_json 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, + 'y' => year, + 'm' => month, + 'd' => day, + 'H' => hour, + 'M' => min, + 'S' => sec, + 'of' => offset.to_s, + 'sg' => start, + } + end + + # 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 +end + + diff --git a/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/exception.rb b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/exception.rb new file mode 100644 index 0000000..5338ff8 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/exception.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true +unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED + require 'json' +end + +class Exception + + # See #as_json. + def self.json_create(object) + result = new(object['m']) + result.set_backtrace object['b'] + result + end + + # Methods Exception#as_json and +Exception.json_create+ may be used + # to serialize and deserialize a \Exception object; + # see Marshal[https://docs.ruby-lang.org/en/master/Marshal.html]. + # + # \Method Exception#as_json 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) # => # + # + def as_json(*) + { + JSON.create_id => self.class.name, + 'm' => message, + 'b' => backtrace, + } + end + + # 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 +end diff --git a/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/ostruct.rb b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/ostruct.rb new file mode 100644 index 0000000..534403b --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/ostruct.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true +unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED + require 'json' +end +begin + require 'ostruct' +rescue LoadError +end + +class OpenStruct + + # See #as_json. + def self.json_create(object) + new(object['t'] || object[:t]) + end + + # Methods OpenStruct#as_json and +OpenStruct.json_create+ may be used + # to serialize and deserialize a \OpenStruct object; + # see Marshal[https://docs.ruby-lang.org/en/master/Marshal.html]. + # + # \Method OpenStruct#as_json 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) + # # => # + # + def as_json(*) + klass = self.class.name + klass.to_s.empty? and raise JSON::JSONError, "Only named structs are supported!" + { + JSON.create_id => klass, + 't' => table, + } + end + + # 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 if defined?(::OpenStruct) diff --git a/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/range.rb b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/range.rb new file mode 100644 index 0000000..eb4b29a --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/range.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true +unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED + require 'json' +end + +class Range + + # See #as_json. + def self.json_create(object) + new(*object['a']) + end + + # Methods Range#as_json and +Range.json_create+ may be used + # to serialize and deserialize a \Range object; + # see Marshal[https://docs.ruby-lang.org/en/master/Marshal.html]. + # + # \Method Range#as_json 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, + 'a' => [ first, last, exclude_end? ] + } + end + + # 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 +end diff --git a/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/rational.rb b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/rational.rb new file mode 100644 index 0000000..1eb2314 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/rational.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true +unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED + require 'json' +end + +class Rational + + # See #as_json. + def self.json_create(object) + Rational(object['n'], object['d']) + end + + # Methods Rational#as_json and +Rational.json_create+ may be used + # to serialize and deserialize a \Rational object; + # see Marshal[https://docs.ruby-lang.org/en/master/Marshal.html]. + # + # \Method Rational#as_json 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, + 'n' => numerator, + 'd' => denominator, + } + end + + # 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 +end diff --git a/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/regexp.rb b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/regexp.rb new file mode 100644 index 0000000..f033dd1 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/regexp.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true +unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED + require 'json' +end + +class Regexp + + # See #as_json. + def self.json_create(object) + new(object['s'], object['o']) + end + + # Methods Regexp#as_json and +Regexp.json_create+ may be used + # to serialize and deserialize a \Regexp object; + # see Marshal[https://docs.ruby-lang.org/en/master/Marshal.html]. + # + # \Method Regexp#as_json 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, + 'o' => options, + 's' => source, + } + end + + # 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 +end diff --git a/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/set.rb b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/set.rb new file mode 100644 index 0000000..c521d8b --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/set.rb @@ -0,0 +1,48 @@ +unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED + require 'json' +end +defined?(::Set) or require 'set' + +class Set + + # See #as_json. + def self.json_create(object) + new object['a'] + end + + # Methods Set#as_json and +Set.json_create+ may be used + # to serialize and deserialize a \Set object; + # see Marshal[https://docs.ruby-lang.org/en/master/Marshal.html]. + # + # \Method Set#as_json 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) # => # + # + def as_json(*) + { + JSON.create_id => self.class.name, + 'a' => to_a, + } + end + + # 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 +end + diff --git a/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/string.rb b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/string.rb new file mode 100644 index 0000000..9c3bde2 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/string.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true +unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED + require 'json' +end + +class String + # call-seq: json_create(o) + # + # Raw Strings are JSON Objects (the raw bytes are stored in an array for the + # key "raw"). The Ruby String can be created by this class method. + def self.json_create(object) + object["raw"].pack("C*") + end + + # call-seq: to_json_raw_object() + # + # This method creates a raw object hash, that can be nested into + # other data structures and will be generated as a raw string. This + # method should be used, if you want to convert raw strings to JSON + # instead of UTF-8 strings, e. g. binary data. + def to_json_raw_object + { + JSON.create_id => self.class.name, + "raw" => unpack("C*"), + } + end + + # call-seq: to_json_raw(*args) + # + # This method creates a JSON text from the result of a call to + # to_json_raw_object of this String. + def to_json_raw(...) + to_json_raw_object.to_json(...) + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/struct.rb b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/struct.rb new file mode 100644 index 0000000..98c38d3 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/struct.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true +unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED + require 'json' +end + +class Struct + + # See #as_json. + def self.json_create(object) + new(*object['v']) + end + + # Methods Struct#as_json and +Struct.json_create+ may be used + # to serialize and deserialize a \Struct object; + # see Marshal[https://docs.ruby-lang.org/en/master/Marshal.html]. + # + # \Method Struct#as_json 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) + # # => # + # + def as_json(*) + klass = self.class.name + klass.to_s.empty? and raise JSON::JSONError, "Only named structs are supported!" + { + JSON.create_id => klass, + 'v' => values, + } + end + + # 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 +end diff --git a/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/symbol.rb b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/symbol.rb new file mode 100644 index 0000000..20dd594 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/symbol.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true +unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED + require 'json' +end + +class Symbol + + # Methods Symbol#as_json and +Symbol.json_create+ may be used + # to serialize and deserialize a \Symbol object; + # see Marshal[https://docs.ruby-lang.org/en/master/Marshal.html]. + # + # \Method Symbol#as_json 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, + 's' => to_s, + } + end + + # Returns a JSON string representing +self+: + # + # require 'json/add/symbol' + # puts :foo.to_json + # + # Output: + # + # # {"json_class":"Symbol","s":"foo"} + # + def to_json(state = nil, *a) + state = ::JSON::State.from_state(state) + if state.strict? + super + else + as_json.to_json(state, *a) + end + end + + # See #as_json. + def self.json_create(o) + o['s'].to_sym + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/time.rb b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/time.rb new file mode 100644 index 0000000..05a1f24 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/add/time.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true +unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED + require 'json' +end + +class 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 + end + at(object['s'], Rational(object['n'], 1000)) + end + + # Methods Time#as_json and +Time.json_create+ may be used + # to serialize and deserialize a \Time object; + # see Marshal[https://docs.ruby-lang.org/en/master/Marshal.html]. + # + # \Method Time#as_json 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(*) + { + JSON.create_id => self.class.name, + 's' => tv_sec, + 'n' => tv_nsec, + } + end + + # 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 +end diff --git a/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/common.rb b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/common.rb new file mode 100644 index 0000000..877b968 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/common.rb @@ -0,0 +1,1142 @@ +# frozen_string_literal: true + +require 'json/version' + +module JSON + autoload :GenericObject, 'json/generic_object' + + module ParserOptions # :nodoc: + class << self + def prepare(opts) + if opts[:object_class] || opts[:array_class] + opts = opts.dup + on_load = opts[:on_load] + + on_load = object_class_proc(opts[:object_class], on_load) if opts[:object_class] + on_load = array_class_proc(opts[:array_class], on_load) if opts[:array_class] + opts[:on_load] = on_load + end + + if opts.fetch(:create_additions, false) != false + opts = create_additions_proc(opts) + end + + opts + end + + private + + def object_class_proc(object_class, on_load) + ->(obj) do + if Hash === obj + object = object_class.new + obj.each { |k, v| object[k] = v } + obj = object + end + on_load.nil? ? obj : on_load.call(obj) + end + end + + def array_class_proc(array_class, on_load) + ->(obj) do + if Array === obj + array = array_class.new + obj.each { |v| array << v } + obj = array + end + on_load.nil? ? obj : on_load.call(obj) + end + end + + # TODO: extract :create_additions support to another gem for version 3.0 + def create_additions_proc(opts) + if opts[:symbolize_names] + raise ArgumentError, "options :symbolize_names and :create_additions cannot be used in conjunction" + end + + opts = opts.dup + create_additions = opts.fetch(:create_additions, false) + on_load = opts[:on_load] + object_class = opts[:object_class] || Hash + + opts[:on_load] = ->(object) do + case object + when String + opts[:match_string]&.each do |pattern, klass| + if match = pattern.match(object) + create_additions_warning if create_additions.nil? + object = klass.json_create(object) + break + end + end + when object_class + if opts[:create_additions] != false + if class_path = object[JSON.create_id] + klass = begin + Object.const_get(class_path) + rescue NameError => e + raise ArgumentError, "can't get const #{class_path}: #{e}" + end + + if klass.respond_to?(:json_creatable?) ? klass.json_creatable? : klass.respond_to?(:json_create) + create_additions_warning if create_additions.nil? + object = klass.json_create(object) + end + end + end + end + + on_load.nil? ? object : on_load.call(object) + end + + opts + end + + def create_additions_warning + JSON.deprecation_warning "JSON.load implicit support for `create_additions: true` is deprecated " \ + "and will be removed in 3.0, use JSON.unsafe_load or explicitly " \ + "pass `create_additions: true`" + end + end + end + + class << self + def deprecation_warning(message, uplevel = 3) # :nodoc: + gem_root = File.expand_path("..", __dir__) + "/" + caller_locations(uplevel, 10).each do |frame| + if frame.path.nil? || frame.path.start_with?(gem_root) || frame.path.end_with?("/truffle/cext_ruby.rb", ".c") + uplevel += 1 + else + break + end + end + + if RUBY_VERSION >= "3.0" + warn(message, uplevel: uplevel, category: :deprecated) + else + warn(message, uplevel: uplevel) + end + end + + # :call-seq: + # JSON[object] -> new_array or new_string + # + # If +object+ is a \String, + # calls JSON.parse with +object+ and +opts+ (see method #parse): + # json = '[0, 1, null]' + # JSON[json]# => [0, 1, nil] + # + # Otherwise, calls JSON.generate with +object+ and +opts+ (see method #generate): + # ruby = [0, 1, nil] + # JSON[ruby] # => '[0,1,null]' + def [](object, opts = nil) + if object.is_a?(String) + return JSON.parse(object, opts) + elsif object.respond_to?(:to_str) + str = object.to_str + if str.is_a?(String) + return JSON.parse(str, opts) + end + end + + JSON.generate(object, opts) + end + + # Returns the JSON parser class that is used by JSON. + attr_reader :parser + + # Set the JSON parser class _parser_ to be used by JSON. + def parser=(parser) # :nodoc: + @parser = parser + remove_const :Parser if const_defined?(:Parser, false) + const_set :Parser, parser + end + + # Set the module _generator_ to be used by JSON. + def generator=(generator) # :nodoc: + old, $VERBOSE = $VERBOSE, nil + @generator = generator + generator_methods = generator::GeneratorMethods + for const in generator_methods.constants + klass = const_get(const) + modul = generator_methods.const_get(const) + klass.class_eval do + instance_methods(false).each do |m| + m.to_s == 'to_json' and remove_method m + end + include modul + end + end + self.state = generator::State + const_set :State, state + ensure + $VERBOSE = old + end + + # Returns the JSON generator module that is used by JSON. + attr_reader :generator + + # Sets or Returns the JSON generator state class that is used by JSON. + attr_accessor :state + + private + + # Called from the extension when a hash has both string and symbol keys + def on_mixed_keys_hash(hash, do_raise) + set = {} + hash.each_key do |key| + key_str = key.to_s + + if set[key_str] + message = "detected duplicate key #{key_str.inspect} in #{hash.inspect}" + if do_raise + raise GeneratorError, message + else + deprecation_warning("#{message}.\nThis will raise an error in json 3.0 unless enabled via `allow_duplicate_key: true`") + end + else + set[key_str] = true + end + end + end + + def deprecated_singleton_attr_accessor(*attrs) + args = RUBY_VERSION >= "3.0" ? ", category: :deprecated" : "" + attrs.each do |attr| + singleton_class.class_eval <<~RUBY + def #{attr} + warn "JSON.#{attr} is deprecated and will be removed in json 3.0.0", uplevel: 1 #{args} + @#{attr} + end + + def #{attr}=(val) + warn "JSON.#{attr}= is deprecated and will be removed in json 3.0.0", uplevel: 1 #{args} + @#{attr} = val + end + + def _#{attr} + @#{attr} + end + RUBY + end + end + end + + # Sets create identifier, which is used to decide if the _json_create_ + # hook of a class should be called; initial value is +json_class+: + # JSON.create_id # => 'json_class' + def self.create_id=(new_value) + Thread.current[:"JSON.create_id"] = new_value.dup.freeze + end + + # Returns the current create identifier. + # See also JSON.create_id=. + def self.create_id + Thread.current[:"JSON.create_id"] || 'json_class' + end + + NaN = Float::NAN + + Infinity = Float::INFINITY + + MinusInfinity = -Infinity + + # The base exception for JSON errors. + class JSONError < StandardError; end + + # This exception is raised if a parser error occurs. + class ParserError < JSONError + attr_reader :line, :column + end + + # This exception is raised if the nesting of parsed data structures is too + # deep. + class NestingError < ParserError; end + + # This exception is raised if a generator or unparser error occurs. + class GeneratorError < JSONError + attr_reader :invalid_object + + def initialize(message, invalid_object = nil) + super(message) + @invalid_object = invalid_object + end + + def detailed_message(...) + # Exception#detailed_message doesn't exist until Ruby 3.2 + super_message = defined?(super) ? super : message + + if @invalid_object.nil? + super_message + else + "#{super_message}\nInvalid object: #{@invalid_object.inspect}" + end + end + end + + # Fragment of JSON document that is to be included as is: + # fragment = JSON::Fragment.new("[1, 2, 3]") + # JSON.generate({ count: 3, items: fragments }) + # + # This allows to easily assemble multiple JSON fragments that have + # been persisted somewhere without having to parse them nor resorting + # to string interpolation. + # + # Note: no validation is performed on the provided string. It is the + # responsibility of the caller to ensure the string contains valid JSON. + Fragment = Struct.new(:json) do + def initialize(json) + unless string = String.try_convert(json) + raise TypeError, " no implicit conversion of #{json.class} into String" + end + + super(string) + end + + def to_json(state = nil, *) + json + end + end + + module_function + + # :call-seq: + # JSON.parse(source, opts) -> object + # + # Returns the Ruby objects created by parsing the given +source+. + # + # Argument +source+ contains the \String to be parsed. + # + # Argument +opts+, if given, contains a \Hash of options for the parsing. + # See {Parsing Options}[#module-JSON-label-Parsing+Options]. + # + # --- + # + # When +source+ is a \JSON array, returns a Ruby \Array: + # source = '["foo", 1.0, true, false, null]' + # ruby = JSON.parse(source) + # ruby # => ["foo", 1.0, true, false, nil] + # ruby.class # => Array + # + # When +source+ is a \JSON object, returns a Ruby \Hash: + # source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}' + # ruby = JSON.parse(source) + # ruby # => {"a"=>"foo", "b"=>1.0, "c"=>true, "d"=>false, "e"=>nil} + # ruby.class # => Hash + # + # For examples of parsing for all \JSON data types, see + # {Parsing \JSON}[#module-JSON-label-Parsing+JSON]. + # + # Parses nested JSON objects: + # source = <<~JSON + # { + # "name": "Dave", + # "age" :40, + # "hats": [ + # "Cattleman's", + # "Panama", + # "Tophat" + # ] + # } + # JSON + # ruby = JSON.parse(source) + # ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]} + # + # --- + # + # Raises an exception if +source+ is not valid JSON: + # # Raises JSON::ParserError (783: unexpected token at ''): + # JSON.parse('') + # + def parse(source, opts = nil) + opts = ParserOptions.prepare(opts) unless opts.nil? + Parser.parse(source, opts) + end + + PARSE_L_OPTIONS = { + max_nesting: false, + allow_nan: true, + }.freeze + private_constant :PARSE_L_OPTIONS + + # :call-seq: + # JSON.parse!(source, opts) -> object + # + # Calls + # parse(source, opts) + # with +source+ and possibly modified +opts+. + # + # Differences from JSON.parse: + # - Option +max_nesting+, if not provided, defaults to +false+, + # which disables checking for nesting depth. + # - Option +allow_nan+, if not provided, defaults to +true+. + def parse!(source, opts = nil) + if opts.nil? + parse(source, PARSE_L_OPTIONS) + else + parse(source, PARSE_L_OPTIONS.merge(opts)) + end + end + + # :call-seq: + # JSON.load_file(path, opts={}) -> object + # + # Calls: + # parse(File.read(path), opts) + # + # See method #parse. + def load_file(filespec, opts = nil) + parse(File.read(filespec, encoding: Encoding::UTF_8), opts) + end + + # :call-seq: + # JSON.load_file!(path, opts = {}) + # + # Calls: + # JSON.parse!(File.read(path, opts)) + # + # See method #parse! + def load_file!(filespec, opts = nil) + parse!(File.read(filespec, encoding: Encoding::UTF_8), opts) + end + + # :call-seq: + # JSON.generate(obj, opts = nil) -> new_string + # + # Returns a \String containing the generated \JSON data. + # + # See also JSON.pretty_generate. + # + # Argument +obj+ is the Ruby object to be converted to \JSON. + # + # Argument +opts+, if given, contains a \Hash of options for the generation. + # See {Generating Options}[#module-JSON-label-Generating+Options]. + # + # --- + # + # When +obj+ is an \Array, returns a \String containing a \JSON array: + # obj = ["foo", 1.0, true, false, nil] + # json = JSON.generate(obj) + # json # => '["foo",1.0,true,false,null]' + # + # When +obj+ is a \Hash, returns a \String containing a \JSON object: + # obj = {foo: 0, bar: 's', baz: :bat} + # json = JSON.generate(obj) + # json # => '{"foo":0,"bar":"s","baz":"bat"}' + # + # For examples of generating from other Ruby objects, see + # {Generating \JSON from Other Objects}[#module-JSON-label-Generating+JSON+from+Other+Objects]. + # + # --- + # + # Raises an exception if any formatting option is not a \String. + # + # Raises an exception if +obj+ contains circular references: + # a = []; b = []; a.push(b); b.push(a) + # # Raises JSON::NestingError (nesting of 100 is too deep): + # JSON.generate(a) + # + def generate(obj, opts = nil) + if State === opts + opts.generate(obj) + else + State.generate(obj, opts, nil) + end + end + + # :call-seq: + # JSON.fast_generate(obj, opts) -> new_string + # + # Arguments +obj+ and +opts+ here are the same as + # arguments +obj+ and +opts+ in JSON.generate. + # + # By default, generates \JSON data without checking + # for circular references in +obj+ (option +max_nesting+ set to +false+, disabled). + # + # Raises an exception if +obj+ contains circular references: + # a = []; b = []; a.push(b); b.push(a) + # # Raises SystemStackError (stack level too deep): + # JSON.fast_generate(a) + def fast_generate(obj, opts = nil) + if RUBY_VERSION >= "3.0" + warn "JSON.fast_generate is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1, category: :deprecated + else + warn "JSON.fast_generate is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1 + end + generate(obj, opts) + end + + PRETTY_GENERATE_OPTIONS = { + indent: ' ', + space: ' ', + object_nl: "\n", + array_nl: "\n", + }.freeze + private_constant :PRETTY_GENERATE_OPTIONS + + # :call-seq: + # JSON.pretty_generate(obj, opts = nil) -> new_string + # + # Arguments +obj+ and +opts+ here are the same as + # arguments +obj+ and +opts+ in JSON.generate. + # + # Default options are: + # { + # indent: ' ', # Two spaces + # space: ' ', # One space + # array_nl: "\n", # Newline + # object_nl: "\n" # Newline + # } + # + # Example: + # obj = {foo: [:bar, :baz], bat: {bam: 0, bad: 1}} + # json = JSON.pretty_generate(obj) + # puts json + # Output: + # { + # "foo": [ + # "bar", + # "baz" + # ], + # "bat": { + # "bam": 0, + # "bad": 1 + # } + # } + # + def pretty_generate(obj, opts = nil) + return opts.generate(obj) if State === opts + + options = PRETTY_GENERATE_OPTIONS + + if opts + unless opts.is_a?(Hash) + 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 + end + options = options.merge(opts) + end + + State.generate(obj, options, nil) + end + + # Sets or returns default options for the JSON.unsafe_load method. + # Initially: + # opts = JSON.load_default_options + # opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true} + deprecated_singleton_attr_accessor :unsafe_load_default_options + + @unsafe_load_default_options = { + :max_nesting => false, + :allow_nan => true, + :allow_blank => true, + :create_additions => true, + } + + # Sets or returns default options for the JSON.load method. + # Initially: + # opts = JSON.load_default_options + # opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true} + deprecated_singleton_attr_accessor :load_default_options + + @load_default_options = { + :allow_nan => true, + :allow_blank => true, + :create_additions => nil, + } + # :call-seq: + # JSON.unsafe_load(source, options = {}) -> object + # JSON.unsafe_load(source, proc = nil, options = {}) -> object + # + # Returns the Ruby objects created by parsing the given +source+. + # + # BEWARE: This method is meant to serialise data from trusted user input, + # like from your own database server or clients under your control, it could + # be dangerous to allow untrusted users to pass JSON sources into it. + # + # - Argument +source+ must be, or be convertible to, a \String: + # - If +source+ responds to instance method +to_str+, + # source.to_str becomes the source. + # - If +source+ responds to instance method +to_io+, + # source.to_io.read becomes the source. + # - If +source+ responds to instance method +read+, + # source.read becomes the source. + # - If both of the following are true, source becomes the \String 'null': + # - Option +allow_blank+ specifies a truthy value. + # - The source, as defined above, is +nil+ or the empty \String ''. + # - Otherwise, +source+ remains the source. + # - Argument +proc+, if given, must be a \Proc that accepts one argument. + # It will be called recursively with each result (depth-first order). + # See details below. + # - Argument +opts+, if given, contains a \Hash of options for the parsing. + # See {Parsing Options}[#module-JSON-label-Parsing+Options]. + # The default options can be changed via method JSON.unsafe_load_default_options=. + # + # --- + # + # When no +proc+ is given, modifies +source+ as above and returns the result of + # parse(source, opts); see #parse. + # + # Source for following examples: + # source = <<~JSON + # { + # "name": "Dave", + # "age" :40, + # "hats": [ + # "Cattleman's", + # "Panama", + # "Tophat" + # ] + # } + # JSON + # + # Load a \String: + # ruby = JSON.unsafe_load(source) + # ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]} + # + # Load an \IO object: + # require 'stringio' + # object = JSON.unsafe_load(StringIO.new(source)) + # object # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]} + # + # Load a \File object: + # path = 't.json' + # File.write(path, source) + # File.open(path) do |file| + # JSON.unsafe_load(file) + # end # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]} + # + # --- + # + # When +proc+ is given: + # - Modifies +source+ as above. + # - Gets the +result+ from calling parse(source, opts). + # - Recursively calls proc(result). + # - Returns the final result. + # + # Example: + # require 'json' + # + # # Some classes for the example. + # class Base + # def initialize(attributes) + # @attributes = attributes + # end + # end + # class User < Base; end + # class Account < Base; end + # class Admin < Base; end + # # The JSON source. + # json = <<-EOF + # { + # "users": [ + # {"type": "User", "username": "jane", "email": "jane@example.com"}, + # {"type": "User", "username": "john", "email": "john@example.com"} + # ], + # "accounts": [ + # {"account": {"type": "Account", "paid": true, "account_id": "1234"}}, + # {"account": {"type": "Account", "paid": false, "account_id": "1235"}} + # ], + # "admins": {"type": "Admin", "password": "0wn3d"} + # } + # EOF + # # Deserializer method. + # def deserialize_obj(obj, safe_types = %w(User Account Admin)) + # type = obj.is_a?(Hash) && obj["type"] + # safe_types.include?(type) ? Object.const_get(type).new(obj) : obj + # end + # # Call to JSON.unsafe_load + # ruby = JSON.unsafe_load(json, proc {|obj| + # case obj + # when Hash + # obj.each {|k, v| obj[k] = deserialize_obj v } + # when Array + # obj.map! {|v| deserialize_obj v } + # end + # obj + # }) + # pp ruby + # Output: + # {"users"=> + # [#"User", "username"=>"jane", "email"=>"jane@example.com"}>, + # #"User", "username"=>"john", "email"=>"john@example.com"}>], + # "accounts"=> + # [{"account"=> + # #"Account", "paid"=>true, "account_id"=>"1234"}>}, + # {"account"=> + # #"Account", "paid"=>false, "account_id"=>"1235"}>}], + # "admins"=> + # #"Admin", "password"=>"0wn3d"}>} + # + def unsafe_load(source, proc = nil, options = nil) + opts = if options.nil? + if proc && proc.is_a?(Hash) + options, proc = proc, nil + options + else + _unsafe_load_default_options + end + else + _unsafe_load_default_options.merge(options) + end + + unless source.is_a?(String) + if source.respond_to? :to_str + source = source.to_str + elsif source.respond_to? :to_io + source = source.to_io.read + elsif source.respond_to?(:read) + source = source.read + end + end + + if opts[:allow_blank] && (source.nil? || source.empty?) + source = 'null' + end + + if proc + opts = opts.dup + opts[:on_load] = proc.to_proc + end + + parse(source, opts) + end + + # :call-seq: + # JSON.load(source, options = {}) -> object + # JSON.load(source, proc = nil, options = {}) -> object + # + # Returns the Ruby objects created by parsing the given +source+. + # + # BEWARE: This method is meant to serialise data from trusted user input, + # like from your own database server or clients under your control, it could + # be dangerous to allow untrusted users to pass JSON sources into it. + # If you must use it, use JSON.unsafe_load instead to make it clear. + # + # Since JSON version 2.8.0, `load` emits a deprecation warning when a + # non native type is deserialized, without `create_additions` being explicitly + # enabled, and in JSON version 3.0, `load` will have `create_additions` disabled + # by default. + # + # - Argument +source+ must be, or be convertible to, a \String: + # - If +source+ responds to instance method +to_str+, + # source.to_str becomes the source. + # - If +source+ responds to instance method +to_io+, + # source.to_io.read becomes the source. + # - If +source+ responds to instance method +read+, + # source.read becomes the source. + # - If both of the following are true, source becomes the \String 'null': + # - Option +allow_blank+ specifies a truthy value. + # - The source, as defined above, is +nil+ or the empty \String ''. + # - Otherwise, +source+ remains the source. + # - Argument +proc+, if given, must be a \Proc that accepts one argument. + # It will be called recursively with each result (depth-first order). + # See details below. + # - Argument +opts+, if given, contains a \Hash of options for the parsing. + # See {Parsing Options}[#module-JSON-label-Parsing+Options]. + # The default options can be changed via method JSON.load_default_options=. + # + # --- + # + # When no +proc+ is given, modifies +source+ as above and returns the result of + # parse(source, opts); see #parse. + # + # Source for following examples: + # source = <<~JSON + # { + # "name": "Dave", + # "age" :40, + # "hats": [ + # "Cattleman's", + # "Panama", + # "Tophat" + # ] + # } + # JSON + # + # Load a \String: + # ruby = JSON.load(source) + # ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]} + # + # Load an \IO object: + # require 'stringio' + # object = JSON.load(StringIO.new(source)) + # object # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]} + # + # Load a \File object: + # path = 't.json' + # File.write(path, source) + # File.open(path) do |file| + # JSON.load(file) + # end # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]} + # + # --- + # + # When +proc+ is given: + # - Modifies +source+ as above. + # - Gets the +result+ from calling parse(source, opts). + # - Recursively calls proc(result). + # - Returns the final result. + # + # Example: + # require 'json' + # + # # Some classes for the example. + # class Base + # def initialize(attributes) + # @attributes = attributes + # end + # end + # class User < Base; end + # class Account < Base; end + # class Admin < Base; end + # # The JSON source. + # json = <<-EOF + # { + # "users": [ + # {"type": "User", "username": "jane", "email": "jane@example.com"}, + # {"type": "User", "username": "john", "email": "john@example.com"} + # ], + # "accounts": [ + # {"account": {"type": "Account", "paid": true, "account_id": "1234"}}, + # {"account": {"type": "Account", "paid": false, "account_id": "1235"}} + # ], + # "admins": {"type": "Admin", "password": "0wn3d"} + # } + # EOF + # # Deserializer method. + # def deserialize_obj(obj, safe_types = %w(User Account Admin)) + # type = obj.is_a?(Hash) && obj["type"] + # safe_types.include?(type) ? Object.const_get(type).new(obj) : obj + # end + # # Call to JSON.load + # ruby = JSON.load(json, proc {|obj| + # case obj + # when Hash + # obj.each {|k, v| obj[k] = deserialize_obj v } + # when Array + # obj.map! {|v| deserialize_obj v } + # end + # obj + # }) + # pp ruby + # Output: + # {"users"=> + # [#"User", "username"=>"jane", "email"=>"jane@example.com"}>, + # #"User", "username"=>"john", "email"=>"john@example.com"}>], + # "accounts"=> + # [{"account"=> + # #"Account", "paid"=>true, "account_id"=>"1234"}>}, + # {"account"=> + # #"Account", "paid"=>false, "account_id"=>"1235"}>}], + # "admins"=> + # #"Admin", "password"=>"0wn3d"}>} + # + def load(source, proc = nil, options = nil) + if proc && options.nil? && proc.is_a?(Hash) + options = proc + proc = nil + end + + opts = if options.nil? + if proc && proc.is_a?(Hash) + options, proc = proc, nil + options + else + _load_default_options + end + else + _load_default_options.merge(options) + end + + unless source.is_a?(String) + if source.respond_to? :to_str + source = source.to_str + elsif source.respond_to? :to_io + source = source.to_io.read + elsif source.respond_to?(:read) + source = source.read + end + end + + if opts[:allow_blank] && (source.nil? || source.empty?) + source = 'null' + end + + if proc + opts = opts.dup + opts[:on_load] = proc.to_proc + end + + parse(source, opts) + end + + # Sets or returns the default options for the JSON.dump method. + # Initially: + # opts = JSON.dump_default_options + # opts # => {:max_nesting=>false, :allow_nan=>true} + deprecated_singleton_attr_accessor :dump_default_options + @dump_default_options = { + :max_nesting => false, + :allow_nan => true, + } + + # :call-seq: + # JSON.dump(obj, io = nil, limit = nil) + # + # Dumps +obj+ as a \JSON string, i.e. calls generate on the object and returns the result. + # + # The default options can be changed via method JSON.dump_default_options. + # + # - Argument +io+, if given, should respond to method +write+; + # the \JSON \String is written to +io+, and +io+ is returned. + # If +io+ is not given, the \JSON \String is returned. + # - Argument +limit+, if given, is passed to JSON.generate as option +max_nesting+. + # + # --- + # + # When argument +io+ is not given, returns the \JSON \String generated from +obj+: + # obj = {foo: [0, 1], bar: {baz: 2, bat: 3}, bam: :bad} + # json = JSON.dump(obj) + # json # => "{\"foo\":[0,1],\"bar\":{\"baz\":2,\"bat\":3},\"bam\":\"bad\"}" + # + # When argument +io+ is given, writes the \JSON \String to +io+ and returns +io+: + # path = 't.json' + # File.open(path, 'w') do |file| + # JSON.dump(obj, file) + # end # => # + # puts File.read(path) + # Output: + # {"foo":[0,1],"bar":{"baz":2,"bat":3},"bam":"bad"} + def dump(obj, anIO = nil, limit = nil, kwargs = nil) + if kwargs.nil? + if limit.nil? + if anIO.is_a?(Hash) + kwargs = anIO + anIO = nil + end + elsif limit.is_a?(Hash) + kwargs = limit + limit = nil + end + end + + unless anIO.nil? + if anIO.respond_to?(:to_io) + anIO = anIO.to_io + elsif limit.nil? && !anIO.respond_to?(:write) + anIO, limit = nil, anIO + end + end + + opts = JSON._dump_default_options + opts = opts.merge(:max_nesting => limit) if limit + opts = opts.merge(kwargs) if kwargs + + begin + State.generate(obj, opts, anIO) + rescue JSON::NestingError + raise ArgumentError, "exceed depth limit" + end + end + + # :stopdoc: + # All these were meant to be deprecated circa 2009, but were just set as undocumented + # so usage still exist in the wild. + def unparse(...) + if RUBY_VERSION >= "3.0" + warn "JSON.unparse is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1, category: :deprecated + else + warn "JSON.unparse is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1 + end + generate(...) + end + module_function :unparse + + def fast_unparse(...) + if RUBY_VERSION >= "3.0" + warn "JSON.fast_unparse is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1, category: :deprecated + else + warn "JSON.fast_unparse is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1 + end + generate(...) + end + module_function :fast_unparse + + def pretty_unparse(...) + if RUBY_VERSION >= "3.0" + warn "JSON.pretty_unparse is deprecated and will be removed in json 3.0.0, just use JSON.pretty_generate", uplevel: 1, category: :deprecated + else + warn "JSON.pretty_unparse is deprecated and will be removed in json 3.0.0, just use JSON.pretty_generate", uplevel: 1 + end + pretty_generate(...) + end + module_function :fast_unparse + + def restore(...) + if RUBY_VERSION >= "3.0" + warn "JSON.restore is deprecated and will be removed in json 3.0.0, just use JSON.load", uplevel: 1, category: :deprecated + else + warn "JSON.restore is deprecated and will be removed in json 3.0.0, just use JSON.load", uplevel: 1 + end + load(...) + end + module_function :restore + + class << self + private + + def const_missing(const_name) + case const_name + when :PRETTY_STATE_PROTOTYPE + if RUBY_VERSION >= "3.0" + warn "JSON::PRETTY_STATE_PROTOTYPE is deprecated and will be removed in json 3.0.0, just use JSON.pretty_generate", uplevel: 1, category: :deprecated + else + warn "JSON::PRETTY_STATE_PROTOTYPE is deprecated and will be removed in json 3.0.0, just use JSON.pretty_generate", uplevel: 1 + end + state.new(PRETTY_GENERATE_OPTIONS) + else + super + end + end + end + # :startdoc: + + # JSON::Coder holds a parser and generator configuration. + # + # module MyApp + # JSONC_CODER = JSON::Coder.new( + # allow_trailing_comma: true + # ) + # end + # + # MyApp::JSONC_CODER.load(document) + # + class Coder + # :call-seq: + # JSON.new(options = nil, &block) + # + # Argument +options+, if given, contains a \Hash of options for both parsing and generating. + # See {Parsing Options}[#module-JSON-label-Parsing+Options], and {Generating Options}[#module-JSON-label-Generating+Options]. + # + # For generation, the strict: true option is always set. When a Ruby object with no native \JSON counterpart is + # encountered, the block provided to the initialize method is invoked, and must return a Ruby object that has a native + # \JSON counterpart: + # + # module MyApp + # API_JSON_CODER = JSON::Coder.new do |object| + # case object + # when Time + # object.iso8601(3) + # else + # object # Unknown type, will raise + # end + # end + # end + # + # puts MyApp::API_JSON_CODER.dump(Time.now.utc) # => "2025-01-21T08:41:44.286Z" + # + def initialize(options = nil, &as_json) + if options.nil? + options = { strict: true } + else + options = options.dup + options[:strict] = true + end + options[:as_json] = as_json if as_json + + @state = State.new(options).freeze + @parser_config = Ext::Parser::Config.new(ParserOptions.prepare(options)).freeze + end + + # call-seq: + # dump(object) -> String + # dump(object, io) -> io + # + # Serialize the given object into a \JSON document. + def dump(object, io = nil) + @state.generate(object, io) + end + alias_method :generate, :dump + + # call-seq: + # load(string) -> Object + # + # Parse the given \JSON document and return an equivalent Ruby object. + def load(source) + @parser_config.parse(source) + end + alias_method :parse, :load + + # call-seq: + # load(path) -> Object + # + # Parse the given \JSON document and return an equivalent Ruby object. + def load_file(path) + load(File.read(path, encoding: Encoding::UTF_8)) + end + end +end + +module ::Kernel + private + + # Outputs _objs_ to STDOUT as JSON strings in the shortest form, that is in + # one line. + def j(*objs) + if RUBY_VERSION >= "3.0" + warn "Kernel#j is deprecated and will be removed in json 3.0.0", uplevel: 1, category: :deprecated + else + warn "Kernel#j is deprecated and will be removed in json 3.0.0", uplevel: 1 + end + + objs.each do |obj| + puts JSON.generate(obj, :allow_nan => true, :max_nesting => false) + end + nil + end + + # Outputs _objs_ to STDOUT as JSON strings in a pretty format, with + # indentation and over many lines. + def jj(*objs) + if RUBY_VERSION >= "3.0" + warn "Kernel#jj is deprecated and will be removed in json 3.0.0", uplevel: 1, category: :deprecated + else + warn "Kernel#jj is deprecated and will be removed in json 3.0.0", uplevel: 1 + end + + objs.each do |obj| + puts JSON.pretty_generate(obj, :allow_nan => true, :max_nesting => false) + end + nil + end + + # If _object_ is string-like, parse the string and return the parsed result as + # a Ruby data structure. Otherwise, generate a JSON text from the Ruby data + # structure object and return it. + # + # The _opts_ argument is passed through to generate/parse respectively. See + # generate and parse for their documentation. + def JSON(object, opts = nil) + JSON[object, opts] + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/ext.rb b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/ext.rb new file mode 100644 index 0000000..5bacc5e --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/ext.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require 'json/common' + +module JSON + # This module holds all the modules/classes that implement JSON's + # functionality as C extensions. + module Ext + class Parser + class << self + def parse(...) + new(...).parse + end + alias_method :parse, :parse # Allow redefinition by extensions + end + + def initialize(source, opts = nil) + @source = source + @config = Config.new(opts) + end + + def source + @source.dup + end + + def parse + @config.parse(@source) + end + end + + require 'json/ext/parser' + Ext::Parser::Config = Ext::ParserConfig + JSON.parser = Ext::Parser + + if RUBY_ENGINE == 'truffleruby' + require 'json/truffle_ruby/generator' + JSON.generator = JSON::TruffleRuby::Generator + else + require 'json/ext/generator' + JSON.generator = Generator + end + end + + JSON_LOADED = true unless defined?(JSON::JSON_LOADED) +end diff --git a/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/ext/generator.so b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/ext/generator.so new file mode 100755 index 0000000..e12d6f9 Binary files /dev/null and b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/ext/generator.so differ diff --git a/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/ext/generator/state.rb b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/ext/generator/state.rb new file mode 100644 index 0000000..ce5c185 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/ext/generator/state.rb @@ -0,0 +1,103 @@ +# frozen_string_literal: true + +module JSON + module Ext + module Generator + class State + # call-seq: new(opts = {}) + # + # Instantiates a new State object, configured by _opts_. + # + # Argument +opts+, if given, contains a \Hash of options for the generation. + # See {Generating Options}[#module-JSON-label-Generating+Options]. + def initialize(opts = nil) + if opts && !opts.empty? + configure(opts) + end + end + + # call-seq: configure(opts) + # + # Configure this State instance with the Hash _opts_, and return + # itself. + def configure(opts) + unless opts.is_a?(Hash) + 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 + end + _configure(opts) + end + + alias_method :merge, :configure + + # call-seq: to_h + # + # Returns the configuration instance variables as a hash, that can be + # passed to the configure method. + def to_h + result = { + indent: indent, + space: space, + space_before: space_before, + object_nl: object_nl, + array_nl: array_nl, + as_json: as_json, + allow_nan: allow_nan?, + ascii_only: ascii_only?, + max_nesting: max_nesting, + script_safe: script_safe?, + strict: strict?, + depth: depth, + buffer_initial_length: buffer_initial_length, + } + + allow_duplicate_key = allow_duplicate_key? + unless allow_duplicate_key.nil? + result[:allow_duplicate_key] = allow_duplicate_key + end + + instance_variables.each do |iv| + iv = iv.to_s[1..-1] + result[iv.to_sym] = self[iv] + end + + result + end + + alias_method :to_hash, :to_h + + # call-seq: [](name) + # + # Returns the value returned by method +name+. + def [](name) + ::JSON.deprecation_warning("JSON::State#[] is deprecated and will be removed in json 3.0.0") + + if respond_to?(name) + __send__(name) + else + instance_variable_get("@#{name}") if + instance_variables.include?("@#{name}".to_sym) # avoid warning + end + end + + # call-seq: []=(name, value) + # + # Sets the attribute name to value. + def []=(name, value) + ::JSON.deprecation_warning("JSON::State#[]= is deprecated and will be removed in json 3.0.0") + + if respond_to?(name_writer = "#{name}=") + __send__ name_writer, value + else + instance_variable_set "@#{name}", value + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/ext/parser.so b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/ext/parser.so new file mode 100755 index 0000000..9b52a04 Binary files /dev/null and b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/ext/parser.so differ diff --git a/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/generic_object.rb b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/generic_object.rb new file mode 100644 index 0000000..5c8ace3 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/generic_object.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true +begin + require 'ostruct' +rescue LoadError + warn "JSON::GenericObject requires 'ostruct'. Please install it with `gem install ostruct`." +end + +module JSON + class GenericObject < OpenStruct + class << self + alias [] new + + def json_creatable? + @json_creatable + end + + attr_writer :json_creatable + + def json_create(data) + data = data.dup + data.delete JSON.create_id + self[data] + end + + def from_hash(object) + case + when object.respond_to?(:to_hash) + result = new + object.to_hash.each do |key, value| + result[key] = from_hash(value) + end + result + when object.respond_to?(:to_ary) + object.to_ary.map { |a| from_hash(a) } + else + object + end + end + + def load(source, proc = nil, opts = {}) + result = ::JSON.load(source, proc, opts.merge(:object_class => self)) + result.nil? ? new : result + end + + def dump(obj, *args) + ::JSON.dump(obj, *args) + end + end + self.json_creatable = false + + def to_hash + table + end + + def |(other) + self.class[other.to_hash.merge(to_hash)] + end + + def as_json(*) + { JSON.create_id => self.class.name }.merge to_hash + end + + def to_json(*a) + as_json.to_json(*a) + end + end if defined?(::OpenStruct) +end diff --git a/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/truffle_ruby/generator.rb b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/truffle_ruby/generator.rb new file mode 100644 index 0000000..9a3f4ae --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/truffle_ruby/generator.rb @@ -0,0 +1,740 @@ +# frozen_string_literal: true +module JSON + module TruffleRuby + module Generator + MAP = { + "\x0" => '\u0000', + "\x1" => '\u0001', + "\x2" => '\u0002', + "\x3" => '\u0003', + "\x4" => '\u0004', + "\x5" => '\u0005', + "\x6" => '\u0006', + "\x7" => '\u0007', + "\b" => '\b', + "\t" => '\t', + "\n" => '\n', + "\xb" => '\u000b', + "\f" => '\f', + "\r" => '\r', + "\xe" => '\u000e', + "\xf" => '\u000f', + "\x10" => '\u0010', + "\x11" => '\u0011', + "\x12" => '\u0012', + "\x13" => '\u0013', + "\x14" => '\u0014', + "\x15" => '\u0015', + "\x16" => '\u0016', + "\x17" => '\u0017', + "\x18" => '\u0018', + "\x19" => '\u0019', + "\x1a" => '\u001a', + "\x1b" => '\u001b', + "\x1c" => '\u001c', + "\x1d" => '\u001d', + "\x1e" => '\u001e', + "\x1f" => '\u001f', + '"' => '\"', + '\\' => '\\\\', + }.freeze # :nodoc: + + SCRIPT_SAFE_MAP = MAP.merge( + '/' => '\\/', + "\u2028" => '\u2028', + "\u2029" => '\u2029', + ).freeze + + SCRIPT_SAFE_ESCAPE_PATTERN = /[\/"\\\x0-\x1f\u2028-\u2029]/ + + def self.native_type?(value) # :nodoc: + (false == value || true == value || nil == value || String === value || Array === value || Hash === value || Integer === value || Float === value || Fragment === value) + end + + def self.native_key?(key) # :nodoc: + (Symbol === key || String === key) + end + + def self.valid_encoding?(string) # :nodoc: + return false unless string.encoding == ::Encoding::UTF_8 || string.encoding == ::Encoding::US_ASCII + string.is_a?(Symbol) || string.valid_encoding? + end + + # Convert a UTF8 encoded Ruby string _string_ to a JSON string, encoded with + # UTF16 big endian characters as \u????, and return it. + def self.utf8_to_json(string, script_safe = false) # :nodoc: + if script_safe + if SCRIPT_SAFE_ESCAPE_PATTERN.match?(string) + string.gsub(SCRIPT_SAFE_ESCAPE_PATTERN, SCRIPT_SAFE_MAP) + else + string + end + else + if /["\\\x0-\x1f]/.match?(string) + string.gsub(/["\\\x0-\x1f]/, MAP) + else + string + end + end + end + + def self.utf8_to_json_ascii(original_string, script_safe = false) # :nodoc: + string = original_string.b + map = script_safe ? SCRIPT_SAFE_MAP : MAP + string.gsub!(/[\/"\\\x0-\x1f]/n) { map[$&] || $& } + string.gsub!(/( + (?: + [\xc2-\xdf][\x80-\xbf] | + [\xe0-\xef][\x80-\xbf]{2} | + [\xf0-\xf4][\x80-\xbf]{3} + )+ | + [\x80-\xc1\xf5-\xff] # invalid + )/nx) { |c| + c.size == 1 and raise GeneratorError.new("invalid utf8 byte: '#{c}'", original_string) + s = c.encode(::Encoding::UTF_16BE, ::Encoding::UTF_8).unpack('H*')[0] + s.force_encoding(::Encoding::BINARY) + s.gsub!(/.{4}/n, '\\\\u\&') + s.force_encoding(::Encoding::UTF_8) + } + string.force_encoding(::Encoding::UTF_8) + string + rescue => e + raise GeneratorError.new(e.message, original_string) + end + + def self.valid_utf8?(string) + encoding = string.encoding + (encoding == Encoding::UTF_8 || encoding == Encoding::ASCII) && + string.valid_encoding? + end + + # This class is used to create State instances, that are use to hold data + # while generating a JSON text from a Ruby data structure. + class State + def self.generate(obj, opts = nil, io = nil) + new(opts).generate(obj, io) + end + + # Creates a State object from _opts_, which ought to be Hash to create + # a new State instance configured by _opts_, something else to create + # an unconfigured instance. If _opts_ is a State object, it is just + # returned. + def self.from_state(opts) + if opts + case + when self === opts + return opts + when opts.respond_to?(:to_hash) + return new(opts.to_hash) + when opts.respond_to?(:to_h) + return new(opts.to_h) + end + end + new + end + + # Instantiates a new State object, configured by _opts_. + # + # _opts_ can have the following keys: + # + # * *indent*: a string used to indent levels (default: ''), + # * *space*: a string that is put after, a : or , delimiter (default: ''), + # * *space_before*: a string that is put before a : pair delimiter (default: ''), + # * *object_nl*: a string that is put at the end of a JSON object (default: ''), + # * *array_nl*: a string that is put at the end of a JSON array (default: ''), + # * *script_safe*: true if U+2028, U+2029 and forward slash (/) should be escaped + # as to make the JSON object safe to interpolate in a script tag (default: false). + # * *check_circular*: is deprecated now, use the :max_nesting option instead, + # * *max_nesting*: sets the maximum level of data structure nesting in + # the generated JSON, max_nesting = 0 if no maximum should be checked. + # * *allow_nan*: true if NaN, Infinity, and -Infinity should be + # generated, otherwise an exception is thrown, if these values are + # encountered. This options defaults to false. + def initialize(opts = nil) + @indent = '' + @space = '' + @space_before = '' + @object_nl = '' + @array_nl = '' + @allow_nan = false + @ascii_only = false + @as_json = false + @depth = 0 + @buffer_initial_length = 1024 + @script_safe = false + @strict = false + @max_nesting = 100 + configure(opts) if opts + end + + # This string is used to indent levels in the JSON text. + attr_accessor :indent + + # This string is used to insert a space between the tokens in a JSON + # string. + attr_accessor :space + + # This string is used to insert a space before the ':' in JSON objects. + attr_accessor :space_before + + # This string is put at the end of a line that holds a JSON object (or + # Hash). + attr_accessor :object_nl + + # This string is put at the end of a line that holds a JSON array. + attr_accessor :array_nl + + # This proc converts unsupported types into native JSON types. + attr_accessor :as_json + + # This integer returns the maximum level of data structure nesting in + # the generated JSON, max_nesting = 0 if no maximum is checked. + attr_accessor :max_nesting + + # If this attribute is set to true, forward slashes will be escaped in + # all json strings. + attr_accessor :script_safe + + # If this attribute is set to true, attempting to serialize types not + # supported by the JSON spec will raise a JSON::GeneratorError + attr_accessor :strict + + # :stopdoc: + attr_reader :buffer_initial_length + + def buffer_initial_length=(length) + if length > 0 + @buffer_initial_length = length + end + end + # :startdoc: + + # This integer returns the current depth data structure nesting in the + # generated JSON. + attr_accessor :depth + + def check_max_nesting # :nodoc: + return if @max_nesting.zero? + current_nesting = depth + 1 + current_nesting > @max_nesting and + raise NestingError, "nesting of #{current_nesting} is too deep. Did you try to serialize objects with circular references?" + end + + # Returns true, if circular data structures are checked, + # otherwise returns false. + def check_circular? + !@max_nesting.zero? + end + + # Returns true if NaN, Infinity, and -Infinity should be considered as + # valid JSON and output. + def allow_nan? + @allow_nan + end + + # Returns true, if only ASCII characters should be generated. Otherwise + # returns false. + def ascii_only? + @ascii_only + end + + # Returns true, if forward slashes are escaped. Otherwise returns false. + def script_safe? + @script_safe + end + + # Returns true, if strict mode is enabled. Otherwise returns false. + # Strict mode only allow serializing JSON native types: Hash, Array, + # String, Integer, Float, true, false and nil. + def strict? + @strict + end + + # Configure this State instance with the Hash _opts_, and return + # itself. + def configure(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 + opts.each do |key, value| + instance_variable_set "@#{key}", value + end + + # NOTE: If adding new instance variables here, check whether #generate should check them for #generate_json + @indent = opts[:indent] || '' if opts.key?(:indent) + @space = opts[:space] || '' if opts.key?(:space) + @space_before = opts[:space_before] || '' if opts.key?(:space_before) + @object_nl = opts[:object_nl] || '' if opts.key?(:object_nl) + @array_nl = opts[:array_nl] || '' if opts.key?(:array_nl) + @allow_nan = !!opts[:allow_nan] if opts.key?(:allow_nan) + @as_json = opts[:as_json].to_proc if opts[:as_json] + @ascii_only = opts[:ascii_only] if opts.key?(:ascii_only) + @depth = opts[:depth] || 0 + @buffer_initial_length ||= opts[:buffer_initial_length] + + @script_safe = if opts.key?(:script_safe) + !!opts[:script_safe] + elsif opts.key?(:escape_slash) + !!opts[:escape_slash] + else + false + end + + if opts.key?(:allow_duplicate_key) + @allow_duplicate_key = !!opts[:allow_duplicate_key] + else + @allow_duplicate_key = nil # nil is deprecation + end + + @strict = !!opts[:strict] if opts.key?(:strict) + + if !opts.key?(:max_nesting) # defaults to 100 + @max_nesting = 100 + elsif opts[:max_nesting] + @max_nesting = opts[:max_nesting] + else + @max_nesting = 0 + end + self + end + alias merge configure + + def allow_duplicate_key? # :nodoc: + @allow_duplicate_key + end + + # Returns the configuration instance variables as a hash, that can be + # passed to the configure method. + def to_h + result = {} + instance_variables.each do |iv| + key = iv.to_s[1..-1] + result[key.to_sym] = instance_variable_get(iv) + end + + if result[:allow_duplicate_key].nil? + result.delete(:allow_duplicate_key) + end + + result + end + + alias to_hash to_h + + # Generates a valid JSON document from object +obj+ and + # returns the result. If no valid JSON document can be + # created this method raises a + # GeneratorError exception. + def generate(obj, anIO = nil) + return dup.generate(obj, anIO) if frozen? + + depth = @depth + if @indent.empty? and @space.empty? and @space_before.empty? and @object_nl.empty? and @array_nl.empty? and + !@ascii_only and !@script_safe and @max_nesting == 0 and (!@strict || Symbol === obj) + result = generate_json(obj, ''.dup) + else + result = obj.to_json(self) + end + JSON::TruffleRuby::Generator.valid_utf8?(result) or raise GeneratorError.new( + "source sequence #{result.inspect} is illegal/malformed utf-8", + obj + ) + if anIO + anIO.write(result) + anIO + else + result + end + ensure + @depth = depth unless frozen? + end + + # Handles @allow_nan, @buffer_initial_length, other ivars must be the default value (see above) + private def generate_json(obj, buf) + case obj + when Hash + buf << '{' + first = true + key_type = nil + obj.each_pair do |k,v| + if first + key_type = k.class + else + if key_type && !@allow_duplicate_key && key_type != k.class + key_type = nil # stop checking + JSON.send(:on_mixed_keys_hash, obj, !@allow_duplicate_key.nil?) + end + buf << ',' + end + + key_str = k.to_s + if key_str.class == String + fast_serialize_string(key_str, buf) + elsif key_str.is_a?(String) + generate_json(key_str, buf) + else + raise TypeError, "#{k.class}#to_s returns an instance of #{key_str.class}, expected a String" + end + + buf << ':' + generate_json(v, buf) + first = false + end + buf << '}' + when Array + buf << '[' + first = true + obj.each do |e| + buf << ',' unless first + generate_json(e, buf) + first = false + end + buf << ']' + when String + if obj.class == String + fast_serialize_string(obj, buf) + else + buf << obj.to_json(self) + end + when Integer + buf << obj.to_s + when Symbol + if @strict + fast_serialize_string(obj.name, buf) + else + buf << obj.to_json(self) + end + else + # Note: Float is handled this way since Float#to_s is slow anyway + buf << obj.to_json(self) + end + end + + # Assumes !@ascii_only, !@script_safe + private def fast_serialize_string(string, buf) # :nodoc: + buf << '"' + unless string.encoding == ::Encoding::UTF_8 + begin + string = string.encode(::Encoding::UTF_8) + rescue Encoding::UndefinedConversionError => error + raise GeneratorError.new(error.message, string) + end + end + raise GeneratorError.new("source sequence is illegal/malformed utf-8", string) unless string.valid_encoding? + + if /["\\\x0-\x1f]/.match?(string) + buf << string.gsub(/["\\\x0-\x1f]/, MAP) + else + buf << string + end + buf << '"' + end + + # Return the value returned by method +name+. + def [](name) + ::JSON.deprecation_warning("JSON::State#[] is deprecated and will be removed in json 3.0.0") + + if respond_to?(name) + __send__(name) + else + instance_variable_get("@#{name}") if + instance_variables.include?("@#{name}".to_sym) # avoid warning + end + end + + def []=(name, value) + ::JSON.deprecation_warning("JSON::State#[]= is deprecated and will be removed in json 3.0.0") + + if respond_to?(name_writer = "#{name}=") + __send__ name_writer, value + else + instance_variable_set "@#{name}", value + end + end + end + + module GeneratorMethods + module Object + # Converts this object to a string (calling #to_s), converts + # it to a JSON string, and returns the result. This is a fallback, if no + # special method #to_json was defined for some object. + def to_json(state = nil, *) + state = State.from_state(state) if state + if state&.strict? + value = self + if state.strict? && !Generator.native_type?(value) + if state.as_json + value = state.as_json.call(value, false) + unless Generator.native_type?(value) + raise GeneratorError.new("#{value.class} returned by #{state.as_json} not allowed in JSON", value) + end + value.to_json(state) + else + raise GeneratorError.new("#{value.class} not allowed in JSON", value) + end + end + else + to_s.to_json + end + end + end + + module Hash + # Returns a JSON string containing a JSON object, that is unparsed from + # this Hash instance. + # _state_ is a JSON::State object, that can also be used to configure the + # produced JSON string output further. + # _depth_ is used to find out nesting depth, to indent accordingly. + def to_json(state = nil, *) + state = State.from_state(state) + depth = state.depth + state.check_max_nesting + json_transform(state) + ensure + state.depth = depth + end + + private + + def json_transform(state) + depth = state.depth += 1 + + if empty? + state.depth -= 1 + return '{}' + end + + delim = ",#{state.object_nl}" + result = +"{#{state.object_nl}" + first = true + key_type = nil + indent = !state.object_nl.empty? + each { |key, value| + if first + key_type = key.class + else + if key_type && !state.allow_duplicate_key? && key_type != key.class + key_type = nil # stop checking + JSON.send(:on_mixed_keys_hash, self, state.allow_duplicate_key? == false) + end + result << delim + end + result << state.indent * depth if indent + + if state.strict? + if state.as_json && (!Generator.native_key?(key) || !Generator.valid_encoding?(key)) + key = state.as_json.call(key, true) + end + + unless Generator.native_key?(key) + raise GeneratorError.new("#{key.class} not allowed as object key in JSON", key) + end + + unless Generator.valid_encoding?(key) + raise GeneratorError.new("source sequence is illegal/malformed utf-8", key) + end + end + + key_str = key.to_s + if key_str.is_a?(String) + key_json = key_str.to_json(state) + else + raise TypeError, "#{key.class}#to_s returns an instance of #{key_str.class}, expected a String" + end + + result = +"#{result}#{key_json}#{state.space_before}:#{state.space}" + if state.strict? && !Generator.native_type?(value) + if state.as_json + value = state.as_json.call(value, false) + unless Generator.native_type?(value) + raise GeneratorError.new("#{value.class} returned by #{state.as_json} not allowed in JSON", value) + end + result << value.to_json(state) + state.depth = depth + else + raise GeneratorError.new("#{value.class} not allowed in JSON", value) + end + elsif value.respond_to?(:to_json) + result << value.to_json(state) + state.depth = depth + else + result << %{"#{String(value)}"} + end + first = false + } + depth -= 1 + unless first + result << state.object_nl + result << state.indent * depth if indent + end + result << '}' + result + end + end + + module Array + # Returns a JSON string containing a JSON array, that is unparsed from + # this Array instance. + # _state_ is a JSON::State object, that can also be used to configure the + # produced JSON string output further. + def to_json(state = nil, *) + state = State.from_state(state) + depth = state.depth + state.check_max_nesting + json_transform(state) + ensure + state.depth = depth + end + + private + + def json_transform(state) + depth = state.depth += 1 + + if empty? + state.depth -= 1 + return '[]' + end + + result = '['.dup + if state.array_nl.empty? + delim = "," + else + result << state.array_nl + delim = ",#{state.array_nl}" + end + + first = true + indent = !state.array_nl.empty? + each { |value| + result << delim unless first + result << state.indent * depth if indent + if state.strict? && !Generator.native_type?(value) + if state.as_json + value = state.as_json.call(value, false) + unless Generator.native_type?(value) + raise GeneratorError.new("#{value.class} returned by #{state.as_json} not allowed in JSON", value) + end + result << value.to_json(state) + else + raise GeneratorError.new("#{value.class} not allowed in JSON", value) + end + elsif value.respond_to?(:to_json) + result << value.to_json(state) + state.depth = depth + else + result << %{"#{String(value)}"} + end + first = false + } + depth -= 1 + result << state.array_nl + result << state.indent * depth if indent + result << ']' + end + end + + module Integer + # Returns a JSON string representation for this Integer number. + def to_json(*) to_s end + end + + module Float + # Returns a JSON string representation for this Float number. + def to_json(state = nil, *args) + state = State.from_state(state) + if infinite? || nan? + if state.allow_nan? + to_s + elsif state.strict? && state.as_json + casted_value = state.as_json.call(self, false) + + if casted_value.equal?(self) + raise GeneratorError.new("#{self} not allowed in JSON", self) + end + unless Generator.native_type?(casted_value) + raise GeneratorError.new("#{casted_value.class} returned by #{state.as_json} not allowed in JSON", casted_value) + end + + state.check_max_nesting + state.depth += 1 + result = casted_value.to_json(state, *args) + state.depth -= 1 + result + else + raise GeneratorError.new("#{self} not allowed in JSON", self) + end + else + to_s + end + end + end + + module Symbol + def to_json(state = nil, *args) + state = State.from_state(state) + if state.strict? + name.to_json(state, *args) + else + super + end + end + end + + module String + # This string should be encoded with UTF-8 A call to this method + # returns a JSON string encoded with UTF16 big endian characters as + # \u????. + def to_json(state = nil, *args) + state = State.from_state(state) + string = self + + if state.strict? && state.as_json + unless Generator.valid_encoding?(string) + string = state.as_json.call(string, false) + unless string.is_a?(::String) + return string.to_json(state, *args) + end + end + end + + if string.encoding == ::Encoding::UTF_8 + unless string.valid_encoding? + raise GeneratorError.new("source sequence is illegal/malformed utf-8", self) + end + else + string = string.encode(::Encoding::UTF_8) + end + + if state.ascii_only? + %("#{JSON::TruffleRuby::Generator.utf8_to_json_ascii(string, state.script_safe)}") + else + %("#{JSON::TruffleRuby::Generator.utf8_to_json(string, state.script_safe)}") + end + rescue Encoding::UndefinedConversionError => error + raise ::JSON::GeneratorError.new(error.message, self) + end + end + + module TrueClass + # Returns a JSON string for true: 'true'. + def to_json(*) 'true' end + end + + module FalseClass + # Returns a JSON string for false: 'false'. + def to_json(*) 'false' end + end + + module NilClass + # Returns a JSON string for nil: 'null'. + def to_json(*) 'null' end + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/version.rb b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/version.rb new file mode 100644 index 0000000..631beba --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/json-2.18.0/lib/json/version.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +module JSON + VERSION = '2.18.0' +end diff --git a/vendor/bundle/ruby/3.3.0/gems/logger-1.7.0/.document b/vendor/bundle/ruby/3.3.0/gems/logger-1.7.0/.document new file mode 100644 index 0000000..e2b9569 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/logger-1.7.0/.document @@ -0,0 +1,4 @@ +BSDL +COPYING +README.md +lib/ diff --git a/vendor/bundle/ruby/3.3.0/gems/logger-1.7.0/.rdoc_options b/vendor/bundle/ruby/3.3.0/gems/logger-1.7.0/.rdoc_options new file mode 100644 index 0000000..2d29a05 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/logger-1.7.0/.rdoc_options @@ -0,0 +1,3 @@ +--- +main_page: README.md +title: Documentation for Logger diff --git a/vendor/bundle/ruby/3.3.0/gems/logger-1.7.0/BSDL b/vendor/bundle/ruby/3.3.0/gems/logger-1.7.0/BSDL new file mode 100644 index 0000000..66d9359 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/logger-1.7.0/BSDL @@ -0,0 +1,22 @@ +Copyright (C) 1993-2013 Yukihiro Matsumoto. 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. diff --git a/vendor/bundle/ruby/3.3.0/gems/logger-1.7.0/COPYING b/vendor/bundle/ruby/3.3.0/gems/logger-1.7.0/COPYING new file mode 100644 index 0000000..48e5a96 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/logger-1.7.0/COPYING @@ -0,0 +1,56 @@ +Ruby is copyrighted free software by Yukihiro Matsumoto . +You can redistribute it and/or modify it under either the terms of the +2-clause BSDL (see the file BSDL), or the conditions below: + +1. You may make and give away verbatim copies of the source form of the + software without restriction, provided that you duplicate all of the + original copyright notices and associated disclaimers. + +2. You may modify your copy of the software in any way, provided that + you do at least ONE of the following: + + a. place your modifications in the Public Domain or otherwise + make them Freely Available, such as by posting said + modifications to Usenet or an equivalent medium, or by allowing + the author to include your modifications in the software. + + b. use the modified software only within your corporation or + organization. + + c. give non-standard binaries non-standard names, with + instructions on where to get the original software distribution. + + d. make other distribution arrangements with the author. + +3. You may distribute the software in object code or binary form, + provided that you do at least ONE of the following: + + a. distribute the binaries and library files of the software, + together with instructions (in the manual page or equivalent) + on where to get the original distribution. + + b. accompany the distribution with the machine-readable source of + the software. + + c. give non-standard binaries non-standard names, with + instructions on where to get the original software distribution. + + d. make other distribution arrangements with the author. + +4. You may modify and include the part of the software into any other + software (possibly commercial). But some files in the distribution + are not written by the author, so that they are not under these terms. + + For the list of those files and their copying conditions, see the + file LEGAL. + +5. The scripts and library files supplied as input to or produced as + output from the software do not automatically fall under the + copyright of the software, but belong to whomever generated them, + and may be sold commercially, and may be aggregated with this + software. + +6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE. diff --git a/vendor/bundle/ruby/3.3.0/gems/logger-1.7.0/README.md b/vendor/bundle/ruby/3.3.0/gems/logger-1.7.0/README.md new file mode 100644 index 0000000..ceb1a21 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/logger-1.7.0/README.md @@ -0,0 +1,104 @@ +# Logger + +Logger is a simple but powerful logging utility to output messages in your Ruby program. + +Logger has the following features: + + * Print messages to different levels such as `info` and `error` + * Auto-rolling of log files + * Setting the format of log messages + * Specifying a program name in conjunction with the message + +## Installation + +Add this line to your application's Gemfile: + +```ruby +gem 'logger' +``` + +And then execute: + + $ bundle + +Or install it yourself as: + + $ gem install logger + +## Usage + +### Simple Example + +```ruby +require 'logger' + +# Create a Logger that prints to STDOUT +log = Logger.new(STDOUT) +log.debug("Created Logger") + +log.info("Program finished") + +# Create a Logger that prints to STDERR +error_log = Logger.new(STDERR) +error_log = error_log.error("fatal error") +``` + +## Development + +After checking out the repo, run the following to install dependencies. + +``` +$ bin/setup +``` + +Then, run the tests as: + +``` +$ rake test +``` + +To install this gem onto your local machine, run + +``` +$ rake install +``` + +To release a new version, update the version number in `lib/logger/version.rb`, and then run + +``` +$ rake release +``` + +which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). + +## Advanced Development + +### Run tests of a specific file + +``` +$ ruby test/logger/test_logger.rb +``` + +### Run tests filtering test methods by a name + +`--name` option is available as: + +``` +$ ruby test/logger/test_logger.rb --name test_lshift +``` + +### Publish documents to GitHub Pages + +``` +$ rake gh-pages +``` + +Then, git commit and push the generated HTMLs onto `gh-pages` branch. + +## Contributing + +Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/logger. + +## License + +The gem is available as open source under the terms of the [BSD-2-Clause](BSDL). diff --git a/vendor/bundle/ruby/3.3.0/gems/logger-1.7.0/lib/logger.rb b/vendor/bundle/ruby/3.3.0/gems/logger-1.7.0/lib/logger.rb new file mode 100644 index 0000000..4911a3f --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/logger-1.7.0/lib/logger.rb @@ -0,0 +1,789 @@ +# frozen_string_literal: true +# logger.rb - simple logging utility +# Copyright (C) 2000-2003, 2005, 2008, 2011 NAKAMURA, Hiroshi . +# +# Documentation:: NAKAMURA, Hiroshi and Gavin Sinclair +# License:: +# You can redistribute it and/or modify it under the same terms of Ruby's +# license; either the dual license version in 2003, or any later version. +# Revision:: $Id$ +# +# A simple system for logging messages. See Logger for more documentation. + +require 'fiber' +require 'monitor' +require 'rbconfig' + +require_relative 'logger/version' +require_relative 'logger/formatter' +require_relative 'logger/log_device' +require_relative 'logger/severity' +require_relative 'logger/errors' + +# \Class \Logger provides a simple but sophisticated logging utility that +# you can use to create one or more +# {event logs}[https://en.wikipedia.org/wiki/Logging_(software)#Event_logs] +# for your program. +# Each such log contains a chronological sequence of entries +# that provides a record of the program's activities. +# +# == About the Examples +# +# All examples on this page assume that \Logger has been required: +# +# require 'logger' +# +# == Synopsis +# +# Create a log with Logger.new: +# +# # Single log file. +# logger = Logger.new('t.log') +# # Size-based rotated logging: 3 10-megabyte files. +# logger = Logger.new('t.log', 3, 10485760) +# # Period-based rotated logging: daily (also allowed: 'weekly', 'monthly'). +# logger = Logger.new('t.log', 'daily') +# # Log to an IO stream. +# logger = Logger.new($stdout) +# +# Add entries (level, message) with Logger#add: +# +# logger.add(Logger::DEBUG, 'Maximal debugging info') +# logger.add(Logger::INFO, 'Non-error information') +# logger.add(Logger::WARN, 'Non-error warning') +# logger.add(Logger::ERROR, 'Non-fatal error') +# logger.add(Logger::FATAL, 'Fatal error') +# logger.add(Logger::UNKNOWN, 'Most severe') +# +# Close the log with Logger#close: +# +# logger.close +# +# == Entries +# +# You can add entries with method Logger#add: +# +# logger.add(Logger::DEBUG, 'Maximal debugging info') +# logger.add(Logger::INFO, 'Non-error information') +# logger.add(Logger::WARN, 'Non-error warning') +# logger.add(Logger::ERROR, 'Non-fatal error') +# logger.add(Logger::FATAL, 'Fatal error') +# logger.add(Logger::UNKNOWN, 'Most severe') +# +# These shorthand methods also add entries: +# +# logger.debug('Maximal debugging info') +# logger.info('Non-error information') +# logger.warn('Non-error warning') +# logger.error('Non-fatal error') +# logger.fatal('Fatal error') +# logger.unknown('Most severe') +# +# When you call any of these methods, +# the entry may or may not be written to the log, +# depending on the entry's severity and on the log level; +# see {Log Level}[rdoc-ref:Logger@Log+Level] +# +# An entry always has: +# +# - A severity (the required argument to #add). +# - An automatically created timestamp. +# +# And may also have: +# +# - A message. +# - A program name. +# +# Example: +# +# logger = Logger.new($stdout) +# logger.add(Logger::INFO, 'My message.', 'mung') +# # => I, [2022-05-07T17:21:46.536234 #20536] INFO -- mung: My message. +# +# The default format for an entry is: +# +# "%s, [%s #%d] %5s -- %s: %s\n" +# +# where the values to be formatted are: +# +# - \Severity (one letter). +# - Timestamp. +# - Process id. +# - \Severity (word). +# - Program name. +# - Message. +# +# You can use a different entry format by: +# +# - Setting a custom format proc (affects following entries); +# see {formatter=}[Logger.html#attribute-i-formatter]. +# - Calling any of the methods above with a block +# (affects only the one entry). +# Doing so can have two benefits: +# +# - Context: the block can evaluate the entire program context +# and create a context-dependent message. +# - Performance: the block is not evaluated unless the log level +# permits the entry actually to be written: +# +# logger.error { my_slow_message_generator } +# +# Contrast this with the string form, where the string is +# always evaluated, regardless of the log level: +# +# logger.error("#{my_slow_message_generator}") +# +# === \Severity +# +# The severity of a log entry has two effects: +# +# - Determines whether the entry is selected for inclusion in the log; +# see {Log Level}[rdoc-ref:Logger@Log+Level]. +# - Indicates to any log reader (whether a person or a program) +# the relative importance of the entry. +# +# === Timestamp +# +# The timestamp for a log entry is generated automatically +# when the entry is created. +# +# The logged timestamp is formatted by method +# {Time#strftime}[https://docs.ruby-lang.org/en/master/Time.html#method-i-strftime] +# using this format string: +# +# '%Y-%m-%dT%H:%M:%S.%6N' +# +# Example: +# +# logger = Logger.new($stdout) +# logger.add(Logger::INFO) +# # => I, [2022-05-07T17:04:32.318331 #20536] INFO -- : nil +# +# You can set a different format using method #datetime_format=. +# +# === Message +# +# The message is an optional argument to an entry method: +# +# logger = Logger.new($stdout) +# logger.add(Logger::INFO, 'My message') +# # => I, [2022-05-07T18:15:37.647581 #20536] INFO -- : My message +# +# For the default entry formatter, Logger::Formatter, +# the message object may be: +# +# - A string: used as-is. +# - An Exception: message.message is used. +# - Anything else: message.inspect is used. +# +# *Note*: Logger::Formatter does not escape or sanitize +# the message passed to it. +# Developers should be aware that malicious data (user input) +# may be in the message, and should explicitly escape untrusted data. +# +# You can use a custom formatter to escape message data; +# see the example at {formatter=}[Logger.html#attribute-i-formatter]. +# +# === Program Name +# +# The program name is an optional argument to an entry method: +# +# logger = Logger.new($stdout) +# logger.add(Logger::INFO, 'My message', 'mung') +# # => I, [2022-05-07T18:17:38.084716 #20536] INFO -- mung: My message +# +# The default program name for a new logger may be set in the call to +# Logger.new via optional keyword argument +progname+: +# +# logger = Logger.new('t.log', progname: 'mung') +# +# The default program name for an existing logger may be set +# by a call to method #progname=: +# +# logger.progname = 'mung' +# +# The current program name may be retrieved with method +# {progname}[Logger.html#attribute-i-progname]: +# +# logger.progname # => "mung" +# +# == Log Level +# +# The log level setting determines whether an entry is actually +# written to the log, based on the entry's severity. +# +# These are the defined severities (least severe to most severe): +# +# logger = Logger.new($stdout) +# logger.add(Logger::DEBUG, 'Maximal debugging info') +# # => D, [2022-05-07T17:57:41.776220 #20536] DEBUG -- : Maximal debugging info +# logger.add(Logger::INFO, 'Non-error information') +# # => I, [2022-05-07T17:59:14.349167 #20536] INFO -- : Non-error information +# logger.add(Logger::WARN, 'Non-error warning') +# # => W, [2022-05-07T18:00:45.337538 #20536] WARN -- : Non-error warning +# logger.add(Logger::ERROR, 'Non-fatal error') +# # => E, [2022-05-07T18:02:41.592912 #20536] ERROR -- : Non-fatal error +# logger.add(Logger::FATAL, 'Fatal error') +# # => F, [2022-05-07T18:05:24.703931 #20536] FATAL -- : Fatal error +# logger.add(Logger::UNKNOWN, 'Most severe') +# # => A, [2022-05-07T18:07:54.657491 #20536] ANY -- : Most severe +# +# The default initial level setting is Logger::DEBUG, the lowest level, +# which means that all entries are to be written, regardless of severity: +# +# logger = Logger.new($stdout) +# logger.level # => 0 +# logger.add(0, "My message") +# # => D, [2022-05-11T15:10:59.773668 #20536] DEBUG -- : My message +# +# You can specify a different setting in a new logger +# using keyword argument +level+ with an appropriate value: +# +# logger = Logger.new($stdout, level: Logger::ERROR) +# logger = Logger.new($stdout, level: 'error') +# logger = Logger.new($stdout, level: :error) +# logger.level # => 3 +# +# With this level, entries with severity Logger::ERROR and higher +# are written, while those with lower severities are not written: +# +# logger = Logger.new($stdout, level: Logger::ERROR) +# logger.add(3) +# # => E, [2022-05-11T15:17:20.933362 #20536] ERROR -- : nil +# logger.add(2) # Silent. +# +# You can set the log level for an existing logger +# with method #level=: +# +# logger.level = Logger::ERROR +# +# These shorthand methods also set the level: +# +# logger.debug! # => 0 +# logger.info! # => 1 +# logger.warn! # => 2 +# logger.error! # => 3 +# logger.fatal! # => 4 +# +# You can retrieve the log level with method #level. +# +# logger.level = Logger::ERROR +# logger.level # => 3 +# +# These methods return whether a given +# level is to be written: +# +# logger.level = Logger::ERROR +# logger.debug? # => false +# logger.info? # => false +# logger.warn? # => false +# logger.error? # => true +# logger.fatal? # => true +# +# == Log File Rotation +# +# By default, a log file is a single file that grows indefinitely +# (until explicitly closed); there is no file rotation. +# +# To keep log files to a manageable size, +# you can use _log_ _file_ _rotation_, which uses multiple log files: +# +# - Each log file has entries for a non-overlapping +# time interval. +# - Only the most recent log file is open and active; +# the others are closed and inactive. +# +# === Size-Based Rotation +# +# For size-based log file rotation, call Logger.new with: +# +# - Argument +logdev+ as a file path. +# - Argument +shift_age+ with a positive integer: +# the number of log files to be in the rotation. +# - Argument +shift_size+ as a positive integer: +# the maximum size (in bytes) of each log file; +# defaults to 1048576 (1 megabyte). +# +# Examples: +# +# logger = Logger.new('t.log', 3) # Three 1-megabyte files. +# logger = Logger.new('t.log', 5, 10485760) # Five 10-megabyte files. +# +# For these examples, suppose: +# +# logger = Logger.new('t.log', 3) +# +# Logging begins in the new log file, +t.log+; +# the log file is "full" and ready for rotation +# when a new entry would cause its size to exceed +shift_size+. +# +# The first time +t.log+ is full: +# +# - +t.log+ is closed and renamed to +t.log.0+. +# - A new file +t.log+ is opened. +# +# The second time +t.log+ is full: +# +# - +t.log.0 is renamed as +t.log.1+. +# - +t.log+ is closed and renamed to +t.log.0+. +# - A new file +t.log+ is opened. +# +# Each subsequent time that +t.log+ is full, +# the log files are rotated: +# +# - +t.log.1+ is removed. +# - +t.log.0 is renamed as +t.log.1+. +# - +t.log+ is closed and renamed to +t.log.0+. +# - A new file +t.log+ is opened. +# +# === Periodic Rotation +# +# For periodic rotation, call Logger.new with: +# +# - Argument +logdev+ as a file path. +# - Argument +shift_age+ as a string period indicator. +# +# Examples: +# +# logger = Logger.new('t.log', 'daily') # Rotate log files daily. +# logger = Logger.new('t.log', 'weekly') # Rotate log files weekly. +# logger = Logger.new('t.log', 'monthly') # Rotate log files monthly. +# +# Example: +# +# logger = Logger.new('t.log', 'daily') +# +# When the given period expires: +# +# - The base log file, +t.log+ is closed and renamed +# with a date-based suffix such as +t.log.20220509+. +# - A new log file +t.log+ is opened. +# - Nothing is removed. +# +# The default format for the suffix is '%Y%m%d', +# which produces a suffix similar to the one above. +# You can set a different format using create-time option +# +shift_period_suffix+; +# see details and suggestions at +# {Time#strftime}[https://docs.ruby-lang.org/en/master/Time.html#method-i-strftime]. +# +class Logger + _, name, rev = %w$Id$ + if name + name = name.chomp(",v") + else + name = File.basename(__FILE__) + end + rev ||= "v#{VERSION}" + ProgName = "#{name}/#{rev}" + + include Severity + + # Logging severity threshold (e.g. Logger::INFO). + def level + level_override[level_key] || @level + end + + # Sets the log level; returns +severity+. + # See {Log Level}[rdoc-ref:Logger@Log+Level]. + # + # Argument +severity+ may be an integer, a string, or a symbol: + # + # logger.level = Logger::ERROR # => 3 + # logger.level = 3 # => 3 + # logger.level = 'error' # => "error" + # logger.level = :error # => :error + # + # Logger#sev_threshold= is an alias for Logger#level=. + # + def level=(severity) + @level = Severity.coerce(severity) + end + + # Adjust the log level during the block execution for the current Fiber only + # + # logger.with_level(:debug) do + # logger.debug { "Hello" } + # end + def with_level(severity) + prev, level_override[level_key] = level, Severity.coerce(severity) + begin + yield + ensure + if prev + level_override[level_key] = prev + else + level_override.delete(level_key) + end + end + end + + # Program name to include in log messages. + attr_accessor :progname + + # Sets the date-time format. + # + # Argument +datetime_format+ should be either of these: + # + # - A string suitable for use as a format for method + # {Time#strftime}[https://docs.ruby-lang.org/en/master/Time.html#method-i-strftime]. + # - +nil+: the logger uses '%Y-%m-%dT%H:%M:%S.%6N'. + # + def datetime_format=(datetime_format) + @default_formatter.datetime_format = datetime_format + end + + # Returns the date-time format; see #datetime_format=. + # + def datetime_format + @default_formatter.datetime_format + end + + # Sets or retrieves the logger entry formatter proc. + # + # When +formatter+ is +nil+, the logger uses Logger::Formatter. + # + # When +formatter+ is a proc, a new entry is formatted by the proc, + # which is called with four arguments: + # + # - +severity+: The severity of the entry. + # - +time+: A Time object representing the entry's timestamp. + # - +progname+: The program name for the entry. + # - +msg+: The message for the entry (string or string-convertible object). + # + # The proc should return a string containing the formatted entry. + # + # This custom formatter uses + # {String#dump}[https://docs.ruby-lang.org/en/master/String.html#method-i-dump] + # to escape the message string: + # + # logger = Logger.new($stdout, progname: 'mung') + # original_formatter = logger.formatter || Logger::Formatter.new + # logger.formatter = proc { |severity, time, progname, msg| + # original_formatter.call(severity, time, progname, msg.dump) + # } + # logger.add(Logger::INFO, "hello \n ''") + # logger.add(Logger::INFO, "\f\x00\xff\\\"") + # + # Output: + # + # I, [2022-05-13T13:16:29.637488 #8492] INFO -- mung: "hello \n ''" + # I, [2022-05-13T13:16:29.637610 #8492] INFO -- mung: "\f\x00\xFF\\\"" + # + attr_accessor :formatter + + alias sev_threshold level + alias sev_threshold= level= + + # Returns +true+ if the log level allows entries with severity + # Logger::DEBUG to be written, +false+ otherwise. + # See {Log Level}[rdoc-ref:Logger@Log+Level]. + # + def debug?; level <= DEBUG; end + + # Sets the log level to Logger::DEBUG. + # See {Log Level}[rdoc-ref:Logger@Log+Level]. + # + def debug!; self.level = DEBUG; end + + # Returns +true+ if the log level allows entries with severity + # Logger::INFO to be written, +false+ otherwise. + # See {Log Level}[rdoc-ref:Logger@Log+Level]. + # + def info?; level <= INFO; end + + # Sets the log level to Logger::INFO. + # See {Log Level}[rdoc-ref:Logger@Log+Level]. + # + def info!; self.level = INFO; end + + # Returns +true+ if the log level allows entries with severity + # Logger::WARN to be written, +false+ otherwise. + # See {Log Level}[rdoc-ref:Logger@Log+Level]. + # + def warn?; level <= WARN; end + + # Sets the log level to Logger::WARN. + # See {Log Level}[rdoc-ref:Logger@Log+Level]. + # + def warn!; self.level = WARN; end + + # Returns +true+ if the log level allows entries with severity + # Logger::ERROR to be written, +false+ otherwise. + # See {Log Level}[rdoc-ref:Logger@Log+Level]. + # + def error?; level <= ERROR; end + + # Sets the log level to Logger::ERROR. + # See {Log Level}[rdoc-ref:Logger@Log+Level]. + # + def error!; self.level = ERROR; end + + # Returns +true+ if the log level allows entries with severity + # Logger::FATAL to be written, +false+ otherwise. + # See {Log Level}[rdoc-ref:Logger@Log+Level]. + # + def fatal?; level <= FATAL; end + + # Sets the log level to Logger::FATAL. + # See {Log Level}[rdoc-ref:Logger@Log+Level]. + # + def fatal!; self.level = FATAL; end + + # :call-seq: + # Logger.new(logdev, shift_age = 0, shift_size = 1048576, **options) + # + # With the single argument +logdev+, + # returns a new logger with all default options: + # + # Logger.new('t.log') # => # + # + # Argument +logdev+ must be one of: + # + # - A string filepath: entries are to be written + # to the file at that path; if the file at that path exists, + # new entries are appended. + # - An IO stream (typically $stdout, $stderr. or + # an open file): entries are to be written to the given stream. + # - +nil+ or +File::NULL+: no entries are to be written. + # + # Argument +shift_age+ must be one of: + # + # - The number of log files to be in the rotation. + # See {Size-Based Rotation}[rdoc-ref:Logger@Size-Based+Rotation]. + # - A string period indicator. + # See {Periodic Rotation}[rdoc-ref:Logger@Periodic+Rotation]. + # + # Argument +shift_size+ is the maximum size (in bytes) of each log file. + # See {Size-Based Rotation}[rdoc-ref:Logger@Size-Based+Rotation]. + # + # Examples: + # + # Logger.new('t.log') + # Logger.new($stdout) + # + # The keyword options are: + # + # - +level+: sets the log level; default value is Logger::DEBUG. + # See {Log Level}[rdoc-ref:Logger@Log+Level]: + # + # Logger.new('t.log', level: Logger::ERROR) + # + # - +progname+: sets the default program name; default is +nil+. + # See {Program Name}[rdoc-ref:Logger@Program+Name]: + # + # Logger.new('t.log', progname: 'mung') + # + # - +formatter+: sets the entry formatter; default is +nil+. + # See {formatter=}[Logger.html#attribute-i-formatter]. + # + # - +datetime_format+: sets the format for entry timestamp; + # default is +nil+. + # See #datetime_format=. + # + # - +binmode+: sets whether the logger writes in binary mode; + # default is +false+. + # + # - +shift_period_suffix+: sets the format for the filename suffix + # for periodic log file rotation; default is '%Y%m%d'. + # See {Periodic Rotation}[rdoc-ref:Logger@Periodic+Rotation]. + # + # - +reraise_write_errors+: An array of exception classes, which will + # be reraised if there is an error when writing to the log device. + # The default is to swallow all exceptions raised. + # - +skip_header+: If +true+, prevents the logger from writing a header + # when creating a new log file. The default is +false+, meaning + # the header will be written as usual. + # + def initialize(logdev, shift_age = 0, shift_size = 1048576, level: DEBUG, + progname: nil, formatter: nil, datetime_format: nil, + binmode: false, shift_period_suffix: '%Y%m%d', + reraise_write_errors: [], skip_header: false) + self.level = level + self.progname = progname + @default_formatter = Formatter.new + self.datetime_format = datetime_format + self.formatter = formatter + @logdev = nil + @level_override = {} + if logdev && logdev != File::NULL + @logdev = LogDevice.new(logdev, shift_age: shift_age, + shift_size: shift_size, + shift_period_suffix: shift_period_suffix, + binmode: binmode, + reraise_write_errors: reraise_write_errors, + skip_header: skip_header) + end + end + + # Sets the logger's output stream: + # + # - If +logdev+ is +nil+, reopens the current output stream. + # - If +logdev+ is a filepath, opens the indicated file for append. + # - If +logdev+ is an IO stream + # (usually $stdout, $stderr, or an open File object), + # opens the stream for append. + # + # Example: + # + # logger = Logger.new('t.log') + # logger.add(Logger::ERROR, 'one') + # logger.close + # logger.add(Logger::ERROR, 'two') # Prints 'log writing failed. closed stream' + # logger.reopen + # logger.add(Logger::ERROR, 'three') + # logger.close + # File.readlines('t.log') + # # => + # # ["# Logfile created on 2022-05-12 14:21:19 -0500 by logger.rb/v1.5.0\n", + # # "E, [2022-05-12T14:21:27.596726 #22428] ERROR -- : one\n", + # # "E, [2022-05-12T14:23:05.847241 #22428] ERROR -- : three\n"] + # + def reopen(logdev = nil, shift_age = nil, shift_size = nil, shift_period_suffix: nil, binmode: nil) + @logdev&.reopen(logdev, shift_age: shift_age, shift_size: shift_size, + shift_period_suffix: shift_period_suffix, binmode: binmode) + self + end + + # Creates a log entry, which may or may not be written to the log, + # depending on the entry's severity and on the log level. + # See {Log Level}[rdoc-ref:Logger@Log+Level] + # and {Entries}[rdoc-ref:Logger@Entries] for details. + # + # Examples: + # + # logger = Logger.new($stdout, progname: 'mung') + # logger.add(Logger::INFO) + # logger.add(Logger::ERROR, 'No good') + # logger.add(Logger::ERROR, 'No good', 'gnum') + # + # Output: + # + # I, [2022-05-12T16:25:31.469726 #36328] INFO -- mung: mung + # E, [2022-05-12T16:25:55.349414 #36328] ERROR -- mung: No good + # E, [2022-05-12T16:26:35.841134 #36328] ERROR -- gnum: No good + # + # These convenience methods have implicit severity: + # + # - #debug. + # - #info. + # - #warn. + # - #error. + # - #fatal. + # - #unknown. + # + def add(severity, message = nil, progname = nil) + severity ||= UNKNOWN + if @logdev.nil? or severity < level + return true + end + if progname.nil? + progname = @progname + end + if message.nil? + if block_given? + message = yield + else + message = progname + progname = @progname + end + end + @logdev.write( + format_message(format_severity(severity), Time.now, progname, message)) + true + end + alias log add + + # Writes the given +msg+ to the log with no formatting; + # returns the number of characters written, + # or +nil+ if no log device exists: + # + # logger = Logger.new($stdout) + # logger << 'My message.' # => 10 + # + # Output: + # + # My message. + # + def <<(msg) + @logdev&.write(msg) + end + + # Equivalent to calling #add with severity Logger::DEBUG. + # + def debug(progname = nil, &block) + add(DEBUG, nil, progname, &block) + end + + # Equivalent to calling #add with severity Logger::INFO. + # + def info(progname = nil, &block) + add(INFO, nil, progname, &block) + end + + # Equivalent to calling #add with severity Logger::WARN. + # + def warn(progname = nil, &block) + add(WARN, nil, progname, &block) + end + + # Equivalent to calling #add with severity Logger::ERROR. + # + def error(progname = nil, &block) + add(ERROR, nil, progname, &block) + end + + # Equivalent to calling #add with severity Logger::FATAL. + # + def fatal(progname = nil, &block) + add(FATAL, nil, progname, &block) + end + + # Equivalent to calling #add with severity Logger::UNKNOWN. + # + def unknown(progname = nil, &block) + add(UNKNOWN, nil, progname, &block) + end + + # Closes the logger; returns +nil+: + # + # logger = Logger.new('t.log') + # logger.close # => nil + # logger.info('foo') # Prints "log writing failed. closed stream" + # + # Related: Logger#reopen. + def close + @logdev&.close + end + +private + + # \Severity label for logging (max 5 chars). + SEV_LABEL = %w(DEBUG INFO WARN ERROR FATAL ANY).freeze + + def format_severity(severity) + SEV_LABEL[severity] || 'ANY' + end + + # Guarantee the existence of this ivar even when subclasses don't call the superclass constructor. + def level_override + unless defined?(@level_override) + bad = self.class.instance_method(:initialize) + file, line = bad.source_location + Kernel.warn <<~";;;", uplevel: 2 + Logger not initialized properly + #{file}:#{line}: info: #{bad.owner}\##{bad.name}: \ + does not call super probably + ;;; + end + @level_override ||= {} + end + + def level_key + Fiber.current + end + + def format_message(severity, datetime, progname, msg) + (@formatter || @default_formatter).call(severity, datetime, progname, msg) + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/logger-1.7.0/lib/logger/errors.rb b/vendor/bundle/ruby/3.3.0/gems/logger-1.7.0/lib/logger/errors.rb new file mode 100644 index 0000000..8858179 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/logger-1.7.0/lib/logger/errors.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class Logger + # not used after 1.2.7. just for compat. + class Error < RuntimeError # :nodoc: + end + class ShiftingError < Error # :nodoc: + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/logger-1.7.0/lib/logger/formatter.rb b/vendor/bundle/ruby/3.3.0/gems/logger-1.7.0/lib/logger/formatter.rb new file mode 100644 index 0000000..c634dbf --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/logger-1.7.0/lib/logger/formatter.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +class Logger + # Default formatter for log messages. + class Formatter + Format = "%.1s, [%s #%d] %5s -- %s: %s\n" + DatetimeFormat = "%Y-%m-%dT%H:%M:%S.%6N" + + attr_accessor :datetime_format + + def initialize + @datetime_format = nil + end + + def call(severity, time, progname, msg) + sprintf(Format, severity, format_datetime(time), Process.pid, severity, progname, msg2str(msg)) + end + + private + + def format_datetime(time) + time.strftime(@datetime_format || DatetimeFormat) + end + + def msg2str(msg) + case msg + when ::String + msg + when ::Exception + "#{ msg.message } (#{ msg.class })\n#{ msg.backtrace.join("\n") if msg.backtrace }" + else + msg.inspect + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/logger-1.7.0/lib/logger/log_device.rb b/vendor/bundle/ruby/3.3.0/gems/logger-1.7.0/lib/logger/log_device.rb new file mode 100644 index 0000000..e16f3b7 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/logger-1.7.0/lib/logger/log_device.rb @@ -0,0 +1,265 @@ +# frozen_string_literal: true + +require_relative 'period' + +class Logger + # Device used for logging messages. + class LogDevice + include Period + + attr_reader :dev + attr_reader :filename + include MonitorMixin + + def initialize( + log = nil, shift_age: nil, shift_size: nil, shift_period_suffix: nil, + binmode: false, reraise_write_errors: [], skip_header: false + ) + @dev = @filename = @shift_age = @shift_size = @shift_period_suffix = nil + @binmode = binmode + @reraise_write_errors = reraise_write_errors + @skip_header = skip_header + mon_initialize + set_dev(log) + set_file(shift_age, shift_size, shift_period_suffix) if @filename + end + + def write(message) + handle_write_errors("writing") do + synchronize do + if @shift_age and @dev.respond_to?(:stat) + handle_write_errors("shifting") {check_shift_log} + end + handle_write_errors("writing") {@dev.write(message)} + end + end + end + + def close + begin + synchronize do + @dev.close rescue nil + end + rescue Exception + @dev.close rescue nil + end + end + + def reopen(log = nil, shift_age: nil, shift_size: nil, shift_period_suffix: nil, binmode: nil) + # reopen the same filename if no argument, do nothing for IO + log ||= @filename if @filename + @binmode = binmode unless binmode.nil? + if log + synchronize do + if @filename and @dev + @dev.close rescue nil # close only file opened by Logger + @filename = nil + end + set_dev(log) + set_file(shift_age, shift_size, shift_period_suffix) if @filename + end + end + self + end + + private + + # :stopdoc: + + MODE = File::WRONLY | File::APPEND + # TruffleRuby < 24.2 does not have File::SHARE_DELETE + if File.const_defined? :SHARE_DELETE + MODE_TO_OPEN = MODE | File::SHARE_DELETE | File::BINARY + else + MODE_TO_OPEN = MODE | File::BINARY + end + MODE_TO_CREATE = MODE_TO_OPEN | File::CREAT | File::EXCL + + def set_dev(log) + if log.respond_to?(:write) and log.respond_to?(:close) + @dev = log + if log.respond_to?(:path) and path = log.path + if File.exist?(path) + @filename = path + end + end + else + @dev = open_logfile(log) + @filename = log + end + end + + def set_file(shift_age, shift_size, shift_period_suffix) + @shift_age = shift_age || @shift_age || 7 + @shift_size = shift_size || @shift_size || 1048576 + @shift_period_suffix = shift_period_suffix || @shift_period_suffix || '%Y%m%d' + + unless @shift_age.is_a?(Integer) + base_time = @dev.respond_to?(:stat) ? @dev.stat.mtime : Time.now + @next_rotate_time = next_rotate_time(base_time, @shift_age) + end + end + + if MODE_TO_OPEN == MODE + def fixup_mode(dev) + dev + end + else + def fixup_mode(dev) + return dev if @binmode + dev.autoclose = false + old_dev = dev + dev = File.new(dev.fileno, mode: MODE, path: dev.path) + old_dev.close + PathAttr.set_path(dev, filename) if defined?(PathAttr) + dev + end + end + + def open_logfile(filename) + begin + dev = File.open(filename, MODE_TO_OPEN) + rescue Errno::ENOENT + create_logfile(filename) + else + dev = fixup_mode(dev) + dev.sync = true + dev.binmode if @binmode + dev + end + end + + def create_logfile(filename) + begin + logdev = File.open(filename, MODE_TO_CREATE) + logdev.flock(File::LOCK_EX) + logdev = fixup_mode(logdev) + logdev.sync = true + logdev.binmode if @binmode + add_log_header(logdev) unless @skip_header + logdev.flock(File::LOCK_UN) + logdev + rescue Errno::EEXIST + # file is created by another process + open_logfile(filename) + end + end + + def handle_write_errors(mesg) + yield + rescue *@reraise_write_errors + raise + rescue + warn("log #{mesg} failed. #{$!}") + end + + def add_log_header(file) + file.write( + "# Logfile created on %s by %s\n" % [Time.now.to_s, Logger::ProgName] + ) if file.size == 0 + end + + def check_shift_log + if @shift_age.is_a?(Integer) + # Note: always returns false if '0'. + if @filename && (@shift_age > 0) && (@dev.stat.size > @shift_size) + lock_shift_log { shift_log_age } + end + else + now = Time.now + if now >= @next_rotate_time + @next_rotate_time = next_rotate_time(now, @shift_age) + lock_shift_log { shift_log_period(previous_period_end(now, @shift_age)) } + end + end + end + + def lock_shift_log + retry_limit = 8 + retry_sleep = 0.1 + begin + File.open(@filename, MODE_TO_OPEN) do |lock| + lock.flock(File::LOCK_EX) # inter-process locking. will be unlocked at closing file + if File.identical?(@filename, lock) and File.identical?(lock, @dev) + yield # log shifting + else + # log shifted by another process (i-node before locking and i-node after locking are different) + @dev.close rescue nil + @dev = open_logfile(@filename) + end + end + true + rescue Errno::ENOENT + # @filename file would not exist right after #rename and before #create_logfile + if retry_limit <= 0 + warn("log rotation inter-process lock failed. #{$!}") + else + sleep retry_sleep + retry_limit -= 1 + retry_sleep *= 2 + retry + end + end + rescue + warn("log rotation inter-process lock failed. #{$!}") + end + + def shift_log_age + (@shift_age-3).downto(0) do |i| + if FileTest.exist?("#{@filename}.#{i}") + File.rename("#{@filename}.#{i}", "#{@filename}.#{i+1}") + end + end + shift_log_file("#{@filename}.0") + end + + def shift_log_period(period_end) + suffix = period_end.strftime(@shift_period_suffix) + age_file = "#{@filename}.#{suffix}" + if FileTest.exist?(age_file) + # try to avoid filename crash caused by Timestamp change. + idx = 0 + # .99 can be overridden; avoid too much file search with 'loop do' + while idx < 100 + idx += 1 + age_file = "#{@filename}.#{suffix}.#{idx}" + break unless FileTest.exist?(age_file) + end + end + shift_log_file(age_file) + end + + def shift_log_file(shifted) + stat = @dev.stat + @dev.close rescue nil + File.rename(@filename, shifted) + @dev = create_logfile(@filename) + mode, uid, gid = stat.mode, stat.uid, stat.gid + begin + @dev.chmod(mode) if mode + mode = nil + @dev.chown(uid, gid) + rescue Errno::EPERM + if mode + # failed to chmod, probably nothing can do more. + elsif uid + uid = nil + retry # to change gid only + end + end + return true + end + end +end + +File.open(__FILE__) do |f| + File.new(f.fileno, autoclose: false, path: "").path +rescue IOError + module PathAttr # :nodoc: + attr_reader :path + + def self.set_path(file, path) + file.extend(self).instance_variable_set(:@path, path) + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/logger-1.7.0/lib/logger/period.rb b/vendor/bundle/ruby/3.3.0/gems/logger-1.7.0/lib/logger/period.rb new file mode 100644 index 0000000..a0359de --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/logger-1.7.0/lib/logger/period.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +class Logger + module Period + module_function + + SiD = 24 * 60 * 60 + + def next_rotate_time(now, shift_age) + case shift_age + when 'daily', :daily + t = Time.mktime(now.year, now.month, now.mday) + SiD + when 'weekly', :weekly + t = Time.mktime(now.year, now.month, now.mday) + SiD * (7 - now.wday) + when 'monthly', :monthly + t = Time.mktime(now.year, now.month, 1) + SiD * 32 + return Time.mktime(t.year, t.month, 1) + when 'now', 'everytime', :now, :everytime + return now + else + raise ArgumentError, "invalid :shift_age #{shift_age.inspect}, should be daily, weekly, monthly, or everytime" + end + if t.hour.nonzero? or t.min.nonzero? or t.sec.nonzero? + hour = t.hour + t = Time.mktime(t.year, t.month, t.mday) + t += SiD if hour > 12 + end + t + end + + def previous_period_end(now, shift_age) + case shift_age + when 'daily', :daily + t = Time.mktime(now.year, now.month, now.mday) - SiD / 2 + when 'weekly', :weekly + t = Time.mktime(now.year, now.month, now.mday) - (SiD * now.wday + SiD / 2) + when 'monthly', :monthly + t = Time.mktime(now.year, now.month, 1) - SiD / 2 + when 'now', 'everytime', :now, :everytime + return now + else + raise ArgumentError, "invalid :shift_age #{shift_age.inspect}, should be daily, weekly, monthly, or everytime" + end + Time.mktime(t.year, t.month, t.mday, 23, 59, 59) + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/logger-1.7.0/lib/logger/severity.rb b/vendor/bundle/ruby/3.3.0/gems/logger-1.7.0/lib/logger/severity.rb new file mode 100644 index 0000000..e96fb0d --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/logger-1.7.0/lib/logger/severity.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +class Logger + # Logging severity. + module Severity + # Low-level information, mostly for developers. + DEBUG = 0 + # Generic (useful) information about system operation. + INFO = 1 + # A warning. + WARN = 2 + # A handleable error condition. + ERROR = 3 + # An unhandleable error that results in a program crash. + FATAL = 4 + # An unknown message that should always be logged. + UNKNOWN = 5 + + LEVELS = { + "debug" => DEBUG, + "info" => INFO, + "warn" => WARN, + "error" => ERROR, + "fatal" => FATAL, + "unknown" => UNKNOWN, + } + private_constant :LEVELS + + def self.coerce(severity) + if severity.is_a?(Integer) + severity + else + key = severity.to_s.downcase + LEVELS[key] || raise(ArgumentError, "invalid log level: #{severity}") + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/logger-1.7.0/lib/logger/version.rb b/vendor/bundle/ruby/3.3.0/gems/logger-1.7.0/lib/logger/version.rb new file mode 100644 index 0000000..0d74bec --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/logger-1.7.0/lib/logger/version.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +class Logger + VERSION = "1.7.0" +end diff --git a/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/History.rdoc b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/History.rdoc new file mode 100644 index 0000000..ed536c2 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/History.rdoc @@ -0,0 +1,1822 @@ +=== 6.0.1 / 2025-12-26 + +* 1 minor enhancement: + + * Added new rake task `test:fu` to Minitest::TestTask, to only run + tests with FU (focused units?) in their name. This should mostly + obviate the need for the minitest-focus plugin. + +* 5 bug fixes: + + * Fixed --help and --version exiting 1. (grosser) + * Fixed method signature of Minitest::Benchmark.run. (flavorjones) + * Flush stdout/stderr before exit./bin/minitest -Ilib ./bug1046.rb:1 < /dev/null (grosser) + * Improved usage banner output by reordering and reformatting. + * Normalize paths while processing file.rb:line args. + +=== 6.0.0 / 2025-12-17 + +This is a major release. Read this. + +Please give feedback here: https://github.com/minitest/minitest/issues/1040 + +Oh god... here we go... (again) + +* 8 deaths in the family(!!): + + * Deleted MiniTest and MiniTest::Unit::TestCase compatibility namespaces. + * Deleted all use of Marshal for serialization. + * Deleted maglev? and rubinius? guard methods. LOL. + * Deleted all minitest/spec expectations from Object. Use _/value/expect. + * Dropped minitest/mock.rb. This has been extracted to the minitest-mock gem. + * assert_equal(nil, value) no longer allowed. Use assert_nil to be explicit. + * Removed assert_send. Use assert_predicate or assert_operator. + * Removed Minitest::Test#class_name. + +* 7 major (oft incompatible) changes: + + * Big: Major refactored MT6's run path! + * Minitest.__run -> Minitest.run_all_suites + * Runnable.run -> Runnable.run_suite & Runnable.filter_runnable_methods + * Runnable.run_one_method -> Runnable.run + * Removed Minitest.run_one_method (might bring it back to raise?) + * Removed deprecated ENV["N"] to specify number of parallel tests. Use MT_CPU. + * Renamed +options[:filter]+ to +options[:include]+, added --include cmdline option. + * --name is still handled, but that will be removed in the future. + * Renamed Minitest::Runnable#test_order to #run_order. + * If #message is passed a proc then that proc overrides all other output. + * They are no longer chained! + * And it does less for formatting of your output. + * Removed reporter arg from with_info_handler as it has never(?) been used. (HACK?) + * Plugin loading is now opt-in! + * Require the plugin in your test_helper. + * Or use Minitest.load :x + * Or call Minitest.load_plugins for the old autoloading behavior. + +* 5 additions: + + * Added minitest-sprint's minitest cmdline, now with line support! + * Added minitest-bisect. Run with minitest --bisect or -b. + * Added minitest-server. + * Added minitest/complete to help with shell's tab-completion of tests. + * Vendored latest version of path_expander. + +* 5 other: + + * Bumped to ruby 3.2+. + * Removed obsolete conditional for CompositeReporter#prerecord. + * Removed obsolete version guards around Minitest::Result in reporters. + * assert_operator and assert_predicate both call assert_respond_to first. + * Assertions reuse themselves a lot more. Bumps assertion count in some places. + +=== 5.27.0 / 2025-12-11 + +* 1 major enhancement: + + * Adding post install message announcing the EOL for minitest 5! + +* 2 minor enhancements: + + * Removed TestTask::Work#initialize since Queue can now initialize with an Enumerable! AMAZING! + * Use Kernel#warn uplevel argument for nicer warnings. (byroot) + +* 5 bug fixes: + + * Cleaned up option aliasing a tad. + * Removed obsolete conditional for prerecord + * Removed obsolete guards around Warning. + * Removed obsolete version guards for pattern matching assertions. + * Switched all internal requires to require_relative. + +=== 5.26.2 / 2025-11-17 + +* 5 bug fixes: + + * Bumped minimum ruby to 3.1. + * Alias Spec#name to #inspect for cleaner output in repls. + * Fix pathing for Hoe::Minitest initialization to be more generic. + * Fixed refute_in_epsilon to use min of abs values. (wtn) + * Improved options processing and usage output to be more clear. + +=== 5.26.1 / 2025-11-08 + +The Ocean Shores, Slightly Less Tipsy Edition! + +* 3 bug fixes: + + * Add links to API doco in README. + * Add missing require thread. + * Bumped ruby version to include 4.0 (trunk). (hsbt) + (see also 5.14.2) + +=== 5.26.0 / 2025-10-07 + +The Seattle.rb Nerd Party, Slightly Tipsy Edition! + +* 2 minor enhancements: + + * Added extra documentation to Minitest::TestTask options. + * Make parallelize_me! a no-op when n_threads=1. + +* 9 bug fixes: + + * Bypass parallel_executor entirely when n_threads=1. + * Don't require rubygems in Rakefile... it is 2025. + * Ensure that minitest exits non-zero on Interrupt. (tavianator) + * Fix Minitest.run sequence rdoc to include loop vars and read consistently. + * Fix call to parallel_executor.shutdown when it isn't defined. + * Removed some 1.8/1.9-based code from the assertions and expectations. + * Still fighting with rdoc? Yup. Still fighting with rdoc... + * Switched assert_equal's diff from Tempfile.open to Tempfile.create. + * Use Regexp.escape for BASE_RE in case pwd has special chars. (astra_1993) + +=== 5.25.5 / 2025-03-12 + +* 4 bug fixes: + + * Bumped minimum ruby to 2.7. + * Fixed expectation docs for must/wont_pattern_match. (jaredcwhite) + * Reorder Minitest::Test.ancestors to allow reaching Minitest::Assertions#skipped? (Edouard-chin) + * Update the ruby and rails compatibility tables. (bquorning) + +=== 5.25.4 / 2024-12-03 + +* 1 bug fix: + + * Fix for must_verify definition if only requiring minitest/mock (but why?). + +=== 5.25.3 / 2024-12-03 + +* 5 bug fixes: + + * Fixed assert_mock to fail instead of raise on unmet mock expectations. + * Fixed assert_mock to take an optional message argument. + * Fixed formatting of unmet mock expectation messages. + * Fixed missing must_verify expectation to match assert_mock. + * minitest/pride: Fixed to use true colors with *-direct terminals (bk2204) + +=== 5.25.2 / 2024-11-21 + +* 4 bug fixes: + + * Include class name in spec name. (thomasmarshall) + * Fixed 'redefining object_id' warning from ruby 3.4. (mattbrictson) + * Minitest top-level namespace no longer includes entire contents of README.rdoc. Too much! + * Refactored spec's describe to more cleanly determine the superclass and name + +=== 5.25.1 / 2024-08-16 + +* 2 bug fixes: + + * Fix incompatibility caused by minitest-hooks & rails invading minitest internals. + * Revert change from =~ to match? to allow for nil if $TERM undefined. + +=== 5.25.0 / 2024-08-13 + +* 2 minor enhancements: + + * Fixed some inefficiencies filtering and matching (mostly backtraces). + * Refactored siginfo handler to reduce runtime costs. Saved ~30%! + +* 5 bug fixes: + + * Added missing rdoc to get back to 100% coverage. + * Cleaning up ancient code checking for defined?(Encoding) and the like. + * Disambiguated some shadowed variables in minitest/compress. + * Fixed an ironic bug if using string-literals AND Werror. + * Improve description of test:slow task. (stomar) + +=== 5.24.1 / 2024-06-29 + +* 1 bug fix: + + * Fix the error message when an extension is invalid value. (y-yagi) + +=== 5.24.0 / 2024-06-18 + +* 2 minor enhancements: + + * Added Minitest.register_plugin. + * Extended plugin system to work with modules/classes for opt-out plugins. + +* 1 bug fix: + + * Removed anacronism, but allow load_plugins to exit gracefully if --disable=gems. + +=== 5.23.1 / 2024-05-21 + +* 1 bug fix: + + * Fully qualify the Queue class to avoid conflicts with other libraries. (rafaelfranca) + +=== 5.23.0 / 2024-05-15 + +* 3 minor enhancements: + + * Added -Werror to raise on any warning output. (byroot) + * Added UnexpectedWarning as a failure summary type, added count to output if activated. + * Added minitest/manual_plugins.rb w/ new Minitest.load method. (tenderlove) + +* 2 bug fixes: + + * Allow empty_run! and reporter to display summary for empty runs. (zzak) + * Make test task verbose using either rake's -v or -t (was just -t). + +=== 5.22.3 / 2024-03-13 + +* 1 minor enhancement: + + * MASSIVE improvement of minitest's pride plugin output: Frequencies doubled! Sine waves shifted!! Comments improved!!! Colors rotated!!!! (havenwood) + +* 3 bug fixes: + + * Improved wording on Minitest::Test#parallelize_me! to clarify it goes INSIDE your test class/describe. + * Minor changes to tests to pass when tests ran with extra flags (eg -p). + * Support Ruby 3.4's new error message format. (mame) + +=== 5.22.2 / 2024-02-07 + +* 1 bug fix: + + * Third time's a charm? Remember: 'ensure' is almost always the + wrong way to go (for results... it's great for cleaning up). + +=== 5.22.1 / 2024-02-06 + +* 1 bug fix: + + * Don't exit non-zero if no tests ran and no filter (aka, the test file is empty). + (I'm starting to think the exit 1 thing for @tenderlove was a mistake...) + +=== 5.22.0 / 2024-02-05 + +* 1 minor enhancement: + + * Added "did you mean" output if your --name filter matches nothing. (tenderlove) + +* 2 bug fixes: + + * Big cleanup of test filtering. Much prettier / more functional. + * Fix situation where Assertion#location can't find the location. (pftg) + +=== 5.21.2 / 2024-01-17 + +* 1 bug fix: + + * Fixed bug in Minitest::Compress#compress formatting w/ nested patterns. Now recurses properly. + +=== 5.21.1 / 2024-01-11 + +* 1 bug fix: + + * Rails' default backtrace filter can't currently work with caller_locations, so reverting back to caller. + +=== 5.21.0 / 2024-01-11 + +* 10 minor enhancements: + + * Add include_all kw arg to assert_respond_to and refute_respond_to. + * Added --quiet flag to skip ProgressReporter (prints the dots). Minor speedup. + * Added Minitest::Compress#compress and added it to UnexpectedError. + * Added ability to initialize BacktraceFilter w/ custom regexp. + * Filter failure backtraces using backtrace_filter before calculating location. (thomasmarshall) + * Make BacktraceFilter#filter compatible with locations (still compares strings). + * Optimized Assertion#location ~30%. + * Output relative paths for all failures/errors/backtraces. + * Refactored location information in assertions, now using locations. + * Removed thread and mutex_m dependencies. (hsbt, eregon) + +* 2 bug fixes: + + * Drop undocumented bt arg in #skip. Dunno why that ever happened, prolly for testing? + * Fix mock to work with ruby debugger enabled. (keithlayne) + +=== 5.20.0 / 2023-09-06 + +* 1 minor enhancement: + + * Optionally allow autorun exit hook to remain active in forked child. (casperisfine) + +=== 5.19.0 / 2023-07-26 + +* 2 minor enhancements: + + * Add metadata lazy accessor to Runnable / Result. (matteeyah) + * Only load minitest/unit (aka ancient MiniTest compatibility layer) if \ENV[\"MT_COMPAT\"] + +* 1 bug fix: + + * Minitest::TestTask enthusiastically added itself to default. (ParadoxV5) + +=== 5.18.1 / 2023-06-16 + +* 3 bug fixes: + + * Avoid extra string allocations when filtering tests. (tenderlove) + * Only mention deprecated \ENV[\'N\'] if it is an integer string. + * Push up test_order to Minitest::Runnable to fix minitest/hell. (koic) + +=== 5.18.0 / 2023-03-04 + +* 2 major enhancements: + + * Added assert_pattern & refute_pattern for pattern matching. (flavorjones) + * Added matching must_pattern_match & wont_pattern_match to minitest/spec. + +* 1 bug fix: + + * Support the new message format of NameError in Ruby 3.3 (mame) + +=== 5.17.0 / 2022-12-31 + +* 1 minor enhancement: + + * Refactor setup hooks into a SETUP_METHODS constant. (MSP-Greg) + +* 3 bug fixes: + + * Fix kwargs for Mock calls to delegator. (blowmage) + * Fix kwargs for expectations. (bobmazanec, blowmage) + * Remove check for .b method. (tenderlove) + +=== 5.16.3 / 2022-08-17 + +* 2 bug fixes: + + * Fixed exception sanitization by removing TypeError restriction on rescue. + * Use A instead of deprecated TESTOPTS in rake test:slow. (davidstosik) + +=== 5.16.2 / 2022-07-03 + +* 4 bug fixes: + + * Added MT_KWARGS_HACK kludge for stub to deal with ruby 2.7 kwargs nastiness. (tsugimoto) + * In #expect, pop Hash class from args if $MT_KWARGS_HACK. (casperisfine) + * In above scenario, set expected kwargs (as Objects) based on actual kwargs. + * Nuke ivars if exception fails to marshal twice (eg better_errors). (irphilli) + +=== 5.16.1 / 2022-06-20 + +* 2 bug fixes: + + * Apparently adding real kwarg support to mocks/stubs broke some code. Fixed. + * Use `MT_KWARGS_HACK=1` to activate the kludgy kwargs support w/ caveats. + * Clarified some doco wrt the block on #stub. + +=== 5.16.0 / 2022-06-14 + +* 2 major enhancements: + + * Added Minitest::TestTask. + * Dropping ruby 2.2 - 2.5. 2.6 is DTM soon too. + +* 11 minor enhancements: + + * Added --show-skips option to show skips at end of run but not require --verbose. (MSP-Greg) + * Added Minitest.seed, the random seed used by the run. + * Calling `srand Minitest.seed` before all shuffles to ensure determinism. + * Extended #stub to handle kwargs for both block and call args. (SampsonCrowley) + * Extended Mock#__call to display kwargs. + * Extended Mock#expect to record kwargs. + * Extended Mock#method_missing to take kwargs & compare them against expected. + * Mock#method_missing displays better errors on arity mismatch. + * Removed minor optimization removing empty suites before run. + * Simplified test randomization (test order will change even with fixed seed). + * assert_match now returns the MatchData on success. (Nakilon) + +* 3 bug fixes: + + * (Re)Fixed marshalling of exceptions, neutering them in 2 passes. + * Fixed more problems with rdoc. + * Had to patch up mock and stub to deal with <=2.7 kwargs oddities + +=== 5.15.0 / 2021-12-14 + +* 1 major enhancement: + + * assert_throws returns the value returned, if any. (volmer) + +* 3 minor enhancements: + + * Added -S option to skip reporting of certain types of output + * Enable Ruby deprecation warnings by default. (casperisfine) + * Use Etc.nprocessors by default in order to maximize cpu usage. (tonytonyjan) + +* 6 bug fixes: + + * Close then unlink tempfiles on Windows. (nobu) + * Fixed #skip_until for windows paths. (MSP-Greg) + * Fixed a bunch of tests for jruby and windows. (MSP-Greg) + * Fixed marshalling of specs if they error. (tenderlove, jeremyevans, et al) + * Updated deprecation message for block expectations. (blowmage) + * Use Kernel.warn directly in expectations in case CUT defines their own warn. (firien) + +=== 5.14.4 / 2021-02-23 + +* 1 bug fix: + + * Fixed deprecation warning using stub with methods using keyword arguments. (Nakilon) + +=== 5.14.3 / 2021-01-05 + +* 1 bug fix: + + * Bumped require_ruby_version to < 4 (trunk = 3.1). + +=== 5.14.2 / 2020-08-31 + +* 1 bug fix: + + * Bumped ruby version to include 3.0 (trunk). + +=== 5.14.1 / 2020-05-15 + +* 3 minor enhancements: + + * Minitest.filter_backtrace returns original backtrace if filter comes back empty. + * Minitest::BacktraceFilter now returns entire backtrace if $MT_DEBUG set in env. + * Return true on a successful refute. (jusleg) + +* 1 bug fix: + + * Fixed expectation doco to not use global expectations. + +=== 5.14.0 / 2020-01-11 + +* 2 minor enhancements: + + * Block-assertions (eg assert_output) now error if raised inside the block. (casperisfine) + * Changed assert_raises to only catch Assertion since that covers Skip and friends. + +* 3 bug fixes: + + * Added example for value wrapper with block to Expectations module. (stomar) + * Fixed use of must/wont_be_within_delta on Expectation instance. (stomar) + * Renamed UnexpectedError#exception to #error to avoid problems with reraising. (casperisfine) + +=== 5.13.0 / 2019-10-29 + +* 9 minor enhancements: + + * Added Minitest::Guard#osx? + * Added examples to documentation for assert_raises. (lxxxvi) + * Added expectations #path_must_exist and #path_wont_exist. Not thrilled with the names. + * Added fail_after(year, month, day, msg) to allow time-bombing after a deadline. + * Added skip_until(year, month, day, msg) to allow deferring until a deadline. + * Deprecated Minitest::Guard#maglev? + * Deprecated Minitest::Guard#rubinius? + * Finally added assert_path_exists and refute_path_exists. (deivid-rodriguez) + * Refactored and pulled Assertions#things_to_diff out of #diff. (BurdetteLamar) + +* 3 bug fixes: + + * Fix autorun bug that affects fork exit status in tests. (dylanahsmith/jhawthorn) + * Improved documentation for _/value/expect, especially for blocks. (svoop) + * Support new Proc#to_s format. (ko1) + +=== 5.12.2 / 2019-09-28 + +* 1 bug fix: + + * After chatting w/ @y-yagi and others, decided to lower support to include ruby 2.2. + +=== 5.12.1 / 2019-09-28 + +* 1 minor enhancement: + + * Added documentation for Reporter classes. (sshaw) + +* 3 bug fixes: + + * Avoid using 'match?' to support older ruby versions. (y-yagi) + * Fixed broken link to reference on goodness-of-fit testing. (havenwood) + * Update requirements in readme and Rakefile/hoe spec. + +=== 5.12.0 / 2019-09-22 + +* 8 minor enhancements: + + * Added a descriptive error if assert_output or assert_raises called without a block. (okuramasafumi) + * Changed mu_pp_for_diff to make having both \n and \\n easier to debug. + * Deprecated $N for specifying number of parallel test runners. Use MT_CPU. + * Deprecated use of global expectations. To be removed from MT6. + * Extended Assertions#mu_pp to encoding validity output for strings to improve diffs. + * Extended Assertions#mu_pp to output encoding and validity if invalid to improve diffs. + * Extended Assertions#mu_pp_for_diff to make escaped newlines more obvious in diffs. + * Fail gracefully when expectation used outside of `it`. + +* 3 bug fixes: + + * Check \option[:filter] klass before match. Fixes 2.6 warning. (y-yagi) + * Fixed Assertions#diff from recalculating if set to nil + * Fixed spec section of readme to not use deprecated global expectations. (CheezItMan) + +=== 5.11.3 / 2018-01-26 + +* 1 bug fix: + + * Pushed #error? up to Reportable module. (composerinteralia) + +=== 5.11.2 / 2018-01-25 + +* 1 minor enhancement: + + * Reversed Test < Result. Back to < Runnable and using Reportable for shared code. + +* 2 bug fixes: + + * Fixed Result#location for instances of Test. (alexisbernard) + * Fixed deprecation message for Runnable#marshal_dump. (y-yagi) + +=== 5.11.1 / 2018-01-02 + +* 1 bug fix: + + * Fixed Result (a superclass of Test) overriding Runnable's name accessors. (y-yagi, MSP-Greg) + +=== 5.11.0 / 2018-01-01 + +* 2 major enhancements: + + * Added Minitest::Result and Minitest::Result.from(runnable). + * Changed Minitest::Test to subclass Result and refactored methods up. + +* 7 minor enhancements: + + * Added --no-plugins and MT_NO_PLUGINS to bypass MT plugin autoloading. Helps with bad actors installed globally. + * Added bench_performance_{logarithmic,power} for spec-style benchmarks. (rickhull) + * Added deprecation warning for Runnable#marshal_dump. + * Minitest.run_one_method now checks for instance of Result, not exact same class. + * Minitest::Test.run returns a Result version of self, not self. + * ProgressReporter#prerecord now explicitly prints klass.name. Allows for fakers. + +* 4 bug fixes: + + * Object.stub no longer calls the passed block if stubbed with a callable. + * Object.stub now passes blocks down to the callable result. + * Pushed Minitest::Test#time & #time_it up to Runnable. + * Test nil equality directly in assert_equal. Fixes #679. (voxik) + +=== 5.11.0b1 / 2017-12-20 + +* 2 major enhancements: + + * Added Minitest::Result and Minitest::Result.from(runnable). + * Changed Minitest::Test to subclass Result and refactored methods up. + +* 6 minor enhancements: + + * Added --no-plugins and MT_NO_PLUGINS to bypass MT plugin autoloading. Helps with bad actors installed globally. + * Added bench_performance_{logarithmic,power} for spec-style benchmarks. (rickhull) + * Minitest.run_one_method now checks for instance of Result, not exact same class. + * Minitest::Test.run returns a Result version of self, not self. + * ProgressReporter#prerecord now explicitly prints klass.name. Allows for fakers. + * Removed Runnable.marshal_dump/load. + +* 4 bug fixes: + + * Object.stub no longer calls the passed block if stubbed with a callable. + * Object.stub now passes blocks down to the callable result. + * Pushed Minitest::Test#time & #time_it up to Runnable. + * Test nil equality directly in assert_equal. Fixes #679. (voxik) + +=== 5.10.3 / 2017-07-21 + +* 1 minor enhancement: + + * Extended documentation for Mock#expect for multiple calls to mock object. (insti) + +* 2 bug fixes: + + * Finished off missing doco. + * Fixed verbose output on parallelize_me! classes. (chanks) + +=== 5.10.2 / 2017-05-09 + +* 1 minor enhancement: + + * Added suggestion in minitest/hell to install minitest/proveit. + +* 7 bug fixes: + + * Expand MT6 to Minitest 6. (xaviershay) + * Fixed location of assert_send deprecation. (rab) + * Fixed location of nil assert_equal deprecation to work with expectations. (jeremyevans) + * Fixed minitest/hell to use parallelize_me! (azul) + * Made deprecation use warn so -W0 will silence it. + * Workaround for rdoc nodoc generation bug that totally f'd up minitest doco. (Paxa) + * Write aggregated_results directly to the IO object to avoid mixed encoding errors. (tenderlove) + +=== 5.10.1 / 2016-12-01 + +* 1 bug fix: + + * Added a hack/kludge to deal with missing #prerecord on reporters that aren't properly subclassing AbstractReporter (I'm looking at you minitest-reporters) + +=== 5.10.0 / 2016-11-30 + +* 1 major enhancement: + + * Deprecated ruby 1.8, 1.9, possibly 2.0, assert_send, & old MiniTest namespace. + +* 3 minor enhancements: + + * Warn if assert_equal expects a nil. This will fail in minitest 6+. (tenderlove) + * Added AbstractReporter#prerecord and extended ProgressReporter and CompositeReporter to use it. + * Minor optimization: remove runnables with no runnable methods before run. + +* 3 bug fixes: + + * Fix assert_throw rescuing any NameError and ArgumentError. (waldyr) + * Clean up (most of the) last remaining vestiges of minitest/unit. + * 2.4: removed deprecation warnings when referring to Fixnum. + +=== 5.9.1 / 2016-09-25 + +* 2 bug fixes: + + * Re-release to refresh gem certificate signing. ugh. + * Fixed hoe/minitest to not augment load path if we're actually testing minitest. + +=== 5.9.0 / 2016-05-16 + +* 8 minor enhancements: + + * Added Minitest.info_signal accessors to customize signal for test run info. (nate) + * Added assert_mock to make it more clear that you're testing w/ them. + * Added negative filter by test name. (utilum) + * Added warning to README that 1.8 and 1.9 support will be dropped in minitest 6. + * Automatically activate minitest/hell if $MT_HELL is defined. + * Improved default error messages for assert and refute. (bhenderson) + * minitest/hell now tries to require minitest/proveit + * mu_pp for strings prints out non-standard encodings to improve assert_equal diffs. + +* 1 bug fix: + + * Removed Interrupt from PASSTHROUGH_EXCEPTIONS (already handled). (waldyr) + +=== 5.8.5 / 2016-09-25 + +* 2 bug fixes: + + * Re-release to refresh gem certificate signing. ugh. + * Fixed hoe/minitest to not augment load path if we're actually testing minitest. + +=== 5.8.4 / 2016-01-21 + +* 1 bug fix: + + * Allow Minitest::Assertion to pass through assert_raises so inner failures are dealt with first. + +=== 5.8.3 / 2015-11-17 + +* 1 minor enhancement: + + * Added extra note about mocks and threads to readme. (zamith) + +* 1 bug fix: + + * Fixed bug in Mock#verify. (pithub/zamith) + +=== 5.8.2 / 2015-10-26 + +* 1 bug fix: + + * Fixed using parallelize_me! and capture_io (or any locking io). (arlt/tenderlove) + +=== 5.8.1 / 2015-09-23 + +* 1 minor enhancement: + + * Refactor assert_raises to be cleaner and to pass SystemExit and SignalException. (bhenderson) + +=== 5.8.0 / 2015-08-06 + +* 2 minor enhancements: + + * Add optional delegation mechanism to extend object with a mock. (zamith) + * Return early if there are no filtered methods. (jeremyevans) + +* 1 bug fix: + + * Don't extend io with pride if io is not a tty. (toy) + +=== 5.7.0 / 2015-05-27 + +* 1 major enhancement: + + * assert_raises now matches subclasses of the expected exception types. (jeremyevans) + +* 3 minor enhancements: + + * Added :block type for minitest/spec's #infect_an_assertion. (jeremyevans) + * Inline verification error messages in minitest/mock for GC performance. (zamith) + * assert_raises defaults to RuntimeError if not specified. (jeremyevans) + +* 4 bug fixes: + + * Added 'class' to minitest/mock's overridden_methods list. (zamith) + * Added file/line to infect_an_assertion's class_eval call. (jeremyevans) + * Cleared UnexpectedError's mesg w/ generic string. + * Fixed non-proc-oriented expectations when used on proc target. (jeremyevans) + +=== 5.6.1 / 2015-04-27 + +* 2 bug fixes: + + * Added Minitest.clock_time and switched all Time.now to it. (tenderlove) + * Moved Minitest::Expectations#_ into Minitest::Spec::DSL. + +=== 5.6.0 / 2015-04-13 + +* 4 major enhancements: + + * Added Minitest::Expectation value monad. + * Added Minitest::Expectations#_ that returns an Expectation. Aliased to value. + * All expectations are added to Minitest::Expectation. + * At some point, the methods on Object will be deprecated and then removed. + +* 4 minor enhancements: + + * Added a note about bundle exec pitfall in ruby 2.2+. (searls) + * Lazily start the parallel executor. (tenderlove) + * Make mocks more debugger-friendly (edward) + * Print out the current test run on interrupt. (riffraff) + +* 3 bug fixes: + + * Fix failing test under Windows. (kimhmadsen) + * Record mocked calls before they happen so mocks can raise exceptions easier (tho I'm not a fan). (corecode) + * Tried to clarify mocks vs stubs terminology better. (kkirsche) + +=== 5.5.1 / 2015-01-09 + +* 1 bug fix: + + * Fixed doco problems. (zzak) + +=== 5.5.0 / 2014-12-12 // mri 2.2.0 (as a real gem) + +* 1 minor enhancement: + + * Allow seed to be given via ENV for rake test loader sadness: eg rake SEED=42. + +=== 5.4.3 / 2014-11-11 + +* 2 bug fixes: + + * Clarified requirements for ruby are now 1.8.7 or better. + * Force encode error output in case mal-encoded exception is raised. (jasonrclark) + +=== 5.4.2 / 2014-09-26 + +* 2 minor enhancements: + + * Extract teardown method list. + * Thanks to minitest-gcstats got a 5-10% speedup via reduced GC! + +=== 5.4.1 / 2014-08-28 + +* 1 bug fix: + + * Fixed specs hidden by nesting/ordering bug (blowmage/apotonick) + +=== 5.4.0 / 2014-07-07 + +* 2 minor enhancements: + + * Kernel#describe extended to splat additional_desc. + * Spec#spec_type extended to take a splat of additional items, passed to matcher procs. + +* 1 bug fix: + + * minitest/spec should require minitest/test, not minitest/unit. (doudou) + +=== 5.3.5 / 2014-06-17 + +* 1 minor enhancement: + + * Spit and polish (mostly spit). + +=== 5.3.4 / 2014-05-15 + +* 1 minor enhancement: + + * Test classes are randomized before running. (judofyr) + +=== 5.3.3 / 2014-04-14 + +* 1 bug fix: + + * Fixed using expectations w/ DSL in Test class w/o describe. (blowmage+others) + +=== 5.3.2 / 2014-04-02 + +* 1 bug fix: + + * Fixed doco on Assertions.assertions. (xaviershay) + +=== 5.3.1 / 2014-03-14 + +* 1 minor enhancement: + + * Modified verbage on bad 'let' names to be more helpful. (Archytaus) + +* 1 bug fix: + + * Fixed 2 cases still using MiniTest. (mikesea) + +=== 5.3.0 / 2014-02-25 + +* 1 minor enhancement: + + * Mocked methods can take a block to verify state. Seattle.rb 12 bday present from ernie! Thanks!! + +=== 5.2.3 / 2014-02-10 + +* 1 bug fix: + + * Fixed Spec#let check to allow overriding of other lets. (mvz) + +=== 5.2.2 / 2014-01-22 + +* 1 minor enhancement: + + * Spec#let raises ArgumentError if you override _any_ instance method (except subject). (rynr) + +* 1 bug fix: + + * Fixed up benchmark spec doco and added a test to demonstrate. (bhenderson) + +=== 5.2.1 / 2014-01-07 + +* 1 bug fix: + + * Properly deal with horrible mix of runtime load errors + other at_exit handlers. (dougo/chqr) + +=== 5.2.0 / 2013-12-13 + +* 1 minor enhancement: + + * Change expectations to allow calling most on procs (but not calling the proc). (bhenderson+others) + +=== 5.1.0 / 2013-12-05 + +* 1 minor enhancement: + + * Use a Queue for scheduling parallel tests. (tenderlove) + +* 1 bug fix: + + * Fixed misspelling in doco. (amatsuda) + +=== 5.0.8 / 2013-09-20 + +* 1 bug fix: + + * Fixed siginfo handler by rearranging reporters and fixing to_s. (tenderlove) + +=== 5.0.7 / 2013-09-05 + +* 2 minor enhancements: + + * Added clarification about the use of thread local variables in expectations. (jemc) + * Added extra message about skipped tests, if any. Disable globally with $MT_NO_SKIP_MSG. + +* 2 bug fixes: + + * Only require minitest, not minitest/autorun in pride_plugin. (judofyr) + * Require rubygems in load_plugins in case you're not using minitest/autorun. + +=== 5.0.6 / 2013-06-28 + +* 3 minor enhancements: + + * Allow stub to pass args to blocks. (swindsor) + * Improved warning message about minitest/autorun to address 1.9's minitest/autorun. + * Made minitest/test require minitest as needed. For lib writers. (erikh) + +* 1 bug fix: + + * Fixed missing require in minitest/test. (erikh) + +=== 4.7.5 / 2013-06-21 // mri 2.1.1 + +* 2 bug fixes: + + * Fix Spec#describe_stack to be thread local. + * Fix multithreaded test failures by defining Time local to mock test namespace + +=== 5.0.5 / 2013-06-20 + +* 6 bug fixes: + + * DOH! Fixed the rest of the new casing on Minitest. (splattael) + * Fixed typo on minitest/mock rdoc. (mrgilman/guiceolin) + * Make Spec::DSL.describe_stack thread local to avoid failing on my own tests. + * Make a fake Time.now local to the tests so they won't interfere with real reporter timings. + * Make everything mockable by wrapping all 'special' methods in a smarter wrapper. (bestie) + * Raise ArgumentError if let name starts with 'test'. (johnmaxwell) + +=== 5.0.4 / 2013-06-07 + +* 5 minor enhancements: + + * Added AbstractReporter, defining required Reporter API to quack properly. + * Added doco for writing reporters. + * Refactored Reporter into ProgressReporter and SummaryReporter. (idea: phiggins, code:me+scotch) + * Refactored SummaryReporter pushing up to StatisticsReporter. (phiggins) + * Removed Reporter#run_and_report... cleaner, but doesn't "fit" in the API. + +=== 5.0.3 / 2013-05-29 + +* 4 minor enhancements: + + * Added Runnable.with_info_handler and Runnable.on_signal. + * Moved io.sync restore to Reporter#run_and_report. + * Refactored inner loop of Reporter#report to #to_s. Callable for status updates. + * Restored MT4's mid-run report (^t). (tenderlove). + +=== 5.0.2 / 2013-05-20 + +* 3 bug fixes: + + * Gem.find_files is smarter than I remember... cause I wrote it that way. *sigh* I'm getting old. + * Pride wasn't doing puts through its #io. (tmiller/tenderlove) + * Replaced Runnable#dup and Test#dup with marshal_dump/load. Too many problems cropping up on untested rails code. (tenderlove/rubys) + +=== 5.0.1 / 2013-05-14 + +* 2 bug fixes: + + * Documented Assertions' need for @assertions to be defined by the includer. + * Only load one plugin version per name. Tries for latest. + +=== 5.0.0 / 2013-05-10 + +Oh god... here we go... + +Minitest 5: + +* 4 deaths in the family: + + * MiniTest.runner is dead. No more manager objects. + * MiniTest::Unit#record is dead. Use a Reporter instance instead. + * MiniTest::Unit._run_* is dead. Runnable things are responsible for their own runs. + * MiniTest::Unit.output is dead. No more centralized IO. + +* 12 major (oft incompatible) changes: + + * Renamed MiniTest to Minitest. Your pinkies will thank me. (aliased to MiniTest) + * Removed MiniTest::Unit entirely. No more manager objects. + * Added Minitest::Runnable. Everything minitest can run subclasses this. + * Renamed MiniTest::Unit::TestCase to Minitest::Test (subclassing Runnable). + * Added Minitest::Benchmark. + * Your benchmarks need to move to their own subclass. + * Benchmarks using the spec DSL have to have "Bench" somewhere in their describe. + * MiniTest::Unit.after_tests moved to Minitest.after_run + * MiniTest::Unit.autorun is now Minitest.autorun. Just require minitest/autorun pls. + * Removed ParallelEach#grep since it isn't used anywhere. + * Renamed Runnable#__name__ to Runnable#name (but uses @NAME internally). + * Runnable#run needs to return self. Allows for swapping of results as needed. + +* 8 minor moves: + + * Moved Assertions module to minitest/assertions.rb + * Moved Expectations module to minitest/expectations.rb + * Moved Test to minitest/test.rb + * Moved everything else in minitest/unit.rb to minitest.rb + * minitest/unit.rb is now just a small (user-test only) compatibility layer. + * Moved most of minitest/pride into minitest/pride_plugin. + * minitest/pride now just activates pride. + * Moved ParallelEach under Minitest. + +* 9 additions: + + * Added a plugin system that can extend command-line options. + * Added Minitest.extensions. + * Added Minitest.reporter (only available during startup). + * Added Minitest.run(args). This is the very top of any Minitest run. + * Added Minitest::Reporter. Everything minitest can report goes through here. + * Minitest.reporter is a composite so you can add your own. + * Added Minitest::CompositeReporter. Much easier to extend with your own reporters. + * Added UnexpectedError, an Assertion subclass, to wrap up errors. + * Minitest::Test#run is now freakin' beautiful. 47 -> 17 loc + +* 11 other: + + * Removed Object.infect_with_assertions (it was already dead code). + * Runnables are responsible for knowing their result_code (eg "." or "F"). + * Minitest.autorun now returns boolean, not exit code. + * Added FAQ entry for extending via modules. (phiggins) + * Implement Runnable#dup to cleanse state back to test results. Helps with serialization. pair:tenderlove + * Moved ParallelEach under Minitest. + * Runnable#run needs to return self. Allows for swapping of results as needed. + * Minitest.init_plugins passes down options. + * Minitest.load_plugins only loads once. + * Fixed minitest/pride to work with rake test loader again. (tmiller) + * Added count/size to ParallelEach to fix use w/in stdlib's test/unit. :( (btaitelb) + +* 5 voodoo: + + * Removed mutex from minitest.rb (phiggins) + * Removed mutex from test.rb (phiggins) + * Removed Minitest::Reporter.synchronize (phiggins) + * Removed Minitest::Test.synchronize (phiggins) + * Upon loading minitest/parallel_each, record, capture_io and capture_subprocess_io are doped with synchronization code. (phiggins) + +=== 4.7.4 / 2013-05-01 + +This is probably the last release of the 4.x series. It will be merged +to ruby and will be put into maintenance mode there. + +I'm not set in stone on this, but at this point further development of +minitest (5+) will be gem-only. It is just too hard to work w/in +ruby-core w/ test-unit compatibility holding minitest development +back. + +* 2 minor enhancements: + + * Added count/size to ParallelEach to fix use w/in stdlib's test/unit. :( (btaitelb) + * Allow disabling of info_signal handler in runner. (erikh) + +=== 4.7.3 / 2013-04-20 + +* 1 bug fix: + + * Reverted stubbing of module methods change. Stub the user, not the impl. (ab9/tyabe) + +=== 4.7.2 / 2013-04-18 + +* 2 bug fixes: + + * Fixed inconsistency in refute_in_delta/epsilon. I double negatived my logic. (nettsundere) + * Fixed stubbing of module methods (eg Kernel#sleep). (steveklabnik) + +=== 4.7.1 / 2013-04-09 + +* 1 minor enhancement: + + * Added FAQ section to README + +* 1 bug fix: + + * Fixed bug where guard runs tests bypassing minitest/autorun and an ivar isn't set right. (darrencauthon) + +=== 4.7.0 / 2013-03-18 + +* 1 major enhancement: + + * Refactored MiniTest::Spec into MiniTest::Spec::DSL. + +* 1 bug fix: + + * Removed $DEBUG handler that detected when test/unit and minitest were both loaded. (tenderlove) + +=== 4.6.2 / 2013-02-27 + +* 1 minor enhancement: + + * Change error output to match Class#method, making it easier to use -n filter. + +=== 4.6.1 / 2013-02-14 + +* 1 bug fix: + + * Fixed an option processing bug caused by test/unit's irresponsibly convoluted code. (floehopper) + +=== 4.6.0 / 2013-02-07 + +* 3 major enhancements: + + * Removed ::reset_setup_teardown_hooks + * Removed the long deprecated assert_block + * Removed the long deprecated lifecycle hooks: add_(setup|teardown)_hook + +* 1 minor enhancement: + + * Allow filtering tests by suite name as well as test name. (lazyatom) + +* 2 bug fixes: + + * Made hex handling (eg object_ids) in mu_pp_for_diff more specific. (maxim) + * nodoc top-level module. (zzak) + +=== 4.5.0 / 2013-01-22 + +* 1 major enhancement: + + * Rearranged minitest/unit.rb so NO parallelization code is loaded/used until you opt-in. + +* 4 minor enhancements: + + * Added TestCase#skipped? for teardown guards + * Added maglev? guard + * Document that record can be sent twice if teardown fails or errors (randycoulman) + * Errors in teardown are now recorded. (randycoulman) + +* 3 bug fixes: + + * Added hacks and skips to get clean test runs on maglev + * Modified float tests for maglev float output differences. Not sure this is right. Not sure I care. + * Test for existance of diff.exe instead of assuming they have devkit. (blowmage/Cumbayah) + +=== 4.4.0 / 2013-01-07 + +* 3 minor enhancements: + + * Added fit_logarithic and assert_performance_logarithmic. (ktheory) + * Merge processed options so others can mess with defaults. (tenderlove) + * TestCase#message can now take another proc to defer custom message cost. (ordinaryzelig/bhenderson) + +* 1 bug fix: + + * TestCase#passed? now true if test is skipped. (qanhd) + +=== 4.3.3 / 2012-12-06 + +* 1 bug fix: + + * Updated information about stubbing. (daviddavis) + +=== 4.3.2 / 2012-11-27 // mri 2.0.0 + +* 1 minor enhancement: + + * Improved assert_equals error message to point you at #== of member objects. (kcurtin) + +=== 4.3.1 / 2012-11-23 + +* 1 bug fix: + + * Moved test_children to serial testcase to prevent random failures. + +=== 4.3.0 / 2012-11-17 + +* 4 minor enhancements: + + * Allow #autorun to run even if loaded with other test libs that call exit. (sunaku) + * Do not include Expectations in Object if $MT_NO_EXPECTATIONS is set (experimental?) + * Gave some much needed love to assert_raises. + * Mock#expect can take a block to custom-validate args. (gmoothart) + +=== 4.2.0 / 2012-11-02 + +* 4 major enhancements: + + * Added minitest/hell - run all your tests through the ringer! + * Added support for :parallel test_order to run test cases in parallel. + * Removed last_error and refactored runner code to be threadsafe. + * _run_suites now runs suites in parallel if they opt-in. + +* 4 minor enhancements: + + * Added TestCase#synchronize + * Added TestCase.make_my_diffs_pretty! + * Added TestCase.parallelize_me! + * Lock on capture_io for thread safety (tenderlove) + +=== 4.1.0 / 2012-10-05 + +* 2 minor enhancements: + + * Added skip example to readme. (dissolved) + * Extracted backtrace filter to object. (tenderlove) + +* 1 bug fix: + + * OMG I'm so dumb. Fixed access to deprecated hook class methods. I hate ruby modules. (route) + +=== 4.0.0 / 2012-09-28 + +* 1 major enhancement: + + * The names of a privately-used undocumented constants are Super Important™. + +* 1 minor enhancement: + + * Support stubbing methods that would be handled via method_missing. (jhsu) + +* 3 bug fixes: + + * Add include_private param to MiniTest::Mock#respond_to? (rf-) + * Fixed use of minitest/pride with --help. (zw963) + * Made 'No visible difference.' message more clear. (ckrailo) + +=== 3.5.0 / 2012-09-21 + +* 1 minor enhancement: + + * Added #capture_subprocess_io. (route) + +=== 3.4.0 / 2012-09-05 + +* 2 minor enhancements: + + * assert_output can now take regexps for expected values. (suggested by stomar) + * Clarified that ruby 1.9/2.0's phony gems cause serious confusion for rubygems. + +=== 3.3.0 / 2012-07-26 + +* 1 major enhancement: + + * Deprecated add_(setup|teardown)_hook in favor of (before|after)_(setup|teardown) [2013-01-01] + +* 4 minor enhancements: + + * Refactored deprecated hook system into a module. + * Refactored lifecycle hooks into a module. + * Removed after_setup/before_teardown + run_X_hooks from Spec. + * Spec#before/after now do a simple define_method and call super. DUR. + +* 2 bug fixes: + + * Fixed #passed? when used against a test that called flunk. (floehopper) + * Fixed rdoc bug preventing doco for some expectations. (stomar). + +=== 3.2.0 / 2012-06-26 + +* 1 minor enhancement: + + * Stubs now yield self. (peterhellberg) + +* 1 bug fix: + + * Fixed verbose test that only fails when run in verbose mode. mmmm irony. + +=== 3.1.0 / 2012-06-13 + +* 2 minor enhancements: + + * Removed LONG deprecated Unit.out accessor + * Removed generated method name munging from minitest/spec. (ordinaryzelig/tenderlove) + +=== 3.0.1 / 2012-05-24 + +* 1 bug fix: + + * I'm a dumbass and refactored into Mock#call. Renamed to #__call so you can mock #call. (mschuerig) + +=== 3.0.0 / 2012-05-08 + +* 3 major enhancements: + + * Added Object#stub (in minitest/mock.rb). + * Mock#expect mocks are used in the order they're given. + * Mock#verify now strictly compares against expect calls. + +* 3 minor enhancements: + + * Added caller to deprecation message. + * Mock error messages are much prettier. + * Removed String check for RHS of assert/refute_match. This lets #to_str work properly. + +* 1 bug fix: + + * Support drive letter on Windows. Patch provided from MRI by Usaku NAKAMURA. (ayumin) + +=== 2.12.1 / 2012-04-10 + +* 1 minor enhancement: + + * Added ruby releases to History.txt to make it easier to see what you're missing + +* 1 bug fix: + + * Rolled my own deprecate msg to allow MT to work with rubygems < 1.7 + +=== 2.12.0 / 2012-04-03 + +* 4 minor enhancements: + + * ::it returns test method name (wojtekmach) + * Added #record method to runner so runner subclasses can cleanly gather data. + * Added Minitest alias for MiniTest because even I forget. + * Deprecated assert_block!! Yay!!! + +* 1 bug fix: + + * Fixed warning in i_suck_and_my_tests_are_order_dependent! (phiggins) + +=== 2.11.4 / 2012-03-20 + +* 2 minor enhancements: + + * Updated known extensions + * You got your unicode in my tests! You got your tests in my unicode! (fl00r) + +* 1 bug fix: + + * Fixed MiniTest::Mock example in the readme. (conradwt) + +=== 2.11.3 / 2012-02-29 + +* 2 bug fixes: + + * Clarified that assert_raises returns the exception for further testing + * Fixed assert_in_epsilon when both args are negative. (tamc) + +=== 2.11.2 / 2012-02-14 + +* 1 minor enhancement: + + * Display failures/errors on SIGINFO. (tenderlove) + +* 1 bug fix: + + * Fixed MiniTest::Unit.after_tests for Ruby 1.9.3. (ysbaddaden) + +=== 2.11.1 / 2012-02-01 + +* 3 bug fixes: + + * Improved description for --name argument. (drd) + * Ensure Mock#expect's expected args is an Array. (mperham) + * Ensure Mock#verify verifies multiple expects of the same method. (chastell) + +=== 2.11.0 / 2012-01-25 + +* 2 minor enhancements: + + * Added before / after hooks for setup and teardown. (tenderlove) + * Pushed run_setup_hooks down to Spec. (tenderlove) + +=== 2.10.1 / 2012-01-17 + +* 1 bug fix: + + * Fixed stupid 1.9 path handling grumble grumble. (graaff) + +=== 2.10.0 / 2011-12-20 + +* 3 minor enhancements: + + * Added specs for must/wont be_empty/respond_to/be_kind_of and others. + * Added tests for assert/refute predicate. + * Split minitest/excludes.rb out to its own gem. + +* 1 bug fix: + + * Fixed must_be_empty and wont_be_empty argument handling. (mrsimo) + +=== 2.9.1 / 2011-12-13 + +* 4 minor enhancements: + + * Added a ton of tests on spec error message output. + * Cleaned up consistency of msg handling on unary expectations. + * Improved error messages on assert/refute_in_delta. + * infect_an_assertion no longer checks arity and better handles args. + +* 1 bug fix: + + * Fixed error message on specs when 2+ args and custom message provided. (chastell) + +=== 2.9.0 / 2011-12-07 + +* 4 minor enhancements: + + * Added TestCase.exclude and load_excludes for programmatic filtering of tests. + * Added guard methods so you can cleanly skip based on platform/impl + * Holy crap! 100% doco! `rdoc -C` ftw + * Switch assert_output to test stderr before stdout to possibly improve debugging + +=== 2.8.1 / 2011-11-17 + +* 1 bug fix: + + * Ugh. 1.9's test/unit violates my internals. Added const_missing. + +=== 2.8.0 / 2011-11-08 + +* 2 minor enhancements: + + * Add a method so that code can be run around a particular test case (tenderlove) + * Turn off backtrace filtering if we're running inside a ruby checkout. (drbrain) + +* 2 bug fixes: + + * Fixed 2 typos and 2 doc glitches. (splattael) + * Remove unused block arguments to avoid creating Proc objects. (k-tsj) + +=== 2.7.0 / 2011-10-25 + +* 2 minor enhancements: + + * Include failed values in the expected arg output in MockExpectationError. (nono) + * Make minitest/pride work with other 256 color capable terms. (sunaku) + +* 2 bug fixes: + + * Clarified the documentation of minitest/benchmark (eregon) + * Fixed using expectations in regular unit tests. (sunaku) + +=== 2.6.2 / 2011-10-19 + +* 1 minor enhancement: + + * Added link to vim bundle. (sunaku) + +* 2 bug fixes: + + * Force gem activation in hoe minitest plugin + * Support RUBY_VERSION='2.0.0' (nagachika) + +=== 2.6.1 / 2011-09-27 + +* 2 bug fixes: + + * Alias Spec.name from Spec.to_s so it works when @name is nil (nathany) + * Fixed assert and refute_operator where second object has a bad == method. + +=== 2.6.0 / 2011-09-13 + +* 2 minor enhancements: + + * Added specify alias for it and made desc optional. + * Spec#must_be and #wont_be can be used with predicates (metaskills) + +* 1 bug fix: + + * Fixed Mock.respond_to?(var) to work with strings. (holli) + +=== 2.5.1 / 2011-08-27 // ruby 1.9.3: p0, p125, p34579 + +* 2 minor enhancements: + + * Added gem activation for minitest in minitest/autoload to help out 1.9 users + * Extended Spec.register_spec_type to allow for procs instead of just regexps. + +=== 2.5.0 / 2011-08-18 + +* 4 minor enhancements: + + * Added 2 more arguments against rspec: let & subject in 9 loc! (emmanuel/luis) + * Added TestCase.i_suck_and_my_tests_are_order_dependent! + * Extended describe to take an optional method name (2 line change!). (emmanuel) + * Refactored and extended minitest/pride to do full 256 color support. (lolcat) + +* 1 bug fix: + + * Doc fixes. (chastell) + +=== 2.4.0 / 2011-08-09 + +* 4 minor enhancements: + + * Added simple examples for all expectations. + * Improved Mock error output when args mismatch. + * Moved all expectations from Object to MiniTest::Expectations. + * infect_with_assertions has been removed due to excessive clever + +* 4 bug fixes: + + * Fix Assertions#mu_pp to deal with immutable encoded strings. (ferrous26) + * Fix minitest/pride for MacRuby (ferrous26) + * Made error output less fancy so it is more readable + * Mock shouldn't undef === and inspect. (dgraham) + +=== 2.3.1 / 2011-06-22 + +* 1 bug fix: + + * Fixed minitest hoe plugin to be a spermy dep and not depend on itself. + +=== 2.3.0 / 2011-06-15 + +* 5 minor enhancements: + + * Add setup and teardown hooks to MiniTest::TestCase. (phiggins) + * Added nicer error messages for MiniTest::Mock. (phiggins) + * Allow for less specific expected arguments in Mock. (bhenderson/phiggins) + * Made MiniTest::Mock a blank slate. (phiggins) + * Refactored minitest/spec to use the hooks instead of define_inheritable_method. (phiggins) + +* 2 bug fixes: + + * Fixed TestCase's inherited hook. (dchelimsky/phiggins/jamis, the 'good' neighbor) + * MiniTest::Assertions#refute_empty should use mu_pp in the default message. (whatthejeff) + +=== 2.2.2 / 2011-06-01 + +* 2 bug fixes: + + * Got rid of the trailing period in message for assert_equal. (tenderlove) + * Windows needs more flushing. (Akio Tajima) + +=== 2.2.1 / 2011-05-31 + +* 1 bug fix: + + * My _ONE_ non-rubygems-using minitest user goes to Seattle.rb! + +=== 2.2.0 / 2011-05-29 + +* 6 minor enhancements: + + * assert_equal (and must_equal) now tries to diff output where it makes sense. + * Added Assertions#diff(exp, act) to be used by assert_equal. + * Added Assertions#mu_pp_for_diff + * Added Assertions.diff and diff= + * Moved minitest hoe-plugin from hoe-seattlerb. (erikh) + * Skipped tests only output details in verbose mode. (tenderlove+zenspider=xoxo) + +=== 2.1.0 / 2011-04-11 + +* 5 minor enhancements: + + * Added MiniTest::Spec.register_spec_type(matcher, klass) and spec_type(desc) + * Added ability for specs to share code via subclassing of Spec. (metaskills) + * Clarified (or tried to) bench_performance_linear's use of threshold. + * MiniTest::Unit.runner=(runner) provides an easy way of creating custom test runners for specialized needs. (justinweiss) + * Reverse order of inheritance in teardowns of specs. (deepfryed) + +* 3 bug fixes: + + * FINALLY fixed problems of inheriting specs in describe/it/describe scenario. (MGPalmer) + * Fixed a new warning in 1.9.3. + * Fixed assert_block's message handling. (nobu) + +=== 2.0.2 / 2010-12-24 + +* 1 minor enhancement: + + * Completed doco on minitest/benchmark for specs. + +* 1 bug fix: + + * Benchmarks in specs that didn't call bench_range would die. (zzak). + +=== 2.0.1 / 2010-12-15 + +* 4 minor enhancements: + + * Do not filter backtrace if $DEBUG + * Exit autorun via nested at_exit handler, in case other libs call exit + * Make options accesor lazy. + * Split printing of test name and its time. (nurse) + +* 1 bug fix: + + * Fix bug when ^T is hit before runner start + +=== 2.0.0 / 2010-11-11 + +* 3 major enhancements: + + * Added minitest/benchmark! Assert your performance! YAY! + * Refactored runner to allow for more extensibility. See minitest/benchmark. + * This makes the runner backwards incompatible in some ways! + +* 9 minor enhancements: + + * Added MiniTest::Unit.after_tests { ... } + * Improved output by adding test rates and a more sortable verbose format + * Improved readme based on feedback from others + * Added io method to TestCase. If used, it'll supplant '.EF' output. + * Refactored IO in MiniTest::Unit. + * Refactored _run_anything to _run_suite to make it easier to wrap (ngauthier) + * Spec class names are now the unmunged descriptions (btakita) + * YAY for not having redundant rdoc/readmes! + * Help output is now generated from the flags you passed straight up. + +* 4 bug fixes: + + * Fixed scoping issue on minitest/mock (srbaker/prosperity) + * Fixed some of the assertion default messages + * Fixes autorun when on windows with ruby install on different drive (larsch) + * Fixed rdoc output bug in spec.rb + +=== 1.7.2 / 2010-09-23 + +* 3 bug fixes: + + * Fixed doco for expectations and Spec. + * Fixed test_capture_io on 1.9.3+ (sora_h) + * assert_raises now lets MiniTest::Skip through. (shyouhei) + +=== 1.7.1 / 2010-09-01 + +* 1 bug fix: + + * 1.9.2 fixes for spec tests + +=== 1.7.0 / 2010-07-15 + +* 5 minor enhancements: + + * Added assert_output (mapped to must_output). + * Added assert_silent (mapped to must_be_silent). + * Added examples to readme (Mike Dalessio) + * Added options output at the top of the run, for fatal run debugging (tenderlove) + * Spec's describe method returns created class + +=== 1.6.0 / 2010-03-27 // ruby 1.9.2-p290 + +* 10 minor enhancements: + + * Added --seed argument so you can reproduce a random order for debugging. + * Added documentation for assertions + * Added more rdoc and tons of :nodoc: + * Added output to give you all the options you need to reproduce that run. + * Added proper argument parsing to minitest. + * Added unique serial # to spec names so order can be preserved (needs tests). (phrogz) + * Empty 'it' fails with default msg. (phrogz) + * Remove previous method on expect to remove 1.9 warnings + * Spec#it is now order-proof wrt subclasses/nested describes. + * assert_same error message now reports in decimal, eg: oid=123. (mattkent) + +* 2 bug fixes: + + * Fixed message on refute_same to be consistent with assert_same. + * Fixed method randomization to be stable for testing. + +=== 1.5.0 / 2010-01-06 + +* 4 minor enhancements: + + * Added ability to specify what assertions should have their args flipped. + * Don't flip arguments on *include and *respond_to assertions. + * Refactored Module.infect_an_assertion from Module.infect_with_assertions. + * before/after :all now bitches and acts like :each + +* 3 bug fixes: + + * Nested describes now map to nested test classes to avoid namespace collision. + * Using undef_method instead of remove_method to clean out inherited specs. + * assert_raises was ignoring passed in message. + +=== 1.4.2 / 2009-06-25 + +* 1 bug fix: + + * Fixed info handler for systems that don't have siginfo. + +=== 1.4.1 / 2009-06-23 + +* 1 major enhancement: + + * Handle ^C and other fatal exceptions by failing + +* 1 minor enhancement: + + * Added something to catch mixed use of test/unit and minitest if $DEBUG + +* 1 bug fix: + + * Added SIGINFO handler for finding slow tests without verbose + +=== 1.4.0 / 2009-06-18 + +* 5 minor enhancement: + + * Added clarification doco. + * Added specs and mocks to autorun. + * Changed spec test class creation to be non-destructive. + * Updated rakefile for new hoe capabilities. + * describes are nestable (via subclass). before/after/def inherits, specs don't. + +* 3 bug fixes: + + * Fixed location on must/wont. + * Switched to __name__ to avoid common ivar name. + * Fixed indentation in test file (1.9). + +=== 1.3.1 / 2009-01-20 // ruby 1.9.1-p431 + +* 1 minor enhancement: + + * Added miniunit/autorun.rb as replacement for test/unit.rb's autorun. + +* 16 bug fixes: + + * 1.9 test fixes. + * Bug fixes from nobu and akira for really odd scenarios. They run ruby funny. + * Fixed (assert|refute)_match's argument order. + * Fixed LocalJumpError in autorun if exception thrown before at_exit. + * Fixed assert_in_delta (should be >=, not >). + * Fixed assert_raises to match Modules. + * Fixed capture_io to not dup IOs. + * Fixed indentation of capture_io for ruby 1.9 warning. + * Fixed location to deal better with custom assertions and load paths. (Yuki) + * Fixed order of (must|wont)_include in MiniTest::Spec. + * Fixed skip's backtrace. + * Got arg order wrong in *_match in tests, message wrong as a result. + * Made describe private. For some reason I thought that an attribute of Kernel. + * Removed disable_autorun method, added autorun.rb instead. + * assert_match escapes if passed string for pattern. + * instance_of? is different from ===, use instance_of. + +=== 1.3.0 / 2008-10-09 + +* 2 major enhancements: + + * renamed to minitest and pulled out test/unit compatibility. + * mini/test.rb is now minitest/unit.rb, everything else maps directly. + +* 12 minor enhancements: + + * assert_match now checks that act can call =~ and converts exp to a + regexp only if needed. + * Added assert_send... seems useless to me tho. + * message now forces to string... ruby-core likes to pass classes and arrays :( + * Added -v handling and switched to @verbose from $DEBUG. + * Verbose output now includes test class name and adds a sortable running time! + * Switched message generation into procs for message deferment. + * Added skip and renamed fail to flunk. + * Improved output failure messages for assert_instance_of, assert_kind_of + * Improved output for assert_respond_to, assert_same. + * at_exit now exits false instead of errors+failures. + * Made the tests happier and more readable imhfo. + * Switched index(s) == 0 to rindex(s, 0) on nobu's suggestion. Faster. + +* 5 bug fixes: + + * 1.9: Added encoding normalization in mu_pp. + * 1.9: Fixed backtrace filtering (BTs are expanded now) + * Added back exception_details to assert_raises. DOH. + * Fixed shadowed variable in mock.rb + * Fixed stupid muscle memory message bug in assert_send. + +=== 1.2.1 / 2008-06-10 + +* 7 minor enhancements: + + * Added deprecations everywhere in test/unit. + * Added test_order to TestCase. :random on mini, :sorted on test/unit (for now). + * Big cleanup in test/unit for rails. Thanks Jeremy Kemper! + * Minor readability cleanup. + * Pushed setup/run/teardown down to testcase allowing specialized testcases. + * Removed pp. Tests run 2x faster. :/ + * Renamed deprecation methods and moved to test/unit/deprecate.rb. + +=== 1.2.0 / 2008-06-09 + +* 2 major enhancements: + + * Added Mini::Spec. + * Added Mini::Mock. Thanks Steven Baker!! + +* 23 minor enhancements: + + * Added bin/use_miniunit to make it easy to test out miniunit. + * Added -n filtering, thanks to Phil Hagelberg! + * Added args argument to #run, takes ARGV from at_exit. + * Added test name output if $DEBUG. + * Added a refute (was deny) for every assert. + * Added capture_io and a bunch of nice assertions from zentest. + * Added deprecation mechanism for assert_no/not methods to test/unit/assertions. + * Added pp output when available. + * Added tests for all assertions. Pretty much maxed out coverage. + * Added tests to verify consistency and good naming. + * Aliased and deprecated all ugly assertions. + * Cleaned out test/unit. Moved autorun there. + * Code cleanup to make extensions easier. Thanks Chad! + * Got spec args reversed in all but a couple assertions. Much more readable. + * Improved error messages across the board. Adds your message to the default. + * Moved into Mini namespace, renamed to Mini::Test and Mini::Spec. + * Pulled the assertions into their own module... + * Removed as much code as I could while still maintaining full functionality. + * Moved filter_backtrace into MiniTest. + * Removed MiniTest::Unit::run. Unnecessary. + * Removed location_of_failure. Unnecessary. + * Rewrote test/unit's filter_backtrace. Flog from 37.0 to 18.1 + * Removed assert_send. Google says it is never used. + * Renamed MiniTest::Unit.autotest to #run. + * Renamed deny to refute. + * Rewrote some ugly/confusing default assertion messages. + * assert_in_delta now defaults to 0.001 precision. Makes specs prettier. + +* 9 bug fixes: + + * Fixed assert_raises to raise outside of the inner-begin/rescue. + * Fixed for ruby 1.9 and rubinius. + * No longer exits 0 if exception in code PRE-test run causes early exit. + * Removed implementors method list from mini/test.rb - too stale. + * assert_nothing_raised takes a class as an arg. wtf? STUPID + * ".EF" output is now unbuffered. + * Bunch of changes to get working with rails... UGH. + * Added stupid hacks to deal with rails not requiring their dependencies. + * Now bitch loudly if someone defines one of my classes instead of requiring. + * Fixed infect method to work better on 1.9. + * Fixed all shadowed variable warnings in 1.9. + +=== 1.1.0 / 2007-11-08 + +* 4 major enhancements: + + * Finished writing all missing assertions. + * Output matches original test/unit. + * Documented every method needed by language implementor. + * Fully switched over to self-testing setup. + +* 2 minor enhancements: + + * Added deny (assert ! test), our favorite extension to test/unit. + * Added .autotest and fairly complete unit tests. (thanks Chad for help here) + +=== 1.0.0 / 2006-10-30 + +* 1 major enhancement + + * Birthday! diff --git a/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/Manifest.txt b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/Manifest.txt new file mode 100644 index 0000000..750240d --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/Manifest.txt @@ -0,0 +1,41 @@ +History.rdoc +Manifest.txt +README.rdoc +Rakefile +bin/minitest +design_rationale.rb +lib/hoe/minitest.rb +lib/minitest.rb +lib/minitest/assertions.rb +lib/minitest/autorun.rb +lib/minitest/benchmark.rb +lib/minitest/bisect.rb +lib/minitest/complete.rb +lib/minitest/compress.rb +lib/minitest/error_on_warning.rb +lib/minitest/expectations.rb +lib/minitest/find_minimal_combination.rb +lib/minitest/hell.rb +lib/minitest/manual_plugins.rb +lib/minitest/parallel.rb +lib/minitest/path_expander.rb +lib/minitest/pride.rb +lib/minitest/pride_plugin.rb +lib/minitest/server.rb +lib/minitest/server_plugin.rb +lib/minitest/spec.rb +lib/minitest/sprint.rb +lib/minitest/sprint_plugin.rb +lib/minitest/test.rb +lib/minitest/test_task.rb +test/minitest/metametameta.rb +test/minitest/test_bisect.rb +test/minitest/test_find_minimal_combination.rb +test/minitest/test_minitest_assertions.rb +test/minitest/test_minitest_benchmark.rb +test/minitest/test_minitest_reporter.rb +test/minitest/test_minitest_spec.rb +test/minitest/test_minitest_test.rb +test/minitest/test_minitest_test_task.rb +test/minitest/test_server.rb +test/minitest/test_path_expander.rb diff --git a/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/README.rdoc b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/README.rdoc new file mode 100644 index 0000000..b1c011c --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/README.rdoc @@ -0,0 +1,762 @@ += minitest/{test,spec,benchmark} + +home :: https://minite.st/ +code :: https://github.com/minitest/minitest +bugs :: https://github.com/minitest/minitest/issues +rdoc :: https://docs.seattlerb.org/minitest +clog :: https://github.com/minitest/minitest/blob/master/History.rdoc +emacs:: https://github.com/arthurnn/minitest-emacs +vim :: https://github.com/vim-test/vim-test + +== DESCRIPTION: + +minitest provides a complete suite of testing facilities supporting +TDD, BDD, and benchmarking. + + "I had a class with Jim Weirich on testing last week and we were + allowed to choose our testing frameworks. Kirk Haines and I were + paired up and we cracked open the code for a few test + frameworks... + + I MUST say that minitest is *very* readable / understandable + compared to the 'other two' options we looked at. Nicely done and + thank you for helping us keep our mental sanity." + + -- Wayne E. Seguin + +minitest/test is a small and incredibly fast unit testing framework. +It provides a rich set of assertions to make your tests clean and +readable. + +minitest/spec is a functionally complete spec engine. It hooks onto +minitest/test and seamlessly bridges test assertions over to spec +expectations. + +minitest/benchmark is an awesome way to assert the performance of your +algorithms in a repeatable manner. Now you can assert that your newb +co-worker doesn't replace your linear algorithm with an exponential +one! + +minitest/pride shows pride in testing and adds coloring to your test +output. I guess it is an example of how to write IO pipes too. :P + +minitest/test is meant to have a clean implementation for language +implementors that need a minimal set of methods to bootstrap a working +test suite. For example, there is no magic involved for test-case +discovery. + + "Again, I can't praise enough the idea of a testing/specing + framework that I can actually read in full in one sitting!" + + -- Piotr Szotkowski + +Comparing to rspec: + + rspec is a testing DSL. minitest is ruby. + + -- Adam Hawkins, "Bow Before MiniTest" + +minitest doesn't reinvent anything that ruby already provides, like: +classes, modules, inheritance, methods. This means you only have to +learn ruby to use minitest and all of your regular OO practices like +extract-method refactorings still apply. + +== FEATURES/PROBLEMS: + +* minitest/autorun - the easy and explicit way to run all your tests. +* minitest/test - a very fast, simple, and clean test system. +* minitest/spec - a very fast, simple, and clean spec system. +* minitest/benchmark - an awesome way to assert your algorithm's performance. +* minitest/pride - show your pride in testing! +* minitest/test_task - a full-featured and clean rake task generator. +* Incredibly small and fast runner, but no bells and whistles. +* Written by squishy human beings. Software can never be perfect. We will all eventually die. + +== RATIONALE: + +See design_rationale.rb to see how specs and tests work in minitest. + +== SYNOPSIS: + +Given that you'd like to test the following class: + + class Meme + def i_can_has_cheezburger? + "OHAI!" + end + + def will_it_blend? + "YES!" + end + end + +=== Unit tests + +Define your tests as methods beginning with +test_+. Use +{assertions}[/minitest/Minitest/Assertions.html] to test for results +or state. + + require "minitest/autorun" + + class TestMeme < Minitest::Test + def setup + @meme = Meme.new + end + + def test_that_kitty_can_eat + assert_equal "OHAI!", @meme.i_can_has_cheezburger? + end + + def test_that_it_will_not_blend + refute_match /^no/i, @meme.will_it_blend? + end + + def test_that_will_be_skipped + skip "test this later" + end + end + +=== Specs + +Use {expectations}[/minitest/Minitest/Expectations.html] to check +results or state. They must be wrapped in a value call (eg +_+). + + require "minitest/autorun" + + describe Meme do + before do + @meme = Meme.new + end + + describe "when asked about cheeseburgers" do + it "must respond positively" do + _(@meme.i_can_has_cheezburger?).must_equal "OHAI!" + end + end + + describe "when asked about blending possibilities" do + it "won't say no" do + _(@meme.will_it_blend?).wont_match /^no/i + end + end + end + +For matchers support check out: + +* https://github.com/wojtekmach/minitest-matchers +* https://github.com/rmm5t/minitest-matchers_vaccine + +=== Benchmarks + +Add {benchmarks}[/minitest/Minitest/Benchmark.html] to your tests. + + # optionally run benchmarks, good for CI-only work! + require "minitest/benchmark" if ENV["BENCH"] + + class TestMeme < Minitest::Benchmark + # Override self.bench_range or default range is [1, 10, 100, 1_000, 10_000] + def bench_my_algorithm + assert_performance_linear 0.9999 do |n| # n is a range value + @obj.my_algorithm(n) + end + end + end + +Or add them to your specs. If you make benchmarks optional, you'll +need to wrap your benchmarks in a conditional since the methods won't +be defined. In minitest 5, the describe name needs to match +/Bench(mark)?$/. + + describe "Meme Benchmark" do + if ENV["BENCH"] then + bench_performance_linear "my_algorithm", 0.9999 do |n| + 100.times do + @obj.my_algorithm(n) + end + end + end + end + +outputs something like: + + # Running benchmarks: + + TestBlah 100 1000 10000 + bench_my_algorithm 0.006167 0.079279 0.786993 + bench_other_algorithm 0.061679 0.792797 7.869932 + +Output is tab-delimited to make it easy to paste into a spreadsheet. + +=== Running Your Tests + +Ideally, you'll use a rake task to run your tests (see below), either +piecemeal or all at once. BUT! You don't have to: + + % ruby -Ilib:test test/minitest/test_minitest_test.rb + Run options: --seed 37685 + + # Running: + + ...................................................................... (etc) + + Finished in 0.107130s, 1446.8403 runs/s, 2959.0217 assertions/s. + + 155 runs, 317 assertions, 0 failures, 0 errors, 0 skips + +There are runtime options available, both from minitest itself, and also +provided via plugins. To see them, simply run with +--help+: + + % ruby -Ilib:test test/minitest/test_minitest_test.rb --help + minitest options: + -h, --help Display this help. + -s, --seed SEED Sets random seed. Also via env. Eg: SEED=n rake + -v, --verbose Verbose. Show progress processing files. + -n, --name PATTERN Filter run on /regexp/ or string. + -e, --exclude PATTERN Exclude /regexp/ or string from run. + + Known extensions: pride, autotest + -p, --pride Pride. Show your testing pride! + -a, --autotest Connect to autotest server. + +=== Rake Tasks + +You can set up a rake task to run all your tests by adding this to your Rakefile: + + require "minitest/test_task" + + Minitest::TestTask.create # named test, sensible defaults + + # or more explicitly: + + Minitest::TestTask.create(:test) do |t| + t.libs << "test" + t.libs << "lib" + t.warning = false + t.test_globs = ["test/**/*_test.rb"] + end + + task :default => :test + +Each of these will generate 4 tasks: + + rake test :: Run the test suite. + rake test:cmd :: Print out the test command. + rake test:isolated :: Show which test files fail when run separately. + rake test:slow :: Show bottom 25 tests sorted by time. + +=== Rake Task Variables + +There are a bunch of variables you can supply to rake to modify the run. + + MT_LIB_EXTRAS :: Extra libs to dynamically override/inject for custom runs. + N :: -n: Tests to run (string or /regexp/). + X :: -x: Tests to exclude (string or /regexp/). + A :: Any extra arguments. Honors shell quoting. + MT_CPU :: How many threads to use for parallel test runs + SEED :: -s --seed Sets random seed. + TESTOPTS :: Deprecated, same as A + FILTER :: Deprecated, same as A + +== Writing Extensions + +To define a plugin, add a file named minitest/XXX_plugin.rb to your +project/gem. That file must be discoverable via ruby's LOAD_PATH (via +rubygems or otherwise). Minitest will find and require that file using +Gem.find_files. It will then try to call +plugin_XXX_init+ during +startup. The option processor will also try to call +plugin_XXX_options+ +passing the OptionParser instance and the current options hash. This +lets you register your own command-line options. Here's a totally +bogus example: + + # minitest/bogus_plugin.rb: + + module Minitest + def self.plugin_bogus_options(opts, options) + opts.on "--myci", "Report results to my CI" do + options[:myci] = true + options[:myci_addr] = get_myci_addr + options[:myci_port] = get_myci_port + end + end + + def self.plugin_bogus_init(options) + self.reporter << MyCI.new(options) if options[:myci] + end + end + +=== Adding custom reporters + +Minitest uses composite reporter to output test results using multiple +reporter instances. You can add new reporters to the composite during +the init_plugins phase. As we saw in +plugin_bogus_init+ above, you +simply add your reporter instance to the composite via <<. + ++AbstractReporter+ defines the API for reporters. You may subclass it +and override any method you want to achieve your desired behavior. + +start :: Called when the run has started. +record :: Called for each result, passed or otherwise. +report :: Called at the end of the run. +passed? :: Called to see if you detected any problems. + +Using our example above, here is how we might implement MyCI: + + # minitest/bogus_plugin.rb + + module Minitest + class MyCI < AbstractReporter + attr_accessor :results, :addr, :port + + def initialize options + self.results = [] + self.addr = options[:myci_addr] + self.port = options[:myci_port] + end + + def record result + self.results << result + end + + def report + CI.connect(addr, port).send_results self.results + end + end + + # code from above... + end + +== FAQ + +=== What versions are compatible with what? Or what versions are supported? + +Minitest is a dependency of rails, which until very recently had an +overzealous backwards compatibility policy. As such, I'm stuck +supporting versions of ruby that are long past EOL. Hopefully I'll be +able to support only current versions of ruby sometime in the near +future. + +NOTICE: At this point, I will only locally test/dev against the +currently 3 supported (non-EOL) versions of ruby. I cannot and will +not maintain that many builds. + +(As of 2025-02-03) + +Current versions of rails: (https://endoflife.date/rails) + + | rails | min ruby | minitest | status | EOL Date | + |-------+----------+----------+----------+------------| + | 8.1 | >= 3.2 | >= 5.1 | Current | 2027-10-07 | + | 8.0 | >= 3.2 | >= 5.1 | Current | 2026-11-07 | + | 7.2 | >= 3.1 | >= 5.1 | Security | 2026-08-09 | + | 7.1 | >= 2.7 | >= 5.1 | EOL | 2025-10-01 | + +If you want to look at the requirements for a specific version, run: + + gem spec -r --ruby rails -v 8.0.0 + +Current versions of ruby: (https://endoflife.date/ruby) + + | ruby | Status | EOL Date | + |------+---------+------------| + | 3.4 | Current | 2028-03-31 | + | 3.3 | Maint | 2027-03-31 | + | 3.2 | Security| 2026-03-31 | + | 3.1 | EOL | 2025-03-31 | + | 3.0 | EOL | 2024-03-31 | + | 2.7 | EOL | 2023-03-31 | + | 2.6 | EOL | 2022-03-31 | + | 2.5 | EOL | 2021-03-31 | + +=== How to test SimpleDelegates? + +The following implementation and test: + + class Worker < SimpleDelegator + def work + end + end + + describe Worker do + before do + @worker = Worker.new(Object.new) + end + + it "must respond to work" do + _(@worker).must_respond_to :work + end + end + +outputs a failure: + + 1) Failure: + Worker#test_0001_must respond to work [bug11.rb:16]: + Expected # (Object) to respond to #work. + +Worker is a SimpleDelegate which in 1.9+ is a subclass of BasicObject. +Expectations are put on Object (one level down) so the Worker +(SimpleDelegate) hits +method_missing+ and delegates down to the ++Object.new+ instance. That object doesn't respond to work so the test +fails. + +You can bypass SimpleDelegate#method_missing by extending the worker +with Minitest::Expectations. You can either do that in your setup at +the instance level, like: + + before do + @worker = Worker.new(Object.new) + @worker.extend Minitest::Expectations + end + +or you can extend the Worker class (within the test file!), like: + + class Worker + include ::Minitest::Expectations + end + +=== How to share code across test classes? + +Use a module. That's exactly what they're for: + + module UsefulStuff + def useful_method + # ... + end + end + + describe Blah do + include UsefulStuff + + def test_whatever + # useful_method available here + end + end + +Remember, +describe+ simply creates test classes. It's just ruby at +the end of the day and all your normal Good Ruby Rules (tm) apply. If +you want to extend your test using setup/teardown via a module, just +make sure you ALWAYS call super. before/after automatically call super +for you, so make sure you don't do it twice. + +=== How to run code before a group of tests? + +Use a constant with begin...end like this: + + describe Blah do + SETUP = begin + # ... this runs once when describe Blah starts + end + # ... + end + +This can be useful for expensive initializations or sharing state. +Remember, this is just ruby code, so you need to make sure this +technique and sharing state doesn't interfere with your tests. + +=== Why am I seeing uninitialized constant MiniTest::Test (NameError)? + +Are you running the test with Bundler (e.g. via bundle exec )? If so, +in order to require minitest, you must first add the gem 'minitest' +to your Gemfile and run +bundle+. Once it's installed, you should be +able to require minitest and run your tests. + +== Prominent Projects using Minitest: + +* arel +* journey +* mime-types +* nokogiri +* rails (active_support et al) +* rake +* rdoc +* ...and of course, everything from seattle.rb... + +== Developing Minitest: + +Minitest requires {Hoe}[https://rubygems.org/gems/hoe]. + +=== Minitest's own tests require UTF-8 external encoding. + +This is a common problem in Windows, where the default external Encoding is +often CP850, but can affect any platform. +Minitest can run test suites using any Encoding, but to run Minitest's +own tests you must have a default external Encoding of UTF-8. + +If your encoding is wrong, you'll see errors like: + + --- expected + +++ actual + @@ -1,2 +1,3 @@ + # encoding: UTF-8 + -"Expected /\\w+/ to not match \"blah blah blah\"." + +"Expected /\\w+/ to not match # encoding: UTF-8 + +\"blah blah blah\"." + +To check your current encoding, run: + + ruby -e 'puts Encoding.default_external' + +If your output is something other than UTF-8, you can set the RUBYOPTS +env variable to a value of '-Eutf-8'. Something like: + + RUBYOPT='-Eutf-8' ruby -e 'puts Encoding.default_external' + +Check your OS/shell documentation for the precise syntax (the above +will not work on a basic Windows CMD prompt, look for the SET command). +Once you've got it successfully outputing UTF-8, use the same setting +when running rake in Minitest. + +=== Minitest's own tests require GNU (or similar) diff. + +This is also a problem primarily affecting Windows developers. PowerShell +has a command called diff, but it is not suitable for use with Minitest. + +If you see failures like either of these, you are probably missing diff tool: + + 4) Failure: + TestMinitestUnitTestCase#test_assert_equal_different_long [D:/ruby/seattlerb/minitest/test/minitest/test_minitest_test.rb:936]: + Expected: "--- expected\n+++ actual\n@@ -1 +1 @@\n-\"hahahahahahahahahahahahahahahahahahahaha\"\n+\"blahblahblahblahblahblahblahblahblahblah\"\n" + Actual: "Expected: \"hahahahahahahahahahahahahahahahahahahaha\"\n Actual: \"blahblahblahblahblahblahblahblahblahblah\"" + + + 5) Failure: + TestMinitestUnitTestCase#test_assert_equal_different_collection_hash_hex_invisible [D:/ruby/seattlerb/minitest/test/minitest/test_minitest_test.rb:845]: + Expected: "No visible difference in the Hash#inspect output.\nYou should look at the implementation of #== on Hash or its members.\n + {1=>#}" + Actual: "Expected: {1=>#}\n Actual: {1=>#}" + + +If you use Cygwin or MSYS2 or similar there are packages that include a +GNU diff for Windows. If you don't, you can download GNU diffutils from +http://gnuwin32.sourceforge.net/packages/diffutils.htm +(make sure to add it to your PATH). + +You can make sure it's installed and path is configured properly with: + + diff.exe -v + +There are multiple lines of output, the first should be something like: + + diff (GNU diffutils) 2.8.1 + +If you are using PowerShell make sure you run diff.exe, not just diff, +which will invoke the PowerShell built in function. + +== Known Extensions: + +capybara_minitest_spec :: Bridge between Capybara RSpec matchers and + Minitest::Spec expectations (e.g. + page.must_have_content("Title")). +color_pound_spec_reporter :: Test names print Ruby Object types in color with + your Minitest Spec style tests. +minispec-metadata :: Metadata for describe/it blocks & CLI tag filter. + E.g. it "requires JS driver", js: true do & + ruby test.rb --tag js runs tests tagged :js. +minispec-rails :: Minimal support to use Spec style in Rails 5+. +mini-apivore :: for swagger based automated API testing. +minitest-around :: Around block for minitest. An alternative to + setup/teardown dance. +minitest-assert_errors :: Adds Minitest assertions to test for errors raised + or not raised by Minitest itself. +minitest-autotest :: autotest is a continuous testing facility meant to + be used during development. +minitest-bacon :: minitest-bacon extends minitest with bacon-like + functionality. +minitest-bang :: Adds support for RSpec-style let! to immediately + invoke let statements before each test. +minitest-bisect :: Helps you isolate and debug random test failures. +minitest-blink1_reporter :: Display test results with a Blink1. +minitest-capistrano :: Assertions and expectations for testing + Capistrano recipes. +minitest-capybara :: Capybara matchers support for minitest unit and + spec. +minitest-cc :: It provides minimal information about code coverage. +minitest-chef-handler :: Run Minitest suites as Chef report handlers +minitest-ci :: CI reporter plugin for Minitest. +minitest-context :: Defines contexts for code reuse in Minitest + specs that share common expectations. +minitest-debugger :: Wraps assert so failed assertions drop into + the ruby debugger. +minitest-display :: Patches Minitest to allow for an easily + configurable output. +minitest-documentation :: Minimal documentation format inspired by rspec's. +minitest-doc_reporter :: Detailed output inspired by rspec's documentation + format. +minitest-emoji :: Print out emoji for your test passes, fails, and + skips. +minitest-english :: Semantically symmetric aliases for assertions and + expectations. +minitest-excludes :: Clean API for excluding certain tests you + don't want to run under certain conditions. +minitest-fail-fast :: Reimplements RSpec's "fail fast" feature +minitest-filecontent :: Support unit tests with expectation results in files. + Differing results will be stored again in files. +minitest-filesystem :: Adds assertion and expectation to help testing + filesystem contents. +minitest-firemock :: Makes your Minitest mocks more resilient. +minitest-focus :: Focus on one test at a time. +minitest-gcstats :: A minitest plugin that adds a report of the top + tests by number of objects allocated. +minitest-global_expectations:: Support minitest expectation methods for all objects +minitest-great_expectations :: Generally useful additions to minitest's + assertions and expectations. +minitest-growl :: Test notifier for minitest via growl. +minitest-happy :: GLOBALLY ACTIVATE MINITEST PRIDE! RAWR! +minitest-have_tag :: Adds Minitest assertions to test for the existence of + HTML tags, including contents, within a provided string. +minitest-heat :: Reporting that builds a heat map of failure locations +minitest-hooks :: Around and before_all/after_all/around_all hooks +minitest-hyper :: Pretty, single-page HTML reports for your Minitest runs +minitest-implicit-subject :: Implicit declaration of the test subject. +minitest-instrument :: Instrument ActiveSupport::Notifications when + test method is executed. +minitest-instrument-db :: Store information about speed of test execution + provided by minitest-instrument in database. +minitest-junit :: JUnit-style XML reporter for minitest. +minitest-keyword :: Use Minitest assertions with keyword arguments. +minitest-libnotify :: Test notifier for minitest via libnotify. +minitest-line :: Run test at line number. +minitest-logger :: Define assert_log and enable minitest to test log messages. + Supports Logger and Log4r::Logger. +minitest-macruby :: Provides extensions to minitest for macruby UI + testing. +minitest-matchers :: Adds support for RSpec-style matchers to + minitest. +minitest-matchers_vaccine :: Adds assertions that adhere to the matcher spec, + but without any expectation infections. +minitest-metadata :: Annotate tests with metadata (key-value). +minitest-mock_expectations :: Provides method call assertions for minitest. +minitest-mongoid :: Mongoid assertion matchers for Minitest. +minitest-must_not :: Provides must_not as an alias for wont in + Minitest. +minitest-optional_retry :: Automatically retry failed test to help with flakiness. +minitest-osx :: Reporter for the Mac OS X notification center. +minitest-parallel_fork :: Fork-based parallelization +minitest-parallel-db :: Run tests in parallel with a single database. +minitest-power_assert :: PowerAssert for Minitest. +minitest-predicates :: Adds support for .predicate? methods. +minitest-profile :: List the 10 slowest tests in your suite. +minitest-rails :: Minitest integration for Rails 3.x. +minitest-rails-capybara :: Capybara integration for Minitest::Rails. +minitest-reporters :: Create customizable Minitest output formats. +minitest-rg :: Colored red/green output for Minitest. +minitest-rspec_mocks :: Use RSpec Mocks with Minitest. +minitest-server :: minitest-server provides a client/server setup + with your minitest process, allowing your test + run to send its results directly to a handler. +minitest-sequel :: Minitest assertions to speed-up development and + testing of Ruby Sequel database setups. +minitest-shared_description :: Support for shared specs and shared spec + subclasses +minitest-should_syntax :: RSpec-style x.should == y assertions for + Minitest. +minitest-shouldify :: Adding all manner of shoulds to Minitest (bad + idea) +minitest-snail :: Print a list of tests that take too long +minitest-spec-context :: Provides rspec-ish context method to + Minitest::Spec. +minitest-spec-expect :: Expect syntax for Minitest::Spec (e.g. + expect(sequences).to_include :celery_man). +minitest-spec-magic :: Minitest::Spec extensions for Rails and beyond. +minitest-spec-rails :: Drop in Minitest::Spec superclass for + ActiveSupport::TestCase. +minitest-sprint :: Runs (Get it? It's fast!) your tests and makes + it easier to rerun individual failures. +minitest-stately :: Find leaking state between tests +minitest-stub_any_instance :: Stub any instance of a method on the given class + for the duration of a block. +minitest-stub-const :: Stub constants for the duration of a block. +minitest-tags :: Add tags for minitest. +minitest-unordered :: Adds a new assertion to minitest for checking the + contents of a collection, ignoring element order. +minitest-vcr :: Automatic cassette management with Minitest::Spec + and VCR. +minitest_log :: Adds structured logging, data explication, and verdicts. +minitest_owrapper :: Get tests results as a TestResult object. +minitest_should :: Shoulda style syntax for minitest test::unit. +minitest_tu_shim :: Bridges between test/unit and minitest. +mongoid-minitest :: Minitest matchers for Mongoid. +mutant-minitest :: Minitest integration for mutant. +pry-rescue :: A pry plugin w/ minitest support. See + pry-rescue/minitest.rb. +rematch :: Declutter your test files from large hardcoded data + and update them automatically when your code changes. +rspec2minitest :: Easily translate any RSpec matchers to Minitest + assertions and expectations. +stubberry :: Multiple stubbing 'berries', sweet and useful + stub helpers and assertions. ( stub_must, + assert_method_called, stubbing ORM objects by id ) + +== Unknown Extensions: + +Authors... Please send me a pull request with a description of your minitest extension. + +* assay-minitest +* detroit-minitest +* em-minitest-spec +* flexmock-minitest +* guard-minitest +* guard-minitest-decisiv +* minitest-activemodel +* minitest-ar-assertions +* minitest-capybara-unit +* minitest-colorer +* minitest-deluxe +* minitest-extra-assertions +* minitest-rails-shoulda +* minitest-spec +* minitest-spec-should +* minitest-sugar +* spork-minitest + +== Minitest related goods + +* minitest/pride fabric: https://www.spoonflower.com/fabric/3928730-again-by-katie_allen + +== REQUIREMENTS: + +* Ruby 3.2+. No magic is involved. I hope. + +== INSTALL: + + sudo gem install minitest + +On 1.9, you already have it. To get newer candy you can still install +the gem, and then requiring "minitest/autorun" should automatically +pull it in. If not, you'll need to do it yourself: + + gem "minitest" # ensures you"re using the gem, and not the built-in MT + require "minitest/autorun" + + # ... usual testing stuffs ... + +DO NOTE: There is a serious problem with the way that ruby 1.9/2.0 +packages their own gems. They install a gem specification file, but +don't install the gem contents in the gem path. This messes up +Gem.find_files and many other things (gem which, gem contents, etc). + +Just install minitest as a gem for real and you'll be happier. + +== 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/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/Rakefile b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/Rakefile new file mode 100644 index 0000000..dd19c4f --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/Rakefile @@ -0,0 +1,86 @@ +# -*- ruby -*- + +require "hoe" +$:.unshift "lib" # to pick up lib/minitest/test_task.rb when minitest not installed + +Hoe.plugin :seattlerb +Hoe.plugin :isolate +Hoe.plugin :rdoc +Hoe.plugin :cov + +Hoe.spec "minitest" do + developer "Ryan Davis", "ryand-ruby@zenspider.com" + + license "MIT" + + require_ruby_version ">= 3.2" + + dependency "prism", "~> 1.5" + + self.cov_filter = %w[ tmp ] +end + +desc "Find missing expectations" +task :specs do + $:.unshift "lib" + require "minitest/test" + require "minitest/spec" + + pos_prefix, neg_prefix = "must", "wont" + skip_re = /^(must|wont)$|wont_(throw)|must_(block|not?_|nothing|send|raise$)/x + dont_flip_re = /(must|wont)_(include|respond_to)/ + + map = { + /(must_throw)s/ => '\1', + /(?!not)_same/ => "_be_same_as", + /_in_/ => "_be_within_", + /_operator/ => "_be", + /_includes/ => "_include", + /(must|wont)_(.*_of|nil|silent|empty)/ => '\1_be_\2', + /must_raises/ => "must_raise", + /(must|wont)_pattern/ => '\1_pattern_match', + /(must|wont)_predicate/ => '\1_be', + /(must|wont)_path_exists/ => 'path_\1_exist', + } + + expectations = Minitest::Expectations.public_instance_methods.map(&:to_s) + assertions = Minitest::Assertions.public_instance_methods.map(&:to_s) + + assertions.sort.each do |assertion| + expectation = case assertion + when /^assert/ then + assertion.sub(/^assert/, pos_prefix.to_s) + when /^refute/ then + assertion.sub(/^refute/, neg_prefix.to_s) + end + + next unless expectation + next if expectation =~ skip_re + + regexp, replacement = map.find { |re, _| expectation =~ re } + expectation.sub! regexp, replacement if replacement + + next if expectations.include? expectation + + args = [assertion, expectation].map(&:to_sym).map(&:inspect) + args << :reverse if expectation =~ dont_flip_re + + puts + puts "##" + puts "# :method: #{expectation}" + puts "# See Minitest::Assertions##{assertion}" + puts + puts "infect_an_assertion #{args.join ", "}" + end +end + +task :bugs do + sh "for f in bug*.rb ; do echo $f; echo; #{Gem.ruby} -Ilib $f && rm $f ; done" +end + +Minitest::TestTask.create :testW0 do |t| + t.warning = false + t.test_prelude = "$-w = nil" +end + +# vim: syntax=Ruby diff --git a/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/bin/minitest b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/bin/minitest new file mode 100755 index 0000000..3be59cb --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/bin/minitest @@ -0,0 +1,5 @@ +#!/usr/bin/env -S ruby + +require_relative "../lib/minitest/sprint" + +Minitest::Sprint.run diff --git a/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/design_rationale.rb b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/design_rationale.rb new file mode 100644 index 0000000..de745ea --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/design_rationale.rb @@ -0,0 +1,54 @@ +# Specs: # Equivalent Unit Tests: +############################################################################### +describe Thingy do # class TestThingy < Minitest::Test + before do # def setup + # super + do_some_setup # do_some_setup + end # end + # + it "should do the first thing" do # def test_first_thing + _(1).must_equal 1 # assert_equal 1, 1 + end # end + # end + # + describe SubThingy do # class TestSubThingy < TestThingy + before do # def setup + # super + do_more_setup # do_more_setup + end # end + # + it "should do the second thing" do # def test_second_thing + _(2).must_equal 2 # assert_equal 2, 2 + end # end + end # end +end # + # +############################################################################### +# runs 2 specs # runs 3 tests +############################################################################### +# The specs generate: + +class ThingySpec < Minitest::Spec + def setup + super + do_some_setup + end + + def test_should_do_the_first_thing + _(1).must_equal 1 + end +end + +class SubThingySpec < ThingySpec + def setup + super + do_more_setup + end + + # because only setup/teardown is inherited, not specs + remove_method :test_should_do_the_first_thing + + def test_should_do_the_second_thing + _(2).must_equal 2 + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/hoe/minitest.rb b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/hoe/minitest.rb new file mode 100644 index 0000000..3bc893d --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/hoe/minitest.rb @@ -0,0 +1,30 @@ +# :stopdoc: + +class Hoe + # empty +end + +module Hoe::Minitest + def minitest? + self.name == "minitest" + end + + def initialize_minitest + unless minitest? then + dir = File.expand_path "../..", __FILE__ + + Hoe.add_include_dirs dir if File.directory? dir + end + + gem "minitest" + require "minitest" + version = Minitest::VERSION.split(".").first(2).join "." + + dependency "minitest", "~> #{version}", :development unless + minitest? or ENV["MT_NO_ISOLATE"] + end + + def define_minitest_tasks + self.testlib = :minitest + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest.rb b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest.rb new file mode 100644 index 0000000..bc08b59 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest.rb @@ -0,0 +1,1227 @@ +require "optparse" +require "stringio" +require "etc" + +require_relative "minitest/parallel" +require_relative "minitest/compress" + +## +# The top-level namespace for Minitest. Also the location of the main +# runtime. See +Minitest.run+ for more information. + +module Minitest + VERSION = "6.0.1" # :nodoc: + + @@installed_at_exit ||= false + @@after_run = [] + @extensions = [] + + def self.cattr_accessor name # :nodoc: + (class << self; self; end).attr_accessor name + end + + ## + # The random seed used for this run. This is used to srand at the + # start of the run and between each +Runnable.run+. + # + # Set via Minitest.run after processing args. + + cattr_accessor :seed + + ## + # Parallel test executor + + cattr_accessor :parallel_executor + + n_threads = (ENV["MT_CPU"] || Etc.nprocessors).to_i + + self.parallel_executor = Parallel::Executor.new n_threads if n_threads > 1 + + ## + # Filter object for backtraces. + + cattr_accessor :backtrace_filter + + ## + # Reporter object to be used for all runs. + # + # NOTE: This accessor is only available during setup, not during runs. + + cattr_accessor :reporter + + ## + # Names of known extension plugins. + + cattr_accessor :extensions + + ## + # The signal to use for dumping information to STDERR. Defaults to "INFO". + + cattr_accessor :info_signal + self.info_signal = "INFO" + + cattr_accessor :allow_fork + self.allow_fork = false + + ## + # Registers Minitest to run at process exit + + def self.autorun + Warning[:deprecated] = true + + at_exit { + next if $! and not ($!.kind_of? SystemExit and $!.success?) + + exit_code = nil + + pid = Process.pid + at_exit { + next if !Minitest.allow_fork && Process.pid != pid + @@after_run.reverse_each(&:call) + exit exit_code || false + } + + exit_code = Minitest.run ARGV + } unless @@installed_at_exit + @@installed_at_exit = true + end + + ## + # A simple hook allowing you to run a block of code after everything + # is done running. Eg: + # + # Minitest.after_run { p $debugging_info } + + def self.after_run &block + @@after_run << block + end + + ## + # Manually load plugins by name. + + def self.load *names + names.each do |name| + require "minitest/#{name}_plugin" + + self.extensions << name.to_s + end + end + + ## + # Register a plugin to be used. Does NOT require / load it. + + def self.register_plugin name_or_mod + self.extensions << name_or_mod + nil + end + + def self.load_plugins # :nodoc: + return unless defined? Gem + + seen = {} + + Gem.find_files("minitest/*_plugin.rb").each do |plugin_path| + name = File.basename plugin_path, "_plugin.rb" + + next if seen[name] + seen[name] = true + + require plugin_path + self.extensions << name + end + end + + def self.init_plugins options # :nodoc: + self.extensions.each do |mod_or_meth| + case mod_or_meth + when Symbol, String then + name = mod_or_meth + msg = "plugin_#{name}_init" + next unless self.respond_to? msg + send msg, options + when Module then + recv = mod_or_meth + next unless recv.respond_to? :minitest_plugin_init + recv.minitest_plugin_init options + else + raise ArgumentError, "plugin is %p, but it must be a symbol, string or module" % [mod_or_meth] + end + end + end + + def self.process_args args = [] # :nodoc: + options = { + :io => $stdout, + } + orig_args = args.dup + + warn "--no-plugins is a no-op" if args.delete "--no-plugins" # TODO: remove me! when? + + OptionParser.new do |opts| + opts.program_name = "minitest" + opts.version = Minitest::VERSION + + opts.banner = [ + "Usage: minitest [paths] [options]", + "ruby path/to/test.rb [options]", + "rake test [A=options] (see Minitest::TestTask for more options)\n\n", + ].join "\n or: " + + opts.on "-h", "--help", "Display this help." do + puts opts + exit! true + end + + opts.on "-V", "--version", "Display the version." do + puts "#{opts.program_name} #{Minitest::VERSION}" + exit! true + end + + desc = "Sets random seed. Also via env, eg: SEED=42" + opts.on "-s", "--seed SEED", Integer, desc do |m| + options[:seed] = m + end + + opts.on "-v", "--verbose", "Verbose. Print each name as they run." do + options[:verbose] = true + end + + opts.on "-q", "--quiet", "Quiet. Show no dots while processing files." do + options[:quiet] = true + end + + opts.on "--show-skips", "Show skipped at the end of run." do + options[:show_skips] = true + end + + opts.on "-b", "--bisect", "Run minitest in bisect-mode to isolate flaky tests." + + opts.on "-i", "--include PATTERN", "Include /regexp/ or string for run." do |a| + options[:include] = a + end + + opts.on "-e", "--exclude PATTERN", "Exclude /regexp/ or string from run." do |a| + options[:exclude] = a + end + + # part of my unofficial embedded gem "makeoptparseworkwell" + def opts.topdict(name) = (name.length > 1 ? top.long : top.short) + def opts.alias(from, to) = (dict = topdict(from) ; dict[to] = dict[from]) + + # these will work but won't show up in --help output: + opts.alias "include", "name" + opts.alias "i", "n" + opts.alias "e", "x" + + opts.on "-S", "--skip CODES", String, "Skip reporting of certain types of results (eg E)." do |s| + options[:skip] = s.chars.to_a + end + + opts.on "-W[error]", String, "Turn Ruby warnings into errors" do |s| + options[:Werror] = true + case s + when "error", "all", nil then + require_relative "minitest/error_on_warning" + $VERBOSE = true + ::Warning[:deprecated] = true + else + ::Warning[s.to_sym] = true # check validity of category + end + end + + unless extensions.empty? + opts.separator "" + opts.separator "Known extensions: #{extensions.join ", "}" + + extensions.each do |mod_or_meth| + case mod_or_meth + when Symbol, String then + meth = mod_or_meth + msg = "plugin_#{meth}_options" + send msg, opts, options if respond_to? msg + when Module + recv = mod_or_meth + next unless recv.respond_to? :minitest_plugin_options + recv.minitest_plugin_options opts, options + else + raise ArgumentError, "plugin is %p, but it must be a symbol, string or module" % [mod_or_meth] + end + end + end + + begin + opts.parse! args + rescue OptionParser::InvalidOption => e + puts + puts e + puts + puts opts + exit 1 + end + + orig_args -= args + end + + unless options[:seed] then + srand + options[:seed] = (ENV["SEED"] || srand).to_i % 0xFFFF + orig_args << "--seed" << options[:seed].to_s + end + + options[:args] = orig_args.map { |s| + s.match?(/[\s|&<>$()]/) ? s.inspect : s + }.join " " + + options + end + + ## + # This is the top-level run method. Everything starts from here. It + # tells each Runnable sub-class to run, and each of those are + # responsible for doing whatever they do. + # + # The overall structure of a run looks like this: + # + # [Minitest.load_plugins] optional, called by user, or require what you want + # Minitest.autorun + # Minitest.run(args) + # Minitest.process_args + # Minitest.init_plugins + # Minitest.run_all_suites(reporter, options) + # Runnable.runnables.each |runnable_klass| + # runnable_klass.run_suite(reporter, options) + # filtered_methods = runnable_klass.filter_runnable_methods options + # filtered_methods.each |runnable_method| + # runnable_klass.run(self, runnable_method, reporter) + # runnable_klass.new(runnable_method).run + + def self.run args = [] + options = process_args args + + Minitest.seed = options[:seed] + srand Minitest.seed + + reporter = CompositeReporter.new + reporter << SummaryReporter.new(options[:io], options) + reporter << ProgressReporter.new(options[:io], options) unless options[:quiet] + + self.reporter = reporter # this makes it available to plugins + self.init_plugins options + self.reporter = nil # runnables shouldn't depend on the reporter, ever + + self.parallel_executor.start if parallel_executor.respond_to? :start + reporter.start + begin + run_all_suites reporter, options + finished = true + rescue Interrupt + warn "Interrupted. Exiting..." + end + self.parallel_executor.shutdown if parallel_executor.respond_to? :shutdown + + # might have been removed/replaced during init_plugins: + summary = reporter.reporters.grep(SummaryReporter).first + + reporter.report + + return empty_run! options if finished && summary && summary.count == 0 + finished and reporter.passed? + end + + def self.empty_run! options # :nodoc: + filter = options[:include] + return true unless filter # no filter, but nothing ran == success + + warn "Nothing ran for filter: %s" % [filter] + + require "did_you_mean" # soft dependency, punt if it doesn't load + + ms = Runnable.runnables.flat_map(&:runnable_methods) + cs = DidYouMean::SpellChecker.new(dictionary: ms).correct filter + + warn DidYouMean::Formatter.message_for cs unless cs.empty? + rescue LoadError + # do nothing + end + + ## + # Internal run method. Responsible for telling all Runnable + # sub-classes to run. + + def self.run_all_suites reporter, options + suites = Runnable.runnables.shuffle + parallel, serial = suites.partition { |s| s.run_order == :parallel } + + # If we run the parallel tests before the serial tests, the parallel tests + # could run in parallel with the serial tests. This would be bad because + # the serial tests won't lock around Reporter#record. Run the serial tests + # first, so that after they complete, the parallel tests will lock when + # recording results. + serial.map { |suite| suite.run_suite reporter, options } + + parallel.map { |suite| suite.run_suite reporter, options } + end + + def self.filter_backtrace bt # :nodoc: + result = backtrace_filter.filter bt + result = bt.dup if result.empty? + result + end + + ## + # Represents anything "runnable", like Test, Spec, Benchmark, or + # whatever you can dream up. + # + # Subclasses of this are automatically registered and available in + # Runnable.runnables. + + class Runnable + ## + # Number of assertions executed in this run. + + attr_accessor :assertions + + ## + # An assertion raised during the run, if any. + + attr_accessor :failures + + ## + # The time it took to run. + + attr_accessor :time + + def time_it # :nodoc: + t0 = Minitest.clock_time + + yield + ensure + self.time = Minitest.clock_time - t0 + end + + ## + # Name of the run. + + def name + @NAME + end + + ## + # Set the name of the run. + + def name= o + @NAME = o + end + + ## + # Returns all instance methods matching the pattern +re+. + + def self.methods_matching re + public_instance_methods(true).grep(re).map(&:to_s) + end + + def self.reset # :nodoc: + @@runnables = [] + end + + reset + + ## + # Returns an array of filtered +runnable_methods+. Uses + # options[:include] (--include arguments) and options[:exclude] + # (--exclude arguments) values to filter. + + def self.filter_runnable_methods options={} + pos = options[:include] + neg = options[:exclude] + + pos = Regexp.new $1 if pos.kind_of?(String) && pos =~ %r%/(.*)/% + neg = Regexp.new $1 if neg.kind_of?(String) && neg =~ %r%/(.*)/% + + # at most 1-2% slower than a 1-pass version, stop optimizing this + self.runnable_methods + .select { |m| !pos || pos === m || pos === "#{self}##{m}" } + .reject { |m| neg && (neg === m || neg === "#{self}##{m}") } + end + + ## + # Responsible for running all runnable methods in a given class, + # each in its own instance. Each instance is passed to the + # reporter to record. + + def Runnable.run_suite reporter, options = {} + filtered_methods = filter_runnable_methods options + + return if filtered_methods.empty? + + t0 = name = nil + + @_info_handler = lambda do + unless reporter.passed? then + warn "Current results:" + warn reporter.reporters.grep(SummaryReporter).first + end + + warn "Current: %s#%s %.2fs" % [self, name, Minitest.clock_time - t0] + end + + with_info_handler do + filtered_methods.each do |method_name| + name = method_name + t0 = Minitest.clock_time + + run self, method_name, reporter + end + end + end + + ## + # Runs a single method and has the reporter record the result. + # This was considered internal API but is factored out of run so + # that subclasses can specialize the running of an individual + # test. See Minitest::ParallelTest::ClassMethods for an example. + + def Runnable.run klass, method_name, reporter + reporter.prerecord klass, method_name + reporter.record klass.new(method_name).run + end + + ## + # Defines the order to run tests (:random by default). Override + # this or use a convenience method to change it for your tests. + + def self.run_order + :random + end + + def self.with_info_handler _reporter=nil, &block # :nodoc: + on_signal ::Minitest.info_signal, @_info_handler, &block + end + + SIGNALS = Signal.list # :nodoc: + + def self.on_signal name, action # :nodoc: + supported = SIGNALS[name] + + old_trap = trap name do + old_trap.call if old_trap.respond_to? :call + action.call + end if supported + + yield + ensure + trap name, old_trap if supported + end + + ## + # Each subclass of Runnable is responsible for overriding this + # method to return all runnable methods. See #methods_matching. + + def self.runnable_methods + raise NotImplementedError, "subclass responsibility" + end + + ## + # Returns all subclasses of Runnable. + + def self.runnables + @@runnables + end + + def failure # :nodoc: + self.failures.first + end + + def initialize name # :nodoc: + self.name = name + self.failures = [] + self.assertions = 0 + # lazy initializer for metadata + end + + ## + # Metadata you attach to the test results that get sent to the reporter. + # + # Lazily initializes to a hash, to keep memory down. + # + # NOTE: this data *must* be plain (read: marshal-able) data! + # Hashes! Arrays! Strings! + + def metadata + @metadata ||= {} + end + + ## + # Sets metadata, mainly used for +Result.from+. + + attr_writer :metadata + + ## + # Returns true if metadata exists. + + def metadata? + defined? @metadata + end + + ## + # Runs a single method. Needs to return self. + + def run + raise NotImplementedError, "subclass responsibility" + end + + ## + # Did this run pass? + # + # Note: skipped runs are not considered passing, but they don't + # cause the process to exit non-zero. + + def passed? + raise NotImplementedError, "subclass responsibility" + end + + ## + # Returns a single character string to print based on the result + # of the run. One of ".", "F", + # "E" or "S". + + def result_code + raise NotImplementedError, "subclass responsibility" + end + + ## + # Was this run skipped? See #passed? for more information. + + def skipped? + raise NotImplementedError, "subclass responsibility" + end + end # Runnable + + ## + # Shared code for anything that can get passed to a Reporter. See + # Minitest::Test & Minitest::Result. + + module Reportable + ## + # Did this run pass? + # + # Note: skipped runs are not considered passing, but they don't + # cause the process to exit non-zero. + + def passed? + not self.failure + end + + BASE_DIR = "#{Dir.pwd}/" # :nodoc: + + ## + # The location identifier of this test. Depends on a method + # existing called class_name. + + def location + loc = " [#{self.failure.location.delete_prefix BASE_DIR}]" unless passed? or error? + "#{self.class_name}##{self.name}#{loc}" + end + + def class_name # :nodoc: + raise NotImplementedError, "subclass responsibility" + end + + ## + # Returns ".", "F", or "E" based on the result of the run. + + def result_code + self.failure and self.failure.result_code or "." + end + + ## + # Was this run skipped? + + def skipped? + self.failure and Skip === self.failure + end + + ## + # Did this run error? + + def error? + self.failures.any? UnexpectedError + end + end # Reportable + + ## + # This represents a test result in a clean way that can be + # marshalled over a wire. Tests can do anything they want to the + # test instance and can create conditions that cause Marshal.dump to + # blow up. By using Result.from(a_test) you can be reasonably sure + # that the test result can be marshalled. + + class Result < Runnable + include Minitest::Reportable + + ## + # The class name of the test result. + + attr_accessor :klass + + ## + # The location of the test method. + + attr_accessor :source_location + + ## + # Create a new test result from a Runnable instance. + + def self.from runnable + o = runnable + + r = self.new o.name + r.klass = o.class.name + r.assertions = o.assertions + r.failures = o.failures.dup + r.time = o.time + r.metadata = o.metadata if o.metadata? + + r.source_location = o.method(o.name).source_location rescue ["unknown", -1] + + r + end + + def class_name # :nodoc: + self.klass # for Minitest::Reportable + end + + def to_s # :nodoc: + return location if passed? and not skipped? + + failures.map { |failure| + "#{failure.result_label}:\n#{self.location}:\n#{failure.message}\n" + }.join "\n" + end + end # Result + + ## + # Defines the API for Reporters. Subclass this and override whatever + # you want. Go nuts. + + class AbstractReporter + + def initialize # :nodoc: + @mutex = Mutex.new + end + + ## + # Starts reporting on the run. + + def start + end + + ## + # About to start running a test. This allows a reporter to show + # that it is starting or that we are in the middle of a test run. + + def prerecord klass, name + end + + ## + # Output and record the result of the test. Call + # {result#result_code}[rdoc-ref:Runnable#result_code] to get the + # result character string. Stores the result of the run if the run + # did not pass. + + def record result + end + + ## + # Outputs the summary of the run. + + def report + end + + ## + # Did this run pass? + + def passed? + true + end + + def synchronize &block # :nodoc: + @mutex.synchronize(&block) + end + end # AbstractReportera + + class Reporter < AbstractReporter # :nodoc: + ## + # The IO used to report. + + attr_accessor :io + + ## + # Command-line options for this run. + + attr_accessor :options + + def initialize io = $stdout, options = {} # :nodoc: + super() + self.io = io + self.options = options + end + end + + ## + # A very simple reporter that prints the "dots" during the run. + # + # This is added to the top-level CompositeReporter at the start of + # the run. If you want to change the output of minitest via a + # plugin, pull this out of the composite and replace it with your + # own. + + class ProgressReporter < Reporter + def prerecord klass, name # :nodoc: + return unless options[:verbose] + + io.print "%s#%s = " % [klass.name, name] + io.flush + end + + def record result # :nodoc: + io.print "%.2f s = " % [result.time] if options[:verbose] + io.print result.result_code + io.puts if options[:verbose] + end + end + + ## + # A reporter that gathers statistics about a test run. Does not do + # any IO because meant to be used as a parent class for a reporter + # that does. + # + # If you want to create an entirely different type of output (eg, + # CI, HTML, etc), this is the place to start. + # + # Example: + # + # class JenkinsCIReporter < StatisticsReporter + # def report + # super # Needed to calculate some statistics + # + # print " #{new_bt.size}" + error.set_backtrace new_bt + end + + self.error = error + end + + def backtrace # :nodoc: + self.error.backtrace + end + + BASE_RE = %r%#{Regexp.escape Dir.pwd}/% # :nodoc: + + def message # :nodoc: + bt = Minitest.filter_backtrace(self.backtrace).join("\n ") + .gsub(BASE_RE, "") + "#{self.error.class}: #{self.error.message}\n #{bt}" + end + + def result_label # :nodoc: + "Error" + end + end + + ## + # Assertion raised on warning when running in -Werror mode. + + class UnexpectedWarning < Assertion + def result_label # :nodoc: + "Warning" + end + end + + ## + # Provides a simple set of guards that you can use in your tests + # to skip execution if it is not applicable. These methods are + # mixed into Test as both instance and class methods so you + # can use them inside or outside of the test methods. + # + # def test_something_for_mri + # skip "bug 1234" if jruby? + # # ... + # end + # + # if windows? then + # # ... lots of test methods ... + # end + + module Guard + + ## + # Is this running on jruby? + + def jruby? platform = RUBY_PLATFORM + "java" == platform + end + + ## + # Is this running on mri? + + def mri? platform = RUBY_DESCRIPTION + platform.start_with? "ruby" + end + + ## + # Is this running on macOS? + + def osx? platform = RUBY_PLATFORM + platform.include? "darwin" + end + + ## + # Is this running on windows? + + def windows? platform = RUBY_PLATFORM + /mswin|mingw/.match? platform + end + end + + ## + # The standard backtrace filter for minitest. + # + # See Minitest.backtrace_filter=. + + class BacktraceFilter + + MT_RE = %r%lib/minitest|internal:warning% # :nodoc: + + ## + # The regular expression to use to filter backtraces. Defaults to +MT_RE+. + + attr_accessor :regexp + + def initialize regexp = MT_RE # :nodoc: + self.regexp = regexp + end + + ## + # Filter +bt+ to something useful. Returns the whole thing if + # $DEBUG (ruby) or $MT_DEBUG (env). + + def filter bt + return ["No backtrace"] unless bt + + return bt.dup if $DEBUG || ENV["MT_DEBUG"] + + new_bt = bt.take_while { |line| !regexp.match? line.to_s } + new_bt = bt.select { |line| !regexp.match? line.to_s } if new_bt.empty? + new_bt = bt.dup if new_bt.empty? + + new_bt + end + end + + self.backtrace_filter = BacktraceFilter.new + + # :stopdoc: + + if defined? Process::CLOCK_MONOTONIC # :nodoc: + def self.clock_time + Process.clock_gettime Process::CLOCK_MONOTONIC + end + else + def self.clock_time + Time.now + end + end + + class Runnable # re-open + def self.inherited klass # :nodoc: + self.runnables << klass + super + end + end + + # :startdoc: +end + +require_relative "minitest/test" diff --git a/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/assertions.rb b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/assertions.rb new file mode 100644 index 0000000..bb638b0 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/assertions.rb @@ -0,0 +1,818 @@ +require "rbconfig" +require "tempfile" +require "stringio" + +module Minitest + ## + # Minitest Assertions. All assertion methods accept a +msg+ which is + # printed if the assertion fails. + # + # Protocol: Nearly everything here boils up to +assert+, which + # expects to be able to increment an instance accessor named + # +assertions+. This is not provided by Assertions and must be + # provided by the thing including Assertions. See Minitest::Runnable + # for an example. + + module Assertions + UNDEFINED = Object.new # :nodoc: + + def UNDEFINED.inspect # :nodoc: + "UNDEFINED" # again with the rdoc bugs... :( + end + + ## + # Returns the diff command to use in #diff. Tries to intelligently + # figure out what diff to use. + + def self.diff + return @diff if defined? @diff + + @diff = if (RbConfig::CONFIG["host_os"] =~ /mswin|mingw/ and + system "diff.exe", __FILE__, __FILE__) then + "diff.exe -u" + elsif system "gdiff", __FILE__, __FILE__ then + "gdiff -u" # solaris and kin suck + elsif system "diff", __FILE__, __FILE__ then + "diff -u" + else + nil + end + end + + ## + # Set the diff command to use in #diff. + + def self.diff= o + @diff = o + end + + ## + # Returns a diff between +exp+ and +act+. If there is no known + # diff command or if it doesn't make sense to diff the output + # (single line, short output), then it simply returns a basic + # comparison between the two. + # + # See +things_to_diff+ for more info. + + def diff exp, act + result = nil + + expect, butwas = things_to_diff exp, act + + return "Expected: #{mu_pp exp}\n Actual: #{mu_pp act}" unless + expect + + Tempfile.create "expect" do |a| + a.puts expect + a.flush + + Tempfile.create "butwas" do |b| + b.puts butwas + b.flush + + result = `#{Minitest::Assertions.diff} #{a.path} #{b.path}` + result.sub!(/^\-\-\- .+/, "--- expected") + result.sub!(/^\+\+\+ .+/, "+++ actual") + + if result.empty? then + klass = exp.class + result = [ + "No visible difference in the #{klass}#inspect output.\n", + "You should look at the implementation of #== on ", + "#{klass} or its members.\n", + expect, + ].join + end + end + end + + result + end + + ## + # Returns things to diff [expect, butwas], or [nil, nil] if nothing to diff. + # + # Criterion: + # + # 1. Strings include newlines or escaped newlines, but not both. + # 2. or: String lengths are > 30 characters. + # 3. or: Strings are equal to each other (but maybe different encodings?). + # 4. and: we found a diff executable. + + def things_to_diff exp, act + expect = mu_pp_for_diff exp + butwas = mu_pp_for_diff act + + e1, e2 = expect.include?("\n"), expect.include?("\\n") + b1, b2 = butwas.include?("\n"), butwas.include?("\\n") + + need_to_diff = + (e1 ^ e2 || + b1 ^ b2 || + expect.size > 30 || + butwas.size > 30 || + expect == butwas) && + Minitest::Assertions.diff + + need_to_diff && [expect, butwas] + end + + ## + # This returns a human-readable version of +obj+. By default + # #inspect is called. You can override this to use #pretty_inspect + # if you want. + # + # See Minitest::Test.make_my_diffs_pretty! + + def mu_pp obj + s = obj.inspect.encode Encoding.default_external + + return s unless String === obj && + (obj.encoding != Encoding.default_external || !obj.valid_encoding?) + + enc = "# encoding: #{obj.encoding}" + val = "# valid: #{obj.valid_encoding?}" + + [enc, val, s].join "\n" + end + + ## + # This returns a diff-able more human-readable version of +obj+. + # This differs from the regular mu_pp because it expands escaped + # newlines and makes hex-values (like object_ids) generic. This + # uses mu_pp to do the first pass and then cleans it up. + + def mu_pp_for_diff obj + str = mu_pp obj + + # both '\n' & '\\n' (_after_ mu_pp (aka inspect)) + single = str.match?(/(?exp == act printing the difference between + # the two, if possible. + # + # If there is no visible difference but the assertion fails, you + # should suspect that your #== is buggy, or your inspect output is + # missing crucial details. For nicer structural diffing, set + # Minitest::Test.make_my_diffs_pretty! + # + # For floats use assert_in_delta. + # + # See also: Minitest::Assertions.diff + + def assert_equal exp, act, msg = nil + msg = message(msg, nil) { diff exp, act } + + refute_nil exp, message { "Use assert_nil if expecting nil" } if exp.nil? # don't count + + assert exp == act, msg + end + + ## + # For comparing Floats. Fails unless +exp+ and +act+ are within +delta+ + # of each other. + # + # assert_in_delta Math::PI, (22.0 / 7.0), 0.01 + + def assert_in_delta exp, act, delta = 0.001, msg = nil + n = (exp - act).abs + msg = message(msg) { + "Expected |#{exp} - #{act}| (#{n}) to be <= #{delta}" + } + assert delta >= n, msg + end + + ## + # For comparing Floats. Fails unless +exp+ and +act+ have a relative + # error less than +epsilon+. + + def assert_in_epsilon exp, act, epsilon = 0.001, msg = nil + assert_in_delta exp, act, [exp.abs, act.abs].min * epsilon, msg + end + + ## + # Fails unless +collection+ includes +obj+. + + def assert_includes collection, obj, msg = nil + msg = message(msg) { + "Expected #{mu_pp collection} to include #{mu_pp obj}" + } + assert_operator collection, :include?, obj, msg + end + + ## + # Fails unless +obj+ is an instance of +cls+. + + def assert_instance_of cls, obj, msg = nil + msg = message(msg) { + "Expected #{mu_pp obj} to be an instance of #{cls}, not #{obj.class}" + } + + assert obj.instance_of?(cls), msg + end + + ## + # Fails unless +obj+ is a kind of +cls+. + + def assert_kind_of cls, obj, msg = nil + msg = message(msg) { + "Expected #{mu_pp obj} to be a kind of #{cls}, not #{obj.class}" + } + + assert obj.kind_of?(cls), msg + end + + ## + # Fails unless +matcher+ =~ +obj+. + + def assert_match matcher, obj, msg = nil + msg = message(msg) { "Expected #{mu_pp matcher} to match #{mu_pp obj}" } + assert_respond_to matcher, :=~ + matcher = Regexp.new Regexp.escape matcher if String === matcher + assert matcher =~ obj, msg + + Regexp.last_match + end + + ## + # Fails unless +obj+ is nil + + def assert_nil obj, msg = nil + msg = message(msg) { "Expected #{mu_pp obj} to be nil" } + assert obj.nil?, msg + end + + ## + # For testing with binary operators. Eg: + # + # assert_operator 5, :<=, 4 + + def assert_operator o1, op, o2 = UNDEFINED, msg = nil + return assert_predicate o1, op, msg if UNDEFINED == o2 + assert_respond_to o1, op + msg = message(msg) { "Expected #{mu_pp o1} to be #{op} #{mu_pp o2}" } + assert o1.__send__(op, o2), msg + end + + ## + # Fails if stdout or stderr do not output the expected results. + # Pass in nil if you don't care about that streams output. Pass in + # "" if you require it to be silent. Pass in a regexp if you want + # to pattern match. + # + # assert_output(/hey/) { method_with_output } + # + # NOTE: this uses #capture_io, not #capture_subprocess_io. + # + # See also: #assert_silent + + def assert_output stdout = nil, stderr = nil + flunk "assert_output requires a block to capture output." unless + block_given? + + out, err = capture_io do + yield + end + + err_msg = Regexp === stderr ? :assert_match : :assert_equal if stderr + out_msg = Regexp === stdout ? :assert_match : :assert_equal if stdout + + y = send err_msg, stderr, err, "In stderr" if err_msg + x = send out_msg, stdout, out, "In stdout" if out_msg + + (!stdout || x) && (!stderr || y) + rescue Assertion + raise + rescue => e + raise UnexpectedError, e + end + + ## + # Fails unless +path+ exists. + + def assert_path_exists path, msg = nil + msg = message(msg) { "Expected path '#{path}' to exist" } + assert File.exist?(path), msg + end + + ## + # For testing with pattern matching (only supported with Ruby 3.0 and later) + # + # # pass + # assert_pattern { [1,2,3] => [Integer, Integer, Integer] } + # + # # fail "length mismatch (given 3, expected 1)" + # assert_pattern { [1,2,3] => [Integer] } + # + # The bare => pattern will raise a NoMatchingPatternError on failure, which would + # normally be counted as a test error. This assertion rescues NoMatchingPatternError and + # generates a test failure. Any other exception will be raised as normal and generate a test + # error. + + def assert_pattern + flunk "assert_pattern requires a block to capture errors." unless block_given? + + yield + pass + rescue NoMatchingPatternError => e + flunk e.message + end + + ## + # For testing with predicates. Eg: + # + # assert_predicate str, :empty? + # + # This is really meant for specs and is front-ended by assert_operator: + # + # str.must_be :empty? + + def assert_predicate o1, op, msg = nil + assert_respond_to o1, op, include_all:true + msg = message(msg) { "Expected #{mu_pp o1} to be #{op}" } + assert o1.__send__(op), msg + end + + ## + # Fails unless the block raises one of +exp+. Returns the + # exception matched so you can check the message, attributes, etc. + # + # +exp+ takes an optional message on the end to help explain + # failures and defaults to StandardError if no exception class is + # passed. Eg: + # + # assert_raises(CustomError) { method_with_custom_error } + # + # With custom error message: + # + # assert_raises(CustomError, 'This should have raised CustomError') { method_with_custom_error } + # + # Using the returned object: + # + # error = assert_raises(CustomError) do + # raise CustomError, 'This is really bad' + # end + # + # assert_equal 'This is really bad', error.message + + def assert_raises *exp + flunk "assert_raises requires a block to capture errors." unless + block_given? + + msg = "#{exp.pop}.\n" if String === exp.last + exp << StandardError if exp.empty? + + begin + yield + rescue *exp => e + pass # count assertion + return e + rescue Minitest::Assertion # incl Skip & UnexpectedError + # don't count assertion + raise + rescue SignalException, SystemExit + raise + rescue Exception => e + flunk proc { + exception_details(e, "#{msg}#{mu_pp exp} exception expected, not") + } + end + + exp = exp.first if exp.size == 1 + + flunk "#{msg}#{mu_pp exp} expected but nothing was raised." + end + + ## + # Fails unless +obj+ responds to +meth+. + # include_all defaults to false to match Object#respond_to? + + def assert_respond_to obj, meth, msg = nil, include_all: false + msg = message(msg) { "Expected #{mu_pp obj} (#{obj.class}) to respond to ##{meth}" } + assert obj.respond_to?(meth, include_all), msg + end + + ## + # Fails unless +exp+ and +act+ are #equal? + + def assert_same exp, act, msg = nil + msg = message(msg) { + data = [mu_pp(act), act.object_id, mu_pp(exp), exp.object_id] + "Expected %s (oid=%d) to be the same as %s (oid=%d)" % data + } + assert exp.equal?(act), msg + end + + ## + # Fails if the block outputs anything to stderr or stdout. + # + # See also: #assert_output + + def assert_silent + assert_output "", "" do + yield + end + end + + ## + # Fails unless the block throws +sym+ + + def assert_throws sym, msg = nil + default = "Expected #{mu_pp sym} to have been thrown" + caught = true + value = catch sym do + begin + yield + rescue ArgumentError => e # 1.9+ exception + raise e unless e.message.include? "uncaught throw" + default += ", not #{e.message.split(/ /).last}" + end + caught = false + end + + assert caught, message(msg) { default } + value + rescue Assertion + raise + rescue => e + raise UnexpectedError, e + end + + ## + # Captures $stdout and $stderr into strings: + # + # out, err = capture_io do + # puts "Some info" + # warn "You did a bad thing" + # end + # + # assert_match %r%info%, out + # assert_match %r%bad%, err + # + # NOTE: For efficiency, this method uses StringIO and does not + # capture IO for subprocesses. Use #capture_subprocess_io for + # that. + + def capture_io + _synchronize do + begin + captured_stdout, captured_stderr = StringIO.new, StringIO.new + + orig_stdout, orig_stderr = $stdout, $stderr + $stdout, $stderr = captured_stdout, captured_stderr + + yield + + return captured_stdout.string, captured_stderr.string + ensure + $stdout = orig_stdout + $stderr = orig_stderr + end + end + end + + ## + # Captures $stdout and $stderr into strings, using Tempfile to + # ensure that subprocess IO is captured as well. + # + # out, err = capture_subprocess_io do + # system "echo Some info" + # system "echo You did a bad thing 1>&2" + # end + # + # assert_match %r%info%, out + # assert_match %r%bad%, err + # + # NOTE: This method is approximately 10x slower than #capture_io so + # only use it when you need to test the output of a subprocess. + + def capture_subprocess_io + _synchronize do + begin + require "tempfile" + + captured_stdout, captured_stderr = Tempfile.new("out"), Tempfile.new("err") + + orig_stdout, orig_stderr = $stdout.dup, $stderr.dup + $stdout.reopen captured_stdout + $stderr.reopen captured_stderr + + yield + + $stdout.rewind + $stderr.rewind + + return 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 + end + end + + ## + # Returns details for exception +e+ + + def exception_details e, msg + [ + msg, + "Class: <#{e.class}>", + "Message: <#{e.message.inspect}>", + "---Backtrace---", + Minitest.filter_backtrace(e.backtrace), + "---------------", + ].join "\n" + end + + ## + # Fails after a given date (in the local time zone). This allows + # you to put time-bombs in your tests if you need to keep + # something around until a later date lest you forget about it. + + def fail_after y, m, d, msg + flunk msg if Time.now > Time.local(y, m, d) + end + + ## + # Fails with +msg+. + + def flunk msg = nil + msg ||= "Epic Fail!" + assert false, msg + end + + ## + # Returns a proc that delays generation of an output message. If + # +msg+ is a proc (eg, from another +message+ call) return +msg+ + # as-is. Otherwise, return a proc that will output +msg+ along + # with the value of the result of the block passed to +message+. + + def message msg = nil, ending = ".", &default + return msg if Proc === msg + proc { + custom_message = "#{msg}.\n" unless msg.nil? or msg.to_s.empty? + "#{custom_message}#{default.call}#{ending}" + } + end + + ## + # used for counting assertions + + def pass _msg = nil + assert true + end + + ## + # Fails if +test+ is truthy. + + def refute test, msg = nil + msg ||= message { "Expected #{mu_pp test} to not be truthy" } + assert !test, msg + end + + ## + # Fails if +obj+ is empty. + + def refute_empty obj, msg = nil + msg = message(msg) { "Expected #{mu_pp obj} to not be empty" } + refute_predicate obj, :empty?, msg + end + + ## + # Fails if exp == act. + # + # For floats use refute_in_delta. + + def refute_equal exp, act, msg = nil + msg = message(msg) { + "Expected #{mu_pp act} to not be equal to #{mu_pp exp}" + } + refute exp == act, msg + end + + ## + # For comparing Floats. Fails if +exp+ is within +delta+ of +act+. + # + # refute_in_delta Math::PI, (22.0 / 7.0) + + def refute_in_delta exp, act, delta = 0.001, msg = nil + n = (exp - act).abs + msg = message(msg) { + "Expected |#{exp} - #{act}| (#{n}) to not be <= #{delta}" + } + refute delta >= n, msg + end + + ## + # For comparing Floats. Fails if +exp+ and +act+ have a relative error + # less than +epsilon+. + + def refute_in_epsilon exp, act, epsilon = 0.001, msg = nil + refute_in_delta exp, act, [exp.abs, act.abs].min * epsilon, msg + end + + ## + # Fails if +obj+ includes +sub+. + + def refute_includes obj, sub, msg = nil + msg = message(msg) { "Expected #{mu_pp obj} to not include #{mu_pp sub}" } + refute_operator obj, :include?, sub, msg + end + + ## + # Fails if +obj+ is an instance of +cls+. + + def refute_instance_of cls, obj, msg = nil + msg = message(msg) { + "Expected #{mu_pp obj} to not be an instance of #{cls}" + } + refute obj.instance_of?(cls), msg + end + + ## + # Fails if +obj+ is a kind of +cls+. + + def refute_kind_of cls, obj, msg = nil + msg = message(msg) { "Expected #{mu_pp obj} to not be a kind of #{cls}" } + refute obj.kind_of?(cls), msg + end + + ## + # Fails if +matcher+ =~ +obj+. + + def refute_match matcher, obj, msg = nil + msg = message(msg) { "Expected #{mu_pp matcher} to not match #{mu_pp obj}" } + matcher = Regexp.new Regexp.escape matcher if String === matcher + refute_operator matcher, :=~, obj, msg + end + + ## + # Fails if +obj+ is nil. + + def refute_nil obj, msg = nil + msg = message(msg) { "Expected #{mu_pp obj} to not be nil" } + refute obj.nil?, msg + end + + ## + # For testing with pattern matching (only supported with Ruby 3.0 and later) + # + # # pass + # refute_pattern { [1,2,3] => [String] } + # + # # fail "NoMatchingPatternError expected, but nothing was raised." + # refute_pattern { [1,2,3] => [Integer, Integer, Integer] } + # + # This assertion expects a NoMatchingPatternError exception, and will fail if none is raised. Any + # other exceptions will be raised as normal and generate a test error. + + def refute_pattern + flunk "refute_pattern requires a block to capture errors." unless block_given? + + yield + flunk "NoMatchingPatternError expected, but nothing was raised." + rescue NoMatchingPatternError + pass + end + + ## + # Fails if +o1+ is not +op+ +o2+. Eg: + # + # refute_operator 1, :>, 2 #=> pass + # refute_operator 1, :<, 2 #=> fail + + def refute_operator o1, op, o2 = UNDEFINED, msg = nil + return refute_predicate o1, op, msg if UNDEFINED == o2 + assert_respond_to o1, op + msg = message(msg) { "Expected #{mu_pp o1} to not be #{op} #{mu_pp o2}" } + refute o1.__send__(op, o2), msg + end + + ## + # Fails if +path+ exists. + + def refute_path_exists path, msg = nil + msg = message(msg) { "Expected path '#{path}' to not exist" } + refute File.exist?(path), msg + end + + ## + # For testing with predicates. + # + # refute_predicate str, :empty? + # + # This is really meant for specs and is front-ended by refute_operator: + # + # str.wont_be :empty? + + def refute_predicate o1, op, msg = nil + assert_respond_to o1, op + msg = message(msg) { "Expected #{mu_pp o1} to not be #{op}" } + refute o1.__send__(op), msg + end + + ## + # Fails if +obj+ responds to the message +meth+. + # include_all defaults to false to match Object#respond_to? + + def refute_respond_to obj, meth, msg = nil, include_all: false + msg = message(msg) { "Expected #{mu_pp obj} to not respond to #{meth}" } + + refute obj.respond_to?(meth, include_all), msg + end + + ## + # Fails if +exp+ is the same (by object identity) as +act+. + + def refute_same exp, act, msg = nil + msg = message(msg) { + data = [mu_pp(act), act.object_id, mu_pp(exp), exp.object_id] + "Expected %s (oid=%d) to not be the same as %s (oid=%d)" % data + } + refute exp.equal?(act), msg + end + + ## + # Skips the current run. If run in verbose-mode, the skipped run + # gets listed at the end of the run but doesn't cause a failure + # exit code. + + def skip msg = nil, _ignored = nil + msg ||= "Skipped, no message given" + @skip = true + raise Minitest::Skip, msg + end + + ## + # Skips the current run until a given date (in the local time + # zone). This allows you to put some fixes on hold until a later + # date, but still holds you accountable and prevents you from + # forgetting it. + + def skip_until y, m, d, msg + skip msg if Time.now < Time.local(y, m, d) + where = caller(1..1).first.rpartition(":in").reject(&:empty?).first + warn "Stale skip_until %p at %s" % [msg, where] + end + + ## + # Was this testcase skipped? Meant for #teardown. + + def skipped? + defined?(@skip) and @skip + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/autorun.rb b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/autorun.rb new file mode 100644 index 0000000..bf7ec8a --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/autorun.rb @@ -0,0 +1,5 @@ +require_relative "../minitest" +require_relative "spec" +require_relative "hell" if ENV["MT_HELL"] + +Minitest.autorun diff --git a/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/benchmark.rb b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/benchmark.rb new file mode 100644 index 0000000..63b0d1d --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/benchmark.rb @@ -0,0 +1,452 @@ +require_relative "test" +require_relative "spec" + +module Minitest + ## + # Subclass Benchmark to create your own benchmark runs. Methods + # starting with "bench_" get executed on a per-class. + # + # See Minitest::Assertions + + class Benchmark < Test + def self.io # :nodoc: + @io + end + + def io # :nodoc: + self.class.io + end + + def self.run klass, method_name, reporter # :nodoc: + @io = reporter.io + super + end + + def self.runnable_methods # :nodoc: + methods_matching(/^bench_/) + end + + ## + # Returns a set of ranges stepped exponentially from +min+ to + # +max+ by powers of +base+. Eg: + # + # bench_exp(2, 16, 2) # => [2, 4, 8, 16] + + def self.bench_exp min, max, base = 10 + min = (Math.log10(min) / Math.log10(base)).to_i + max = (Math.log10(max) / Math.log10(base)).to_i + + (min..max).map { |m| base ** m }.to_a + end + + ## + # Returns a set of ranges stepped linearly from +min+ to +max+ by + # +step+. Eg: + # + # bench_linear(20, 40, 10) # => [20, 30, 40] + + def self.bench_linear min, max, step = 10 + (min..max).step(step).to_a + end + + ## + # Specifies the ranges used for benchmarking for that class. + # Defaults to exponential growth from 1 to 10k by powers of 10. + # Override if you need different ranges for your benchmarks. + # + # See also: ::bench_exp and ::bench_linear. + + def self.bench_range + bench_exp 1, 10_000 + end + + ## + # Runs the given +work+, gathering the times of each run. Range + # and times are then passed to a given +validation+ proc. Outputs + # the benchmark name and times in tab-separated format, making it + # easy to paste into a spreadsheet for graphing or further + # analysis. + # + # Ranges are specified by ::bench_range. + # + # Eg: + # + # def bench_algorithm + # validation = proc { |x, y| ... } + # assert_performance validation do |n| + # @obj.algorithm(n) + # end + # end + + def assert_performance validation, &work + range = self.class.bench_range + + io.print self.name + + times = [] + + range.each do |x| + GC.start + t0 = Minitest.clock_time + instance_exec(x, &work) + t = Minitest.clock_time - t0 + + io.print "\t%9.6f" % t + times << t + end + io.puts + + validation[range, times] + end + + ## + # Runs the given +work+ and asserts that the times gathered fit to + # match a constant rate (eg, linear slope == 0) within a given + # +threshold+. Note: because we're testing for a slope of 0, R^2 + # is not a good determining factor for the fit, so the threshold + # is applied against the slope itself. As such, you probably want + # to tighten it from the default. + # + # See https://www.graphpad.com/guides/prism/8/curve-fitting/reg_intepretingnonlinr2.htm + # for more details. + # + # Fit is calculated by #fit_linear. + # + # Ranges are specified by ::bench_range. + # + # Eg: + # + # def bench_algorithm + # assert_performance_constant 0.9999 do |n| + # @obj.algorithm(n) + # end + # end + + def assert_performance_constant threshold = 0.99, &work + validation = proc do |range, times| + a, b, rr = fit_linear range, times + assert_in_delta 0, b, 1 - threshold + [a, b, rr] + end + + assert_performance validation, &work + end + + ## + # Runs the given +work+ and asserts that the times gathered fit to + # match a exponential curve within a given error +threshold+. + # + # Fit is calculated by #fit_exponential. + # + # Ranges are specified by ::bench_range. + # + # Eg: + # + # def bench_algorithm + # assert_performance_exponential 0.9999 do |n| + # @obj.algorithm(n) + # end + # end + + def assert_performance_exponential threshold = 0.99, &work + assert_performance validation_for_fit(:exponential, threshold), &work + end + + ## + # Runs the given +work+ and asserts that the times gathered fit to + # match a logarithmic curve within a given error +threshold+. + # + # Fit is calculated by #fit_logarithmic. + # + # Ranges are specified by ::bench_range. + # + # Eg: + # + # def bench_algorithm + # assert_performance_logarithmic 0.9999 do |n| + # @obj.algorithm(n) + # end + # end + + def assert_performance_logarithmic threshold = 0.99, &work + assert_performance validation_for_fit(:logarithmic, threshold), &work + end + + ## + # Runs the given +work+ and asserts that the times gathered fit to + # match a straight line within a given error +threshold+. + # + # Fit is calculated by #fit_linear. + # + # Ranges are specified by ::bench_range. + # + # Eg: + # + # def bench_algorithm + # assert_performance_linear 0.9999 do |n| + # @obj.algorithm(n) + # end + # end + + def assert_performance_linear threshold = 0.99, &work + assert_performance validation_for_fit(:linear, threshold), &work + end + + ## + # Runs the given +work+ and asserts that the times gathered curve + # fit to match a power curve within a given error +threshold+. + # + # Fit is calculated by #fit_power. + # + # Ranges are specified by ::bench_range. + # + # Eg: + # + # def bench_algorithm + # assert_performance_power 0.9999 do |x| + # @obj.algorithm + # end + # end + + def assert_performance_power threshold = 0.99, &work + assert_performance validation_for_fit(:power, threshold), &work + end + + ## + # Takes an array of x/y pairs and calculates the general R^2 value. + # + # See: https://en.wikipedia.org/wiki/Coefficient_of_determination + + def fit_error xys + y_bar = sigma(xys) { |_, y| y } / xys.size.to_f + ss_tot = sigma(xys) { |_, y| (y - y_bar) ** 2 } + ss_err = sigma(xys) { |x, y| (yield(x) - y) ** 2 } + + 1 - (ss_err / ss_tot) + end + + ## + # To fit a functional form: y = ae^(bx). + # + # Takes x and y values and returns [a, b, r^2]. + # + # See: https://mathworld.wolfram.com/LeastSquaresFittingExponential.html + + def fit_exponential xs, ys + n = xs.size + xys = xs.zip ys + sxlny = sigma(xys) { |x, y| x * Math.log(y) } + slny = sigma(xys) { |_, y| Math.log(y) } + sx2 = sigma(xys) { |x, _| x * x } + sx = sigma xs + + c = n * sx2 - sx ** 2 + a = (slny * sx2 - sx * sxlny) / c + b = ( n * sxlny - sx * slny ) / c + + return Math.exp(a), b, fit_error(xys) { |x| Math.exp(a + b * x) } + end + + ## + # To fit a functional form: y = a + b*ln(x). + # + # Takes x and y values and returns [a, b, r^2]. + # + # See: https://mathworld.wolfram.com/LeastSquaresFittingLogarithmic.html + + def fit_logarithmic xs, ys + n = xs.size + xys = xs.zip ys + slnx2 = sigma(xys) { |x, _| Math.log(x) ** 2 } + slnx = sigma(xys) { |x, _| Math.log(x) } + sylnx = sigma(xys) { |x, y| y * Math.log(x) } + sy = sigma(xys) { |_, y| y } + + c = n * slnx2 - slnx ** 2 + b = ( n * sylnx - sy * slnx ) / c + a = (sy - b * slnx) / n + + return a, b, fit_error(xys) { |x| a + b * Math.log(x) } + end + + ## + # Fits the functional form: a + bx. + # + # Takes x and y values and returns [a, b, r^2]. + # + # See: https://mathworld.wolfram.com/LeastSquaresFitting.html + + def fit_linear xs, ys + n = xs.size + xys = xs.zip ys + sx = sigma xs + sy = sigma ys + sx2 = sigma(xs) { |x| x ** 2 } + sxy = sigma(xys) { |x, y| x * y } + + c = n * sx2 - sx**2 + a = (sy * sx2 - sx * sxy) / c + b = ( n * sxy - sx * sy ) / c + + return a, b, fit_error(xys) { |x| a + b * x } + end + + ## + # To fit a functional form: y = ax^b. + # + # Takes x and y values and returns [a, b, r^2]. + # + # See: https://mathworld.wolfram.com/LeastSquaresFittingPowerLaw.html + + def fit_power xs, ys + n = xs.size + xys = xs.zip ys + slnxlny = sigma(xys) { |x, y| Math.log(x) * Math.log(y) } + slnx = sigma(xs) { |x | Math.log(x) } + slny = sigma(ys) { | y| Math.log(y) } + slnx2 = sigma(xs) { |x | Math.log(x) ** 2 } + + b = (n * slnxlny - slnx * slny) / (n * slnx2 - slnx ** 2) + a = (slny - b * slnx) / n + + return Math.exp(a), b, fit_error(xys) { |x| (Math.exp(a) * (x ** b)) } + end + + ## + # Enumerates over +enum+ mapping +block+ if given, returning the + # sum of the result. Eg: + # + # sigma([1, 2, 3]) # => 1 + 2 + 3 => 6 + # sigma([1, 2, 3]) { |n| n ** 2 } # => 1 + 4 + 9 => 14 + + def sigma enum, &block + enum = enum.map(&block) if block + enum.sum + end + + ## + # Returns a proc that calls the specified fit method and asserts + # that the error is within a tolerable threshold. + + def validation_for_fit msg, threshold + proc do |range, times| + a, b, rr = send "fit_#{msg}", range, times + assert_operator rr, :>=, threshold + [a, b, rr] + end + end + end +end + +module Minitest + ## + # The spec version of Minitest::Benchmark. + + class BenchSpec < Benchmark + extend Minitest::Spec::DSL + + ## + # This is used to define a new benchmark method. You usually don't + # use this directly and is intended for those needing to write new + # performance curve fits (eg: you need a specific polynomial fit). + # + # See ::bench_performance_linear for an example of how to use this. + + def self.bench name, &block + define_method "bench_#{name.gsub(/\W+/, "_")}", &block + end + + ## + # Specifies the ranges used for benchmarking for that class. + # + # bench_range do + # bench_exp(2, 16, 2) + # end + # + # See Minitest::Benchmark#bench_range for more details. + + def self.bench_range &block + return super unless block + + meta = (class << self; self; end) + meta.send :define_method, "bench_range", &block + end + + ## + # Create a benchmark that verifies that the performance is linear. + # + # describe "my class Bench" do + # bench_performance_linear "fast_algorithm", 0.9999 do |n| + # @obj.fast_algorithm(n) + # end + # end + + def self.bench_performance_linear name, threshold = 0.99, &work + bench name do + assert_performance_linear threshold, &work + end + end + + ## + # Create a benchmark that verifies that the performance is constant. + # + # describe "my class Bench" do + # bench_performance_constant "zoom_algorithm!" do |n| + # @obj.zoom_algorithm!(n) + # end + # end + + def self.bench_performance_constant name, threshold = 0.99, &work + bench name do + assert_performance_constant threshold, &work + end + end + + ## + # Create a benchmark that verifies that the performance is exponential. + # + # describe "my class Bench" do + # bench_performance_exponential "algorithm" do |n| + # @obj.algorithm(n) + # end + # end + + def self.bench_performance_exponential name, threshold = 0.99, &work + bench name do + assert_performance_exponential threshold, &work + end + end + + ## + # Create a benchmark that verifies that the performance is logarithmic. + # + # describe "my class Bench" do + # bench_performance_logarithmic "algorithm" do |n| + # @obj.algorithm(n) + # end + # end + + def self.bench_performance_logarithmic name, threshold = 0.99, &work + bench name do + assert_performance_logarithmic threshold, &work + end + end + + ## + # Create a benchmark that verifies that the performance is power. + # + # describe "my class Bench" do + # bench_performance_power "algorithm" do |n| + # @obj.algorithm(n) + # end + # end + + def self.bench_performance_power name, threshold = 0.99, &work + bench name do + assert_performance_power threshold, &work + end + end + end + + Minitest::Spec.register_spec_type(/Bench(mark)?$/, Minitest::BenchSpec) +end diff --git a/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/bisect.rb b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/bisect.rb new file mode 100644 index 0000000..3373194 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/bisect.rb @@ -0,0 +1,306 @@ +require_relative "find_minimal_combination" +require_relative "server" +require "shellwords" +require "rbconfig" +require_relative "path_expander" # this is gonna break some shit? + +module Minitest; end # :nodoc: + +## +# Minitest::Bisect helps you isolate and debug random test failures. + +class Minitest::Bisect + VERSION = "1.8.0" # :nodoc: + + class PathExpander < Minitest::VendoredPathExpander # :nodoc: + TEST_GLOB = "**/{test_*,*_test,spec_*,*_spec}.rb" # :nodoc: + + attr_accessor :rb_flags + + def initialize args = ARGV # :nodoc: + super args, TEST_GLOB, "test" + self.rb_flags = %w[-Itest:lib] + end + + ## + # Overrides PathExpander#process_flags to filter out ruby flags + # from minitest flags. Only supports -I, -d, and -w for + # ruby. + + def process_flags flags + flags.reject { |flag| # all hits are truthy, so this works out well + case flag + when /^-I(.*)/ then + rb_flags << flag + when /^-d/ then + rb_flags << flag + when /^-w/ then + rb_flags << flag + else + false + end + } + end + end + + mtbv = ENV["MTB_VERBOSE"].to_i + SHH = case # :nodoc: + when mtbv == 1 then " > /dev/null" + when mtbv >= 2 then nil + else " > /dev/null 2>&1" + end + + # Borrowed from rake + RUBY = ENV['RUBY'] || + File.join(RbConfig::CONFIG['bindir'], + RbConfig::CONFIG['ruby_install_name'] + + RbConfig::CONFIG['EXEEXT']).sub(/.*\s.*/m, '"\&"') + + ## + # True if this run has seen a failure. + + attr_accessor :tainted + alias :tainted? :tainted + + ## + # Failures seen in this run. Shape: + # + # {"file.rb"=>{"Class"=>["test_method1", "test_method2"] ...} ...} + + attr_accessor :failures + + ## + # An array of tests seen so far. NOT cleared by #reset. + + attr_accessor :culprits + + attr_accessor :seen_bad # :nodoc: + + ## + # Top-level runner. Instantiate and call +run+, handling exceptions. + + def self.run files + new.run files + rescue => e + warn e.message + warn "Try running with MTB_VERBOSE=2 to verify." + exit 1 + end + + ## + # Instantiate a new Bisect. + + def initialize + self.culprits = [] + self.failures = Hash.new { |h, k| h[k] = Hash.new { |h2, k2| h2[k2] = [] } } + end + + ## + # Reset per-bisect-run variables. + + def reset + self.seen_bad = false + self.tainted = false + failures.clear + # not clearing culprits on purpose + end + + ## + # Instance-level runner. Handles Minitest::Server, argument + # processing, and invoking +bisect_methods+. + + def run args + Minitest::Server.run self + + cmd = nil + + mt_flags = args.dup + expander = Minitest::Bisect::PathExpander.new mt_flags + + files = expander.process.to_a + rb_flags = expander.rb_flags + mt_flags += ["--server", $$.to_s] + + cmd = bisect_methods files, rb_flags, mt_flags + + puts "Final reproduction:" + puts + + system cmd.sub(/--server \d+/, "") + ensure + Minitest::Server.stop + end + + ## + # Normal: find "what is the minimal combination of tests to run to + # make X fail?" + # + # Run with: minitest_bisect ... --seed=N + # + # 1. Verify the failure running normally with the seed. + # 2. If no failure, punt. + # 3. If no passing tests before failure, punt. (No culprits == no debug) + # 4. Verify the failure doesn't fail in isolation. + # 5. If it still fails by itself, warn that it might not be an ordering + # issue. + # 6. Cull all tests after the failure, they're not involved. + # 7. Bisect the culprits + bad until you find a minimal combo that fails. + # 8. Display minimal combo by running one last time. + # + # Inverted: find "what is the minimal combination of tests to run to + # make this test pass?" + # + # Run with: minitest_bisect ... --seed=N -n="/failing_test_name_regexp/" + # + # 1. Verify the failure by running normally w/ the seed and -n=/.../ + # 2. If no failure, punt. + # 3. Verify the passing case by running everything. + # 4. If failure, punt. This is not a false positive. + # 5. Cull all tests after the bad test from #1, they're not involved. + # 6. Bisect the culprits + bad until you find a minimal combo that passes. + # 7. Display minimal combo by running one last time. + + def bisect_methods files, rb_flags, mt_flags + bad_names, mt_flags = mt_flags.partition { |s| s =~ /^(?:-n|--name)/ } + normal = bad_names.empty? + inverted = !normal + + if inverted then + time_it "reproducing w/ scoped failure (inverted run!)...", build_methods_cmd(build_files_cmd(files, rb_flags, mt_flags + bad_names)) + raise "No failures. Probably not a false positive. Aborting." if failures.empty? + bad = map_failures + end + + cmd = build_files_cmd(files, rb_flags, mt_flags) + + msg = normal ? "reproducing..." : "reproducing false positive..." + time_it msg, build_methods_cmd(cmd) + + if normal then + raise "Reproduction run passed? Aborting." unless tainted? + raise "Verification failed. No culprits? Aborting." if culprits.empty? && seen_bad + else + raise "Reproduction failed? Not false positive. Aborting." if tainted? + raise "Verification failed. No culprits? Aborting." if culprits.empty? || seen_bad + end + + if normal then + bad = map_failures + + time_it "verifying...", build_methods_cmd(cmd, [], bad) + + new_bad = map_failures + + if bad == new_bad then + warn "Tests fail by themselves. This may not be an ordering issue." + end + end + + idx = culprits.index bad.first + self.culprits = culprits.take idx+1 if idx # cull tests after bad + + # culprits populated by initial reproduction via minitest/server + found, count = culprits.find_minimal_combination_and_count do |test| + prompt = "# of culprit methods: #{test.size}" + + time_it prompt, build_methods_cmd(cmd, test, bad) + + normal == tainted? # either normal and failed, or inverse and passed + end + + puts + puts "Minimal methods found in #{count} steps:" + puts + puts "Culprit methods: %p" % [found + bad] + puts + cmd = build_methods_cmd cmd, found, bad + puts cmd.sub(/--server \d+/, "") + puts + cmd + end + + def time_it prompt, cmd # :nodoc: + print prompt + t0 = Time.now + system "#{cmd} #{SHH}" + puts " in %.2f sec" % (Time.now - t0) + end + + def map_failures # :nodoc: + # from: {"file.rb"=>{"Class"=>["test_method1", "test_method2"]}} + # to: ["Class#test_method1", "Class#test_method2"] + failures.values.map { |h| + h.map { |k,vs| vs.map { |v| "#{k}##{v}" } } + }.flatten.sort + end + + def build_files_cmd culprits, rb, mt # :nodoc: + tests = culprits.flatten.compact.map { |f| %(require "./#{f}") }.join " ; " + + %(#{RUBY} #{rb.shelljoin} -e '#{tests}' -- #{mt.map(&:to_s).shelljoin}) + end + + def build_methods_cmd cmd, culprits = [], bad = nil # :nodoc: + reset + + if bad then + re = build_re culprits + bad + + cmd += " -n \"#{re}\"" if bad + end + + if ENV["MTB_VERBOSE"].to_i >= 1 then + puts + puts cmd + puts + end + + cmd + end + + def build_re bad # :nodoc: + re = [] + + # bad by class, you perv + bbc = bad.map { |s| s.split(/#/, 2) }.group_by(&:first) + + bbc.each do |klass, methods| + methods = methods.map(&:last).flatten.uniq.map { |method| + re_escape method + } + + methods = methods.join "|" + re << /#{re_escape klass}#(?:#{methods})/.to_s[7..-2] # (?-mix:...) + end + + re = re.join("|").to_s.gsub(/-mix/, "") + + "/^(?:#{re})$/" + end + + def re_escape str # :nodoc: + str.gsub(/([`'"!?&\[\]\(\)\{\}\|\+])/, '\\\\\1') + end + + ############################################################ + # Server Methods: + + def minitest_start # :nodoc: + self.failures.clear + end + + def minitest_result file, klass, method, fails, assertions, time # :nodoc: + fails.reject! { |fail| Minitest::Skip === fail } + + if fails.empty? then + culprits << "#{klass}##{method}" unless seen_bad # UGH + else + self.seen_bad = true + end + + return if fails.empty? + + self.tainted = true + self.failures[file][klass] << method + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/complete.rb b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/complete.rb new file mode 100755 index 0000000..0c5e23f --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/complete.rb @@ -0,0 +1,56 @@ +#!/usr/bin/env -S ruby + +# :stopdoc: + +require "optparse" +require "shellwords" + +# complete -o bashdefault -f -C 'ruby lib/minitest/complete.rb' minitest +# using eg: +# COMP_LINE="blah test/test_file.rb -n test_pattern" +# or test directly with: +# ./lib/minitest/complete.rb test/test_file.rb -n test_pattern + +argv = Shellwords.split ENV["COMP_LINE"] || ARGV.join(" ") +comp_re = nil + +begin + OptionParser.new do |opts| + # part of my unofficial embedded gem "makeoptparseworkwell" + def opts.topdict(name) = (name.length > 1 ? top.long : top.short) + def opts.alias(from, to) = (dict = topdict(from) ; dict[to] = dict[from]) + + opts.on "-n", "--name [METHOD]", "minitest option" do |m| + comp_re = Regexp.new m + end + + opts.alias "name", "include" + opts.alias "name", "exclude" + opts.alias "n", "i" + opts.alias "n", "e" + opts.alias "n", "x" + end.parse! argv +rescue + retry # ignore options passed to Ruby +end + +path = argv.find_all { |f| File.file? f }.last + +exit unless comp_re && path + +require "prism" + +names, queue = [], [Prism.parse_file(path).value] + +while node = queue.shift do + if node.type == :def_node then + name = node.name + names << name if name =~ comp_re + else + queue.concat node.compact_child_nodes # no need to process def body + end +end + +puts names.sort + +# :startdoc: diff --git a/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/compress.rb b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/compress.rb new file mode 100644 index 0000000..7ba0c53 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/compress.rb @@ -0,0 +1,94 @@ +module Minitest + ## + # Compresses backtraces. + + module Compress + + ## + # Takes a backtrace (array of strings) and compresses repeating + # cycles in it to make it more readable. + + def compress orig + ary = orig + + eswo = ->(a, n, off) { # each_slice_with_offset + if off.zero? then + a.each_slice n + else + # [ ...off... [...n...] [...n...] ... ] + front, back = a.take(off), a.drop(off) + [front].chain back.each_slice n + end + } + + 3.times do # maybe don't use loop do here? + index = ary # [ a b c b c b c d ] + .size + .times # 0...size + .group_by { |i| ary[i] } # { a: [0] b: [1 3 5], c: [2 4 6], d: [7] } + + order = index + .reject { |k, v| v.size == 1 } # { b: [1 3 5], c: [2 4 6] } + .sort_by { |k, a1| ### sort by max dist + min offset + d = a1.each_cons(2).sum { |a2, b| b-a2 } + [-d, a1.first] + } # b: [1 3 5] c: [2 4 6] + + ranges = order + .map { |k, a1| # [[1..2 3..4] [2..3 4..5]] + a1 + .each_cons(2) + .map { |a2, b| a2..b-1 } + } + + big_ranges = ranges + .flat_map { |a| # [1..2 3..4 2..3 4..5] + a.sort_by { |r| [-r.size, r.first] }.first 5 + } + .first(100) + + culprits = big_ranges + .map { |r| + eswo[ary, r.size, r.begin] # [o1 s1 s1 s2 s2] + .chunk_while { |a, b| a == b } # [[o1] [s1 s1] [s2 s2]] + .map { |a| [a.size, a.first] } # [[1 o1] [2 s1] [2 s2]] + } + .select { |chunks| + chunks.any? { |a| a.first > 1 } # compressed anything? + } + + min = culprits + .min_by { |a| a.flatten.size } # most compressed + + break unless min + + ary = min.flat_map { |(n, lines)| + if n > 1 then + [[n, compress(lines)]] # [o1 [2 s1] [2 s2]] + else + lines + end + } + end + + format = ->(lines) { + lines.flat_map { |line| + case line + when Array then + n, lines = line + lines = format[lines] + [ + " +->> #{n} cycles of #{lines.size} lines:", + *lines.map { |s| " | #{s}" }, + " +-<<", + ] + else + line + end + } + } + + format[ary] + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/error_on_warning.rb b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/error_on_warning.rb new file mode 100644 index 0000000..d9dc16c --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/error_on_warning.rb @@ -0,0 +1,11 @@ +module Minitest + + module ErrorOnWarning # :nodoc: + def warn message, category: nil + message = "[#{category}] #{message}" if category + raise UnexpectedWarning, message + end + end + + ::Warning.singleton_class.prepend ErrorOnWarning +end diff --git a/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/expectations.rb b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/expectations.rb new file mode 100644 index 0000000..ef144d4 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/expectations.rb @@ -0,0 +1,321 @@ +## +# It's where you hide your "assertions". +# +# Please note, because of the way that expectations are implemented, +# all expectations (eg must_equal) are dependent upon a thread local +# variable +:current_spec+. If your specs rely on mixing threads into +# the specs themselves, you're better off using assertions or the new +# _(value) wrapper. For example: +# +# it "should still work in threads" do +# my_threaded_thingy do +# (1+1).must_equal 2 # bad +# assert_equal 2, 1+1 # good +# _(1 + 1).must_equal 2 # good +# value(1 + 1).must_equal 2 # good, also #expect +# _ { 1 + "1" }.must_raise TypeError # good +# end +# end + +module Minitest::Expectations + + ## + # See Minitest::Assertions#assert_empty. + # + # _(collection).must_be_empty + # + # :method: must_be_empty + + infect_an_assertion :assert_empty, :must_be_empty, :unary + + ## + # See Minitest::Assertions#assert_equal + # + # _(a).must_equal b + # + # :method: must_equal + + infect_an_assertion :assert_equal, :must_equal + + ## + # See Minitest::Assertions#assert_in_delta + # + # _(n).must_be_close_to m [, delta] + # + # :method: must_be_close_to + + infect_an_assertion :assert_in_delta, :must_be_close_to + + infect_an_assertion :assert_in_delta, :must_be_within_delta # :nodoc: + + ## + # See Minitest::Assertions#assert_in_epsilon + # + # _(n).must_be_within_epsilon m [, epsilon] + # + # :method: must_be_within_epsilon + + infect_an_assertion :assert_in_epsilon, :must_be_within_epsilon + + ## + # See Minitest::Assertions#assert_includes + # + # _(collection).must_include obj + # + # :method: must_include + + infect_an_assertion :assert_includes, :must_include, :reverse + + ## + # See Minitest::Assertions#assert_instance_of + # + # _(obj).must_be_instance_of klass + # + # :method: must_be_instance_of + + infect_an_assertion :assert_instance_of, :must_be_instance_of + + ## + # See Minitest::Assertions#assert_kind_of + # + # _(obj).must_be_kind_of mod + # + # :method: must_be_kind_of + + infect_an_assertion :assert_kind_of, :must_be_kind_of + + ## + # See Minitest::Assertions#assert_match + # + # _(a).must_match b + # + # :method: must_match + + infect_an_assertion :assert_match, :must_match + + ## + # See Minitest::Assertions#assert_nil + # + # _(obj).must_be_nil + # + # :method: must_be_nil + + infect_an_assertion :assert_nil, :must_be_nil, :unary + + ## + # See Minitest::Assertions#assert_operator + # + # _(n).must_be :<=, 42 + # + # This can also do predicates: + # + # _(str).must_be :empty? + # + # :method: must_be + + infect_an_assertion :assert_operator, :must_be, :reverse + + ## + # See Minitest::Assertions#assert_output + # + # _ { ... }.must_output out_or_nil [, err] + # + # :method: must_output + + infect_an_assertion :assert_output, :must_output, :block + + ## + # See Minitest::Assertions#assert_pattern + # + # _ { ... }.must_pattern_match [...] + # + # :method: must_pattern_match + + infect_an_assertion :assert_pattern, :must_pattern_match, :block + + ## + # See Minitest::Assertions#assert_raises + # + # _ { ... }.must_raise exception + # + # :method: must_raise + + infect_an_assertion :assert_raises, :must_raise, :block + + ## + # See Minitest::Assertions#assert_respond_to + # + # _(obj).must_respond_to msg + # + # :method: must_respond_to + + infect_an_assertion :assert_respond_to, :must_respond_to, :reverse + + ## + # See Minitest::Assertions#assert_same + # + # _(a).must_be_same_as b + # + # :method: must_be_same_as + + infect_an_assertion :assert_same, :must_be_same_as + + ## + # See Minitest::Assertions#assert_silent + # + # _ { ... }.must_be_silent + # + # :method: must_be_silent + + infect_an_assertion :assert_silent, :must_be_silent, :block + + ## + # See Minitest::Assertions#assert_throws + # + # _ { ... }.must_throw sym + # + # :method: must_throw + + infect_an_assertion :assert_throws, :must_throw, :block + + ## + # See Minitest::Assertions#assert_path_exists + # + # _(some_path).path_must_exist + # + # :method: path_must_exist + + infect_an_assertion :assert_path_exists, :path_must_exist, :unary + + ## + # See Minitest::Assertions#refute_path_exists + # + # _(some_path).path_wont_exist + # + # :method: path_wont_exist + + infect_an_assertion :refute_path_exists, :path_wont_exist, :unary + + ## + # See Minitest::Assertions#refute_empty + # + # _(collection).wont_be_empty + # + # :method: wont_be_empty + + infect_an_assertion :refute_empty, :wont_be_empty, :unary + + ## + # See Minitest::Assertions#refute_equal + # + # _(a).wont_equal b + # + # :method: wont_equal + + infect_an_assertion :refute_equal, :wont_equal + + ## + # See Minitest::Assertions#refute_in_delta + # + # _(n).wont_be_close_to m [, delta] + # + # :method: wont_be_close_to + + infect_an_assertion :refute_in_delta, :wont_be_close_to + + infect_an_assertion :refute_in_delta, :wont_be_within_delta # :nodoc: + + ## + # See Minitest::Assertions#refute_in_epsilon + # + # _(n).wont_be_within_epsilon m [, epsilon] + # + # :method: wont_be_within_epsilon + + infect_an_assertion :refute_in_epsilon, :wont_be_within_epsilon + + ## + # See Minitest::Assertions#refute_includes + # + # _(collection).wont_include obj + # + # :method: wont_include + + infect_an_assertion :refute_includes, :wont_include, :reverse + + ## + # See Minitest::Assertions#refute_instance_of + # + # _(obj).wont_be_instance_of klass + # + # :method: wont_be_instance_of + + infect_an_assertion :refute_instance_of, :wont_be_instance_of + + ## + # See Minitest::Assertions#refute_kind_of + # + # _(obj).wont_be_kind_of mod + # + # :method: wont_be_kind_of + + infect_an_assertion :refute_kind_of, :wont_be_kind_of + + ## + # See Minitest::Assertions#refute_match + # + # _(a).wont_match b + # + # :method: wont_match + + infect_an_assertion :refute_match, :wont_match + + ## + # See Minitest::Assertions#refute_nil + # + # _(obj).wont_be_nil + # + # :method: wont_be_nil + + infect_an_assertion :refute_nil, :wont_be_nil, :unary + + ## + # See Minitest::Assertions#refute_operator + # + # _(n).wont_be :<=, 42 + # + # This can also do predicates: + # + # str.wont_be :empty? + # + # :method: wont_be + + infect_an_assertion :refute_operator, :wont_be, :reverse + + ## + # See Minitest::Assertions#refute_pattern + # + # _ { ... }.wont_pattern_match [...] + # + # :method: wont_pattern_match + + infect_an_assertion :refute_pattern, :wont_pattern_match, :block + + ## + # See Minitest::Assertions#refute_respond_to + # + # _(obj).wont_respond_to msg + # + # :method: wont_respond_to + + infect_an_assertion :refute_respond_to, :wont_respond_to, :reverse + + ## + # See Minitest::Assertions#refute_same + # + # _(a).wont_be_same_as b + # + # :method: wont_be_same_as + + infect_an_assertion :refute_same, :wont_be_same_as +end diff --git a/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/find_minimal_combination.rb b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/find_minimal_combination.rb new file mode 100755 index 0000000..402713b --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/find_minimal_combination.rb @@ -0,0 +1,127 @@ +#!/usr/bin/ruby -w + +## +# Finds the minimal combination of a collection of items that satisfy +# +test+. + +class ComboFinder + ## + # Find the minimal combination of a collection of items that satisfy + # +test+. + # + # If you think of the collection as a binary tree, this algorithm + # does a breadth first search of the combinations that satisfy + # +test+. + #-- + # level collection + # + # 0 A + # 1 B C + # 2 D E F G + # 3 1 2 3 4 5 6 7 8 + # + # This assumes that A has already been tested and you're now trying + # to reduce the match. Starting at level 1, test B & C separately. + # If either test positive, reduce the search space accordingly. If + # not, step down to level 2 and search w/ finer granularity (ie, DF, + # DG, EF--DE and FG were already tested as B & C). Repeat until a + # minimal combination is found. + + def find_minimal_combination ary + level, n_combos = 1, 1 + seen = {} + + d "Total number of culprits: #{ary.size}" + + loop do + size = 2 ** (Math.log(ary.size) / Math.log(2)).round + divs = 2 ** level + done = divs >= size + divs = size if done + + subsections = ary.each_slice(size/divs).to_a.combination(n_combos) + + d + d "# new round!" + d "# of subsections in this round: #{subsections.to_a.size}" + d + + found = subsections.find { |a| + b = a.flatten + + next if seen[b] + + d "# trying #{b.size} at level #{level} / combo #{n_combos}" + cache_result yield(b), b, seen + } + + if found then + ary = found.flatten + break if done + + seen.delete ary + + d "# FOUND!" + d "# search space size = #{ary.size}" + d "# resetting level and n_combos to 1" + + level = n_combos = 1 + else + if done then + n_combos += 1 + d "# increasing n_combos to #{n_combos}" + break if n_combos > size + else + level += 1 + n_combos = level + d "# setting level to #{level} and n_combos to #{n_combos}" + end + end + end + + ary + end + + def d s = "" # :nodoc: + warn s if ENV["MTB_DEBUG"] + end + + def cache_result result, data, cache # :nodoc: + cache[data] = true + + return result if result + + unless result or data.size > 128 then + max = data.size + subdiv = 2 + until subdiv >= max do + data.each_slice(max / subdiv) do |sub_data| + cache[sub_data] = true + end + subdiv *= 2 + end + end + + result + end +end + +class Array # :nodoc: + ## + # Find the minimal combination of a collection of items that satisfy +test+. + + def find_minimal_combination &test + ComboFinder.new.find_minimal_combination(self, &test) + end + + def find_minimal_combination_and_count + count = 0 + + found = self.find_minimal_combination do |ary| + count += 1 + yield ary + end + + return found, count + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/hell.rb b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/hell.rb new file mode 100644 index 0000000..69881c6 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/hell.rb @@ -0,0 +1,11 @@ +require_relative "parallel" + +class Minitest::Test + parallelize_me! +end + +begin + require "minitest/proveit" +rescue LoadError + warn "NOTE: `gem install minitest-proveit` for even more hellish tests" +end diff --git a/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/manual_plugins.rb b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/manual_plugins.rb new file mode 100644 index 0000000..7a1db21 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/manual_plugins.rb @@ -0,0 +1,4 @@ +# +# See the functionality in Minitest#load +# +warn "This file is no longer necessary. Called from #{caller.first}" diff --git a/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/parallel.rb b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/parallel.rb new file mode 100644 index 0000000..523495e --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/parallel.rb @@ -0,0 +1,72 @@ +require "thread" + +module Minitest + module Parallel # :nodoc: + + ## + # The engine used to run multiple tests in parallel. + + class Executor + + ## + # The size of the pool of workers. + + attr_reader :size + + ## + # Create a parallel test executor of with +size+ workers. + + def initialize size + @size = size + @queue = Thread::Queue.new + @pool = nil + end + + ## + # Start the executor + + def start + @pool = Array.new(size) { + Thread.new @queue do |queue| + Thread.current.abort_on_exception = true + while job = queue.pop do + klass, method, reporter = job + reporter.synchronize { reporter.prerecord klass, method } + result = klass.new(method).run + reporter.synchronize { reporter.record result } + end + end + } + end + + ## + # Add a job to the queue + + def << work; @queue << work; end + + ## + # Shuts down the pool of workers by signalling them to quit and + # waiting for them all to finish what they're currently working + # on. + + def shutdown + size.times { @queue << nil } + @pool.each(&:join) + end + end + + module Test # :nodoc: + def _synchronize; Minitest::Test.io_lock.synchronize { yield }; end # :nodoc: + + module ClassMethods # :nodoc: + def run klass, method_name, reporter + Minitest.parallel_executor << [klass, method_name, reporter] + end + + def run_order + :parallel + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/path_expander.rb b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/path_expander.rb new file mode 100644 index 0000000..b57df33 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/path_expander.rb @@ -0,0 +1,425 @@ +require "prism" +require "pathname" # for ruby 3 + +module Minitest; end # :nodoc: + +## +# PathExpander helps pre-process command-line arguments expanding +# directories into their constituent files. It further helps by +# providing additional mechanisms to make specifying subsets easier +# with path subtraction and allowing for command-line arguments to be +# saved in a file. +# +# NOTE: this is NOT an options processor. It is a path processor +# (basically everything else besides options). It does provide a +# mechanism for pre-filtering cmdline options, but not with the intent +# of actually processing them in PathExpander. Use OptionParser to +# deal with options either before or after passing ARGV through +# PathExpander. + +class Minitest::VendoredPathExpander + # extracted version = "2.0.0" + + ## + # The args array to process. + + attr_accessor :args + + ## + # The glob used to expand dirs to files. + + attr_accessor :glob + + ## + # The path to scan if no paths are found in the initial scan. + + attr_accessor :path + + ## + # Create a new path expander that operates on args and expands via + # glob as necessary. Takes an optional +path+ arg to fall back on if + # no paths are found on the initial scan (see #process_args). + + def initialize args, glob, path = "." + self.args = args + self.glob = glob + self.path = path + end + + ## + # Takes an array of paths and returns an array of paths where all + # directories are expanded to all files found via the glob provided + # to PathExpander. + # + # Paths are normalized to not have a leading "./". + + def expand_dirs_to_files *dirs + dirs.flatten.map { |p| + if File.directory? p then + Dir[File.join(p, glob)].find_all { |f| File.file? f } + else + p + end + }.flatten.sort.map { |s| _normalize s } + end + + def _normalize(f) = Pathname.new(f).cleanpath.to_s # :nodoc: + + ## + # Process a file into more arguments. Override this to add + # additional capabilities. + + def process_file path + File.readlines(path).map(&:chomp) + end + + ## + # Enumerate over args passed to PathExpander and return a list of + # files and flags to process. Arguments are processed as: + # + # @file_of_args :: Read the file and append to args. + # -file_path :: Subtract path from file to be processed. + # -dir_path :: Expand and subtract paths from files to be processed. + # -not_a_path :: Add to flags to be processed. + # dir_path :: Expand and add to files to be processed. + # file_path :: Add to files to be processed. + # - :: Add "-" (stdin) to files to be processed. + # + # See expand_dirs_to_files for details on how expansion occurs. + # + # Subtraction happens last, regardless of argument ordering. + # + # If no files are found (which is not the same as having an empty + # file list after subtraction), then fall back to expanding on the + # default #path given to initialize. + + def process_args + pos_files = [] + neg_files = [] + flags = [] + clean = true + + root_dir = File.expand_path "/" # needed for windows paths + + args.each do |arg| + case arg + when /^@(.*)/ then # push back on, so they can have dirs/-/@ as well + clean = false + args.concat process_file $1 + when "-" then + pos_files << arg + when /^-(.*)/ then + if File.exist? $1 then + clean = false + neg_files += expand_dirs_to_files($1) + else + flags << arg + end + else + root_path = File.expand_path(arg) == root_dir # eg: -n /./ + if File.exist? arg and not root_path then + clean = false + pos_files += expand_dirs_to_files(arg) + else + flags << arg + end + end + end + + files = pos_files - neg_files + files += expand_dirs_to_files(self.path) if files.empty? && clean + + [files, flags] + end + + ## + # Process over flags and treat any special ones here. Returns an + # array of the flags you haven't processed. + # + # This version does nothing. Subclass and override for + # customization. + + def process_flags flags + flags + end + + ## + # Top-level method processes args. If no block is given, immediately + # returns with an Enumerator for further chaining. + # + # Otherwise, it calls +pre_process+, +process_args+ and + # +process_flags+, enumerates over the files, and then calls + # +post_process+, returning self for any further chaining. + # + # Most of the time, you're going to provide a block to process files + # and do nothing more with the result. Eg: + # + # PathExpander.new(ARGV).process do |f| + # puts "./#{f}" + # end + # + # or: + # + # PathExpander.new(ARGV).process # => Enumerator + + def process(&b) + return enum_for(:process) unless block_given? + + pre_process + + files, flags = process_args + + args.replace process_flags flags + + files.uniq.each(&b) + + post_process + + self + end + + def pre_process = nil + def post_process = nil + + ## + # A file filter mechanism similar to, but not as extensive as, + # .gitignore files: + # + # + If a pattern does not contain a slash, it is treated as a shell glob. + # + If a pattern ends in a slash, it matches on directories (and contents). + # + Otherwise, it matches on relative paths. + # + # File.fnmatch is used throughout, so glob patterns work for all 3 types. + # + # Takes a list of +files+ and either an io or path of +ignore+ data + # and returns a list of files left after filtering. + + def filter_files files, ignore + ignore_paths = if ignore.respond_to? :read then + ignore.read + elsif File.exist? ignore then + File.read ignore + end + + if ignore_paths then + nonglobs, globs = ignore_paths.split("\n").partition { |p| p.include? "/" } + dirs, ifiles = nonglobs.partition { |p| p.end_with? "/" } + dirs = dirs.map { |s| s.chomp "/" } + + dirs.map! { |i| File.expand_path i } + globs.map! { |i| File.expand_path i } + ifiles.map! { |i| File.expand_path i } + + only_paths = File::FNM_PATHNAME + files = files.reject { |f| + f = File.expand_path(f) + dirs.any? { |i| File.fnmatch?(i, File.dirname(f), only_paths) } || + globs.any? { |i| File.fnmatch?(i, f) } || + ifiles.any? { |i| File.fnmatch?(i, f, only_paths) } + } + end + + files + end +end # VendoredPathExpander + +## +# Minitest's PathExpander to find and filter tests. + +class Minitest::PathExpander < Minitest::VendoredPathExpander + attr_accessor :by_line # :nodoc: + + TEST_GLOB = "**/{test_*,*_test,spec_*,*_spec}.rb" # :nodoc: + + def initialize args = ARGV # :nodoc: + super args, TEST_GLOB, "test" + self.by_line = {} + end + + def process_args # :nodoc: + args.reject! { |arg| # this is a good use of overriding + case arg + when /^(.*):([\d,-]+)$/ then + f, ls = $1, $2 + ls = ls + .split(/,/) + .map { |l| + case l + when /^\d+$/ then + l.to_i + when /^(\d+)-(\d+)$/ then + $1.to_i..$2.to_i + else + raise "unhandled argument format: %p" % [l] + end + } + next unless File.exist? f + f = _normalize f + args << f # push path on lest it run whole dir + by_line[f] = ls # implies rejection + end + } + + super + end + + ## + # Overrides PathExpander#process_flags to filter out ruby flags + # from minitest flags. Only supports -I, -d, and -w for + # ruby. + + def process_flags flags + flags.reject { |flag| # all hits are truthy, so this works out well + case flag + when /^-I(.*)/ then + $LOAD_PATH.prepend(*$1.split(/:/)) + when /^-d/ then + $DEBUG = true + when /^-w/ then + $VERBOSE = true + else + false + end + } + end + + ## + # Add additional arguments to args to handle path:line argument filtering + + def post_process + return if by_line.empty? + + tests = tests_by_class + + exit! 1 if handle_missing_tests? tests + + test_res = tests_to_regexp tests + self.args << "-n" << "/#{test_res.join "|"}/" + end + + ## + # Find and return all known tests as a hash of klass => [TM...] + # pairs. + + def all_tests + Minitest.seed = 42 # minor hack to deal with runnable_methods shuffling + Minitest::Runnable.runnables + .to_h { |k| + ms = k.runnable_methods + .sort + .map { |m| TM.new k, m.to_sym } + .sort_by { |t| [t.path, t.line_s] } + [k, ms] + } + .reject { |k, v| v.empty? } + end + + ## + # Returns a hash mapping Minitest runnable classes to TMs + + def tests_by_class + all_tests + .transform_values { |ms| + ms.select { |m| + bl = by_line[m.path] + not bl or bl.any? { |l| m.include? l } + } + } + .reject { |k, v| v.empty? } + end + + ## + # Converts +tests+ to an array of "klass#(methods+)" regexps to be + # used for test selection. + + def tests_to_regexp tests + tests # { k1 => [Test(a), ...} + .transform_values { |tms| tms.map(&:name) } # { k1 => %w[a, b], ...} + .map { |k, ns| # [ "k1#(?:a|b)", "k2#c", ...] + if ns.size > 1 then + ns.map! { |n| Regexp.escape n } + "%s#\(?:%s\)" % [Regexp.escape(k.name), ns.join("|")] + else + "%s#%s" % [Regexp.escape(k.name), ns.first] + end + } + end + + ## + # Handle the case where a line number doesn't match any known tests. + # Returns true to signal that running should stop. + + def handle_missing_tests? tests + _tests = tests.values.flatten + not_found = by_line + .flat_map { |f, ls| ls.map { |l| [f, l] } } + .reject { |f, l| + _tests.any? { |t| t.path == f and t.include? l } + } + + unless not_found.empty? then + by_path = all_tests.values.flatten.group_by(&:path) + + puts + puts "ERROR: test(s) not found at:" + not_found.each do |f, l| + puts " %s:%s" % [f, l] + puts + puts "Did you mean?" + puts + l = l.begin if l.is_a? Range + by_path[f] and + by_path[f] + .sort_by { |m| (m.line_s - l).abs } + .first(2) + .each do |m| + puts " %-30s (dist=%+d) (%s)" % [m, m.line_s - l, m.name] + end + puts + end + $stdout.flush + $stderr.flush + true + end + end + + ## + # Simple TestMethod (abbr TM) Data object. + + TM = Data.define :klass, :name, :path, :lines do + def initialize klass:, name: + method = klass.instance_method name + path, line_s = method.source_location + + path = path.delete_prefix "#{Dir.pwd}/" + + line_e = line_s + TM.source_for(method).lines.size - 1 + + lines = line_s..line_e + + super klass:, name:, path:, lines: + end + + def self.source_for method + path, line = method.source_location + file = cache[path] ||= File.readlines(path) + + ruby = +"" + + file[line-1..].each do |l| + ruby << l + return ruby if Prism.parse_success? ruby + end + + nil + end + + def self.cache = @cache ||= {} + + def include?(o) = o.is_a?(Integer) ? lines.include?(o) : lines.overlap?(o) + + def to_s = "%s:%d-%d" % [path, lines.begin, lines.end] + + def line_s = lines.begin + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/pride.rb b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/pride.rb new file mode 100644 index 0000000..0505c2c --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/pride.rb @@ -0,0 +1,4 @@ +require_relative "../minitest" + +Minitest.load :pride +Minitest::PrideIO.pride! diff --git a/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/pride_plugin.rb b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/pride_plugin.rb new file mode 100644 index 0000000..afbfa7a --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/pride_plugin.rb @@ -0,0 +1,135 @@ +require_relative "../minitest" + +module Minitest + def self.plugin_pride_options opts, _options # :nodoc: + opts.on "-p", "--pride", "Pride. Show your testing pride!" do + PrideIO.pride! + end + end + + def self.plugin_pride_init options # :nodoc: + return unless PrideIO.pride? + + klass = ENV["TERM"] =~ /^xterm|-(?:256color|direct)$/ ? PrideLOL : PrideIO + io = klass.new options[:io] + + self.reporter.reporters.grep(Minitest::Reporter).each do |rep| + rep.io = io if rep.io.tty? + end + end + + ## + # Show your testing pride! + + class PrideIO + ## + # Activate the pride plugin. Called from both -p option and minitest/pride + + def self.pride! + @pride = true + end + + ## + # Are we showing our testing pride? + + def self.pride? + @pride ||= false + end + + # Start an escape sequence + ESC = "\e[" + + # End the escape sequence + NND = "#{ESC}0m" + + # The IO we're going to pipe through. + attr_reader :io + + def initialize io # :nodoc: + @io = io + # stolen from /System/Library/Perl/5.10.0/Term/ANSIColor.pm + # also reference https://en.wikipedia.org/wiki/ANSI_escape_code + @colors ||= (31..36).to_a + @size = @colors.size + @index = 0 + end + + ## + # Wrap print to colorize the output. + + def print o + case o + when ".", "S" then + io.print pride o + when "E", "F" then + io.print "#{ESC}41m#{ESC}37m#{o}#{NND}" + else + io.print o + end + end + + def puts *o # :nodoc: + o.map! { |s| + s.to_s.sub("Finished") { + @index = 0 + "Fabulous run".chars.map { |c| pride(c) }.join + } + } + + io.puts(*o) + end + + ## + # Color a string. + + def pride string + string = "*" if string == "." + c = @colors[@index % @size] + @index += 1 + "#{ESC}#{c}m#{string}#{NND}" + end + + def method_missing msg, *args # :nodoc: + io.send(msg, *args) + end + end + + ## + # If you thought the PrideIO was colorful... + # + # (Inspired by lolcat, but with clean math) + + class PrideLOL < PrideIO + PI_3 = Math::PI / 3 # :nodoc: + + def initialize io # :nodoc: + # walk red, green, and blue around a circle separated by equal thirds. + # + # To visualize, type this into wolfram-alpha: + # + # plot (3*sin(x)+3), (3*sin(x+2*pi/3)+3), (3*sin(x+4*pi/3)+3) + + @colors = Array.new(6 * 7) { |n| + n *= 1.0 / 3 + r = (3 * Math.sin(n ) + 3).to_i + g = (3 * Math.sin(n + 4 * PI_3) + 3).to_i + b = (3 * Math.sin(n + 2 * PI_3) + 3).to_i + + # Then we take rgb and encode them in a single number using + # base 6, shifted by 16 for the base 16 ansi colors. + 36 * r + 6 * g + b + 16 + }.rotate(4) # puts "red" first + + super + end + + ## + # Make the string even more colorful. Damnit. + + def pride string + c = @colors[@index % @size] + @index += 1 + "#{ESC}38;5;#{c}m#{string}#{NND}" + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/server.rb b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/server.rb new file mode 100644 index 0000000..e6eeffa --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/server.rb @@ -0,0 +1,45 @@ +require "drb" +require "tmpdir" +require_relative "../minitest" + +class Minitest::Server + VERSION = "1.0.9" + + TOPDIR = Dir.pwd + "/" + + def self.path pid = $$ + "drbunix:#{Dir.tmpdir}/minitest.#{pid}" + end + + def self.run client + DRb.start_service path, new(client) + end + + def self.stop + DRb.stop_service + end + + attr_accessor :client + + def initialize client + self.client = client + end + + def quit + self.class.stop + end + + def start + client.minitest_start + end + + def result file, klass, method, fails, assertions, time + file = file.sub(/^#{TOPDIR}/, "") + + client.minitest_result file, klass, method, fails, assertions, time + end + + def report + # do nothing + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/server_plugin.rb b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/server_plugin.rb new file mode 100644 index 0000000..50e4a5e --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/server_plugin.rb @@ -0,0 +1,84 @@ +require_relative "../minitest" + +module Minitest + @server = false + + def self.plugin_server_options opts, options # :nodoc: + opts.on "--server=pid", Integer, "Connect to minitest server w/ pid." do |s| + @server = s + end + end + + def self.plugin_server_init options + if @server then + require_relative "server" + self.reporter << Minitest::ServerReporter.new(@server) + end + end +end + +class Minitest::ServerReporter < Minitest::AbstractReporter + def initialize pid + uri = Minitest::Server.path(pid) + @mt_server = DRbObject.new_with_uri uri + super() + end + + def start + @mt_server.start + end + + def record result + r = result + c = r.class + + case r + when Minitest::Result then + file, = r.source_location + cn = r.klass + else + # TODO: remove? when is this used? + file, = r.method(r.name).source_location + cn = c.name + end + + sanitize r.failures + + @mt_server.result file, cn, r.name, r.failures, r.assertions, r.time + end + + def sanitize failures + failures.map! { |e| + case e + when Minitest::UnexpectedError then + # embedded exception might not be able to be marshaled. + bt = e.error.backtrace + + ex = RuntimeError.new(e.error.message) + e.error = ex + ex.set_backtrace bt + + e = Minitest::UnexpectedError.new ex # ugh. some rails plugin. ugh. + + if ex.instance_variables.include? :@bindings then # web-console is Evil + ex.instance_variable_set :@bindings, nil + e.instance_variable_set :@bindings, nil + end + when Minitest::Skip then + # do nothing + when Minitest::Assertion then + bt = e.backtrace + e = e.class.new(e.message) + e.set_backtrace bt + else + warn "Unhandled exception type: #{e.class}\n\n#{e.inspect}" + end + + e + } + end + + def report + @mt_server.report + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/spec.rb b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/spec.rb new file mode 100644 index 0000000..b1370e1 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/spec.rb @@ -0,0 +1,324 @@ +require_relative "test" + +class Module # :nodoc: + def infect_an_assertion meth, new_name, dont_flip = false # :nodoc: + block = dont_flip == :block + dont_flip = false if block + + # https://eregon.me/blog/2021/02/13/correct-delegation-in-ruby-2-27-3.html + # Drop this when we can drop ruby 2.6 (aka after rails 6.1 EOL, ~2024-06) + kw_extra = "ruby2_keywords %p" % [new_name] if respond_to? :ruby2_keywords, true + + self.class_eval <<-EOM, __FILE__, __LINE__ + 1 + def #{new_name} *args + raise "Calling ##{new_name} outside of test." unless ctx + case + when #{!!dont_flip} then + ctx.#{meth}(target, *args) + when #{block} && Proc === target then + ctx.#{meth}(*args, &target) + else + ctx.#{meth}(args.first, target, *args[1..-1]) + end + end + #{kw_extra} + EOM + end +end + +# :stopdoc: +module Minitest # fucking hell rdoc... + Expectation = Struct.new :target, :ctx +end +# :startdoc: + +## +# Kernel extensions for minitest + +module Kernel + ## + # Describe a series of expectations for a given target +desc+. + # + # Defines a test class subclassing from either Minitest::Spec or + # from the surrounding describe's class. The surrounding class may + # subclass Minitest::Spec manually in order to easily share code: + # + # class MySpec < Minitest::Spec + # # ... shared code ... + # end + # + # class TestStuff < MySpec + # it "does stuff" do + # # shared code available here + # end + # describe "inner stuff" do + # it "still does stuff" do + # # ...and here + # end + # end + # end + # + # For more information on getting started with writing specs, see: + # + # http://www.rubyinside.com/a-minitestspec-tutorial-elegant-spec-style-testing-that-comes-with-ruby-5354.html + # + # For some suggestions on how to improve your specs, try: + # + # https://betterspecs.org + # + # but do note that several items there are debatable or specific to + # rspec. + # + # For more information about expectations, see Minitest::Expectations. + + def describe desc, *additional_desc, &block # :doc: + stack = Minitest::Spec.describe_stack + is_spec_class = Class === self && kind_of?(Minitest::Spec::DSL) + name = [stack.last, desc, *additional_desc] + name.prepend self if stack.empty? && is_spec_class + sclas = + stack.last \ + || (is_spec_class && self) \ + || Minitest::Spec.spec_type(desc, *additional_desc) + + cls = sclas.create name.compact.join("::"), desc + + stack.push cls + cls.class_eval(&block) + stack.pop + cls + end + private :describe +end + +## +# Minitest::Spec -- The faster, better, less-magical spec framework! +# +# For a list of expectations, see Minitest::Expectations. + +class Minitest::Spec < Minitest::Test + + ## + # Oh look! A Minitest::Spec::DSL module! Eat your heart out DHH. + + module DSL + ## + # Contains pairs of matchers and Spec classes to be used to + # calculate the superclass of a top-level describe. This allows for + # automatically customizable spec types. + # + # See: register_spec_type and spec_type + + TYPES = [[//, Minitest::Spec]] + + ## + # Register a new type of spec that matches the spec's description. + # This method can take either a Regexp and a spec class or a spec + # class and a block that takes the description and returns true if + # it matches. + # + # Eg: + # + # register_spec_type(/Controller$/, Minitest::Spec::Rails) + # + # or: + # + # register_spec_type(Minitest::Spec::RailsModel) do |desc| + # desc.superclass == ActiveRecord::Base + # end + + def register_spec_type *args, &block + if block then + matcher, klass = block, args.first + else + matcher, klass = *args + end + TYPES.unshift [matcher, klass] + end + + ## + # Figure out the spec class to use based on a spec's description. Eg: + # + # spec_type("BlahController") # => Minitest::Spec::Rails + + def spec_type desc, *additional + TYPES.find { |matcher, _klass| + if matcher.respond_to? :call then + matcher.call desc, *additional + else + matcher === desc.to_s + end + }.last + end + + def describe_stack # :nodoc: + Thread.current[:describe_stack] ||= [] + end + + def children # :nodoc: + @children ||= [] + end + + def nuke_test_methods! # :nodoc: + self.public_instance_methods.grep(/^test_/).each do |name| + self.send :undef_method, name + end + end + + ## + # Define a 'before' action. Inherits the way normal methods should. + # + # NOTE: +type+ is ignored and is only there to make porting easier. + # + # Equivalent to Minitest::Test#setup. + + def before _type = nil, &block + define_method :setup do + super() + self.instance_eval(&block) + end + end + + ## + # Define an 'after' action. Inherits the way normal methods should. + # + # NOTE: +type+ is ignored and is only there to make porting easier. + # + # Equivalent to Minitest::Test#teardown. + + def after _type = nil, &block + define_method :teardown do + self.instance_eval(&block) + super() + end + end + + ## + # Define an expectation with name +desc+. Name gets morphed to a + # proper test method name. For some freakish reason, people who + # write specs don't like class inheritance, so this goes way out of + # its way to make sure that expectations aren't inherited. + # + # This is also aliased to #specify and doesn't require a +desc+ arg. + # + # Hint: If you _do_ want inheritance, use minitest/test. You can mix + # and match between assertions and expectations as much as you want. + + def it desc = "anonymous", &block + block ||= proc { skip "(no tests defined)" } + + @specs ||= 0 + @specs += 1 + + name = "test_%04d_%s" % [ @specs, desc ] + + undef_klasses = self.children.reject { |c| c.public_method_defined? name } + + define_method name, &block + + undef_klasses.each do |undef_klass| + undef_klass.send :undef_method, name + end + + name + end + + ## + # Essentially, define an accessor for +name+ with +block+. + # + # Why use let instead of def? I honestly don't know. + + def let name, &block + name = name.to_s + pre, post = "let '#{name}' cannot ", ". Please use another name." + methods = Minitest::Spec.instance_methods.map(&:to_s) - %w[subject] + raise ArgumentError, "#{pre}begin with 'test'#{post}" if + name.start_with? "test" + raise ArgumentError, "#{pre}override a method in Minitest::Spec#{post}" if + methods.include? name + + define_method name do + @_memoized ||= {} + @_memoized.fetch(name) { |k| @_memoized[k] = instance_eval(&block) } + end + end + + ## + # Another lazy man's accessor generator. Made even more lazy by + # setting the name for you to +subject+. + + def subject &block + let :subject, &block + end + + def create name, desc # :nodoc: + cls = Class.new self do + @name = name + @desc = desc + + nuke_test_methods! + end + + children << cls + + cls + end + + def name # :nodoc: + defined?(@name) ? @name : super + end + + alias to_s name + alias inspect name + + attr_reader :desc # :nodoc: + alias specify it + + ## + # Rdoc... why are you so dumb? + + module InstanceMethods + ## + # Takes a value or a block and returns a value monad that has + # all of Expectations methods available to it. + # + # _(1 + 1).must_equal 2 + # + # And for blocks: + # + # _ { 1 + "1" }.must_raise TypeError + # + # This method of expectation-based testing is preferable to + # straight-expectation methods (on Object) because it stores its + # test context, bypassing our hacky use of thread-local variables. + # + # It is also aliased to #value and #expect for your aesthetic + # pleasure: + # + # _(1 + 1).must_equal 2 + # value(1 + 1).must_equal 2 + # expect(1 + 1).must_equal 2 + + def _ value = nil, &block + Minitest::Expectation.new block || value, self + end + + alias value _ + alias expect _ + end + + def self.extended obj # :nodoc: + obj.send :include, InstanceMethods + end + end + + extend DSL + + TYPES = DSL::TYPES # :nodoc: +end + +require_relative "expectations" + +class Minitest::Expectation + include Minitest::Expectations +end diff --git a/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/sprint.rb b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/sprint.rb new file mode 100644 index 0000000..9b73f55 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/sprint.rb @@ -0,0 +1,104 @@ +$LOAD_PATH.unshift "test", "lib" + +require "simplecov" if ENV["MT_COV"] || ARGV.delete("--simplecov") +require_relative "autorun" +require_relative "path_expander" + +## +# Runs (Get it? It's fast!) your tests and makes it easier to rerun individual +# failures. + +module Minitest + class Sprint + # extracted version = "1.5.0" + + ## + # Process and run minitest cmdline. + + def self.run args = ARGV + if ARGV.delete("--bisect") or ARGV.delete("-b") then + require_relative "bisect" + + return Minitest::Bisect.run ARGV + end + + Minitest::PathExpander.new(args).process { |f| + require "./#{f}" if File.file? f + } + end + + ## + # An extra minitest reporter to output how to rerun failures in + # various styles. + + class SprintReporter < AbstractReporter + ## + # The style to report, either lines or regexp. Defaults to lines. + attr_accessor :style + attr_accessor :results # :nodoc: + + def initialize style = :regexp # :nodoc: + self.results = [] + self.style = style + end + + def record result # :nodoc: + results << result unless result.passed? or result.skipped? + end + + def report # :nodoc: + return if results.empty? + + puts + puts "Happy Happy Sprint List:" + puts + print_list + puts + end + + def print_list # :nodoc: + case style + when :regexp + results.each do |result| + puts " minitest -n #{result.class_name}##{result.name}" + end + when :lines + files = Hash.new { |h,k| h[k] = [] } + results.each do |result| + path, line = result.source_location + path = path.delete_prefix "#{Dir.pwd}/" + files[path] << line + end + + files.sort.each do |path, lines| + puts " minitest %s:%s" % [path, lines.sort.join(",")] + end + else + raise "unsupported style: %p" % [style] + end + end + end + + ## + # An extra minitest reporter to output how to rerun failures using + # rake. + + class RakeReporter < SprintReporter + ## + # The name of the rake task to rerun. Defaults to nil. + + attr_accessor :name + + def initialize name = nil # :nodoc: + super() + self.name = name + end + + def print_list # :nodoc: + results.each do |result| + puts [" rake", name, "N=#{result.class_name}##{result.name}"].compact.join(" ") + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/sprint_plugin.rb b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/sprint_plugin.rb new file mode 100644 index 0000000..afd2f08 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/sprint_plugin.rb @@ -0,0 +1,39 @@ +require_relative "../minitest" + +# :stopdoc: +class OptionParser # unofficial embedded gem "makeoptparseworkwell" + def hidden(...) = define(...).tap { |sw| def sw.summarize(*) = nil } + def deprecate(from, to) = hidden(from) { abort "#{from} is deprecated. Use #{to}." } + def topdict(name) = name.length > 1 ? top.long : top.short + def alias(from, to) = (dict = topdict(from) and dict[to] = dict[from]) +end unless OptionParser.method_defined? :hidden +# :startdoc: + +module Minitest # :nodoc: + def self.plugin_sprint_options opts, options # :nodoc: + opts.on "--rake [TASK]", "Report how to re-run failures with rake." do |task| + options[:sprint] = :rake + options[:rake_task] = task + end + + opts.deprecate "--binstub", "--rerun" + + sprint_styles = %w[rake lines names binstub] + + opts.on "-r", "--rerun [STYLE]", sprint_styles, "Report how to re-run failures using STYLE (names, lines)." do |style| + options[:sprint] = (style || :lines).to_sym + end + end + + def self.plugin_sprint_init options + require_relative "sprint" + case options[:sprint] + when :rake then + self.reporter << Minitest::Sprint::RakeReporter.new(options[:rake_task]) + when :binstub, :names then + self.reporter << Minitest::Sprint::SprintReporter.new + when :lines then + self.reporter << Minitest::Sprint::SprintReporter.new(:lines) + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/test.rb b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/test.rb new file mode 100644 index 0000000..671b0c9 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/test.rb @@ -0,0 +1,232 @@ +require_relative "../minitest" unless defined? Minitest::Runnable + +module Minitest + ## + # Subclass Test to create your own tests. Typically you'll want a + # Test subclass per implementation class. + # + # See Minitest::Assertions + + class Test < Runnable + require_relative "assertions" + include Minitest::Reportable + include Minitest::Assertions + + PASSTHROUGH_EXCEPTIONS = [NoMemoryError, SignalException, SystemExit] # :nodoc: + + SETUP_METHODS = %w[ before_setup setup after_setup ] # :nodoc: + + TEARDOWN_METHODS = %w[ before_teardown teardown after_teardown ] # :nodoc: + + # :stopdoc: + class << self; attr_accessor :io_lock; end + self.io_lock = Mutex.new + # :startdoc: + + ## + # Call this at the top of your tests when you absolutely + # positively need to have ordered tests. In doing so, you're + # admitting that you suck and your tests are weak. + + def self.i_suck_and_my_tests_are_order_dependent! + class << self + undef_method :run_order if method_defined? :run_order + define_method :run_order do :alpha end + end + end + + ## + # Make diffs for this Test use #pretty_inspect so that diff + # in assert_equal can have more details. NOTE: this is much slower + # than the regular inspect but much more usable for complex + # objects. + + def self.make_my_diffs_pretty! + require "pp" + + define_method :mu_pp, &:pretty_inspect + end + + ## + # Call this at the top of your tests (inside the +Minitest::Test+ + # subclass or +describe+ block) when you want to run your tests in + # parallel. In doing so, you're admitting that you rule and your + # tests are awesome. + + def self.parallelize_me! + return unless Minitest.parallel_executor + include Minitest::Parallel::Test + extend Minitest::Parallel::Test::ClassMethods + end + + ## + # Returns all instance methods starting with "test_". Based on + # #run_order, the methods are either sorted, randomized + # (default), or run in parallel. + + def self.runnable_methods + methods = methods_matching(/^test_/) + + case self.run_order + when :random, :parallel then + srand Minitest.seed + methods.sort.shuffle + when :alpha, :sorted then + methods.sort + else + raise "Unknown_order: %p" % [self.run_order] + end + end + + ## + # Runs a single test with setup/teardown hooks. + + def run + time_it do + capture_exceptions do + SETUP_METHODS.each do |hook| + self.send hook + end + + self.send self.name + end + + TEARDOWN_METHODS.each do |hook| + capture_exceptions do + self.send hook + end + end + end + + Result.from self # per contract + end + + ## + # Provides before/after hooks for setup and teardown. These are + # meant for library writers, NOT for regular test authors. See + # #before_setup for an example. + + module LifecycleHooks + + ## + # Runs before every test, before setup. This hook is meant for + # libraries to extend minitest. It is not meant to be used by + # test developers. + # + # As a simplistic example: + # + # module MyMinitestPlugin + # def before_setup + # super + # # ... stuff to do before setup is run + # end + # + # def after_setup + # # ... stuff to do after setup is run + # super + # end + # + # def before_teardown + # super + # # ... stuff to do before teardown is run + # end + # + # def after_teardown + # # ... stuff to do after teardown is run + # super + # end + # end + # + # class Minitest::Test + # include MyMinitestPlugin + # end + + def before_setup; end + + ## + # Runs before every test. Use this to set up before each test + # run. + + def setup; end + + ## + # Runs before every test, after setup. This hook is meant for + # libraries to extend minitest. It is not meant to be used by + # test developers. + # + # See #before_setup for an example. + + def after_setup; end + + ## + # Runs after every test, before teardown. This hook is meant for + # libraries to extend minitest. It is not meant to be used by + # test developers. + # + # See #before_setup for an example. + + def before_teardown; end + + ## + # Runs after every test. Use this to clean up after each test + # run. + + def teardown; end + + ## + # Runs after every test, after teardown. This hook is meant for + # libraries to extend minitest. It is not meant to be used by + # test developers. + # + # See #before_setup for an example. + + def after_teardown; end + end # LifecycleHooks + + def capture_exceptions # :nodoc: + yield + rescue *PASSTHROUGH_EXCEPTIONS + raise + rescue Assertion => e + self.failures << e + rescue Exception => e + self.failures << UnexpectedError.new(sanitize_exception e) + end + + def sanitize_exception e # :nodoc: + Marshal.dump e + e # good: use as-is + rescue + neuter_exception e + end + + def neuter_exception e # :nodoc: + bt = e.backtrace + msg = e.message.dup + + new_exception e.class, msg, bt # e.class can be a problem... + rescue + msg.prepend "Neutered Exception #{e.class}: " + + new_exception RuntimeError, msg, bt, true # but if this raises, we die + end + + def new_exception klass, msg, bt, kill = false # :nodoc: + ne = klass.new msg + ne.set_backtrace bt + + if kill then + ne.instance_variables.each do |v| + ne.remove_instance_variable v + end + end + + Marshal.dump ne # can raise TypeError + ne + end + + include LifecycleHooks + include Guard + extend Guard + end # Test +end diff --git a/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/test_task.rb b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/test_task.rb new file mode 100644 index 0000000..9e17e03 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/lib/minitest/test_task.rb @@ -0,0 +1,331 @@ +require "shellwords" +require "rbconfig" + +begin + require "rake/tasklib" +rescue LoadError => e + warn e.message + return +end + +module Minitest # :nodoc: + + ## + # Minitest::TestTask is a rake helper that generates several rake + # tasks under the main test task's name-space. + # + # task :: the main test task + # task :cmd :: prints the command to use + # task :deps :: runs each test file by itself to find dependency errors + # task :slow :: runs the tests and reports the slowest 25 tests. + # + # Examples: + # + # Minitest::TestTask.create + # + # The most basic and default setup. + # + # Minitest::TestTask.create :my_tests + # + # The most basic/default setup, but with a custom name + # + # Minitest::TestTask.create :unit do |t| + # t.test_globs = ["test/unit/**/*_test.rb"] + # t.warning = false + # end + # + # Customize the name and only run unit tests. + # + # NOTE: To hook this task up to the default, make it a dependency: + # + # task default: :unit + + class TestTask < Rake::TaskLib + WINDOWS = RbConfig::CONFIG["host_os"] =~ /mswin|mingw/ # :nodoc: + + ## + # Create several test-oriented tasks under +name+ (default: + # "test"). Takes an optional block to customize variables. + # Examples: + # + # Minitest::TestTask.create # named "test", all defaults + # + # Minitest::TestTask.create do |t| + # t.warning = false # my tests are noisy + # t.framework = %(require_relative "./test/helper.rb") + # end + + def self.create name = :test, &block + task = new name + task.instance_eval(&block) if block + task.process_env + task.define + task + end + + ## + # Extra arguments to pass to the tests. Defaults empty but gets + # populated by a number of enviroment variables: + # + # N (-n flag) :: a string or regexp of tests to run. + # X (-e flag) :: a string or regexp of tests to exclude. + # A (arg) :: quick way to inject an arbitrary argument (eg A=--help). + # + # See #process_env + + attr_accessor :extra_args + + ## + # The code to load the framework. Defaults to requiring + # minitest/autorun. + # + # If you have a test helper file that requires minitest/autorun + # and anything else your project standardizes on, then you'll + # probably want to change this to: + # + # Minitest::TestTask.create do |t| + # t.framework = %(require_relative "./test/helper.rb") + # end + # + # or something similar. NOTE: if you do this, then helper must + # require "minitest/autorun" at the top to start the tests. + + attr_accessor :framework + + ## + # Extra library directories to include. Defaults to %w[lib test + # .]. Also uses $MT_LIB_EXTRAS allowing you to dynamically + # override/inject directories for custom runs. + + attr_accessor :libs + + ## + # The name of the task and base name for the other tasks generated. + + attr_accessor :name + + ## + # File globs to find test files. Defaults to something sensible to + # find test files under the test directory. + + attr_accessor :test_globs + + ## + # Turn on ruby warnings (-w flag). Defaults to true. + + attr_accessor :warning + + ## + # Optional: Additional ruby to run before the test framework is loaded. + # Example: + # + # Minitest::TestTask.create "test:cov" do |t| + # t.test_prelude = %(require "simplecov") + # end + + attr_accessor :test_prelude + + ## + # Print out commands as they run. Defaults to Rake's +trace+ (-t + # flag) option. + + attr_accessor :verbose + + ## + # Use TestTask.create instead. + + def initialize name = :test # :nodoc: + self.extra_args = [] + self.framework = %(require "minitest/autorun") + self.libs = %w[lib test .] + self.name = name + self.test_globs = ["test/**/test_*.rb", + "test/**/*_test.rb"] + self.test_prelude = nil + self.verbose = Rake.application.options.trace || Rake.verbose == true + self.warning = true + end + + ## + # Extract variables from the environment and convert them to + # command line arguments. See #extra_args. + # + # Environment Variables: + # + # MT_LIB_EXTRAS :: Extra libs to dynamically override/inject for custom runs. + # N :: Tests to run (string or /regexp/). + # X :: Tests to exclude (string or /regexp/). + # A :: Any extra arguments. Honors shell quoting. + # + # Deprecated: + # + # TESTOPTS :: For argument passing, use +A+. + # N :: For parallel testing, use +MT_CPU+. + # FILTER :: Same as +TESTOPTS+. + + def process_env + warn "TESTOPTS is deprecated in Minitest::TestTask. Use A instead" if + ENV["TESTOPTS"] + warn "FILTER is deprecated in Minitest::TestTask. Use A instead" if + ENV["FILTER"] + + lib_extras = (ENV["MT_LIB_EXTRAS"] || "").split File::PATH_SEPARATOR + self.libs[0, 0] = lib_extras + + extra_args << "-i" << ENV["N"] if ENV["N"] + extra_args << "-i" << ENV["I"] if ENV["I"] + extra_args << "-x" << ENV["X"] if ENV["X"] + extra_args << "-x" << ENV["E"] if ENV["E"] + extra_args.concat Shellwords.split(ENV["TESTOPTS"]) if ENV["TESTOPTS"] + extra_args.concat Shellwords.split(ENV["FILTER"]) if ENV["FILTER"] + extra_args.concat Shellwords.split(ENV["A"]) if ENV["A"] + + # TODO? RUBY_DEBUG = ENV["RUBY_DEBUG"] + # TODO? ENV["RUBY_FLAGS"] + + extra_args.compact! + end + + def define # :nodoc: + desc "Run the test suite. Use I, X, and A to add flags/args." + task name do + ruby make_test_cmd, verbose: verbose + end + + desc "Run the test suite, filtering for 'FU' in name (focused units?)." + task "#{name}:fu" do + ruby make_test_cmd(include:"/FU/"), verbose: verbose + end + + desc "Print out the test command. Good for profiling and other tools." + task "#{name}:cmd" do + puts "ruby #{make_test_cmd}" + end + + desc "Show which test files fail when run in isolation." + task "#{name}:isolated" do + tests = Dir[*self.test_globs].uniq + + # 3 seems to be the magic number... (tho not by that much) + bad, good, n = {}, [], (ENV.delete("K") || 3).to_i + file = ENV.delete "F" + times = {} + + tt0 = Time.now + + n.threads_do tests.sort do |path| + t0 = Time.now + output = `#{Gem.ruby} #{make_test_cmd path:path} 2>&1` + t1 = Time.now - t0 + + times[path] = t1 + + if $?.success? + $stderr.print "." + good << path + else + $stderr.print "x" + bad[path] = output + end + end + + puts "done" + puts "Ran in %.2f seconds" % [ Time.now - tt0 ] + + if file then + require "json" + File.open file, "w" do |io| + io.puts JSON.pretty_generate times + end + end + + unless good.empty? + puts + puts "# Good tests:" + puts + good.sort.each do |path| + puts "%.2fs: %s" % [times[path], path] + end + end + + unless bad.empty? + puts + puts "# Bad tests:" + puts + bad.keys.sort.each do |path| + puts "%.2fs: %s" % [times[path], path] + end + puts + puts "# Bad Test Output:" + puts + bad.sort.each do |path, output| + puts + puts "# #{path}:" + puts output + end + exit 1 + end + end + + task "#{name}:deps" => "#{name}:isolated" # now just an alias + + desc "Run the test suite and report the slowest 25 tests." + task "#{name}:slow" do + sh ["rake #{name} A=-v", + "egrep '#test_.* s = .'", + "sort -n -k2 -t=", + "tail -25"].join " | " + end + end + + ## + # Generate the test command-line. + + def make_test_cmd **option + globs = option[:path] || test_globs + + tests = [] + tests.concat Dir[*globs].sort.shuffle # TODO: SEED -> srand first? + tests.map! { |f| %(require "#{f}") } + + runner = [] + runner << test_prelude if test_prelude + runner << framework + runner.concat tests + runner = runner.join "; " + + extra_args << "-i" << option[:include] if option[:include] + + args = [] + args << "-I#{libs.join File::PATH_SEPARATOR}" unless libs.empty? + args << "-w" if warning + args << "-e" + args << "'#{runner}'" + args << "--" + args << extra_args.map(&:shellescape) + + args.join " " + end + end +end + +class Work < Queue # :nodoc: + def initialize jobs # :nodoc: + super + close + end +end + +class Integer # :nodoc: + def threads_do jobs # :nodoc: + q = Work.new jobs + + Array.new(self) { + Thread.new do + while job = q.pop # go until quit value + yield job + end + end + }.each(&:join) + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/test/minitest/metametameta.rb b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/test/minitest/metametameta.rb new file mode 100644 index 0000000..c2fdb4a --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/test/minitest/metametameta.rb @@ -0,0 +1,150 @@ +require "tempfile" +require "stringio" +require "minitest/autorun" + +class Minitest::Test + def with_empty_backtrace_filter + with_backtrace_filter Minitest::BacktraceFilter.new %r%.% do + yield + end + end + + def with_backtrace_filter filter + original = Minitest.backtrace_filter + + Minitest::Test.io_lock.synchronize do # try not to trounce in parallel + begin + Minitest.backtrace_filter = filter + yield + ensure + Minitest.backtrace_filter = original + end + end + end + + def error_on_warn? + defined?(Minitest::ErrorOnWarning) + end + + def assert_deprecation re = /DEPRECATED/ + re = // if $-w.nil? # "skip" if running `rake testW0` + assert_output "", re do + yield + end + rescue Minitest::UnexpectedWarning => e # raised if -Werror was used + assert_match re, e.message + end +end + +class FakeNamedTest < Minitest::Test + @@count = 0 + + def self.name + @fake_name ||= begin + @@count += 1 + "FakeNamedTest%02d" % @@count + end + end +end + +module MyModule; end +class AnError < StandardError; include MyModule; end + +class MetaMetaMetaTestCase < Minitest::Test + attr_accessor :reporter, :output, :tu + + def with_stderr err + old = $stderr + $stderr = err + yield + ensure + $stderr = old + end + + def run_tu_with_fresh_reporter flags = %w[--seed 42] + options = Minitest.process_args flags + + @output = StringIO.new(+"") + + self.reporter = Minitest::CompositeReporter.new + reporter << Minitest::SummaryReporter.new(@output, options) + reporter << Minitest::ProgressReporter.new(@output, options) + + with_stderr @output do + reporter.start + + yield reporter if block_given? + + @tus ||= [@tu] + @tus.each do |tu| + Minitest::Runnable.runnables.delete tu + + tu.run_suite reporter, options + end + + reporter.report + end + end + + def first_reporter + reporter.reporters.first + end + + def assert_report expected, flags = %w[--seed 42], &block + header = <<~EOM + Run options: #{flags.map { |s| s.include?("|") ? s.inspect : s }.join " "} + + # Running: + + EOM + + run_tu_with_fresh_reporter flags, &block + + output = normalize_output @output.string.dup + + assert_equal header + expected, output + end + + def normalize_output output + output.sub!(/Finished in .*/, "Finished in 0.00") + output.sub!(/Loaded suite .*/, "Loaded suite blah") + + output.gsub!(/FakeNamedTest\d+/, "FakeNamedTestXX") + output.gsub!(/ = \d+.\d\d s = /, " = 0.00 s = ") + output.gsub!(/0x[A-Fa-f0-9]+/, "0xXXX") + output.gsub!(/ +$/, "") + + file = ->(s) { s.start_with?("/") ? "FULLFILE" : "FILE" } + + if windows? then + output.gsub!(/\[(?:[A-Za-z]:)?[^\]:]+:\d+\]/, "[FILE:LINE]") + output.gsub!(/^(\s+)(?:[A-Za-z]:)?[^:]+:\d+:in [`']/, '\1FILE:LINE:in \'') + else + output.gsub!(/\[([^\]:]+):\d+\]/) { "[#{file[$1]}:LINE]" } + output.gsub!(/^(\s+)([^:]+):\d+:in [`']/) { "#{$1}#{file[$2]}:LINE:in '" } + end + + output.gsub!(/in [`']block in (?:([^']+)[#.])?/, "in 'block in") + output.gsub!(/in [`'](?:([^']+)[#.])?/, "in '") + + output.gsub!(/( at )([^:]+):\d+/) { "#{$1}[#{file[$2]}:LINE]" } # eval? + + output + end + + def restore_env + old_value = ENV["MT_NO_SKIP_MSG"] + ENV.delete "MT_NO_SKIP_MSG" + + yield + ensure + ENV["MT_NO_SKIP_MSG"] = old_value + end + + def setup + super + Minitest.seed = 42 + Minitest::Test.reset + @tu = nil + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/test/minitest/test_bisect.rb b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/test/minitest/test_bisect.rb new file mode 100644 index 0000000..c240cbe --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/test/minitest/test_bisect.rb @@ -0,0 +1,235 @@ +require "minitest/autorun" +require "minitest/bisect" + +module TestMinitest; end + +class TestMinitest::TestBisect < Minitest::Test + attr_accessor :bisect + + def setup + self.bisect = Minitest::Bisect.new + bisect.reset + end + + def test_class_run + skip "Need to write test_class_run" + end + + def test_bisect_files + skip "Need to write test_bisect_files" + end + + def test_bisect_methods + skip "Need to write test_bisect_methods" + end + + def test_build_files_cmd + files = %w[a.rb b.rb c.rb] + rb = %w[-Ilib:test] + mt = %w[--seed 42] + + ruby = Minitest::Bisect::RUBY + body = "require \"./a.rb\" ; require \"./b.rb\" ; require \"./c.rb\"" + + exp = "#{ruby} -Ilib:test -e '#{body}' -- --seed 42" + act = bisect.build_files_cmd(files, rb, mt) + + assert_equal exp, act + end + + def test_build_methods_cmd + cmd = "cmd" + assert_equal "cmd", bisect.build_methods_cmd(cmd) + end + + def test_build_methods_cmd_verify + cmd = "cmd" + cul = [] + bad = %w[A#test_1 B#test_2] + + exp = "cmd -n \"/^(?:A#(?:test_1)|B#(?:test_2))$/\"" + + assert_equal exp, bisect.build_methods_cmd(cmd, cul, bad) + end + + def test_build_methods_cmd_verify_same + cmd = "cmd" + cul = [] + bad = %w[C#test_5 C#test_6] + + exp = "cmd -n \"/^(?:C#(?:test_5|test_6))$/\"" + + assert_equal exp, bisect.build_methods_cmd(cmd, cul, bad) + end + + def test_build_methods_cmd_full + cmd = "cmd" + cul = %w[A#test_1 A#test_2 B#test_3 B#test_4] + bad = %w[C#test_5 C#test_6] + + a = "A#(?:test_1|test_2)" + b = "B#(?:test_3|test_4)" + c = "C#(?:test_5|test_6)" + exp = "cmd -n \"/^(?:#{a}|#{b}|#{c})$/\"" + + assert_equal exp, bisect.build_methods_cmd(cmd, cul, bad) + end + + def test_build_re + bad = %w[A#test_1 B#test_2] + + exp = "/^(?:A#(?:test_1)|B#(?:test_2))$/" + + assert_equal exp, bisect.build_re(bad) + end + + def test_build_re_same + bad = %w[C#test_5 C#test_6] + + exp = "/^(?:C#(?:test_5|test_6))$/" + + assert_equal exp, bisect.build_re(bad) + end + + def test_build_re_class_escaping + bad = ["{}#[]"] + + exp = "/^(?:\\{\\}#(?:\\[\\]))$/" + + assert_equal exp, bisect.build_re(bad) + end + + def test_build_re_method_escaping + bad = ["Some Class#It shouldn't care what the name is"] + + exp = "/^(?:Some Class#(?:It shouldn\\'t care what the name is))$/" + + assert_equal exp, bisect.build_re(bad) + end + + def test_map_failures + bisect.failures = + { + "file.rb" => { "Class" => %w[test_method1 test_method2] }, + "blah.rb" => { "Apple" => %w[test_method3 test_method4] }, + } + + exp = %w[ + Apple#test_method3 + Apple#test_method4 + Class#test_method1 + Class#test_method2 + ] + + assert_equal exp, bisect.map_failures + end + + def test_minitest_result + bisect.minitest_result "file.rb", "TestClass", "test_method", [], 1, 1 + + assert_equal false, bisect.tainted + assert_empty bisect.failures + assert_equal ["TestClass#test_method"], bisect.culprits + end + + def test_minitest_result_skip + fail = Minitest::Skip.new("woot") + + bisect.minitest_result "file.rb", "TestClass", "test_method", [fail], 1, 1 + + assert_equal false, bisect.tainted + assert_empty bisect.failures + assert_equal ["TestClass#test_method"], bisect.culprits + end + + def test_minitest_result_fail + fail = Minitest::Assertion.new "msg" + + bisect.minitest_result "file.rb", "TestClass", "test_method", [fail], 1, 1 + + exp = {"file.rb" => {"TestClass" => ["test_method"] }} + + assert_equal true, bisect.tainted + assert_equal exp, bisect.failures + assert_empty bisect.culprits + end + + def test_minitest_result_error + fail = Minitest::UnexpectedError.new RuntimeError.new("woot") + + bisect.minitest_result "file.rb", "TestClass", "test_method", [fail], 1, 1 + + exp = {"file.rb" => {"TestClass" => ["test_method"] }} + + assert_equal true, bisect.tainted + assert_equal exp, bisect.failures + assert_empty bisect.culprits + end + + def test_minitest_start + bisect.failures["file.rb"]["Class"] << "test_bad1" + + bisect.minitest_start + + assert_empty bisect.failures + end + + def test_reset + bisect.seen_bad = true + bisect.tainted = true + bisect.failures["file.rb"]["Class"] << "test_bad1" + bisect.culprits << "A#test_1" << "B#test_2" + + bisect.reset + + assert_equal false, bisect.seen_bad + assert_equal false, bisect.tainted + assert_empty bisect.failures + assert_equal %w[A#test_1 B#test_2], bisect.culprits + end + + def test_run + skip "Need to write test_run" + end + + def test_time_it + exp = /\Ado stuff: in 0.\d\d sec\n\z/ + + assert_output exp, "" do + bisect.time_it "do stuff:", "echo you should not see me" + end + end +end + +class TestMinitest::TestBisect::TestPathExpander < Minitest::Test + def test_sanity + args = %w[1 -Iblah 2 -d 3 -w 4 5 6] + + mtbpe = Minitest::Bisect::PathExpander + expander = mtbpe.new args + + assert_equal %w[-Itest:lib], expander.rb_flags + assert_same mtbpe::TEST_GLOB, expander.glob + end + + def test_process_flags + args = %w[1 -Iblah 2 -d 3 -w 4 5 6] + + expander = Minitest::Bisect::PathExpander.new args + + exp_files = %w[1 2 3 4 5 6] + exp_flags = %w[-Itest:lib -Iblah -d -w] + + files = expander.process_flags(args) + + assert_equal files, exp_files + + # process_flags only filters and does not mutate args + assert_same args, expander.args + refute_equal args, exp_files + refute_equal files, args + + # separates rb_flags out for separate handling + assert_equal exp_flags, expander.rb_flags + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/test/minitest/test_find_minimal_combination.rb b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/test/minitest/test_find_minimal_combination.rb new file mode 100755 index 0000000..be73ae8 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/test/minitest/test_find_minimal_combination.rb @@ -0,0 +1,138 @@ +#!/usr/bin/ruby -w + +$: << "." << "lib" + +require "minitest/autorun" +require "minitest/find_minimal_combination" + +describe Array, :find_minimal_combination do + def check(*bad) + lambda { |sample| bad & sample == bad } + end + + def record_and_check(tests, *bad) + lambda { |test| tests << test.join; bad & test == bad } + end + + def parse_trials s + s.lines.map { |s| s.chomp.sub(/#.*/, '').delete " " }.reject(&:empty?) + end + + def assert_steps input, bad, exp + tests = [] + + found = input.find_minimal_combination(&record_and_check(tests, *bad)) + + assert_equal bad, found, "algorithm is bad" + + assert_equal parse_trials(exp), tests + end + + HEX = "0123456789ABCDEF".chars.to_a + + # lvl collection + # + # 0 | A + # 1 | B C + # 2 | D E F G + # 3 | H I J K L M N O + # | + # 4 | 0123456789ABCDEF + + def test_ordering_best_case_1 + ary = HEX + bad = %w[0] + exp = <<~EOT + #123456789ABCDEF + 01234567 # HIT! -- level 1 = B, C + 0123 # HIT! -- level 2 = D, E + 01 # HIT! -- level 3 = H, I + 0 # HIT! + EOT + + assert_steps ary, bad, exp + end + + def test_ordering_best_case_2 + ary = HEX + bad = %w[0 1] + exp = <<~EOT + 01234567 # HIT! -- level 1 = B, C + 0123 # HIT! -- level 2 = D, E + 01 # HIT! -- level 3 = H, I + 0 # miss -- level 4 = 0, 1, n_combos = 1 + 1 # miss + 01 # HIT! -- level 3 = H, n_combos = 2 + EOT + + assert_steps ary, bad, exp + end + + def test_ordering + ary = HEX + bad = %w[1 F] + exp = <<~EOT + 01234567 # miss -- level 1 = B, C + 89ABCDEF # miss + 0123 89AB # miss -- level 2 = DF, DG, EF, EG + 0123 CDEF # HIT! + 01 CD # miss -- level 3 = HN, HO + 01 EF # HIT! + 0 E # miss -- level 4 = 0E, 0F, 1E, 1F + 0 F # miss + 1 E # miss + 1 F # HIT! + EOT + + assert_steps ary, bad, exp + end + + def self.test_find_minimal_combination max, *bad + define_method "%s_%s_%s" % [__method__, max, bad.join("_")] do + a = (1..max).to_a + + assert_equal bad, a.find_minimal_combination(&check(*bad)) + end + end + + def self.test_find_minimal_combination_and_count max, nsteps, *bad + define_method "%s_%s_%s_%s" % [__method__, max, nsteps, bad.join("_")] do + a = (1..max).to_a + + found, count = a.find_minimal_combination_and_count(&check(*bad)) + + assert_equal bad, found + assert_equal nsteps, count + end + end + + test_find_minimal_combination 8, 5 + test_find_minimal_combination 8, 2, 7 + test_find_minimal_combination 8, 1, 2, 7 + test_find_minimal_combination 8, 1, 4, 7 + test_find_minimal_combination 8, 1, 3, 5, 7 + + test_find_minimal_combination 9, 5 + test_find_minimal_combination 9, 9 + test_find_minimal_combination 9, 2, 7 + test_find_minimal_combination 9, 1, 2, 7 + test_find_minimal_combination 9, 1, 4, 7 + test_find_minimal_combination 9, 1, 3, 5, 7 + + test_find_minimal_combination 1023, 5 + test_find_minimal_combination 1023, 1005 + test_find_minimal_combination 1023, 802, 907 + test_find_minimal_combination 1023, 7, 15, 166, 1001 + test_find_minimal_combination 1023, 1000, 1001, 1002 + test_find_minimal_combination 1023, 1001, 1003, 1005, 1007 + test_find_minimal_combination 1024, 1001, 1003, 1005, 1007 + test_find_minimal_combination 1024, 1, 1024 + + test_find_minimal_combination_and_count 1024, 12, 1, 2 + test_find_minimal_combination_and_count 1024, 23, 1, 1023 + test_find_minimal_combination_and_count 1024, 24, 1, 1024 + test_find_minimal_combination_and_count 1023, 26, 1, 1023 + + test_find_minimal_combination_and_count 1024, 93, 1001, 1003, 1005, 1007 + test_find_minimal_combination_and_count 1023, 93, 1001, 1003, 1005, 1007 +end diff --git a/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/test/minitest/test_minitest_assertions.rb b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/test/minitest/test_minitest_assertions.rb new file mode 100644 index 0000000..2d7751b --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/test/minitest/test_minitest_assertions.rb @@ -0,0 +1,1663 @@ +require "minitest/autorun" +require_relative "metametameta" + +e = Encoding.default_external +if e != Encoding::UTF_8 then + warn "" + warn "" + warn "NOTE: External encoding #{e} is not UTF-8. Tests WILL fail." + warn " Run tests with `RUBYOPT=-Eutf-8 rake` to avoid errors." + warn "" + warn "" +end + +SomeError = Class.new Exception + +unless defined? MyModule then + module MyModule; end + class AnError < StandardError; include MyModule; end +end + +class TestMinitestAssertions < Minitest::Test + # do not call parallelize_me! - teardown accesses @tc._assertions + # which is not threadsafe. Nearly every method in here is an + # assertion test so it isn't worth splitting it out further. + + # not included in JRuby + RE_LEVELS = /\(\d+ levels\) / + + class DummyTest + include Minitest::Assertions + + attr_accessor :assertions, :failure + + def initialize + self.assertions = 0 + self.failure = nil + end + end + + def setup + super + + Minitest::Test.reset + + @tc = DummyTest.new + @zomg = "zomg ponies!" # TODO: const + @assertion_count = 1 + end + + def teardown + assert_equal(@assertion_count, @tc.assertions, + "expected #{@assertion_count} assertions to be fired during the test, not #{@tc.assertions}") + end + + def assert_triggered expected, klass = Minitest::Assertion + e = assert_raises klass do + yield + end + + msg = e.message.sub(/(---Backtrace---).*/m, '\1') + msg.gsub!(/\(oid=[-0-9]+\)/, "(oid=N)") + msg.gsub!(/(\d\.\d{6})\d+/, '\1xxx') # normalize: ruby version, impl, platform + + assert_msg = Regexp === expected ? :assert_match : :assert_equal + self.send assert_msg, expected, msg + end + + def assert_unexpected expected + expected = Regexp.new expected if String === expected + + assert_triggered expected, Minitest::UnexpectedError do + yield + end + end + + def non_verbose + orig_verbose = $VERBOSE + $VERBOSE = false + + yield + ensure + $VERBOSE = orig_verbose + end + + def test_assert + @assertion_count = 2 + + @tc.assert_equal true, @tc.assert(true), "returns true on success" + end + + def test_assert__triggered + assert_triggered "Expected false to be truthy." do + @tc.assert false + end + end + + def test_assert__triggered_message + assert_triggered @zomg do + @tc.assert false, @zomg + end + end + + def test_assert__triggered_lambda + assert_triggered "whoops" do + @tc.assert false, lambda { "whoops" } + end + end + + def test_assert_empty + @assertion_count = 2 + + @tc.assert_empty [] + end + + def test_assert_empty_triggered + @assertion_count = 2 + + assert_triggered "Expected [1] to be empty." do + @tc.assert_empty [1] + end + end + + def test_assert_equal + @tc.assert_equal 1, 1 + end + + def test_assert_equal_different_collection_array_hex_invisible + exp = Object.new + act = Object.new + msg = <<~EOM.chomp + No visible difference in the Array#inspect output. + You should look at the implementation of #== on Array or its members. + [#] + EOM + assert_triggered msg do + @tc.assert_equal [exp], [act] + end + end + + def test_assert_equal_different_collection_hash_hex_invisible + exp, act = {}, {} + exp[1] = Object.new + act[1] = Object.new + act_obj = act[1] + def act_obj.inspect = "#" + msg = <<~EOM.chomp % [act] + No visible difference in the Hash#inspect output. + You should look at the implementation of #== on Hash or its members. + %p + EOM + + assert_triggered msg do + @tc.assert_equal exp, act + end + end + + def test_assert_equal_different_diff_deactivated + without_diff do + assert_triggered util_msg("haha" * 10, "blah" * 10) do + exp = "haha" * 10 + act = "blah" * 10 + + @tc.assert_equal exp, act + end + end + end + + def test_assert_equal_string_message + assert_triggered "whoops.\nExpected: 1\n Actual: 2" do + @tc.assert_equal 1, 2, "whoops" + end + end + + def test_assert_equal_different_message + assert_triggered "whoops." do + @tc.assert_equal 1, 2, message { "whoops" } + end + end + + def test_assert_equal_different_lambda + assert_triggered "whoops" do + @tc.assert_equal 1, 2, lambda { "whoops" } + end + end + + def test_assert_equal_different_hex + c = Class.new do + def initialize s; @name = s; end + end + + exp = c.new "a" + act = c.new "b" + msg = <<~EOS + --- expected + +++ actual + @@ -1 +1 @@ + -#<#:0xXXXXXX @name="a"> + +#<#:0xXXXXXX @name="b"> + EOS + + assert_triggered msg do + @tc.assert_equal exp, act + end + end + + def test_assert_equal_different_hex_invisible + exp = Object.new + act = Object.new + + msg = <<~EOM.chomp + No visible difference in the Object#inspect output. + You should look at the implementation of #== on Object or its members. + # + EOM + + assert_triggered msg do + @tc.assert_equal exp, act + end + end + + def test_assert_equal_different_long + msg = <<~EOM + --- expected + +++ actual + @@ -1 +1 @@ + -"hahahahahahahahahahahahahahahahahahahaha" + +"blahblahblahblahblahblahblahblahblahblah" + EOM + + assert_triggered msg do + exp = "haha" * 10 + act = "blah" * 10 + + @tc.assert_equal exp, act + end + end + + def test_assert_equal_different_long_invisible + msg = <<~EOM.chomp + No visible difference in the String#inspect output. + You should look at the implementation of #== on String or its members. + "blahblahblahblahblahblahblahblahblahblah" + EOM + + assert_triggered msg do + exp = "blah" * 10 + act = "blah" * 10 + def exp.== _ + false + end + @tc.assert_equal exp, act + end + end + + def test_assert_equal_different_long_msg + msg = <<~EOM + message. + --- expected + +++ actual + @@ -1 +1 @@ + -"hahahahahahahahahahahahahahahahahahahaha" + +"blahblahblahblahblahblahblahblahblahblah" + EOM + + assert_triggered msg do + exp = "haha" * 10 + act = "blah" * 10 + @tc.assert_equal exp, act, "message" + end + end + + def test_assert_equal_different_short + assert_triggered util_msg(1, 2) do + @tc.assert_equal 1, 2 + end + end + + def test_assert_equal_different_short_msg + assert_triggered util_msg(1, 2, "message") do + @tc.assert_equal 1, 2, "message" + end + end + + def test_assert_equal_different_short_multiline + msg = "--- expected\n+++ actual\n@@ -1,2 +1,2 @@\n \"a\n-b\"\n+c\"\n" + assert_triggered msg do + @tc.assert_equal "a\nb", "a\nc" + end + end + + def test_assert_equal_does_not_allow_lhs_nil + assert_triggered(/Use assert_nil if expecting nil/) do + @tc.assert_equal nil, nil + end + end + + def test_assert_equal_string_bug791 + exp = <<~EOM.chomp + Expected: "\\\\n" + Actual: "\\\\" + EOM + assert_triggered exp do + @tc.assert_equal "\\n", "\\" + end + end + + def test_assert_equal_string_both_escaped_unescaped_newlines + msg = <<~'EOM' # NOTE: single quotes on heredoc + --- expected + +++ actual + @@ -1,2 +1 @@ + -"A\n + -B" + +"A\n\\nB" + EOM + + assert_triggered msg do + exp = "A\\nB" + act = "A\n\\nB" + + @tc.assert_equal exp, act + end + end + + def test_assert_equal_string_encodings + msg = <<~EOM + --- expected + +++ actual + @@ -1,3 +1,3 @@ + -# encoding: UTF-8 + -# valid: false + +# encoding: #{Encoding::BINARY.name} + +# valid: true + "bad-utf8-\\xF1.txt" + EOM + + assert_triggered msg do + exp = "bad-utf8-\xF1.txt" + act = exp.dup.b + @tc.assert_equal exp, act + end + end + + def test_assert_equal_string_encodings_both_different + msg = <<~EOM + --- expected + +++ actual + @@ -1,3 +1,3 @@ + -# encoding: US-ASCII + -# valid: false + +# encoding: #{Encoding::BINARY.name} + +# valid: true + "bad-utf8-\\xF1.txt" + EOM + + assert_triggered msg do + exp = "bad-utf8-\xF1.txt".dup.force_encoding Encoding::ASCII + act = exp.dup.b + @tc.assert_equal exp, act + end + end + + def test_assert_equal_unescape_newlines + msg = <<~'EOM' # NOTE single quotes on heredoc + --- expected + +++ actual + @@ -1,2 +1,2 @@ + -"hello + +"hello\n + world" + EOM + + assert_triggered msg do + exp = "hello\nworld" + act = 'hello\nworld' # notice single quotes + + @tc.assert_equal exp, act + end + end + + def test_assert_in_delta + @tc.assert_in_delta 0.0, 1.0 / 1000, 0.1 + end + + def test_assert_in_delta_triggered + x = "1.0e-06" + assert_triggered "Expected |0.0 - 0.001| (0.001) to be <= #{x}." do + @tc.assert_in_delta 0.0, 1.0 / 1000, 0.000001 + end + end + + def test_assert_in_epsilon + @assertion_count = 10 + + @tc.assert_in_epsilon 10_000, 9991 + @tc.assert_in_epsilon 9991, 10_000 + @tc.assert_in_epsilon 1.0, 1.001 + @tc.assert_in_epsilon 1.001, 1.0 + + @tc.assert_in_epsilon 10_000, 9999.1, 0.0001 + @tc.assert_in_epsilon 9999.1, 10_000, 0.0001 + @tc.assert_in_epsilon 1.0, 1.0001, 0.0001 + @tc.assert_in_epsilon 1.0001, 1.0, 0.0001 + + @tc.assert_in_epsilon(-1, -1) + @tc.assert_in_epsilon(-10_000, -9991) + end + + def test_assert_in_epsilon_triggered + assert_triggered "Expected |10000 - 9990| (10) to be <= 9.99." do + @tc.assert_in_epsilon 10_000, 9990 + end + end + + def test_assert_in_epsilon_triggered_negative_case + x = "0.100000xxx" + y = "0.1" + assert_triggered "Expected |-1.1 - -1| (#{x}) to be <= #{y}." do + @tc.assert_in_epsilon(-1.1, -1, 0.1) + end + end + + def test_assert_includes + @assertion_count = 2 + + @tc.assert_includes [true], true + end + + def test_assert_includes_triggered + @assertion_count = 3 + + e = @tc.assert_raises Minitest::Assertion do + @tc.assert_includes [true], false + end + + expected = "Expected [true] to include false." + assert_equal expected, e.message + end + + def test_assert_instance_of + @tc.assert_instance_of String, "blah" + end + + def test_assert_instance_of_triggered + assert_triggered 'Expected "blah" to be an instance of Array, not String.' do + @tc.assert_instance_of Array, "blah" + end + end + + def test_assert_kind_of + @tc.assert_kind_of String, "blah" + end + + def test_assert_kind_of_triggered + assert_triggered 'Expected "blah" to be a kind of Array, not String.' do + @tc.assert_kind_of Array, "blah" + end + end + + def test_assert_match + @assertion_count = 2 + m = @tc.assert_match(/\w+/, "blah blah blah") + + assert_kind_of MatchData, m + assert_equal "blah", m[0] + end + + def test_assert_match_matchee_to_str + @assertion_count = 2 + + obj = Object.new + def obj.to_str; "blah" end + + @tc.assert_match "blah", obj + end + + def test_assert_match_matcher_object + @assertion_count = 2 + + pattern = Object.new + def pattern.=~ _; true end + + @tc.assert_match pattern, 5 + end + + def test_assert_match_object_triggered + @assertion_count = 2 + + pattern = Object.new + def pattern.=~ _; false end + def pattern.inspect; "[Object]" end + + assert_triggered "Expected [Object] to match 5." do + @tc.assert_match pattern, 5 + end + end + + def test_assert_match_triggered + @assertion_count = 2 + assert_triggered 'Expected /\d+/ to match "blah blah blah".' do + @tc.assert_match(/\d+/, "blah blah blah") + end + end + + def test_assert_nil + @tc.assert_nil nil + end + + def test_assert_nil_triggered + assert_triggered "Expected 42 to be nil." do + @tc.assert_nil 42 + end + end + + def test_assert_operator + @assertion_count += 1 + + @tc.assert_operator 2, :>, 1 + end + + def test_assert_operator_bad_object + @assertion_count += 1 + + bad = Object.new + def bad.== _; true end + + @tc.assert_operator bad, :equal?, bad + end + + def test_assert_operator_triggered + @assertion_count += 1 + + assert_triggered "Expected 2 to be < 1." do + @tc.assert_operator 2, :<, 1 + end + end + + def test_assert_output_both + @assertion_count = 2 + + @tc.assert_output "yay", "blah" do + print "yay" + $stderr.print "blah" + end + end + + def test_assert_output_both_regexps + @assertion_count = 4 + + @tc.assert_output(/y.y/, /bl.h/) do + print "yay" + $stderr.print "blah" + end + end + + def test_assert_output_err + @tc.assert_output nil, "blah" do + $stderr.print "blah" + end + end + + def test_assert_output_neither + @assertion_count = 0 + + @tc.assert_output do + # do nothing + end + end + + def test_assert_output_out + @tc.assert_output "blah" do + print "blah" + end + end + + def test_assert_output_triggered_both + assert_triggered util_msg("blah", "blah blah", "In stderr") do + @tc.assert_output "yay", "blah" do + print "boo" + $stderr.print "blah blah" + end + end + end + + def test_assert_output_triggered_err + assert_triggered util_msg("blah", "blah blah", "In stderr") do + @tc.assert_output nil, "blah" do + $stderr.print "blah blah" + end + end + end + + def test_assert_output_triggered_out + assert_triggered util_msg("blah", "blah blah", "In stdout") do + @tc.assert_output "blah" do + print "blah blah" + end + end + end + + def test_assert_output_no_block + assert_triggered "assert_output requires a block to capture output." do + @tc.assert_output "blah" + end + end + + def test_assert_output_nested_assert_uncaught + @assertion_count = 1 + + assert_triggered "Epic Fail!" do + @tc.assert_output "blah\n" do + puts "blah" + @tc.flunk + end + end + end + + def test_assert_output_nested_raise + @assertion_count = 2 + + @tc.assert_output "blah\n" do + @tc.assert_raises RuntimeError do + puts "blah" + raise "boom!" + end + end + end + + def test_assert_output_nested_raise_bad + @assertion_count = 0 + + assert_unexpected "boom!" do + @tc.assert_raises do # 2) bypassed via UnexpectedError + @tc.assert_output "blah\n" do # 1) captures and raises UnexpectedError + puts "not_blah" + raise "boom!" + end + end + end + end + + def test_assert_output_nested_raise_mismatch + # this test is redundant, but illustrative + @assertion_count = 0 + + assert_unexpected "boom!" do + @tc.assert_raises RuntimeError do # 2) bypassed via UnexpectedError + @tc.assert_output "blah\n" do # 1) captures and raises UnexpectedError + puts "not_blah" + raise ArgumentError, "boom!" + end + end + end + end + + def test_assert_output_nested_throw_caught + @assertion_count = 2 + + @tc.assert_output "blah\n" do + @tc.assert_throws :boom! do + puts "blah" + throw :boom! + end + end + end + + def test_assert_output_nested_throw_caught_bad + @assertion_count = 1 # want 0; can't prevent throw from escaping :( + + @tc.assert_throws :boom! do # 2) captured via catch + @tc.assert_output "blah\n" do # 1) bypassed via throw + puts "not_blah" + throw :boom! + end + end + end + + def test_assert_output_nested_throw_mismatch + @assertion_count = 0 + + assert_unexpected "uncaught throw :boom!" do + @tc.assert_throws :not_boom! do # 2) captured via assert_throws+rescue + @tc.assert_output "blah\n" do # 1) bypassed via throw + puts "not_blah" + throw :boom! + end + end + end + end + + def test_assert_output_uncaught_raise + @assertion_count = 0 + + assert_unexpected "RuntimeError: boom!" do + @tc.assert_output "blah\n" do + puts "not_blah" + raise "boom!" + end + end + end + + def test_assert_output_uncaught_throw + @assertion_count = 0 + + assert_unexpected "uncaught throw :boom!" do + @tc.assert_output "blah\n" do + puts "not_blah" + throw :boom! + end + end + end + + def test_assert_predicate + @assertion_count += 1 + + @tc.assert_predicate "", :empty? + end + + def test_assert_predicate_triggered + @assertion_count += 1 + + assert_triggered 'Expected "blah" to be empty?.' do + @tc.assert_predicate "blah", :empty? + end + end + + def test_assert_raises + @tc.assert_raises RuntimeError do + raise "blah" + end + end + + def test_assert_raises_default + @tc.assert_raises do + raise StandardError, "blah" + end + end + + def test_assert_raises_default_triggered + e = assert_raises Minitest::Assertion do + @tc.assert_raises do + raise SomeError, "blah" + end + end + + expected = <<~EOM.chomp + [StandardError] exception expected, not + Class: + Message: <"blah"> + ---Backtrace--- + FILE:LINE:in 'block in test_assert_raises_default_triggered' + --------------- + EOM + + actual = e.message.gsub(/^.+:\d+/, "FILE:LINE") + actual.gsub! RE_LEVELS, "" unless jruby? + actual.gsub!(/[`']block in (?:TestMinitestAssertions#)?/, "'block in ") + + assert_equal expected, actual + end + + def test_assert_raises_exit + @tc.assert_raises SystemExit do + exit 1 + end + end + + def test_assert_raises_module + @tc.assert_raises MyModule do + raise AnError + end + end + + def test_assert_raises_signals + @tc.assert_raises SignalException do + raise SignalException, :INT + end + end + + def test_assert_raises_throw_nested_bad + @assertion_count = 0 + + assert_unexpected "RuntimeError: boom!" do + @tc.assert_raises do + @tc.assert_throws :blah do + raise "boom!" + throw :not_blah + end + end + end + end + + ## + # *sigh* This is quite an odd scenario, but it is from real (albeit + # ugly) test code in ruby-core: + + # https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=29259 + + def test_assert_raises_skip + @assertion_count = 0 + + assert_triggered "skipped", Minitest::Skip do + @tc.assert_raises ArgumentError do + begin + raise "blah" + rescue + skip "skipped" + end + end + end + end + + def test_assert_raises_subclass + @tc.assert_raises StandardError do + raise AnError + end + end + + def test_assert_raises_subclass_triggered + e = assert_raises Minitest::Assertion do + @tc.assert_raises SomeError do + raise AnError, "some message" + end + end + + expected = <<~EOM + [SomeError] exception expected, not + Class: + Message: <"some message"> + ---Backtrace--- + FILE:LINE:in 'block in test_assert_raises_subclass_triggered' + --------------- + EOM + + actual = e.message.gsub(/^.+:\d+/, "FILE:LINE") + actual.gsub! RE_LEVELS, "" unless jruby? + actual.gsub!(/[`']block in (?:TestMinitestAssertions#)?/, "'block in ") + + assert_equal expected.chomp, actual + end + + def test_assert_raises_triggered_different + e = assert_raises Minitest::Assertion do + @tc.assert_raises RuntimeError do + raise SyntaxError, "icky" + end + end + + expected = <<~EOM.chomp + [RuntimeError] exception expected, not + Class: + Message: <"icky"> + ---Backtrace--- + FILE:LINE:in 'block in test_assert_raises_triggered_different' + --------------- + EOM + + actual = e.message.gsub(/^.+:\d+/, "FILE:LINE") + actual.gsub! RE_LEVELS, "" unless jruby? + actual.gsub!(/[`']block in (?:TestMinitestAssertions#)?/, "'block in ") + + assert_equal expected, actual + end + + def test_assert_raises_triggered_different_msg + e = assert_raises Minitest::Assertion do + @tc.assert_raises RuntimeError, "XXX" do + raise SyntaxError, "icky" + end + end + + expected = <<~EOM + XXX. + [RuntimeError] exception expected, not + Class: + Message: <"icky"> + ---Backtrace--- + FILE:LINE:in 'block in test_assert_raises_triggered_different_msg' + --------------- + EOM + + actual = e.message.gsub(/^.+:\d+/, "FILE:LINE") + actual.gsub! RE_LEVELS, "" unless jruby? + actual.gsub!(/[`']block in (?:TestMinitestAssertions#)?/, "'block in ") + + assert_equal expected.chomp, actual + end + + def test_assert_raises_triggered_none + e = assert_raises Minitest::Assertion do + @tc.assert_raises Minitest::Assertion do + # do nothing + end + end + + expected = "Minitest::Assertion expected but nothing was raised." + + assert_equal expected, e.message + end + + def test_assert_raises_triggered_none_msg + e = assert_raises Minitest::Assertion do + @tc.assert_raises Minitest::Assertion, "XXX" do + # do nothing + end + end + + expected = "XXX.\nMinitest::Assertion expected but nothing was raised." + + assert_equal expected, e.message + end + + def test_assert_raises_without_block + assert_triggered "assert_raises requires a block to capture errors." do + @tc.assert_raises StandardError + end + end + + def test_assert_respond_to + @tc.assert_respond_to "blah", :empty? + end + + def test_assert_respond_to_triggered + assert_triggered 'Expected "blah" (String) to respond to #rawr!.' do + @tc.assert_respond_to "blah", :rawr! + end + end + + def test_assert_respond_to__include_all + @tc.assert_respond_to @tc, :exit, include_all: true + end + + def test_assert_respond_to__include_all_triggered + assert_triggered(/Expected .+::DummyTest. to respond to #exit\?/) do + @tc.assert_respond_to @tc, :exit?, include_all: true + end + end + + def test_assert_same + @assertion_count = 3 + + o = "blah" + @tc.assert_same 1, 1 + @tc.assert_same :blah, :blah + @tc.assert_same o, o + end + + def test_assert_same_triggered + @assertion_count = 2 + + assert_triggered "Expected 2 (oid=N) to be the same as 1 (oid=N)." do + @tc.assert_same 1, 2 + end + + s1 = +"blah" + s2 = +"blah" + + assert_triggered 'Expected "blah" (oid=N) to be the same as "blah" (oid=N).' do + @tc.assert_same s1, s2 + end + end + + def test_assert_silent + @assertion_count = 2 + + @tc.assert_silent do + # do nothing + end + end + + def test_assert_silent_triggered_err + assert_triggered util_msg("", "blah blah", "In stderr") do + @tc.assert_silent do + $stderr.print "blah blah" + end + end + end + + def test_assert_silent_triggered_out + @assertion_count = 2 + + assert_triggered util_msg("", "blah blah", "In stdout") do + @tc.assert_silent do + print "blah blah" + end + end + end + + def test_assert_throws + v = @tc.assert_throws :blah do + throw :blah + end + + assert_nil v + end + + def test_assert_throws_value + v = @tc.assert_throws :blah do + throw :blah, 42 + end + + assert_equal 42, v + end + + def test_assert_throws_argument_exception + @assertion_count = 0 + + assert_unexpected "ArgumentError" do + @tc.assert_throws :blah do + raise ArgumentError + end + end + end + + def test_assert_throws_different + assert_triggered "Expected :blah to have been thrown, not :not_blah." do + @tc.assert_throws :blah do + throw :not_blah + end + end + end + + def test_assert_throws_name_error + @assertion_count = 0 + + assert_unexpected "NameError" do + @tc.assert_throws :blah do + raise NameError + end + end + end + + def test_assert_throws_unthrown + assert_triggered "Expected :blah to have been thrown." do + @tc.assert_throws :blah do + # do nothing + end + end + end + + def test_assert_path_exists + @tc.assert_path_exists __FILE__ + end + + def test_assert_path_exists_triggered + assert_triggered "Expected path 'blah' to exist." do + @tc.assert_path_exists "blah" + end + end + + def test_assert_pattern + @tc.assert_pattern do + assert_output nil, "" do + [1,2,3] => [Integer, Integer, Integer] + end + end + end + + def test_assert_pattern_traps_nomatchingpatternerror + exp = /length mismatch/ + + assert_triggered exp do + @tc.assert_pattern do + [1,2,3] => [Integer, Integer] + end + end + end + + def test_assert_pattern_raises_other_exceptions + @assertion_count = 0 + + assert_raises RuntimeError do + @tc.assert_pattern do + raise "boom" + end + end + end + + def test_assert_pattern_with_no_block + assert_triggered "assert_pattern requires a block to capture errors." do + @tc.assert_pattern + end + end + + def test_capture_io + @assertion_count = 0 + + non_verbose do + out, err = capture_io do + puts "hi" + $stderr.puts "bye!" + end + + assert_equal "hi\n", out + assert_equal "bye!\n", err + end + end + + def test_capture_subprocess_io + @assertion_count = 0 + + non_verbose do + out, err = capture_subprocess_io do + system "echo hi" + system "echo bye! 1>&2" + end + + assert_equal "hi\n", out + assert_equal "bye!", err.strip + end + end + + def test_class_asserts_match_refutes + @assertion_count = 0 + + methods = Minitest::Assertions.public_instance_methods.map(&:to_s) + + # These don't have corresponding refutes _on purpose_. They're + # useless and will never be added, so don't bother. + ignores = %w[assert_output assert_raises + assert_silent assert_throws assert_mock] + + ignores += %w[assert_allocations] # for minitest-gcstats + + asserts = methods.grep(/^assert/).sort - ignores + refutes = methods.grep(/^refute/).sort - ignores + + assert_empty refutes.map { |n| n.sub(/^refute/, "assert") } - asserts + assert_empty asserts.map { |n| n.sub(/^assert/, "refute") } - refutes + end + + def test_delta_consistency + @assertion_count = 2 + + @tc.assert_in_delta 0, 1, 1 + + assert_triggered "Expected |0 - 1| (1) to not be <= 1." do + @tc.refute_in_delta 0, 1, 1 + end + end + + def test_epsilon_consistency + @assertion_count = 2 + + @tc.assert_in_epsilon 1.0, 1.001 + + msg = "Expected |1.0 - 1.001| (0.000999xxx) to not be <= 0.001." + assert_triggered msg do + @tc.refute_in_epsilon 1.0, 1.001 + end + end + + def assert_fail_after t + @tc.fail_after t.year, t.month, t.day, "remove the deprecations" + end + + def test_fail_after + d0 = Time.now + d1 = d0 + 86_400 # I am an idiot + + assert_silent do + assert_fail_after d1 + end + + assert_triggered "remove the deprecations" do + assert_fail_after d0 + end + end + + def test_flunk + assert_triggered "Epic Fail!" do + @tc.flunk + end + end + + def test_flunk_message + assert_triggered @zomg do + @tc.flunk @zomg + end + end + + def test_pass + @tc.pass + end + + def test_refute + @assertion_count = 2 + + @tc.assert_equal true, @tc.refute(false), "returns true on success" + end + + def test_refute_empty + @assertion_count = 2 + + @tc.refute_empty [1] + end + + def test_refute_empty_triggered + @assertion_count = 2 + + assert_triggered "Expected [] to not be empty." do + @tc.refute_empty [] + end + end + + def test_refute_equal + @tc.refute_equal "blah", "yay" + end + + def test_refute_equal_triggered + assert_triggered 'Expected "blah" to not be equal to "blah".' do + @tc.refute_equal "blah", "blah" + end + end + + def test_refute_in_delta + @tc.refute_in_delta 0.0, 1.0 / 1000, 0.000001 + end + + def test_refute_in_delta_triggered + x = "0.1" + assert_triggered "Expected |0.0 - 0.001| (0.001) to not be <= #{x}." do + @tc.refute_in_delta 0.0, 1.0 / 1000, 0.1 + end + end + + def test_refute_in_epsilon + @tc.refute_in_epsilon 10_000, 9990-1 + end + + def test_refute_in_epsilon_triggered + assert_triggered "Expected |10000 - 9991| (9) to not be <= 9.991." do + @tc.refute_in_epsilon 10_000, 9991 + flunk + end + end + + def test_refute_in_epsilon_minimum + @tc.refute_in_epsilon 10_000, 9990 + end + + def test_refute_includes + @assertion_count = 2 + + @tc.refute_includes [true], false + end + + def test_refute_includes_triggered + @assertion_count = 3 + + e = @tc.assert_raises Minitest::Assertion do + @tc.refute_includes [true], true + end + + expected = "Expected [true] to not include true." + assert_equal expected, e.message + end + + def test_refute_instance_of + @tc.refute_instance_of Array, "blah" + end + + def test_refute_instance_of_triggered + assert_triggered 'Expected "blah" to not be an instance of String.' do + @tc.refute_instance_of String, "blah" + end + end + + def test_refute_kind_of + @tc.refute_kind_of Array, "blah" + end + + def test_refute_kind_of_triggered + assert_triggered 'Expected "blah" to not be a kind of String.' do + @tc.refute_kind_of String, "blah" + end + end + + def test_refute_match + @assertion_count = 2 + @tc.refute_match(/\d+/, "blah blah blah") + end + + def test_refute_match_matcher_object + @assertion_count = 2 + pattern = Object.new + def pattern.=~ _; false end + @tc.refute_match pattern, 5 + end + + def test_refute_match_object_triggered + @assertion_count = 2 + + pattern = Object.new + def pattern.=~ _; true end + def pattern.inspect; "[Object]" end + + assert_triggered "Expected [Object] to not match 5." do + @tc.refute_match pattern, 5 + end + end + + def test_refute_match_triggered + @assertion_count = 2 + assert_triggered 'Expected /\w+/ to not match "blah blah blah".' do + @tc.refute_match(/\w+/, "blah blah blah") + end + end + + def test_refute_nil + @tc.refute_nil 42 + end + + def test_refute_nil_triggered + assert_triggered "Expected nil to not be nil." do + @tc.refute_nil nil + end + end + + def test_refute_operator + @assertion_count += 1 + + @tc.refute_operator 2, :<, 1 + end + + def test_refute_operator_bad_object + @assertion_count += 1 + + bad = Object.new + def bad.== _; true end + + @tc.refute_operator true, :equal?, bad + end + + def test_refute_operator_triggered + @assertion_count += 1 + + assert_triggered "Expected 2 to not be > 1." do + @tc.refute_operator 2, :>, 1 + end + end + + def test_refute_pattern + @tc.refute_pattern do + [1,2,3] => [Integer, Integer, String] + end + end + + def test_refute_pattern_expects_nomatchingpatternerror + assert_triggered(/NoMatchingPatternError expected, but nothing was raised./) do + @tc.refute_pattern do + [1,2,3] => [Integer, Integer, Integer] + end + end + end + + def test_refute_pattern_raises_other_exceptions + @assertion_count = 0 + + assert_raises RuntimeError do + @tc.refute_pattern do + raise "boom" + end + end + end + + def test_refute_pattern_with_no_block + assert_triggered "refute_pattern requires a block to capture errors." do + @tc.refute_pattern + end + end + + def test_refute_predicate + @assertion_count += 1 + + @tc.refute_predicate "42", :empty? + end + + def test_refute_predicate_triggered + @assertion_count += 1 + + assert_triggered 'Expected "" to not be empty?.' do + @tc.refute_predicate "", :empty? + end + end + + def test_refute_respond_to + @tc.refute_respond_to "blah", :rawr! + end + + def test_refute_respond_to_triggered + assert_triggered 'Expected "blah" to not respond to empty?.' do + @tc.refute_respond_to "blah", :empty? + end + end + + def test_refute_respond_to__include_all + @tc.refute_respond_to "blah", :missing, include_all: true + end + + def test_refute_respond_to__include_all_triggered + assert_triggered(/Expected .*DummyTest.* to not respond to exit./) do + @tc.refute_respond_to @tc, :exit, include_all: true + end + end + + def test_refute_same + @tc.refute_same 1, 2 + end + + def test_refute_same_triggered + assert_triggered "Expected 1 (oid=N) to not be the same as 1 (oid=N)." do + @tc.refute_same 1, 1 + end + end + + def test_refute_path_exists + @tc.refute_path_exists "blah" + end + + def test_refute_path_exists_triggered + assert_triggered "Expected path '#{__FILE__}' to not exist." do + @tc.refute_path_exists __FILE__ + end + end + + def test_skip + @assertion_count = 0 + + assert_triggered "haha!", Minitest::Skip do + @tc.skip "haha!" + end + end + + def assert_skip_until t, msg + @tc.skip_until t.year, t.month, t.day, msg + end + + def test_skip_until + @assertion_count = 0 + + d0 = Time.now + d1 = d0 + 86_400 # I am an idiot + + assert_deprecation(/Stale skip_until \"not yet\" at .*?:\d+$/) do + assert_skip_until d0, "not yet" + end + + assert_triggered "not ready yet", Minitest::Skip do + assert_skip_until d1, "not ready yet" + end + end + + def util_msg exp, act, msg = nil + s = "Expected: #{exp.inspect}\n Actual: #{act.inspect}" + s = "#{msg}.\n#{s}" if msg + s + end + + def without_diff + old_diff = Minitest::Assertions.diff + Minitest::Assertions.diff = nil + + yield + ensure + Minitest::Assertions.diff = old_diff + end +end + +class TestMinitestAssertionHelpers < Minitest::Test + def assert_mu_pp exp, input, raw = false + act = mu_pp input + + if String === input && !raw then + assert_equal "\"#{exp}\"", act + else + assert_equal exp, act + end + end + + def assert_mu_pp_for_diff exp, input, raw = false + act = mu_pp_for_diff input + + if String === input && !raw then + assert_equal "\"#{exp}\"", act + else + assert_equal exp, act + end + end + + def test_diff_equal + msg = <<~EOM.chomp + No visible difference in the String#inspect output. + You should look at the implementation of #== on String or its members. + "blahblahblahblahblahblahblahblahblahblah" + EOM + + o1 = "blah" * 10 + o2 = "blah" * 10 + def o1.== _ + false + end + + assert_equal msg, diff(o1, o2) + end + + def test_diff_str_mixed + msg = <<~'EOM' # NOTE single quotes on heredoc + --- expected + +++ actual + @@ -1 +1 @@ + -"A\\n\nB" + +"A\n\\nB" + EOM + + exp = "A\\n\nB" + act = "A\n\\nB" + + assert_equal msg, diff(exp, act) + end + + def test_diff_str_multiline + msg = <<~EOM + --- expected + +++ actual + @@ -1,2 +1,2 @@ + "A + -B" + +C" + EOM + + exp = "A\nB" + act = "A\nC" + + assert_equal msg, diff(exp, act) + end + + def test_diff_str_simple + msg = <<~EOM.chomp + Expected: "A" + Actual: "B" + EOM + + exp = "A" + act = "B" + + assert_equal msg, diff(exp, act) + end + + def test_message + assert_equal "blah2.", message { "blah2" }.call + assert_equal "blah2.", message("") { "blah2" }.call + assert_equal "blah1.\nblah2.", message(:blah1) { "blah2" }.call + assert_equal "blah1.\nblah2.", message("blah1") { "blah2" }.call + + message = proc { "blah1" } + assert_equal "blah1", message(message) { "blah2" }.call + + message = message { "blah1" } + assert_equal "blah1.", message(message) { "blah2" }.call + end + + def test_message_deferred + var = nil + + msg = message { var = "blah" } + + assert_nil var + + msg.call + + assert_equal "blah", var + end + + def test_mu_pp + assert_mu_pp 42.inspect, 42 + assert_mu_pp %w[a b c].inspect, %w[a b c] + assert_mu_pp "A B", "A B" + assert_mu_pp "A\\nB", "A\nB" + assert_mu_pp "A\\\\nB", 'A\nB' # notice single quotes + end + + def test_mu_pp_for_diff + assert_mu_pp_for_diff "#", Object.new + assert_mu_pp_for_diff "A B", "A B" + assert_mu_pp_for_diff [1, 2, 3].inspect, [1, 2, 3] + assert_mu_pp_for_diff "A\nB", "A\nB" + end + + def test_mu_pp_for_diff_str_bad_encoding + str = "\666".dup.force_encoding Encoding::UTF_8 + exp = "# encoding: UTF-8\n# valid: false\n\"\\xB6\"" + + assert_mu_pp_for_diff exp, str, :raw + end + + def test_mu_pp_for_diff_str_bad_encoding_both + str = "\666A\\n\nB".dup.force_encoding Encoding::UTF_8 + exp = "# encoding: UTF-8\n# valid: false\n\"\\xB6A\\\\n\\nB\"" + + assert_mu_pp_for_diff exp, str, :raw + end + + def test_mu_pp_for_diff_str_encoding + str = "A\nB".b + exp = "# encoding: #{Encoding::BINARY.name}\n# valid: true\n\"A\nB\"" + + assert_mu_pp_for_diff exp, str, :raw + end + + def test_mu_pp_for_diff_str_encoding_both + str = "A\\n\nB".b + exp = "# encoding: #{Encoding::BINARY.name}\n# valid: true\n\"A\\\\n\\nB\"" + + assert_mu_pp_for_diff exp, str, :raw + end + + def test_mu_pp_for_diff_str_nerd + assert_mu_pp_for_diff "A\\nB\\\\nC", "A\nB\\nC" + assert_mu_pp_for_diff "\\nB\\\\nC", "\nB\\nC" + assert_mu_pp_for_diff "\\nB\\\\n", "\nB\\n" + assert_mu_pp_for_diff "\\n\\\\n", "\n\\n" + assert_mu_pp_for_diff "\\\\n\\n", "\\n\n" + assert_mu_pp_for_diff "\\\\nB\\n", "\\nB\n" + assert_mu_pp_for_diff "\\\\nB\\nC", "\\nB\nC" + assert_mu_pp_for_diff "A\\\\n\\nB", "A\\n\nB" + assert_mu_pp_for_diff "A\\n\\\\nB", "A\n\\nB" + assert_mu_pp_for_diff "\\\\n\\n", "\\n\n" + assert_mu_pp_for_diff "\\n\\\\n", "\n\\n" + end + + def test_mu_pp_for_diff_str_normal + assert_mu_pp_for_diff "", "" + assert_mu_pp_for_diff "A\\n\n", "A\\n" + assert_mu_pp_for_diff "A\\n\nB", "A\\nB" + assert_mu_pp_for_diff "A\n", "A\n" + assert_mu_pp_for_diff "A\nB", "A\nB" + assert_mu_pp_for_diff "\\n\n", "\\n" + assert_mu_pp_for_diff "\n", "\n" + assert_mu_pp_for_diff "\\n\nA", "\\nA" + assert_mu_pp_for_diff "\nA", "\nA" + end + + def test_mu_pp_str_bad_encoding + str = "\666".dup.force_encoding Encoding::UTF_8 + exp = "# encoding: UTF-8\n# valid: false\n\"\\xB6\"" + + assert_mu_pp exp, str, :raw + end + + def test_mu_pp_str_encoding + str = "A\nB".b + exp = "# encoding: #{Encoding::BINARY.name}\n# valid: true\n\"A\\nB\"" + + assert_mu_pp exp, str, :raw + end + + def test_mu_pp_str_immutable + printer = Class.new { extend Minitest::Assertions } + str = "test".freeze + assert_equal '"test"', printer.mu_pp(str) + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/test/minitest/test_minitest_benchmark.rb b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/test/minitest/test_minitest_benchmark.rb new file mode 100644 index 0000000..de070a9 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/test/minitest/test_minitest_benchmark.rb @@ -0,0 +1,151 @@ +require "minitest/autorun" +require "minitest/benchmark" + +## +# Used to verify data: +# https://www.wolframalpha.com/examples/RegressionAnalysis.html + +class TestMinitestBenchmark < Minitest::Test + def test_cls_bench_exp + assert_equal [2, 4, 8, 16, 32], Minitest::Benchmark.bench_exp(2, 32, 2) + end + + def test_cls_bench_linear + assert_equal [2, 4, 6, 8, 10], Minitest::Benchmark.bench_linear(2, 10, 2) + end + + def test_cls_runnable_methods + assert_equal [], Minitest::Benchmark.runnable_methods + + c = Class.new Minitest::Benchmark do + def bench_blah + end + end + + assert_equal ["bench_blah"], c.runnable_methods + end + + def test_cls_run + c = Class.new Minitest::Benchmark do + def bench_dummy + assert true + end + end + + reporter = Minitest::StatisticsReporter.new(StringIO.new(+"")) + + c.run c, "bench_dummy", reporter + + assert_equal 1, reporter.count + end + + def test_cls_bench_range + assert_equal [1, 10, 100, 1_000, 10_000], Minitest::Benchmark.bench_range + end + + def test_fit_exponential_clean + x = [1.0, 2.0, 3.0, 4.0, 5.0] + y = x.map { |n| 1.1 * Math.exp(2.1 * n) } + + assert_fit :exponential, x, y, 1.0, 1.1, 2.1 + end + + def test_fit_exponential_noisy + x = [1.0, 1.9, 2.6, 3.4, 5.0] + y = [12, 10, 8.2, 6.9, 5.9] + + # verified with Numbers and R + assert_fit :exponential, x, y, 0.95, 13.81148, -0.1820 + end + + def test_fit_logarithmic_clean + x = [1.0, 2.0, 3.0, 4.0, 5.0] + y = x.map { |n| 1.1 + 2.1 * Math.log(n) } + + assert_fit :logarithmic, x, y, 1.0, 1.1, 2.1 + end + + def test_fit_logarithmic_noisy + x = [1.0, 2.0, 3.0, 4.0, 5.0] + # Generated with + # y = x.map { |n| jitter = 0.999 + 0.002 * rand; (Math.log(n) ) * jitter } + y = [0.0, 0.6935, 1.0995, 1.3873, 1.6097] + + assert_fit :logarithmic, x, y, 0.95, 0, 1 + end + + def test_fit_constant_clean + x = (1..5).to_a + y = [5.0, 5.0, 5.0, 5.0, 5.0] + + assert_fit :linear, x, y, nil, 5.0, 0 + end + + def test_fit_constant_noisy + x = (1..5).to_a + y = [1.0, 1.2, 1.0, 0.8, 1.0] + + # verified in numbers and R + assert_fit :linear, x, y, nil, 1.12, -0.04 + end + + def test_fit_linear_clean + # y = m * x + b where m = 2.2, b = 3.1 + x = (1..5).to_a + y = x.map { |n| 2.2 * n + 3.1 } + + assert_fit :linear, x, y, 1.0, 3.1, 2.2 + end + + def test_fit_linear_noisy + x = [ 60, 61, 62, 63, 65] + y = [3.1, 3.6, 3.8, 4.0, 4.1] + + # verified in numbers and R + assert_fit :linear, x, y, 0.8315, -7.9635, 0.1878 + end + + def test_fit_power_clean + # y = A x ** B, where B = b and A = e ** a + # if, A = 1, B = 2, then + + x = [1.0, 2.0, 3.0, 4.0, 5.0] + y = [1.0, 4.0, 9.0, 16.0, 25.0] + + assert_fit :power, x, y, 1.0, 1.0, 2.0 + end + + def test_fit_power_noisy + # from www.engr.uidaho.edu/thompson/courses/ME330/lecture/least_squares.html + x = [10, 12, 15, 17, 20, 22, 25, 27, 30, 32, 35] + y = [95, 105, 125, 141, 173, 200, 253, 298, 385, 459, 602] + + # verified in numbers + assert_fit :power, x, y, 0.90, 2.6217, 1.4556 + + # income to % of households below income amount + # https://library.wolfram.com/infocenter/Conferences/6461/PowerLaws.nb + x = [15_000, 25_000, 35_000, 50_000, 75_000, 100_000] + y = [0.154, 0.283, 0.402, 0.55, 0.733, 0.843] + + # verified in numbers + assert_fit :power, x, y, 0.96, 3.119e-5, 0.8959 + end + + def assert_fit msg, x, y, fit, exp_a, exp_b + bench = Minitest::Benchmark.new :blah + + a, b, rr = bench.send "fit_#{msg}", x, y + + assert_operator rr, :>=, fit if fit + assert_in_delta exp_a, a + assert_in_delta exp_b, b + end +end + +describe "my class Bench" do + klass = self + it "should provide bench methods" do + klass.must_respond_to :bench + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/test/minitest/test_minitest_reporter.rb b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/test/minitest/test_minitest_reporter.rb new file mode 100644 index 0000000..da55132 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/test/minitest/test_minitest_reporter.rb @@ -0,0 +1,437 @@ +require "minitest/autorun" +require_relative "metametameta" +require "forwardable" + +class FakeTest < Minitest::Test + def woot + assert true + end +end + +class TestMinitestReporter < MetaMetaMetaTestCase + + attr_accessor :r, :io + + def new_composite_reporter + reporter = Minitest::CompositeReporter.new + reporter << Minitest::SummaryReporter.new(self.io) + reporter << Minitest::ProgressReporter.new(self.io) + + # eg reporter.results -> reporters.first.results + reporter.extend Forwardable + reporter.delegate :first => :reporters + reporter.delegate %i[results count assertions options to_s] => :first + + reporter + end + + def setup + super + self.io = StringIO.new(+"") + self.r = new_composite_reporter + @et = @ft = @pt = @st = @sse = nil + end + + def error_test + unless @et then + @et = FakeTest.new :woot + @et.failures << Minitest::UnexpectedError.new(begin + raise "no" + rescue => e + e + end) + @et = Minitest::Result.from @et + end + @et + end + + def system_stack_error_test + unless @sse then + + ex = SystemStackError.new + + pre = ("a".."c").to_a + mid = ("aa".."ad").to_a * 67 + post = ("d".."f").to_a + ary = pre + mid + post + + ex.set_backtrace ary + + @sse = FakeTest.new :woot + @sse.failures << Minitest::UnexpectedError.new(ex) + @sse = Minitest::Result.from @sse + end + @sse + end + + def fail_test + unless @ft then + @ft = FakeTest.new :woot + @ft.failures << begin + raise Minitest::Assertion, "boo" + rescue Minitest::Assertion => e + e + end + @ft = Minitest::Result.from @ft + end + @ft + end + + def passing_test + @pt ||= Minitest::Result.from FakeTest.new(:woot) + end + + def passing_test_with_metadata + test = FakeTest.new :woot + test.metadata[:meta] = :data + @pt ||= Minitest::Result.from test + end + + def skip_test + unless @st then + @st = FakeTest.new :woot + @st.failures << begin + raise Minitest::Skip + rescue Minitest::Assertion => e + e + end + @st = Minitest::Result.from @st + end + @st + end + + def test_to_s + r.record passing_test + r.record fail_test + assert_match "woot", r.to_s + end + + def test_options_skip_F + r.options[:skip] = "F" + + r.record passing_test + r.record fail_test + + refute_match "woot", r.to_s + end + + def test_options_skip_E + r.options[:skip] = "E" + + r.record passing_test + r.record error_test + + refute_match "RuntimeError: no", r.to_s + end + + def test_passed_eh_empty + assert_predicate r, :passed? + end + + def test_passed_eh_failure + r.results << fail_test + + refute_predicate r, :passed? + end + + SKIP_MSG = "\n\nYou have skipped tests. Run with --verbose for details." + + def test_passed_eh_error + r.start + + r.results << error_test + + refute_predicate r, :passed? + + r.report + + refute_match SKIP_MSG, io.string + end + + def test_passed_eh_skipped + r.start + r.results << skip_test + assert r.passed? + + restore_env do + r.report + end + + assert_match SKIP_MSG, io.string + end + + def test_passed_eh_skipped_verbose + r.options[:verbose] = true + + r.start + r.results << skip_test + assert r.passed? + r.report + + refute_match SKIP_MSG, io.string + end + + def test_start + r.start + + exp = "Run options: \n\n# Running:\n\n" + + assert_equal exp, io.string + end + + def test_record_pass + r.record passing_test + + assert_equal ".", io.string + assert_empty r.results + assert_equal 1, r.count + assert_equal 0, r.assertions + end + + def test_record_pass_with_metadata + reporter = self.r + + def reporter.metadata + @metadata + end + + def reporter.record result + super + @metadata = result.metadata if result.metadata? + end + + r.record passing_test_with_metadata + + exp = { :meta => :data } + assert_equal exp, reporter.metadata + + assert_equal ".", io.string + assert_empty r.results + assert_equal 1, r.count + assert_equal 0, r.assertions + end + + def test_record_fail + fail_test = self.fail_test + r.record fail_test + + assert_equal "F", io.string + assert_equal [fail_test], r.results + assert_equal 1, r.count + assert_equal 0, r.assertions + end + + def test_record_error + error_test = self.error_test + r.record error_test + + assert_equal "E", io.string + assert_equal [error_test], r.results + assert_equal 1, r.count + assert_equal 0, r.assertions + end + + def test_record_skip + skip_test = self.skip_test + r.record skip_test + + assert_equal "S", io.string + assert_equal [skip_test], r.results + assert_equal 1, r.count + assert_equal 0, r.assertions + end + + def test_report_empty + r.start + r.report + + exp = <<~EOM + Run options: + + # Running: + + + + Finished in 0.00 + + 0 runs, 0 assertions, 0 failures, 0 errors, 0 skips + EOM + + assert_equal exp, normalize_output(io.string) + end + + def test_report_passing + r.start + r.record passing_test + r.report + + exp = <<~EOM + Run options: + + # Running: + + . + + Finished in 0.00 + + 1 runs, 0 assertions, 0 failures, 0 errors, 0 skips + EOM + + assert_equal exp, normalize_output(io.string) + end + + def test_report_failure + r.start + r.record fail_test + r.report + + exp = <<~EOM + Run options: + + # Running: + + F + + Finished in 0.00 + + 1) Failure: + FakeTest#woot [FILE:LINE]: + boo + + 1 runs, 0 assertions, 1 failures, 0 errors, 0 skips + EOM + + assert_equal exp, normalize_output(io.string) + end + + def test_report_error + r.start + r.record error_test + r.report + + exp = <<~EOM + Run options: + + # Running: + + E + + Finished in 0.00 + + 1) Error: + FakeTest#woot: + RuntimeError: no + FILE:LINE:in 'error_test' + FILE:LINE:in 'test_report_error' + + 1 runs, 0 assertions, 0 failures, 1 errors, 0 skips + EOM + + assert_equal exp, normalize_output(io.string) + end + + def test_report_error__sse + r.start + r.record system_stack_error_test + r.report + + exp = <<~EOM + Run options: + + # Running: + + E + + Finished in 0.00 + + 1) Error: + FakeTest#woot: + SystemStackError: 274 -> 12 + a + b + c + +->> 67 cycles of 4 lines: + | aa + | ab + | ac + | ad + +-<< + d + e + f + + 1 runs, 0 assertions, 0 failures, 1 errors, 0 skips + EOM + + assert_equal exp, normalize_output(io.string) + end + + def test_report_skipped + r.start + r.record skip_test + + restore_env do + r.report + end + + exp = <<~EOM + Run options: + + # Running: + + S + + Finished in 0.00 + + 1 runs, 0 assertions, 0 failures, 0 errors, 1 skips + + You have skipped tests. Run with --verbose for details. + EOM + + assert_equal exp, normalize_output(io.string) + end + + def test_report_failure_uses_backtrace_filter + filter = Minitest::BacktraceFilter.new + def filter.filter _bt + ["foo.rb:123:in 'foo'"] + end + + with_backtrace_filter filter do + r.start + r.record fail_test + r.report + end + + exp = "FakeTest#woot [foo.rb:123]" + + assert_includes io.string, exp + end + + def test_report_failure_uses_backtrace_filter_complex_sorbet + backtrace = <<~EOBT + /Users/user/.gem/ruby/3.2.2/gems/minitest-5.20.0/lib/minitest/assertions.rb:183:in 'assert' + example_test.rb:9:in 'assert_false' + /Users/user/.gem/ruby/3.2.2/gems/sorbet-runtime-0.5.11068/lib/types/private/methods/call_validation.rb:256:in 'bind_call' + /Users/user/.gem/ruby/3.2.2/gems/sorbet-runtime-0.5.11068/lib/types/private/methods/call_validation.rb:256:in 'validate_call' + /Users/user/.gem/ruby/3.2.2/gems/sorbet-runtime-0.5.11068/lib/types/private/methods/_methods.rb:275:in 'block in _on_method_added' + example_test.rb:25:in 'test_something' + /Users/user/.gem/ruby/3.2.2/gems/minitest-5.20.0/lib/minitest/test.rb:94:in 'block (3 levels) in run' + /Users/user/.gem/ruby/3.2.2/gems/minitest-5.20.0/lib/minitest/test.rb:191:in 'capture_exceptions' + /Users/user/.gem/ruby/3.2.2/gems/minitest-5.20.0/lib/minitest/test.rb:89:in 'block (2 levels) in run' + ... so many lines ... + EOBT + + filter = Minitest::BacktraceFilter.new %r%lib/minitest|gems/sorbet% + + with_backtrace_filter filter do + begin + assert_equal 1, 2 + rescue Minitest::Assertion => e + e.set_backtrace backtrace.lines.map(&:chomp) + + assert_match "example_test.rb:25", e.location + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/test/minitest/test_minitest_spec.rb b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/test/minitest/test_minitest_spec.rb new file mode 100644 index 0000000..941c198 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/test/minitest/test_minitest_spec.rb @@ -0,0 +1,1095 @@ +require_relative "metametameta" +require "stringio" + +class MiniSpecA < Minitest::Spec; end +class MiniSpecB < Minitest::Test; extend Minitest::Spec::DSL; end +class MiniSpecC < MiniSpecB; end +class NamedExampleA < MiniSpecA; end +class NamedExampleB < MiniSpecB; end +class NamedExampleC < MiniSpecC; end +class ExampleA; end +class ExampleB < ExampleA; end + +describe Minitest::Spec do + # do not parallelize this suite... it just can"t handle it. + + def assert_triggered expected = "blah", klass = Minitest::Assertion + @assertion_count += 1 + + e = assert_raises klass do + yield + end + + msg = e.message.sub(/(---Backtrace---).*/m, '\1') + msg.gsub!(/\(oid=[-0-9]+\)/, "(oid=N)") + msg.gsub!(/(\d\.\d{6})\d+/, '\1xxx') # normalize: ruby version, impl, platform + msg.gsub!(/:0x[Xa-fA-F0-9]{4,}[ @].+?>/, ":0xXXXXXX@PATH>") + + return unless expected + + @assertion_count += 1 + case expected + when String then + assert_equal expected, msg + when Regexp then + @assertion_count += 1 + assert_match expected, msg + else + flunk "Unknown: #{expected.inspect}" + end + end + + def assert_success spec + assert_equal true, spec + end + + before do + @assertion_count = 4 + end + + after do + _(self.assertions).must_equal @assertion_count if passed? and not skipped? + end + + it "needs to be able to catch a Minitest::Assertion exception" do + @assertion_count = 1 + + assert_triggered "Expected 1 to not be equal to 1." do + _(1).wont_equal 1 + end + end + + it "needs to check for file existence" do + @assertion_count = 3 + + assert_success _(__FILE__).path_must_exist + + assert_triggered "Expected path 'blah' to exist." do + _("blah").path_must_exist + end + end + + it "needs to check for file non-existence" do + @assertion_count = 3 + + assert_success _("blah").path_wont_exist + + assert_triggered "Expected path '#{__FILE__}' to not exist." do + _(__FILE__).path_wont_exist + end + end + + it "needs to be sensible about must_include order" do + @assertion_count += 3 # must_include is 2 assertions + + assert_success _([1, 2, 3]).must_include(2) + + assert_triggered "Expected [1, 2, 3] to include 5." do + _([1, 2, 3]).must_include 5 + end + + assert_triggered "msg.\nExpected [1, 2, 3] to include 5." do + _([1, 2, 3]).must_include 5, "msg" + end + end + + it "needs to be sensible about wont_include order" do + @assertion_count += 3 # wont_include is 2 assertions + + assert_success _([1, 2, 3]).wont_include(5) + + assert_triggered "Expected [1, 2, 3] to not include 2." do + _([1, 2, 3]).wont_include 2 + end + + assert_triggered "msg.\nExpected [1, 2, 3] to not include 2." do + _([1, 2, 3]).wont_include 2, "msg" + end + end + + it "needs to catch an expected exception" do + @assertion_count -= 2 + + expect { raise "blah" }.must_raise RuntimeError + expect { raise Minitest::Assertion }.must_raise Minitest::Assertion + end + + it "needs to catch an unexpected exception" do + @assertion_count -= 2 # no positive + + msg = <<-EOM.gsub(/^ {6}/, "").chomp + [RuntimeError] exception expected, not + Class: + Message: <"woot"> + ---Backtrace--- + EOM + + assert_triggered msg do + expect { raise StandardError, "woot" }.must_raise RuntimeError + end + + assert_triggered "msg.\n#{msg}" do + expect { raise StandardError, "woot" }.must_raise RuntimeError, "msg" + end + end + + def good_pattern + capture_io do # 3.0 is noisy + eval "[1,2,3] => [Integer, Integer, Integer]" # eval to escape parser for ruby<3 + end + end + + def bad_pattern + capture_io do # 3.0 is noisy + eval "[1,2,3] => [Integer, Integer]" # eval to escape parser for ruby<3 + end + end + + it "needs to pattern match" do + @assertion_count = 1 + + expect { good_pattern }.must_pattern_match + end + + it "needs to error on bad pattern match" do + @assertion_count = 1 + + exp = /length mismatch/ + + assert_triggered exp do + expect { bad_pattern }.must_pattern_match + end + end + + it "needs to ensure silence" do + @assertion_count -= 1 # no msg + @assertion_count += 2 # assert_output is 2 assertions + + assert_success expect {}.must_be_silent + + assert_triggered "In stdout.\nExpected: \"\"\n Actual: \"xxx\"" do + expect { print "xxx" }.must_be_silent + end + end + + it "needs to have all methods named well" do + skip "N/A" if ENV["MT_NO_EXPECTATIONS"] + + @assertion_count = 2 + + methods = Minitest::Expectations.public_instance_methods.grep(/must|wont/) + methods.map!(&:to_s) if Symbol === methods.first + + musts, wonts = methods.sort.partition { |m| m.include? "must" } + + expected_musts = %w[must_be + must_be_close_to + must_be_empty + must_be_instance_of + must_be_kind_of + must_be_nil + must_be_same_as + must_be_silent + must_be_within_delta + must_be_within_epsilon + must_equal + must_include + must_match + must_output + must_pattern_match + must_raise + must_respond_to + must_throw + path_must_exist] + + bad = %w[not raise throw send output be_silent verify] + + if methods.include? "must_infect" then # from test_minitest_mock.rb + expected_musts += %w[must_infect must_infect_without_flipping] + expected_musts.sort! + bad << "infect" + end + + expected_wonts = expected_musts.map { |m| m.sub("must", "wont") }.sort + expected_wonts.reject! { |m| m =~ /wont_#{Regexp.union(*bad)}/ } + + _(musts).must_equal expected_musts + _(wonts).must_equal expected_wonts + end + + it "needs to raise if an expected exception is not raised" do + @assertion_count -= 2 # no positive test + + assert_triggered "RuntimeError expected but nothing was raised." do + expect { 42 }.must_raise RuntimeError + end + + assert_triggered "msg.\nRuntimeError expected but nothing was raised." do + expect { 42 }.must_raise RuntimeError, "msg" + end + end + + it "needs to verify binary messages" do + @assertion_count += 3 + + assert_success _(42).wont_be(:<, 24) + + assert_triggered "Expected 24 to not be < 42." do + _(24).wont_be :<, 42 + end + + assert_triggered "msg.\nExpected 24 to not be < 42." do + _(24).wont_be :<, 42, "msg" + end + end + + it "needs to verify emptyness" do + @assertion_count += 3 # empty is 2 assertions + + assert_success _([]).must_be_empty + + assert_triggered "Expected [42] to be empty." do + _([42]).must_be_empty + end + + assert_triggered "msg.\nExpected [42] to be empty." do + _([42]).must_be_empty "msg" + end + end + + it "needs to verify equality" do + @assertion_count += 1 + + assert_success _(6 * 7).must_equal(42) + + assert_triggered "Expected: 42\n Actual: 54" do + _(6 * 9).must_equal 42 + end + + assert_triggered "msg.\nExpected: 42\n Actual: 54" do + _(6 * 9).must_equal 42, "msg" + end + + assert_triggered(/^-42\n\+#\n/) do + _(proc { 42 }).must_equal 42 # proc isn't called, so expectation fails + end + end + + it "needs to fail on equality with nil" do + @assertion_count -= 2 + expect { _(nil).must_equal(nil) }.must_raise Minitest::Assertion + end + + it "needs to verify floats outside a delta" do + @assertion_count += 1 # extra test + + assert_success _(24).wont_be_close_to(42) + + assert_triggered "Expected |42 - 42.0| (0.0) to not be <= 0.001." do + _(6 * 7.0).wont_be_close_to 42 + end + + assert_triggered "Expected |42 - 42.0| (0.0) to not be <= 1.0e-05." do + _(6 * 7.0).wont_be_close_to 42, 0.00001 + end + + assert_triggered "msg.\nExpected |42 - 42.0| (0.0) to not be <= 1.0e-05." do + _(6 * 7.0).wont_be_close_to 42, 0.00001, "msg" + end + end + + it "needs to verify floats outside an epsilon" do + @assertion_count += 1 # extra test + + assert_success _(24).wont_be_within_epsilon(42) + + assert_triggered "Expected |42 - 42.0| (0.0) to not be <= 0.042." do + _(6 * 7.0).wont_be_within_epsilon 42 + end + + assert_triggered "Expected |42 - 42.0| (0.0) to not be <= 0.00042." do + _(6 * 7.0).wont_be_within_epsilon 42, 0.00001 + end + + assert_triggered "msg.\nExpected |42 - 42.0| (0.0) to not be <= 0.00042." do + _(6 * 7.0).wont_be_within_epsilon 42, 0.00001, "msg" + end + end + + it "needs to verify floats within a delta" do + @assertion_count += 1 # extra test + + assert_success _(6.0 * 7).must_be_close_to(42.0) + + assert_triggered "Expected |0.0 - 0.01| (0.01) to be <= 0.001." do + _(1.0 / 100).must_be_close_to 0.0 + end + + assert_triggered "Expected |0.0 - 0.001| (0.001) to be <= 1.0e-06." do + _(1.0 / 1000).must_be_close_to 0.0, 0.000001 + end + + assert_triggered "msg.\nExpected |0.0 - 0.001| (0.001) to be <= 1.0e-06." do + _(1.0 / 1000).must_be_close_to 0.0, 0.000001, "msg" + end + end + + it "needs to verify floats within an epsilon" do + @assertion_count += 1 # extra test + + assert_success _(6.0 * 7).must_be_within_epsilon(42.0) + + assert_triggered "Expected |0.0 - 0.01| (0.01) to be <= 0.0." do + _(1.0 / 100).must_be_within_epsilon 0.0 + end + + assert_triggered "Expected |0.0 - 0.001| (0.001) to be <= 0.0." do + _(1.0 / 1000).must_be_within_epsilon 0.0, 0.000001 + end + + assert_triggered "msg.\nExpected |0.0 - 0.001| (0.001) to be <= 0.0." do + _(1.0 / 1000).must_be_within_epsilon 0.0, 0.000001, "msg" + end + end + + it "needs to verify identity" do + assert_success _(1).must_be_same_as(1) + + assert_triggered "Expected 1 (oid=N) to be the same as 2 (oid=N)." do + _(1).must_be_same_as 2 + end + + assert_triggered "msg.\nExpected 1 (oid=N) to be the same as 2 (oid=N)." do + _(1).must_be_same_as 2, "msg" + end + end + + it "needs to verify inequality" do + @assertion_count += 2 + assert_success _(42).wont_equal(6 * 9) + assert_success _(proc {}).wont_equal(42) + + assert_triggered "Expected 1 to not be equal to 1." do + _(1).wont_equal 1 + end + + assert_triggered "msg.\nExpected 1 to not be equal to 1." do + _(1).wont_equal 1, "msg" + end + end + + it "needs to verify instances of a class" do + assert_success _(42).wont_be_instance_of(String) + + assert_triggered "Expected 42 to not be a kind of Integer." do + _(42).wont_be_kind_of Integer + end + + assert_triggered "msg.\nExpected 42 to not be an instance of Integer." do + _(42).wont_be_instance_of Integer, "msg" + end + end + + it "needs to verify kinds of a class" do + @assertion_count += 2 + + assert_success _(42).wont_be_kind_of(String) + assert_success _(proc {}).wont_be_kind_of(String) + + assert_triggered "Expected 42 to not be a kind of Integer." do + _(42).wont_be_kind_of Integer + end + + assert_triggered "msg.\nExpected 42 to not be a kind of Integer." do + _(42).wont_be_kind_of Integer, "msg" + end + end + + it "needs to verify kinds of objects" do + @assertion_count += 3 # extra test + + assert_success _(6 * 7).must_be_kind_of(Integer) + assert_success _(6 * 7).must_be_kind_of(Numeric) + + assert_triggered "Expected 42 to be a kind of String, not Integer." do + _(6 * 7).must_be_kind_of String + end + + assert_triggered "msg.\nExpected 42 to be a kind of String, not Integer." do + _(6 * 7).must_be_kind_of String, "msg" + end + + exp = "Expected # to be a kind of String, not Proc." + assert_triggered exp do + _(proc {}).must_be_kind_of String + end + end + + it "needs to verify mismatch" do + @assertion_count += 3 # match is 2 + + assert_success _("blah").wont_match(/\d+/) + + assert_triggered "Expected /\\w+/ to not match \"blah\"." do + _("blah").wont_match(/\w+/) + end + + assert_triggered "msg.\nExpected /\\w+/ to not match \"blah\"." do + _("blah").wont_match(/\w+/, "msg") + end + end + + it "needs to verify nil" do + assert_success _(nil).must_be_nil + + assert_triggered "Expected 42 to be nil." do + _(42).must_be_nil + end + + assert_triggered "msg.\nExpected 42 to be nil." do + _(42).must_be_nil "msg" + end + end + + it "needs to verify non-emptyness" do + @assertion_count += 3 # empty is 2 assertions + + assert_success _(["some item"]).wont_be_empty + + assert_triggered "Expected [] to not be empty." do + _([]).wont_be_empty + end + + assert_triggered "msg.\nExpected [] to not be empty." do + _([]).wont_be_empty "msg" + end + end + + it "needs to verify non-identity" do + assert_success _(1).wont_be_same_as(2) + + assert_triggered "Expected 1 (oid=N) to not be the same as 1 (oid=N)." do + _(1).wont_be_same_as 1 + end + + assert_triggered "msg.\nExpected 1 (oid=N) to not be the same as 1 (oid=N)." do + _(1).wont_be_same_as 1, "msg" + end + end + + it "needs to verify non-nil" do + assert_success _(42).wont_be_nil + + assert_triggered "Expected nil to not be nil." do + _(nil).wont_be_nil + end + + assert_triggered "msg.\nExpected nil to not be nil." do + _(nil).wont_be_nil "msg" + end + end + + it "needs to verify objects not responding to a message" do + assert_success _("").wont_respond_to(:woot!) + + assert_triggered "Expected \"\" to not respond to to_s." do + _("").wont_respond_to :to_s + end + + assert_triggered "msg.\nExpected \"\" to not respond to to_s." do + _("").wont_respond_to :to_s, "msg" + end + end + + it "needs to verify output in stderr" do + @assertion_count -= 1 # no msg + + assert_success expect { $stderr.print "blah" }.must_output(nil, "blah") + + assert_triggered "In stderr.\nExpected: \"blah\"\n Actual: \"xxx\"" do + expect { $stderr.print "xxx" }.must_output(nil, "blah") + end + end + + it "needs to verify output in stdout" do + @assertion_count -= 1 # no msg + + assert_success expect { print "blah" }.must_output("blah") + + assert_triggered "In stdout.\nExpected: \"blah\"\n Actual: \"xxx\"" do + expect { print "xxx" }.must_output("blah") + end + end + + it "needs to verify regexp matches" do + @assertion_count += 3 # must_match is 2 assertions + + assert_kind_of MatchData, _("blah").must_match(/\w+/) + + assert_triggered "Expected /\\d+/ to match \"blah\"." do + _("blah").must_match(/\d+/) + end + + assert_triggered "msg.\nExpected /\\d+/ to match \"blah\"." do + _("blah").must_match(/\d+/, "msg") + end + end + + describe "expect" do + before do + @assertion_count -= 3 + end + + it "can use expect" do + _(1 + 1).must_equal 2 + end + + it "can use expect with a lambda" do + _ { raise "blah" }.must_raise RuntimeError + end + + it "can use expect in a thread" do + Thread.new { _(1 + 1).must_equal 2 }.join + end + end + + it "needs to verify throw" do + @assertion_count += 4 # 2 extra tests + + assert_nil expect { throw :blah }.must_throw(:blah) + assert_equal 42, expect { throw :blah, 42 }.must_throw(:blah) + + assert_triggered "Expected :blah to have been thrown." do + expect {}.must_throw :blah + end + + assert_triggered "Expected :blah to have been thrown, not :xxx." do + expect { throw :xxx }.must_throw :blah + end + + assert_triggered "msg.\nExpected :blah to have been thrown." do + expect {}.must_throw :blah, "msg" + end + + assert_triggered "msg.\nExpected :blah to have been thrown, not :xxx." do + expect { throw :xxx }.must_throw :blah, "msg" + end + end + + it "needs to verify types of objects" do + assert_success _(6 * 7).must_be_instance_of(Integer) + + exp = "Expected 42 to be an instance of String, not Integer." + + assert_triggered exp do + _(6 * 7).must_be_instance_of String + end + + assert_triggered "msg.\n#{exp}" do + _(6 * 7).must_be_instance_of String, "msg" + end + end + + it "needs to verify using any (negative) predicate" do + @assertion_count += 1 + + assert_success _("blah").wont_be(:empty?) + + assert_triggered "Expected \"\" to not be empty?." do + _("").wont_be :empty? + end + end + + it "needs to verify using any binary operator" do + @assertion_count += 1 + + assert_success _(41).must_be(:<, 42) + + assert_triggered "Expected 42 to be < 41." do + _(42).must_be :<, 41 + end + end + + it "needs to verify using any predicate" do + @assertion_count += 1 + + assert_success _("").must_be(:empty?) + + assert_triggered "Expected \"blah\" to be empty?." do + _("blah").must_be :empty? + end + end + + it "needs to verify using respond_to" do + assert_success _(42).must_respond_to(:+) + + assert_triggered "Expected 42 (Integer) to respond to #clear." do + _(42).must_respond_to :clear + end + + assert_triggered "msg.\nExpected 42 (Integer) to respond to #clear." do + _(42).must_respond_to :clear, "msg" + end + end +end + +describe Minitest::Spec, :let do + def _count + $let_count ||= 0 + end + + let :count do + $let_count += 1 + $let_count + end + + it "is evaluated once per example" do + exp = _count + 1 + + _(count).must_equal exp + _(count).must_equal exp + + _(_count).must_equal exp + end + + it "is REALLY evaluated once per example" do + exp = _count + 1 + + _(count).must_equal exp + _(count).must_equal exp + + _(_count).must_equal exp + end + + it 'raises an error if the name begins with "test"' do + expect { self.class.let(:test_value) { true } }.must_raise ArgumentError + end + + it "raises an error if the name shadows a normal instance method" do + expect { self.class.let(:message) { true } }.must_raise ArgumentError + end + + it "doesn't raise an error if it is just another let" do + v = proc do + describe :outer do + let :bar + describe :inner do + let :bar + end + end + :good + end.call + _(v).must_equal :good + end + + it "procs come after dont_flip" do + p = proc {} + assert_respond_to p, :call + _(p).must_respond_to :call + end +end + +describe Minitest::Spec, :subject do + attr_reader :subject_evaluation_count + + subject do + @subject_evaluation_count ||= 0 + @subject_evaluation_count += 1 + @subject_evaluation_count + end + + it "is evaluated once per example" do + _(subject).must_equal 1 + _(subject).must_equal 1 + _(subject_evaluation_count).must_equal 1 + end +end + +class TestMetaStatic < Minitest::Test + def assert_method_count expected, klass + assert_equal expected, klass.public_instance_methods.grep(/^test_/).count + end + + def test_children + Minitest::Spec.children.clear # prevents parallel run + + y = z = nil + x = describe "top-level thingy" do + y = describe "first thingy" do end + + it "top-level-it" do end + + z = describe "second thingy" do end + end + + assert_equal [x], Minitest::Spec.children + assert_equal [y, z], x.children + assert_equal [], y.children + assert_equal [], z.children + end + + def test_it_wont_remove_existing_child_test_methods + Minitest::Spec.children.clear # prevents parallel run + + inner = nil + outer = describe "outer" do + inner = describe "inner" do + it do + assert true + end + end + it do + assert true + end + end + + assert_method_count 1, outer + assert_method_count 1, inner + end + + def test_it_wont_add_test_methods_to_children + Minitest::Spec.children.clear # prevents parallel run + + inner = nil + outer = describe "outer" do + inner = describe "inner" do end + it do + assert true + end + end + + assert_method_count 1, outer + assert_method_count 0, inner + end +end + +class TestMeta < MetaMetaMetaTestCase + # do not call parallelize_me! here because specs use register_spec_type globally + + def assert_defined_methods expected, klass + assert_equal expected, klass.instance_methods(false).sort.map(&:to_s) + end + + def util_structure + y = z = nil + before_list = [] + after_list = [] + x = describe "top-level thingy" do + before { before_list << 1 } + after { after_list << 1 } + + it "top-level-it" do end + + y = describe "inner thingy" do + before { before_list << 2 } + after { after_list << 2 } + it "inner-it" do end + + z = describe "very inner thingy" do + before { before_list << 3 } + after { after_list << 3 } + it "inner-it" do end + + it { } # ignore me + specify { } # anonymous it + end + end + end + + return x, y, z, before_list, after_list + end + + def test_register_spec_type + original_types = Minitest::Spec::TYPES.dup + + assert_includes Minitest::Spec::TYPES, [//, Minitest::Spec] + + Minitest::Spec.register_spec_type(/woot/, TestMeta) + + p = lambda do |_| true end + Minitest::Spec.register_spec_type TestMeta, &p + + keys = Minitest::Spec::TYPES.map(&:first) + + assert_includes keys, /woot/ + assert_includes keys, p + ensure + Minitest::Spec::TYPES.replace original_types + end + + def test_spec_type + original_types = Minitest::Spec::TYPES.dup + + Minitest::Spec.register_spec_type(/A$/, MiniSpecA) + Minitest::Spec.register_spec_type MiniSpecB do |desc| + desc.superclass == ExampleA + end + Minitest::Spec.register_spec_type MiniSpecC do |_desc, *addl| + addl.include? :woot + end + + assert_equal MiniSpecA, Minitest::Spec.spec_type(ExampleA) + assert_equal MiniSpecB, Minitest::Spec.spec_type(ExampleB) + assert_equal MiniSpecC, Minitest::Spec.spec_type(ExampleB, :woot) + ensure + Minitest::Spec::TYPES.replace original_types + end + + def test_bug_dsl_expectations + spec_class = Class.new MiniSpecB do + it "should work" do + _(0).must_equal 0 + end + end + + test_name = spec_class.instance_methods.sort.grep(/test_/).first + + spec = spec_class.new test_name + + result = spec.run + + assert spec.passed? + assert result.passed? + assert_equal 1, result.assertions + end + + def test_name + spec_a = describe ExampleA do; end + spec_b = describe ExampleB, :random_method do; end + spec_c = describe ExampleB, :random_method, :addl_context do; end + + assert_equal "ExampleA", spec_a.name + assert_equal "ExampleB::random_method", spec_b.name + assert_equal "ExampleB::random_method::addl_context", spec_c.name + end + + def test_inspect + spec_a = describe ExampleA do; end + spec_b = describe ExampleB, :random_method do; end + spec_c = describe ExampleB, :random_method, :addl_context do; end + + assert_equal "ExampleA", spec_a.inspect + assert_equal "ExampleB::random_method", spec_b.inspect + assert_equal "ExampleB::random_method::addl_context", spec_c.inspect + end + + def test_name2 + assert_equal "NamedExampleA", NamedExampleA.name + assert_equal "NamedExampleB", NamedExampleB.name + assert_equal "NamedExampleC", NamedExampleC.name + + spec_a = describe ExampleA do; end + spec_b = describe ExampleB, :random_method do; end + + assert_equal "ExampleA", spec_a.name + assert_equal "ExampleB::random_method", spec_b.name + end + + def test_name_inside_class + spec_a = nil + spec_b = nil + inside_class_example = Class.new Minitest::Spec + Object.const_set :InsideClassExample, inside_class_example + inside_class_example.class_eval do + spec_a = describe "a" do + spec_b = describe "b" do; end + end + end + + assert_equal "InsideClassExample::a", spec_a.name + assert_equal "InsideClassExample::a::b", spec_b.name + ensure + Object.send :remove_const, :InsideClassExample + end + + def test_structure + x, y, z, * = util_structure + + assert_equal "top-level thingy", x.to_s + assert_equal "top-level thingy::inner thingy", y.to_s + assert_equal "top-level thingy::inner thingy::very inner thingy", z.to_s + + assert_equal "top-level thingy", x.desc + assert_equal "inner thingy", y.desc + assert_equal "very inner thingy", z.desc + + top_methods = %w[setup teardown test_0001_top-level-it] + inner_methods1 = %w[setup teardown test_0001_inner-it] + inner_methods2 = inner_methods1 + + %w[test_0002_anonymous test_0003_anonymous] + + assert_defined_methods top_methods, x + assert_defined_methods inner_methods1, y + assert_defined_methods inner_methods2, z + end + + def test_structure_postfix_it + z = nil + y = describe "outer" do + # NOT here, below the inner-describe! + # it "inner-it" do end + + z = describe "inner" do + it "inner-it" do end + end + + # defined AFTER inner describe means we'll try to wipe out the inner-it + it "inner-it" do end + end + + assert_defined_methods %w[test_0001_inner-it], y + assert_defined_methods %w[test_0001_inner-it], z + end + + def test_setup_teardown_behavior + _, _, z, before_list, after_list = util_structure + + @tu = z + + run_tu_with_fresh_reporter + + size = z.runnable_methods.size + assert_equal [1, 2, 3] * size, before_list + assert_equal [3, 2, 1] * size, after_list + end + + def test_describe_first_structure + x1 = x2 = y = z = nil + x = describe "top-level thingy" do + y = describe "first thingy" do end + + x1 = it "top level it" do end + x2 = it "не латинские &いった α, β, γ, δ, ε hello!!! world" do end + + z = describe "second thingy" do end + end + + test_methods = [ + "test_0001_top level it", + "test_0002_не латинские &いった α, β, γ, δ, ε hello!!! world", + ].sort + + assert_equal test_methods, [x1, x2] + assert_defined_methods test_methods, x + assert_defined_methods [], y + assert_defined_methods [], z + end + + def test_structure_subclasses + z = nil + x = Class.new Minitest::Spec do + def xyz; end + end + y = Class.new x do + z = describe("inner") { } + end + + assert_respond_to x.new(nil), "xyz" + assert_respond_to y.new(nil), "xyz" + assert_respond_to z.new(nil), "xyz" + end +end + +class TestSpecInTestCase < MetaMetaMetaTestCase + def setup + super + + Thread.current[:current_spec] = self + @tc = self + @assertion_count = 2 + end + + def assert_triggered expected, klass = Minitest::Assertion + @assertion_count += 1 + + e = assert_raises klass do + yield + end + + msg = e.message.sub(/(---Backtrace---).*/m, "\1") + msg.gsub!(/\(oid=[-0-9]+\)/, "(oid=N)") + + assert_equal expected, msg + end + + def teardown + msg = "expected #{@assertion_count} assertions, not #{@tc.assertions}" + assert_equal @assertion_count, @tc.assertions, msg + end + + def test_expectation + @tc.assert_equal true, _(1).must_equal(1) + end + + def test_expectation_triggered + assert_triggered "Expected: 2\n Actual: 1" do + _(1).must_equal 2 + end + end + + include Minitest::Spec::DSL::InstanceMethods + + def test_expectation_with_a_message + assert_triggered "woot.\nExpected: 2\n Actual: 1" do + _(1).must_equal 2, "woot" + end + end +end + +class ValueMonadTest < Minitest::Test + attr_accessor :struct + + def setup + @struct = { :_ => "a", :value => "b", :expect => "c" } + def @struct.method_missing k # think openstruct + self[k] + end + end + + def test_value_monad_method + assert_equal "a", struct._ + end + + def test_value_monad_value_alias + assert_equal "b", struct.value + end + + def test_value_monad_expect_alias + assert_equal "c", struct.expect + end +end + +describe Minitest::Spec, :infect_an_assertion do + attr_accessor :infect_mock + + before do + mock = Object.new + mock.singleton_class.attr_accessor :a, :k + def mock.assert_infects *args, **kwargs + self.a, self.k = args, kwargs + end + + self.infect_mock = mock + end + + def assert_infects exp, act, msg = nil, foo: nil, bar: nil + self.infect_mock.assert_infects exp, act, msg, foo: foo, bar: bar + end + + Minitest::Expectations.infect_an_assertion :assert_infects, :must_infect + Minitest::Expectations.infect_an_assertion :assert_infects, :must_infect_without_flipping, :dont_flip + + it "infects assertions with kwargs" do + _(:act).must_infect :exp, foo: :foo, bar: :bar + + assert_equal [:exp, :act, nil], infect_mock.a + assert_equal({foo: :foo, bar: :bar}, infect_mock.k) + end + + it "infects assertions with kwargs (dont_flip)" do + _(:act).must_infect_without_flipping :exp, foo: :foo, bar: :bar + + assert_equal [:act, :exp, nil], infect_mock.a + assert_equal({foo: :foo, bar: :bar}, infect_mock.k) + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/test/minitest/test_minitest_test.rb b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/test/minitest/test_minitest_test.rb new file mode 100644 index 0000000..14e32eb --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/test/minitest/test_minitest_test.rb @@ -0,0 +1,1295 @@ +require_relative "metametameta" + +e = Encoding.default_external +if e != Encoding::UTF_8 then + warn "" + warn "" + warn "NOTE: External encoding #{e} is not UTF-8. Tests WILL fail." + warn " Run tests with `RUBYOPT=-Eutf-8 rake` to avoid errors." + warn "" + warn "" +end + +class Minitest::Runnable + attr_reader :gc_stats # only needed if running w/ minitest-gcstats + + def whatever # faked for testing + assert true + end +end + +class TestMinitestUnit < MetaMetaMetaTestCase + parallelize_me! + + MINITEST_BASE_DIR = "./lib/minitest/mini" + BT_MIDDLE = ["#{MINITEST_BASE_DIR}/test.rb:161:in 'each'", + "#{MINITEST_BASE_DIR}/test.rb:158:in 'each'", + "#{MINITEST_BASE_DIR}/test.rb:139:in 'run'", + "#{MINITEST_BASE_DIR}/test.rb:106:in 'run'"] + + def test_filter_backtrace + # this is a semi-lame mix of relative paths. + # I cheated by making the autotest parts not have ./ + bt = (["lib/autotest.rb:571:in 'add_exception'", + "test/test_autotest.rb:62:in 'test_add_exception'", + "#{MINITEST_BASE_DIR}/test.rb:165:in '__send__'"] + + BT_MIDDLE + + ["#{MINITEST_BASE_DIR}/test.rb:29", + "test/test_autotest.rb:422"]) + bt = util_expand_bt bt + + ex = ["lib/autotest.rb:571:in 'add_exception'", + "test/test_autotest.rb:62:in 'test_add_exception'"] + ex = util_expand_bt ex + + Minitest::Test.io_lock.synchronize do # try not to trounce in parallel + fu = Minitest.filter_backtrace bt + + assert_equal ex, fu + end + end + + def test_filter_backtrace_all_unit + bt = (["#{MINITEST_BASE_DIR}/test.rb:165:in '__send__'"] + + BT_MIDDLE + + ["#{MINITEST_BASE_DIR}/test.rb:29"]) + ex = bt.clone + fu = Minitest.filter_backtrace bt + assert_equal ex, fu + end + + def test_filter_backtrace_unit_starts + bt = (["#{MINITEST_BASE_DIR}/test.rb:165:in '__send__'"] + + BT_MIDDLE + + ["#{MINITEST_BASE_DIR}/mini/test.rb:29", + "-e:1"]) + + bt = util_expand_bt bt + + ex = ["-e:1"] + Minitest::Test.io_lock.synchronize do # try not to trounce in parallel + fu = Minitest.filter_backtrace bt + assert_equal ex, fu + end + end + + def test_filter_backtrace__empty + with_empty_backtrace_filter do + bt = %w[first second third] + fu = Minitest.filter_backtrace bt.dup + assert_equal bt, fu + end + end + + def test_infectious_binary_encoding + @tu = Class.new FakeNamedTest do + def test_this_is_not_ascii_assertion + assert_equal "ЁЁЁ", "ёёё" + end + + def test_this_is_non_ascii_failure_message + raise "ЁЁЁ".dup.force_encoding(Encoding::BINARY) + end + end + + expected = <<~EOM + FE + + Finished in 0.00 + + 1) Failure: + FakeNamedTestXX#test_this_is_not_ascii_assertion [FILE:LINE]: + Expected: "ЁЁЁ" + Actual: "ёёё" + + 2) Error: + FakeNamedTestXX#test_this_is_non_ascii_failure_message: + RuntimeError: ЁЁЁ + FILE:LINE:in 'test_this_is_non_ascii_failure_message' + + 2 runs, 1 assertions, 1 failures, 1 errors, 0 skips + EOM + + Minitest::Test.io_lock.synchronize do # try not to trounce in parallel + assert_report expected + end + end + + def test_passed_eh_teardown_good + test_class = Class.new FakeNamedTest do + def teardown; assert true; end + def test_omg; assert true; end + end + + test = test_class.new :test_omg + test.run + + refute_predicate test, :error? + assert_predicate test, :passed? + refute_predicate test, :skipped? + end + + def test_passed_eh_teardown_skipped + test_class = Class.new FakeNamedTest do + def teardown; assert true; end + def test_omg; skip "bork"; end + end + + test = test_class.new :test_omg + test.run + + refute_predicate test, :error? + refute_predicate test, :passed? + assert_predicate test, :skipped? + end + + def test_passed_eh_teardown_flunked + test_class = Class.new FakeNamedTest do + def teardown; flunk; end + def test_omg; assert true; end + end + + test = test_class.new :test_omg + test.run + + refute_predicate test, :error? + refute_predicate test, :passed? + refute_predicate test, :skipped? + end + + def test_skipped_is_reachable + test_class = Class.new FakeNamedTest do + def test_omg + skip + ensure + flunk unless skipped? + end + end + + test = test_class.new :test_omg + test.run + + refute_predicate test, :error? + refute_predicate test, :passed? + + assert_predicate test, :skipped? + end + + def util_expand_bt bt + bt.map { |f| f.start_with?(".") ? File.expand_path(f) : f } + end +end + +class TestMinitestUnitInherited < MetaMetaMetaTestCase + def with_overridden_include + Class.class_eval do + def inherited_with_hacks _klass + throw :inherited_hook + end + + alias inherited_without_hacks inherited + alias inherited inherited_with_hacks + alias IGNORE_ME! inherited # 1.8 bug. god I love venture bros + end + + yield + ensure + Class.class_eval do + alias inherited inherited_without_hacks + + undef_method :inherited_with_hacks + undef_method :inherited_without_hacks + end + + refute_respond_to Class, :inherited_with_hacks + refute_respond_to Class, :inherited_without_hacks + end + + def test_inherited_hook_plays_nice_with_others + with_overridden_include do + assert_throws :inherited_hook do + Class.new FakeNamedTest + end + end + end +end + +class TestMinitestRunner < MetaMetaMetaTestCase + # do not parallelize this suite... it just can't handle it. + + def test_class_runnables + @assertion_count = 0 + + tc = Class.new Minitest::Test + + assert_equal 1, Minitest::Test.runnables.size + assert_equal [tc], Minitest::Test.runnables + end + + def test_run_test + @tu = Class.new FakeNamedTest do + attr_reader :foo + + def run + @foo = "hi mom!" + r = super + @foo = "okay" + + r + end + + def test_something + assert_equal "hi mom!", foo + end + end + + expected = <<~EOM + . + + Finished in 0.00 + + 1 runs, 1 assertions, 0 failures, 0 errors, 0 skips + EOM + + assert_report expected + end + + def test_run_error + @tu = Class.new FakeNamedTest do + def test_something + assert true + end + + def test_error + raise "unhandled exception" + end + end + + expected = <<~EOM + .E + + Finished in 0.00 + + 1) Error: + FakeNamedTestXX#test_error: + RuntimeError: unhandled exception + FILE:LINE:in 'test_error' + + 2 runs, 1 assertions, 0 failures, 1 errors, 0 skips + EOM + + assert_report expected + end + + def test_run_error_teardown + @tu = Class.new FakeNamedTest do + def test_something + assert true + end + + def teardown + raise "unhandled exception" + end + end + + expected = <<~EOM + E + + Finished in 0.00 + + 1) Error: + FakeNamedTestXX#test_something: + RuntimeError: unhandled exception + FILE:LINE:in 'teardown' + + 1 runs, 1 assertions, 0 failures, 1 errors, 0 skips + EOM + + assert_report expected + end + + def test_run_failing + setup_basic_tu + + expected = <<~EOM + .F + + Finished in 0.00 + + 1) Failure: + FakeNamedTestXX#test_failure [FILE:LINE]: + Expected false to be truthy. + + 2 runs, 2 assertions, 1 failures, 0 errors, 0 skips + EOM + + assert_report expected + end + + def setup_basic_tu + @tu = Class.new FakeNamedTest do + def test_something + assert true + end + + def test_failure + assert false + end + end + end + + def test_seed # this is set for THIS run, so I'm not testing it's actual value + assert_instance_of Integer, Minitest.seed + end + + def test_filter_runnable_methods + cls = Class.new Minitest::Runnable + def cls.runnable_methods + %w[ x y z ] + end + + assert_equal %w[x y z], cls.filter_runnable_methods + assert_equal %w[x y], cls.filter_runnable_methods(exclude: "z") + assert_equal %w[x], cls.filter_runnable_methods(include: "x") + + assert_empty cls.filter_runnable_methods(exclude: "/./") + assert_empty cls.filter_runnable_methods(include: "x", exclude: "x") + end + + def test_run_failing_filtered + setup_basic_tu + + expected = <<~EOM + . + + Finished in 0.00 + + 1 runs, 1 assertions, 0 failures, 0 errors, 0 skips + EOM + + assert_report expected, %w[--name /some|thing/ --seed 42] + end + + def assert_filtering filter, name, expected, a = false + args = %W[--#{filter} #{name} --seed 42] + + alpha = Class.new FakeNamedTest do + define_method :test_something do + assert a + end + end + Object.const_set :Alpha, alpha + + beta = Class.new FakeNamedTest do + define_method :test_something do + assert true + end + end + Object.const_set :Beta, beta + + @tus = [alpha, beta] + + assert_report expected, args + ensure + Object.send :remove_const, :Alpha + Object.send :remove_const, :Beta + end + + def test_run_filtered_including_suite_name + expected = <<~EOM + . + + Finished in 0.00 + + 1 runs, 1 assertions, 0 failures, 0 errors, 0 skips + EOM + + assert_filtering "name", "/Beta#test_something/", expected + end + + def test_run_filtered_including_suite_name_string + expected = <<~EOM + . + + Finished in 0.00 + + 1 runs, 1 assertions, 0 failures, 0 errors, 0 skips + EOM + + assert_filtering "name", "Beta#test_something", expected + end + + def test_run_filtered_string_method_only + expected = <<~EOM + .. + + Finished in 0.00 + + 2 runs, 2 assertions, 0 failures, 0 errors, 0 skips + EOM + + assert_filtering "name", "test_something", expected, :pass + end + + def test_run_failing_excluded + setup_basic_tu + + expected = <<~EOM + . + + Finished in 0.00 + + 1 runs, 1 assertions, 0 failures, 0 errors, 0 skips + EOM + + assert_report expected, %w[--exclude /failure/ --seed 42] + end + + def test_run_filtered_excluding_suite_name + expected = <<~EOM + . + + Finished in 0.00 + + 1 runs, 1 assertions, 0 failures, 0 errors, 0 skips + EOM + + assert_filtering "exclude", "/Alpha#test_something/", expected + end + + def test_run_filtered_excluding_suite_name_string + expected = <<~EOM + . + + Finished in 0.00 + + 1 runs, 1 assertions, 0 failures, 0 errors, 0 skips + EOM + + assert_filtering "exclude", "Alpha#test_something", expected + end + + def test_run_filtered_excluding_string_method_only + expected = <<~EOM + + + Finished in 0.00 + + 0 runs, 0 assertions, 0 failures, 0 errors, 0 skips + EOM + + assert_filtering "exclude", "test_something", expected, :pass + end + + def test_run_passing + @tu = Class.new FakeNamedTest do + def test_something + assert true + end + end + + expected = <<~EOM + . + + Finished in 0.00 + + 1 runs, 1 assertions, 0 failures, 0 errors, 0 skips + EOM + + assert_report expected + end + + def test_run_skip + @tu = Class.new FakeNamedTest do + def test_something + assert true + end + + def test_skip + skip "not yet" + end + end + + expected = <<~EOM + .S + + Finished in 0.00 + + 2 runs, 1 assertions, 0 failures, 0 errors, 1 skips + + You have skipped tests. Run with --verbose for details. + EOM + + restore_env do + assert_report expected + end + end + + def test_run_skip_verbose + @tu = Class.new FakeNamedTest do + def test_something + assert true + end + + def test_skip + skip "not yet" + end + end + + expected = <<~EOM + FakeNamedTestXX#test_something = 0.00 s = . + FakeNamedTestXX#test_skip = 0.00 s = S + + Finished in 0.00 + + 1) Skipped: + FakeNamedTestXX#test_skip [FILE:LINE]: + not yet + + 2 runs, 1 assertions, 0 failures, 0 errors, 1 skips + EOM + + assert_report expected, %w[--seed 42 --verbose] + end + + def test_run_skip_show_skips + @tu = Class.new FakeNamedTest do + def test_something + assert true + end + + def test_skip + skip "not yet" + end + end + + expected = <<~EOM + .S + + Finished in 0.00 + + 1) Skipped: + FakeNamedTestXX#test_skip [FILE:LINE]: + not yet + + 2 runs, 1 assertions, 0 failures, 0 errors, 1 skips + EOM + + assert_report expected, %w[--seed 42 --show-skips] + end + + def test_run_with_other_runner + @tu = Class.new FakeNamedTest do + def self.run_suite reporter, options = {} + @reporter = reporter + before_my_suite + super + end + + def self.name; "wacky!" end + + def self.before_my_suite + @reporter.io.puts "Running #{self.name} tests" + @@foo = 1 + end + + def test_something + assert_equal 1, @@foo + end + + def test_something_else + assert_equal 1, @@foo + end + end + + expected = <<~EOM + Running wacky! tests + .. + + Finished in 0.00 + + 2 runs, 2 assertions, 0 failures, 0 errors, 0 skips + EOM + + assert_report expected + end + + require "monitor" + + class Latch + def initialize count = 1 + @count = count + @lock = Monitor.new + @cv = @lock.new_cond + end + + def release + @lock.synchronize do + @count -= 1 if @count > 0 + @cv.broadcast if @count == 0 + end + end + + def await + @lock.synchronize { @cv.wait_while { @count > 0 } } + end + end + + def test_run_parallel + test_count = 2 + test_latch = Latch.new test_count + wait_latch = Latch.new test_count + main_latch = Latch.new + + thread = Thread.new { + Thread.current.abort_on_exception = true + + # This latch waits until both test latches have been released. Both + # latches can't be released unless done in separate threads because + # `main_latch` keeps the test method from finishing. + test_latch.await + main_latch.release + } + + @tu = Class.new FakeNamedTest do + parallelize_me! + + test_count.times do |i| + define_method :"test_wait_on_main_thread_#{i}" do + test_latch.release + + # This latch blocks until the "main thread" releases it. The main + # thread can't release this latch until both test latches have + # been released. This forces the latches to be released in separate + # threads. + main_latch.await + assert true + end + end + end + + expected = <<~EOM + .. + + Finished in 0.00 + + 2 runs, 2 assertions, 0 failures, 0 errors, 0 skips + EOM + + skip unless Minitest.parallel_executor # locks up test runner if 1 CPU + + assert_report expected do |reporter| + reporter.extend Module.new { + define_method :record do |result| + super(result) + wait_latch.release + end + + define_method :report do + wait_latch.await + super() + end + } + end + assert thread.join + end +end + +class TestMinitestUnitOrder < MetaMetaMetaTestCase + # do not parallelize this suite... it just can't handle it. + + def test_before_setup + call_order = [] + + @tu = Class.new FakeNamedTest do + define_method :setup do + super() + call_order << :setup + end + + define_method :before_setup do + call_order << :before_setup + end + + def test_omg; assert true; end + end + + run_tu_with_fresh_reporter + + expected = %i[before_setup setup] + assert_equal expected, call_order + end + + def test_after_teardown + call_order = [] + @tu = Class.new FakeNamedTest do + define_method :teardown do + super() + call_order << :teardown + end + + define_method :after_teardown do + call_order << :after_teardown + end + + def test_omg; assert true; end + end + + run_tu_with_fresh_reporter + + expected = %i[teardown after_teardown] + assert_equal expected, call_order + end + + def test_all_teardowns_are_guaranteed_to_run + call_order = [] + + @tu = Class.new FakeNamedTest do + define_method :after_teardown do + super() + call_order << :after_teardown + raise + end + + define_method :teardown do + super() + call_order << :teardown + raise + end + + define_method :before_teardown do + super() + call_order << :before_teardown + raise + end + + def test_omg; assert true; end + end + + run_tu_with_fresh_reporter + + expected = %i[before_teardown teardown after_teardown] + assert_equal expected, call_order + end + + def test_setup_and_teardown_survive_inheritance + call_order = [] + + @tu = Class.new FakeNamedTest do + define_method :setup do + call_order << :setup_method + end + + define_method :teardown do + call_order << :teardown_method + end + + define_method :test_something do + call_order << :test + end + end + + run_tu_with_fresh_reporter + + @tu = Class.new @tu + run_tu_with_fresh_reporter + + # Once for the parent class, once for the child + expected = %i[setup_method test teardown_method] * 2 + + assert_equal expected, call_order + end +end + +class BetterError < RuntimeError # like better_error w/o infecting RuntimeError + def set_backtrace bt + super + @bad_ivar = binding + end +end + +class TestMinitestRunnable < Minitest::Test + def test_spec_marshal + klass = describe("whatever") { it("passes") { assert true } } + rm = klass.runnable_methods.first + + # Run the test + @tc = klass.new(rm).run + + assert_kind_of Minitest::Result, @tc + + # Pass it over the wire + over_the_wire = Marshal.load Marshal.dump @tc + + assert_equal @tc.time, over_the_wire.time + assert_equal @tc.name, over_the_wire.name + assert_equal @tc.assertions, over_the_wire.assertions + assert_equal @tc.failures, over_the_wire.failures + assert_equal @tc.klass, over_the_wire.klass + end + + def test_spec_marshal_with_exception + klass = describe("whatever") { + it("raises, badly") { + raise Class.new(StandardError), "this is bad!" + } + } + + rm = klass.runnable_methods.first + + # Run the test + @tc = klass.new(rm).run + + assert_kind_of Minitest::Result, @tc + assert_instance_of Minitest::UnexpectedError, @tc.failure + + msg = @tc.failure.error.message + assert_includes msg, "Neutered Exception #: boom", msg + + # Pass it over the wire + over_the_wire = Marshal.load Marshal.dump @tc + + assert_equal @tc.time, over_the_wire.time + assert_equal @tc.name, over_the_wire.name + assert_equal @tc.assertions, over_the_wire.assertions + assert_equal @tc.failures, over_the_wire.failures + assert_equal @tc.klass, over_the_wire.klass + end +end + +class TestMinitestUnitTestCase < Minitest::Test + # do not call parallelize_me! - teardown accesses @tc._assertions + # which is not threadsafe. Nearly every method in here is an + # assertion test so it isn't worth splitting it out further. + + def setup + super + + Minitest::Test.reset + + @tc = Minitest::Test.new "fake tc" + @zomg = "zomg ponies!" + @assertion_count = 1 + end + + def teardown + assert_equal(@assertion_count, @tc.assertions, + message { "expected #{@assertion_count} assertions to be fired during the test, not #{@tc.assertions}" }) if @tc.passed? + end + + def non_verbose + orig_verbose = $VERBOSE + $VERBOSE = false + + yield + ensure + $VERBOSE = orig_verbose + end + + def sample_test_case rand + srand rand + Class.new FakeNamedTest do + 100.times do |i| + define_method("test_#{i}") { assert true } + end + end.runnable_methods + end + + # srand varies with OS + def test_runnable_methods_random + @assertion_count = 0 + + random_tests_1 = sample_test_case 42 + random_tests_2 = sample_test_case 42 + random_tests_3 = sample_test_case 1_000 + + assert_equal random_tests_1, random_tests_2 + assert_equal random_tests_1, random_tests_3 + end + + def test_runnable_methods_sorted + @assertion_count = 0 + + sample_test_case = Class.new FakeNamedTest do + def self.run_order; :sorted end + def test_test3; assert "does not matter" end + def test_test2; assert "does not matter" end + def test_test1; assert "does not matter" end + end + + expected = %w[test_test1 test_test2 test_test3] + assert_equal expected, sample_test_case.runnable_methods + end + + def test_i_suck_and_my_tests_are_order_dependent_bang_sets_run_order_alpha + @assertion_count = 0 + + shitty_test_case = Class.new FakeNamedTest + + shitty_test_case.i_suck_and_my_tests_are_order_dependent! + + assert_equal :alpha, shitty_test_case.run_order + end + + def test_i_suck_and_my_tests_are_order_dependent_bang_does_not_warn + @assertion_count = 0 + + shitty_test_case = Class.new FakeNamedTest + + def shitty_test_case.run_order; :lol end + + assert_silent do + shitty_test_case.i_suck_and_my_tests_are_order_dependent! + end + end + + def test_autorun_does_not_affect_fork_success_status + @assertion_count = 0 + skip "windows doesn't have fork" unless Process.respond_to? :fork + Process.waitpid(fork {}) + assert_equal true, $?.success? + end + + def test_autorun_does_not_affect_fork_exit_status + @assertion_count = 0 + skip "windows doesn't have fork" unless Process.respond_to? :fork + Process.waitpid(fork { exit 42 }) + assert_equal 42, $?.exitstatus + end + + def test_autorun_optionally_can_affect_fork_exit_status + @assertion_count = 0 + skip "windows doesn't have fork" unless Process.respond_to? :fork + Minitest.allow_fork = true + Process.waitpid(fork { exit 42 }) + refute_equal 42, $?.exitstatus + ensure + Minitest.allow_fork = false + end +end + +class TestMinitestGuard < Minitest::Test + parallelize_me! + + def test_mri_eh + assert self.class.mri? "ruby blah" + assert self.mri? "ruby blah" + end + + def test_jruby_eh + assert self.class.jruby? "java" + assert self.jruby? "java" + end + + def test_osx_eh + assert self.class.osx? "darwin" + assert self.osx? "darwin" + end + + def test_windows_eh + assert self.class.windows? "mswin" + assert self.windows? "mswin" + end +end + +class TestMinitestUnitRecording < MetaMetaMetaTestCase + # do not parallelize this suite... it just can't handle it. + + def assert_run_record *expected, &block + @tu = Class.new FakeNamedTest, &block + + run_tu_with_fresh_reporter + + recorded = first_reporter.results.map(&:failures).flatten.map { |f| f.error.class } + + assert_equal expected, recorded + end + + def test_record_passing + assert_run_record do + def test_method + assert true + end + end + end + + def test_record_failing + assert_run_record Minitest::Assertion do + def test_method + assert false + end + end + end + + def test_record_error + assert_run_record RuntimeError do + def test_method + raise "unhandled exception" + end + end + end + + def test_record_error_teardown + assert_run_record RuntimeError do + def test_method + assert true + end + + def teardown + raise "unhandled exception" + end + end + end + + def test_record_error_in_test_and_teardown + assert_run_record AnError, RuntimeError do + def test_method + raise AnError + end + + def teardown + raise "unhandled exception" + end + end + end + + def test_to_s_error_in_test_and_teardown + @tu = Class.new FakeNamedTest do + def test_method + raise AnError + end + + def teardown + raise "unhandled exception" + end + end + + run_tu_with_fresh_reporter + + exp = <<~EOM + Error: + FakeNamedTestXX#test_method: + AnError: AnError + FILE:LINE:in 'test_method' + + Error: + FakeNamedTestXX#test_method: + RuntimeError: unhandled exception + FILE:LINE:in 'teardown' + EOM + + assert_equal exp.strip, normalize_output(first_reporter.results.first.to_s).strip + end + + def test_record_skip + assert_run_record Minitest::Skip do + def test_method + skip "not yet" + end + end + end +end + +class TestUnexpectedError < Minitest::Test + def assert_compress exp, input + e = Minitest::UnexpectedError.new RuntimeError.new + + exp = exp.lines.map(&:chomp) if String === exp + act = e.compress input + + assert_equal exp, act + end + + ACT1 = %w[ a b c b c b c b c d ] + + def test_normal + assert_compress <<~EXP, %w[ a b c b c b c b c d ] + a + +->> 4 cycles of 2 lines: + | b + | c + +-<< + d + EXP + end + + def test_normal2 + assert_compress <<~EXP, %w[ a b c b c b c b c ] + a + +->> 4 cycles of 2 lines: + | b + | c + +-<< + EXP + end + + def test_longer_c_than_b + # the extra c in the front makes the overall length longer sorting it first + assert_compress <<~EXP, %w[ c a b c b c b c b c b d ] + c + a + b + +->> 4 cycles of 2 lines: + | c + | b + +-<< + d + EXP + end + + def test_1_line_cycles + assert_compress <<~EXP, %w[ c a b c b c b c b c b b b d ] + c + a + +->> 4 cycles of 2 lines: + | b + | c + +-<< + +->> 3 cycles of 1 lines: + | b + +-<< + d + EXP + end + + def test_sanity3 + pre = ("aa".."am").to_a + mid = ("a".."z").to_a * 67 + post = ("aa".."am").to_a + ary = pre + mid + post + + exp = pre + + [" +->> 67 cycles of 26 lines:"] + + ("a".."z").map { |s| " | #{s}" } + + [" +-<<"] + + post + + assert_compress exp, ary + end + + def test_absurd_patterns + assert_compress <<~EXP, %w[ a b c b c a b c b c a b c ] + +->> 2 cycles of 5 lines: + | a + | +->> 2 cycles of 2 lines: + | | b + | | c + | +-<< + +-<< + a + b + c + EXP + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/test/minitest/test_minitest_test_task.rb b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/test/minitest/test_minitest_test_task.rb new file mode 100644 index 0000000..58902f5 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/test/minitest/test_minitest_test_task.rb @@ -0,0 +1,57 @@ +require "minitest/autorun" + +begin + require "hoe" +rescue LoadError => e + warn e.message + return +end + +require "minitest/test_task" + +Hoe.load_plugins # make sure Hoe::Test is loaded + +class TestHoeTest < Minitest::Test + PATH = "test/minitest/test_minitest_test_task.rb" + + def util_cmd_string *prelude_framework + mt_path = %w[lib test .].join File::PATH_SEPARATOR + mt_expected = "-I%s -w -e '%srequire %p' -- " + + mt_expected % [mt_path, prelude_framework.join("; "), PATH] + end + + def util_exp_cmd + @tester.make_test_cmd.sub(/ -- .+/, " -- ") + end + + def test_make_test_cmd_for_minitest + skip "Using TESTOPTS... skipping" if ENV["TESTOPTS"] + + require "minitest/test_task" + + framework = %(require "minitest/autorun"; ) + + @tester = Minitest::TestTask.create :test do |t| + t.test_globs = [PATH] + end + + assert_equal util_cmd_string(framework), util_exp_cmd + end + + def test_make_test_cmd_for_minitest_prelude + skip "Using TESTOPTS... skipping" if ENV["TESTOPTS"] + + require "minitest/test_task" + + prelude = %(require "other/file") + framework = %(require "minitest/autorun"; ) + + @tester = Minitest::TestTask.create :test do |t| + t.test_prelude = prelude + t.test_globs = [PATH] + end + + assert_equal util_cmd_string(prelude, framework), util_exp_cmd + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/test/minitest/test_path_expander.rb b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/test/minitest/test_path_expander.rb new file mode 100644 index 0000000..5bbde6a --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/test/minitest/test_path_expander.rb @@ -0,0 +1,229 @@ +require "minitest/autorun" +require "minitest/path_expander" + +class TestPathExpander < Minitest::Test + attr_accessor :args + attr_accessor :expander + + MT_VPE = Minitest::VendoredPathExpander + + def setup + super + + self.args = [] + + self.expander = MT_VPE.new args, "*.rb" + + @pe_tmp_path = "test/pe_tmp" + @pe_tst_path = "test/pe_tmp/test" + + @orig_pwd = Dir.pwd + FileUtils.mkdir_p @pe_tst_path + FileUtils.touch ["#{@pe_tst_path}/test_path_expander.rb", "#{@pe_tst_path}/test_bad.rb"] + Dir.chdir @pe_tmp_path + end + + def teardown + super + + Dir.chdir @orig_pwd + + FileUtils.rm_rf @pe_tmp_path + end + + def assert_filter_files exp, filter, files = %w[test/dog_and_cat.rb] + ignore = StringIO.new filter + act = expander.filter_files files, ignore + assert_equal exp, act + end + + def assert_filter_files_absolute_paths exp, filter, files = [File.join(Dir.pwd, 'test/dog_and_cat.rb')] + assert_filter_files exp, filter, files + end + + def assert_process_args exp_files, exp_args, *args + expander.args.concat args + + assert_equal [exp_files.sort, exp_args], expander.process_args + end + + def test_expand_dirs_to_files + exp = %w[test/test_bad.rb test/test_path_expander.rb] + + assert_equal exp, expander.expand_dirs_to_files("test") + assert_equal %w[Rakefile], expander.expand_dirs_to_files("Rakefile") + end + + def test_expand_dirs_to_files__sorting + exp = %w[test/test_bad.rb test/test_path_expander.rb] + input = %w[test/test_path_expander.rb test/test_bad.rb] + + assert_equal exp, expander.expand_dirs_to_files(*input) + assert_equal %w[Rakefile], expander.expand_dirs_to_files("Rakefile") + end + + def test_expand_dirs_to_files__leading_dot + exp = %w[test/test_bad.rb test/test_path_expander.rb] + + assert_equal exp, expander.expand_dirs_to_files("./test") + end + + def test_filter_files_dir + assert_filter_files [], "test/" + assert_filter_files_absolute_paths [], "test/" + end + + def test_filter_files_files + example = %w[test/file.rb test/sub/file.rb top/test/perf.rb] + example_absolute_paths = example.map { |e| File.join(Dir.pwd, e) } + + assert_filter_files [], "test/*.rb" + + assert_filter_files example[1..-1], "test/*.rb", example + + assert_filter_files_absolute_paths [], "test/*.rb" + + assert_filter_files_absolute_paths example_absolute_paths[1..-1], "test/*.rb", example_absolute_paths + end + + def test_filter_files_glob + assert_filter_files [], "test*" + assert_filter_files [], "test*", ["test/lib/woot.rb"] + assert_filter_files [], "*.rb" + assert_filter_files [], "*dog*.rb" + + assert_filter_files_absolute_paths [], "test*" + assert_filter_files_absolute_paths [], "test*", [File.join(Dir.pwd, "test/lib/woot.rb")] + assert_filter_files_absolute_paths [], "*.rb" + assert_filter_files_absolute_paths [], "*dog*.rb" + end + + def test_filter_files_glob_miss + miss = %w[test/dog_and_cat.rb] + miss_absolute = [File.join(Dir.pwd, 'test/dog_and_cat.rb')] + + assert_filter_files miss, "test" + assert_filter_files miss, "nope" + + assert_filter_files_absolute_paths miss_absolute, "test" + assert_filter_files_absolute_paths miss_absolute, "nope" + end + + def test_filter_files__ignore_file + files = expander.expand_dirs_to_files "test" + + File.write ".mtignore", "test/*.rb" + + act = expander.filter_files files, ".mtignore" + + assert_equal [], act + end + + def test_process + self.args.concat %w[test --seed 42] + + act = expander.process + + assert_kind_of Enumerator, act + assert_equal %w[test/test_bad.rb test/test_path_expander.rb], act.to_a + assert_equal %w[--seed 42], expander.args + assert_equal %w[--seed 42], args # affected our original array (eg, ARGV) + end + + def test_process__block + self.args.concat %w[test --seed 42] + + act = [] + result = expander.process { |x| act << x } + + assert_same expander, result + assert_equal %w[test/test_bad.rb test/test_path_expander.rb], act.to_a + assert_equal %w[--seed 42], expander.args + assert_equal %w[--seed 42], args # affected our original array (eg, ARGV) + end + + def with_tempfile *lines + require "tempfile" + + Tempfile.open("tmp") do |f| + f.puts lines + f.flush + f.rewind + + yield f + end + end + + def test_process_args_at + with_tempfile %w[test -test/test_bad.rb --seed 24] do |f| + assert_process_args(%w[test/test_path_expander.rb], + %w[--seed 24], + "@#{f.path}") + end + end + + def test_process_args_dash_dir + assert_process_args(%w[], + %w[], + "test", "-test") + end + + def test_process_args_dash_file + assert_process_args(%w[test/test_path_expander.rb], + %w[], + "test", "-test/test_bad.rb") + + end + + def test_process_args_dash_other + assert_process_args(%w[], + %w[--verbose], + "--verbose") + end + + def test_process_args_dir + assert_process_args(%w[test/test_bad.rb test/test_path_expander.rb], + %w[], + "test") + end + + def test_process_args_file + assert_process_args(%w[test/test_path_expander.rb], + %w[], + "test/test_path_expander.rb") + end + + def test_process_args_other + assert_process_args(%w[], + %w[42], + "42") + end + + def test_process_args_root + assert_process_args(%w[], + %w[-n /./], + "-n", + "/./") + end + + def test_process_args_no_files + self.expander = MT_VPE.new args, "*.rb", "test" # extra test default + + assert_process_args(%w[test/test_bad.rb test/test_path_expander.rb], + %w[-v], + "-v") + end + + def test_process_args_dash + assert_process_args(%w[-], + %w[-v], + "-", "-v") + end + + def test_process_flags + exp = %w[a b c] + act = expander.process_flags %w[a b c] + + assert_equal exp, act + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/test/minitest/test_server.rb b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/test/minitest/test_server.rb new file mode 100644 index 0000000..ebb6bb7 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/minitest-6.0.1/test/minitest/test_server.rb @@ -0,0 +1,149 @@ +require "minitest/autorun" +require "minitest/server" +require "minitest/server_plugin" + +require "minitest" +require "minitest/test" +require "minitest/server" +require "minitest/server_plugin" +require "minitest/autorun" + +class BogoTests < Minitest::Test + def pass_test + assert true + end + + def fail_test + assert false, "fail" + end + + def error_test + raise "error" + end + + def unmarshalable_ivar_test + raise "error" + rescue => e + e.instance_variable_set :@binding, binding + raise + end + + def unmarshalable_class_test + exc = Class.new RuntimeError + raise exc, "error" + rescue => e + e.instance_variable_set :@binding, binding + raise + end + + def wtf_test + assert false, "wtf" + rescue Minitest::Assertion => e + e.instance_variable_set :@proc, proc { 42 } + raise e + end +end + +class TestServerReporter < Minitest::ServerReporter + def record o + super + + Marshal.dump o + end +end + +class Client + def run pid, type + reporter = TestServerReporter.new pid + + unless Minitest.respond_to? :run_one_method then # MT6 + BogoTests.run BogoTests, "#{type}_test", reporter + else + reporter.start + reporter.record Minitest.run_one_method(BogoTests, "#{type}_test") + end + end +end + +class Server + attr_accessor :results + + def self.run type = nil + s = self.new + s.run type + s.results + end + + def run type = nil + Minitest::Server.run self + + Client.new.run $$, type + ensure + Minitest::Server.stop + end + + def minitest_start + # do nothing + end + + def minitest_result(*vals) + self.results = vals + end +end + +class ServerTest < Minitest::Test + def test_pass + assert_run "pass", [], 1 + end + + def test_fail + assert_run "fail", ["fail"], 1 + end + + FILE = __FILE__.delete_prefix "#{Dir.pwd}/" + + def test_error + msg = <<~EOM.chomp + RuntimeError: error + #{FILE}:21:in `error_test' + EOM + + assert_run "error", [msg], 0 + end + + def test_error_unmarshalable__ivar + msg = <<~EOM.chomp + RuntimeError: error + #{FILE}:25:in `unmarshalable_ivar_test' + EOM + + assert_run "unmarshalable_ivar", [msg], 0 + end + + def test_error_unmarshalable__class + msg = <<~EOM.chomp + RuntimeError: Neutered Exception #: error + #{FILE}:33:in `unmarshalable_class_test' + EOM + + assert_run "unmarshalable_class", [msg], 0 + end + + def test_wtf + assert_run "wtf", ["wtf"], 1 + end + + def assert_run type, e, n + e[0] = e[0].sub "`", "'BogoTests#" if RUBY_VERSION > "3.4" if e[0] + + act = Server.run type + act[-1] = 0 # time + act[-3].map!(&:message) + + act[-3][0] = act[-3][0].gsub(/0x\h+/, "0xXXXXXX") if act[-3][0] + + exp = ["test/minitest/test_server.rb", "BogoTests", "#{type}_test", e, n, 0] + + assert_equal exp, act + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/.gemtest b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/.gemtest new file mode 100644 index 0000000..e69de29 diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/.github/FUNDING.yml b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/.github/FUNDING.yml new file mode 100644 index 0000000..c4c9e52 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/.github/FUNDING.yml @@ -0,0 +1 @@ +github: floehopper diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/.rubocop.yml b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/.rubocop.yml new file mode 100644 index 0000000..189af4d --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/.rubocop.yml @@ -0,0 +1,92 @@ +plugins: + - rubocop-rake + +inherit_from: .rubocop_todo.yml + +inherit_mode: + merge: + - Include + +AllCops: + TargetRubyVersion: 2.2 + + Include: + - "**/Gemfile.*" + + Exclude: + - "**/Gemfile*.lock" + + NewCops: enable + +# Even the reference in the documentation suggests that you should prefer +# `alias_method` vs `alias`, so I don't understand why that isn't the default. +Style/Alias: + EnforcedStyle: prefer_alias_method + +Style/Documentation: + Enabled: false + +# Kernel#__dir__ has only been available since Ruby v2.0 +Style/ExpandPathArguments: + Enabled: false + +# I'm not keen on this cop, because it's easy to miss the conditional +# I think the results are particularly unhelpful when Metrics/LineLength is big +Style/IfUnlessModifier: + Enabled: false + +# Lambda literal syntax has only been supported since Ruby v2.0 +Style/Lambda: + EnforcedStyle: lambda + +# Symbol array literal syntax has only been supported since Ruby v2.0 +Style/SymbolArray: + Enabled: false + +# I'm not keen on this cop, because it's easy to miss the while/until +Style/WhileUntilModifier: + Enabled: false + +# This recently introduced cop seems to have stirred up some controversy +Style/AccessModifierDeclarations: + Enabled: false + +# `Module#===` is useful in presence of objects such as mocks +# that may have a `is_a?` implementation that lies. +Style/CaseEquality: + Enabled: false + +# This is useful when using `ExecutionPoint.current` to make tests more robust +Style/Semicolon: + Enabled: false + +# Enabling this cop results in an "Infinite loop detected" exception +Layout/AccessModifierIndentation: + Enabled: false + +# Allow long comment lines, e.g. YARD documentation +Layout/LineLength: + AllowedPatterns: ['\A\s*#'] + +# It's not possible to set TargetRubyVersion to Ruby < v2.2 +Gemspec/RequiredRubyVersion: + Enabled: false + +# This cop is useful for required environment variables, but not for optional ones +Style/FetchEnvVar: + AllowedVars: + - MOCHA_RUN_INTEGRATION_TESTS + +Naming/FileName: + ExpectMatchingDefinition: true + Exclude: + - lib/mocha/version.rb + - lib/mocha/minitest.rb + - lib/mocha/test_unit.rb + - lib/mocha/ruby_version.rb + - lib/mocha/macos_version.rb + - test/test_helper.rb + +Metrics/BlockLength: + Exclude: + - "Rakefile" diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/.rubocop_todo.yml b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/.rubocop_todo.yml new file mode 100644 index 0000000..87dc5f4 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/.rubocop_todo.yml @@ -0,0 +1,39 @@ +# This configuration was generated by +# `rubocop --auto-gen-config` +# on 2025-01-03 14:37:32 UTC using RuboCop version 1.69.2. +# The point is for the user to remove these configuration records +# one by one as the offenses are removed from the code base. +# Note that changes in the inspected code, or installation of new +# versions of RuboCop, may require this file to be generated again. + +# Offense count: 34 +# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes. +Metrics/AbcSize: + Max: 27 + +# Offense count: 28 +# Configuration parameters: CountComments, CountAsOne. +Metrics/ClassLength: + Max: 381 + +# Offense count: 2 +# Configuration parameters: AllowedMethods, AllowedPatterns. +Metrics/CyclomaticComplexity: + Max: 9 + +# Offense count: 200 +# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns. +Metrics/MethodLength: + Max: 27 + +# Offense count: 3 +# Configuration parameters: AllowedMethods, AllowedPatterns. +Metrics/PerceivedComplexity: + Max: 10 + +# Offense count: 68 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings. +# URISchemes: http, https +Layout/LineLength: + Max: 173 diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/.yardopts b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/.yardopts new file mode 100644 index 0000000..3fa20db --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/.yardopts @@ -0,0 +1,25 @@ +--output-dir docs +--no-private +lib/mocha.rb +lib/mocha/api.rb +lib/mocha/hooks.rb +lib/mocha/mock.rb +lib/mocha/expectation.rb +lib/mocha/object_methods.rb +lib/mocha/class_methods.rb +lib/mocha/parameter_matchers.rb +lib/mocha/parameter_matchers +lib/mocha/state_machine.rb +lib/mocha/sequence.rb +lib/mocha/configuration.rb +lib/mocha/expectation_error_factory.rb +lib/mocha/expectation_error.rb +lib/mocha/stubbing_error.rb +lib/mocha/unexpected_invocation.rb +lib/mocha/integration.rb +lib/mocha/integration/test_unit/adapter.rb +lib/mocha/integration/minitest/adapter.rb +- +RELEASE.md +COPYING.md +MIT-LICENSE.md diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/CONTRIBUTING.md b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/CONTRIBUTING.md new file mode 100644 index 0000000..e9fa603 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/CONTRIBUTING.md @@ -0,0 +1,7 @@ +* Pull requests are welcomed. +* Fork the repository. +* Make your changes in a branch. +* Add/modify/remove tests as appropriate. +* Open a pull request based on a branch on your fork. +* Wait for your pull request build to pass on [Circle CI](https://app.circleci.com/pipelines/github/freerange/mocha). +* Pull requests with failing tests will not be accepted. diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/COPYING.md b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/COPYING.md new file mode 100644 index 0000000..3cdf960 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/COPYING.md @@ -0,0 +1,3 @@ +Copyright James Mead 2006 + +You may use, copy and redistribute this library under the same terms as [Ruby itself](https://www.ruby-lang.org/en/about/license.txt) or under the [MIT license](https://mit-license.org/). diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/Gemfile b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/Gemfile new file mode 100644 index 0000000..3562297 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/Gemfile @@ -0,0 +1,16 @@ +source 'https://rubygems.org' + +gemspec + +group :development do + gem 'benchmark' + gem 'introspection', '~> 0.0.1' + gem 'minitest' + gem 'rake' + + if ENV['MOCHA_GENERATE_DOCS'] + gem 'rdoc' + gem 'redcarpet' + gem 'yard' + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/MIT-LICENSE.md b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/MIT-LICENSE.md new file mode 100644 index 0000000..4b9081c --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/MIT-LICENSE.md @@ -0,0 +1,7 @@ +Copyright (c) 2006 James Mead + +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/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/README.md b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/README.md new file mode 100644 index 0000000..a1e0e24 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/README.md @@ -0,0 +1,361 @@ +## Mocha [![CircleCI status of freerange/mocha](https://circleci.com/gh/freerange/mocha.svg?style=shield)](https://app.circleci.com/pipelines/github/freerange/mocha) [![Gem Version](https://badge.fury.io/rb/mocha.svg)](http://badge.fury.io/rb/mocha) + +### Description + +* A Ruby library for [mocking](http://xunitpatterns.com/Mock%20Object.html) and [stubbing](http://xunitpatterns.com/Test%20Stub.html) - but deliberately not (yet) [faking](http://xunitpatterns.com/Fake%20Object.html) or [spying](http://xunitpatterns.com/Test%20Spy.html). +* A unified, simple and readable syntax for both full & partial mocking. +* Built-in support for Minitest and Test::Unit. +* Supported by many other test frameworks. + +### Intended Usage + +Mocha is intended to be used in unit tests for the [Mock Object](http://xunitpatterns.com/Mock%20Object.html) or [Test Stub](http://xunitpatterns.com/Test%20Stub.html) types of [Test Double](http://xunitpatterns.com/Test%20Double.html), not the [Fake Object](http://xunitpatterns.com/Fake%20Object.html) or [Test Spy](http://xunitpatterns.com/Test%20Spy.html) types. Although it would be possible to extend Mocha to allow the implementation of fakes and spies, we have chosen to keep it focused on mocks and stubs. + +### Installation + +#### Gem + +Install the latest version of the gem with the following command... + + $ gem install mocha + +Note: If you are intending to use Mocha with Test::Unit or Minitest, you should only setup Mocha *after* loading the relevant test library... + +##### Test::Unit + +```ruby +require 'rubygems' +gem 'mocha' +require 'test/unit' +require 'mocha/test_unit' +``` + +##### Minitest + +```ruby +require 'rubygems' +gem 'mocha' +require 'minitest/autorun' +require 'mocha/minitest' +``` + +#### Bundler + +If you're using Bundler, include Mocha in the `Gemfile` and then setup Mocha later once you know the test library has been loaded... + +##### Test::Unit + +```ruby +# Gemfile +gem 'mocha' + +# Elsewhere after Bundler has loaded gems e.g. after `require 'bundler/setup'` +require 'test/unit' +require 'mocha/test_unit' +``` + +##### Minitest + +```ruby +# Gemfile +gem 'mocha' + +# Elsewhere after Bundler has loaded gems e.g. after `require 'bundler/setup'` +require 'minitest/autorun' +require 'mocha/minitest' +``` + +##### RSpec + +RSpec includes a mocha adapter. Just tell RSpec you want to mock with `:mocha`: + +```ruby +# Gemfile in Rails app +gem 'mocha' + +# Within `spec/spec_helper.rb` +RSpec.configure do |config| + config.mock_with :mocha +end +``` + +Note: There is no need to use a require statement to setup Mocha; RSpec does this itself. + +##### Cucumber + +```ruby +# In e.g. features/support/mocha.rb +require 'mocha/api' + +World(Mocha::API) + +Around do |scenario, block| + begin + mocha_setup + block.call + mocha_verify + ensure + mocha_teardown + end +end +``` + +#### Rails + +If you're loading Mocha using Bundler within a Rails application, you should setup Mocha manually e.g. at the bottom of your `test_helper.rb`. + +##### Minitest + +Note that since Rails v4 (at least), `ActiveSupport::TestCase` has inherited from `Minitest::Test` or its earlier equivalents. Thus unless you are *explicitly* using Test::Unit, you are likely to be using Minitest. + +```ruby +# Gemfile in Rails app +gem 'mocha' + +# At bottom of test_helper.rb (or at least after `require 'rails/test_help'`) +require 'mocha/minitest' +``` + +##### Other Test Framework + +Follow the instructions for the relevant test framework in the [Bundler](#bundler) section, but ensure that the relevant Mocha file (`mocha/minitest`, `mocha/test_unit`, or `mocha/api`) is required **after** the test framework has been loaded, e.g. at the bottom of `test_helper.rb` or `spec_helper.rb`, or at least after `rails/test_help` has been required. + +#### Known Issues + +* Prior to v1.15.0 (when support for Ruby v1.8 was dropped), stubbing an aliased class method where the original method is defined in a module that's used to `extend` the class doesn't work in Ruby v1.8. See `test/acceptance/stub_method_defined_on_module_and_aliased_test.rb` for an example of this behaviour. + +### Usage + +#### Quick Start + +```ruby +require 'test/unit' +require 'mocha/test_unit' + +class MiscExampleTest < Test::Unit::TestCase + def test_mocking_a_class_method + product = Product.new + Product.expects(:find).with(1).returns(product) + assert_equal product, Product.find(1) + end + + def test_mocking_an_instance_method_on_a_real_object + product = Product.new + product.expects(:save).returns(true) + assert product.save + end + + def test_stubbing_instance_methods_on_real_objects + prices = [stub(pence: 1000), stub(pence: 2000)] + product = Product.new + product.stubs(:prices).returns(prices) + assert_equal [1000, 2000], product.prices.collect {|p| p.pence} + end + + def test_stubbing_an_instance_method_on_all_instances_of_a_class + Product.any_instance.stubs(:name).returns('stubbed_name') + product = Product.new + assert_equal 'stubbed_name', product.name + end + + def test_traditional_mocking + object = mock('object') + object.expects(:expected_method).with(:p1, :p2).returns(:result) + assert_equal :result, object.expected_method(:p1, :p2) + end + + def test_shortcuts + object = stub(method1: :result1, method2: :result2) + assert_equal :result1, object.method1 + assert_equal :result2, object.method2 + end +end +``` + +#### Mock Objects + +```ruby +class Enterprise + def initialize(dilithium) + @dilithium = dilithium + end + + def go(warp_factor) + warp_factor.times { @dilithium.nuke(:anti_matter) } + end +end + +require 'test/unit' +require 'mocha/test_unit' + +class EnterpriseTest < Test::Unit::TestCase + def test_should_boldly_go + dilithium = mock() + dilithium.expects(:nuke).with(:anti_matter).at_least_once # auto-verified at end of test + enterprise = Enterprise.new(dilithium) + enterprise.go(2) + end +end +``` + +#### Partial Mocking + +```ruby +class Order + attr_accessor :shipped_on + + def total_cost + line_items.inject(0) { |total, line_item| total + line_item.price } + shipping_cost + end + + def total_weight + line_items.inject(0) { |total, line_item| total + line_item.weight } + end + + def shipping_cost + total_weight * 5 + 10 + end + + class << self + def find_all + # Database.connection.execute('select * from orders... + end + + def number_shipped_since(date) + find_all.select { |order| order.shipped_on > date }.length + end + + def unshipped_value + find_all.inject(0) { |total, order| order.shipped_on ? total : total + order.total_cost } + end + end +end + +require 'test/unit' +require 'mocha/test_unit' + +class OrderTest < Test::Unit::TestCase + # illustrates stubbing instance method + def test_should_calculate_shipping_cost_based_on_total_weight + order = Order.new + order.stubs(:total_weight).returns(10) + assert_equal 60, order.shipping_cost + end + + # illustrates stubbing class method + def test_should_count_number_of_orders_shipped_after_specified_date + now = Time.now; week_in_secs = 7 * 24 * 60 * 60 + order_1 = Order.new; order_1.shipped_on = now - 1 * week_in_secs + order_2 = Order.new; order_2.shipped_on = now - 3 * week_in_secs + Order.stubs(:find_all).returns([order_1, order_2]) + assert_equal 1, Order.number_shipped_since(now - 2 * week_in_secs) + end + + # illustrates stubbing instance method for all instances of a class + def test_should_calculate_value_of_unshipped_orders + Order.stubs(:find_all).returns([Order.new, Order.new, Order.new]) + Order.any_instance.stubs(:shipped_on).returns(nil) + Order.any_instance.stubs(:total_cost).returns(10) + assert_equal 30, Order.unshipped_value + end +end +``` + +### Thread safety + +Mocha currently *does not* attempt to be thread-safe. + +#### Can I test multi-threaded code with Mocha? + +The short answer is no. In multi-threaded code Mocha exceptions may be raised in a thread other than the one which is running the test and thus a Mocha exception may not be correctly intercepted by Mocha exception handling code. + +#### Can I run my tests across multiple threads? + +Maybe, but probably not. Partial mocking changes the state of objects in the `ObjectSpace` which is shared across all threads in the Ruby process and this access to what is effectively global state is not synchronized. So, for example, if two tests are running concurrently and one uses `#any_instance` to modify a class, both tests will see those changes immediately. + +### Expectation matching / invocation order + +Stubs and expectations are basically the same thing. A stub is just an expectation of zero or more invocations. The `Expectation#stubs` method is syntactic sugar to make the intent of the test more explicit. + +When a method is invoked on a mock object, the mock object searches through its expectations from newest to oldest to find one that matches the invocation. After the invocation, the matching expectation might stop matching further invocations. If the expectation that matches the invocation has a cardinality of "never", then an unexpected invocation error is reported. + +See the [documentation](https://mocha.jamesmead.org/Mocha/Mock.html) for `Mocha::Mock` for further details. + +### Configuration + +If you want, Mocha can generate a warning or raise an exception when: + +* stubbing a method unnecessarily +* stubbing method on a non-mock object +* stubbing a non-existent method +* stubbing a non-public method + +See the [documentation](https://mocha.jamesmead.org/Mocha/Configuration.html) for `Mocha::Configuration` for further details. + +### Debugging + +Mocha provides some extra output to help with debugging when the standard Ruby debug option (`-d`) is set. + +### Semantic versioning + +* Every effort is made to comply with [semantic versioning](https://semver.org/). +* However, this only applies to the behaviour documented in the public API. +* The documented public API does *not* include the content or format of messsages displayed to the user, e.g. assertion failure messages. + +### Useful Links + +* [Official Documentation](https://mocha.jamesmead.org) +* [Source Code](http://github.com/freerange/mocha) +* [Mailing List](http://groups.google.com/group/mocha-developer) +* [James Mead's Blog](http://jamesmead.org/blog/) +* [An Introduction To Mock Objects In Ruby](http://jamesmead.org/talks/2007-07-09-introduction-to-mock-objects-in-ruby-at-lrug/) +* [Mocks Aren't Stubs](http://martinfowler.com/articles/mocksArentStubs.html) +* [Growing Object-Oriented Software Guided By Tests](http://www.growing-object-oriented-software.com/) +* [Mock Roles Not Objects](http://www.jmock.org/oopsla2004.pdf) +* [jMock](http://www.jmock.org/) + +### Contributors + +See this [list of contributors](https://github.com/freerange/mocha/graphs/contributors). + +### Releasing a new version + +* Update the RELEASE.md file with a summary of changes +* Bump the version in `lib/mocha/version.rb` +* Commit & push to GitHub +* Check CircleCI build is passing - https://app.circleci.com/pipelines/github/freerange/mocha + +* Generate documentation: + +```bash +$ MOCHA_GENERATE_DOCS=true bundle install + +$ MOCHA_GENERATE_DOCS=true rake docs +``` +* Commit documentation & push to GitHub +* Sign in to rubygems.org and find API key - https://rubygems.org/profile/edit + +```bash +$ curl -u -H 'OTP:' https://rubygems.org/api/v1/api_key.yaml > ~/.gem/credentials; chmod 0600 ~/.gem/credentials +``` + +* Release gem to Rubygems: + +```bash +$ rake release +[runs tests] +mocha 1.2.0 built to pkg/mocha-1.2.0.gem. +Tagged v1.2.0. +Pushed git commits and tags. +Pushed mocha 1.2.0 to rubygems.org. +``` + +### History + +Mocha was initially harvested from projects at [Reevoo](http://www.reevoo.com/). It's syntax is heavily based on that of [jMock](http://www.jmock.org). + +### License + +© Copyright James Mead 2006 + +You may use, copy and redistribute this library under the same terms as [Ruby itself](https://www.ruby-lang.org/en/about/license.txt) or under the [MIT license](https://mit-license.org/). diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/RELEASE.md b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/RELEASE.md new file mode 100644 index 0000000..af8c3d0 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/RELEASE.md @@ -0,0 +1,1217 @@ +# Release Notes + +## 3.0.1 + +### External changes + +* Fix Mocha/RSpec integration by reinstating argument default value for `Hooks#mocha_setup` (#768 & #769) - thanks to @mackuba for reporting. + +## 3.0.0 + +### External changes + +* Improvements to docs for cardinality-related methods (#700) +* Drop support for Ruby v2.1 (#628 & #695) +* Improve gemspec description (#692 & #696) +* Remove docs for `use_test_unit_gem` option (eaf03493) +* Correct docs for `MOCHA_OPTIONS` "debug" value (69ef41c0) +* Use built-in Ruby debug option vs custom module (#714 & #715) +* Enable strict keyword argument matching by default in Ruby >= v3 (#697 & #718) +* Use Ruby v1.9 Hash syntax in `Hash#mocha_inspect` used in failure messages (#651 & #719) +* Improve `#mocha_inspect` for empty keyword argument `Hash` (#588 & #720) - thanks to @herwinw for reporting +* Improve error if `Mockery.teardown` called before `Mockery.setup` (#611, #646 & #721) - thanks to @riniculous for reporting +* Freeze string literals (#722) +* Allow keyword arguments to match an expectation expecting *only* positional arguments (#593 & #732) - thanks to @seandilda +for reporting +* Fix compatibility with Ruby 3.5 (#755) - thanks to @Earlopain +* Add missing quotes to docs for `Mocha::ParameterMatchers::Methods#equivalent_uri` (#757) - thanks to @bensherman +* Increment assertion count on never expected invocation (#763 & #764) +* Remove deprecated `ParameterMatchers::Base` class; include `ParameterMatchers::BaseMethods` module instead (415ae768) +* Remove deprecated `ParameterMatcher` methods now available in `ParameterMatcher::Methods` (8de49979) +* Removed deprecated access to `ParameterMatcher` classes (6ea9e3f5) + +### Internal changes + +* Add `Expectation#thrice` (#701) - thanks to @andrewn617 +* Add `base64` to Gemfile for Ruby >= v3.3.0 to avoid warning (8c49314c & a76330d6) +* Include custom Gemfiles in Rubocop linting (04063f0d) +* Use ruby:3.4.0-rc1 vs ruby:3.4.0-preview2 in CI in preparation for the Ruby v3.4 release (b2127407) +* Add `ostruct` to Gemfile for Ruby >= v3.4.0 to avoid warning (2b05e09e) +* Add Ruby v3.4 to CI build matrix (#708) +* Fix Ruby v3.4 warnings in ObjectInspectTest (#709 & #710) +* Rubocop-related improvements (#702 & #705) +* Consistently use development group in Gemfiles (#706 & #716) +* Add missing requires for 'mocha/ruby_version' (7f99db69) +* Convert `ParameterMatchers::Base` class -> module (#712 & #723) +* Enable `ExpectMatchingDefinition` option on `Naming/FileName` cop (#726) +* Remove duplicate `DefaultReceiverTest` (39d99954) +* Improvements to keyword argument matching tests (#730) +* Improvements to keyword argument matcher unit test (#731) +* Remove support for running tests with Minitest < v5 (#727) +* Remove workaround for JRuby jar-dependencies issue (#737) +* Add benchmark to `Gemfile` to fix warning (#733 & #740) - thanks to @nitishr +* Add documentation coverage check (#707, #743 & #745) - thanks to @nitishr +* Fix `Lint/LiteralAsCondition` violation (#746) - thanks to @nitishr +* Fix rdoc-related warning when running doc rake task (#741 & #747) +* Fail fast in CI builds on any Ruby warnings (#729 & #741) - thanks to @nitishr +* Add `ObjectMethods#stubba_respond_to?` and use instead of `Object#respond_to?` (#713, #742 & #750) - thanks to @nitishr +* Rename `#stubbee` -> `#stubba_object` and `#mock_owner` -> `#stubbee` (#463 & #751) - thanks to @nitishr +* Fix `enable-frozen-string-literal` in CI build (#752) - thanks to @nitishr +* Various rubocop fixed (#754 & #756) +* Fix `Layout/EmptyLinesAfterModuleInclusion` violations (3f683220) +* Auto-correct `Style/RedundantParentheses` violations (0277a592) +* Automatically retry failed CI builds (9bbdbc66) + +## 2.8.2 + +### External changes + +* Improve source location of deprecation warning for `ParameterMatchers` matcher classes (c5171488) + +## 2.8.1 + +### External changes + +* Restore unqualified access to `ParameterMatchers` matcher classes, but deprecate such access (5231a4f7) +* Restore unqualified access to `ParameterMatchers::BaseMethods`, but deprecate such access (8a898567) + +## 2.8.0 + +Many thanks to @etiennebarrie for his help in testing v3.0.0 release candidates which led to many of these changes. + +### External changes + +* Extract `ParameterMatchers::BaseMethods` module and deprecate inheriting from `ParameterMatchers::Base` class (0ddfbe4b) +* Move matcher builder methods into `ParameterMatchers::Methods` module and only include that module into `API` (2de41423) +* Provide deprecated access to matcher builder methods when including `ParameterMatchers` module (299488e1) +* Provide deprecated access to matcher classes, e.g. `ParameterMatchers::Equals`, from within tests/specs (dcced1b4) + +### Internal changes + +* Move `ParameterMatchers#parse_option` -> `HasEntry.parse_option` (9e2a6f66) + +## 2.7.1 + +### External changes + +* Deprecate `Configuration#stubbing_method_on_nil=` (#694) +* Indicate when parameter matcher logic is defined by block passed to `Expectation#with` (#698, b30e4434) +* Improve documentation for `Expectation#with`, especially when it is passed a block (#698, #682, #606 & #681) + +## 2.7.0 + +### External changes + +* Fail fast if invocation matches never expectation (#679, #678, #490, #131 & #44) - thanks to @ducmtran & @davidstosik for reporting + +### Internal changes + +* Workaround for JRuby jar-dependencies issue (#690) +* Ruby v3.4 stacktrace uses single-quote vs backtick (#688 & #689) - thanks to Vít Ondruch + +**WARNING: This release fixes a very _old_ bug** +* The bug relates to the use of `Expectation#never` in combination with other expectations on the same method. +* Please ensure you fix the relevant deprecation warnings when running against v2.6.1 *before* upgrading to v2.7.0. +* Previously, the following test would have passed, but now it will fail with an unexpected invocation error on the `foo.bar` line. + + foo = mock('foo') + foo.stubs(:bar) + foo.expects(:bar).never + foo.bar + +## 2.6.1 + +### External changes + +* Fix logic for displaying deprecation warning for expectation with never cardinality (#686) - thanks to @davidstosik for reporting + +## 2.6.0 + +### External changes + +* Expectation with never cardinality should display deprecation warning (#681) - thanks to @ducmtran for reporting and testing + +**WARNING: This release results in some incorrect deprecation warnings:** +* The logic for displaying the deprecation warnings is fixed in v2.6.1 (#686). + +### Internal changes + +* Simplify backtrace related assertions (#680) +* Remove unused `ExpectationList#match_but_out_of_order` (f2fa9919) + +## 2.5.0 + +### External changes + +* Add metadata to gem specification, including `changelog_uri` (#608, eb1b8ea2) - thanks to @mattbrictson +* Fix warnings in Ruby v3.4 (#672, #669) - thanks to @radville for reporting +* Add warnings & notes about regressions, known issues, etc to release notes (#675, #676 & #677) - thanks to @davidstosik + +### Internal changes + +* Fix `jaro_winkler` compilation errors on MacOS (5c7d14cb) +* Fix typos in `IncludesTest` test names (6fb5a5a6) +* Fix `rubocop` version constraint for Ruby > v2.2.0 (d5c6b98a) + +## 2.4.5 + +### External changes + +* Fix regression when stubbed method expects `Hash` but receives `ActionController::Parameters` object (#662, #664) - thanks to @evgeni for reporting and testing + +## 2.4.4 + +### External changes + +* Fix regression when method expecting `Hash` parameter or keyword arguments is invoked with no arguments (#662, #663) - thanks to @vlad-pisanov for reporting + +**WARNING: This release includes a regression:** +* A `NoMethodError` was raised when a stubbed method was expecting a `Hash`, but was invoked with an instance of `ActionController::Parameters`. See #662 for the report and #664 for the fix which was released in v2.4.5. + +## 2.4.3 + +### External changes + +* Fix regression when matching `Hash` parameter or keyword arguments (#657, #660) - thanks to @josesei for reporting and testing + +**WARNING: This release inadvertently introduced a couple of regressions:** +* A `NoMethodError` was raised when a stubbed method was expecting a `Hash`, but was invoked with no arguments, e.g. with `C.expects(:foo).with(bar: 42)` and invoking `C.expects(:foo)`. See #662 for the report and #663 for the fix which was released in v2.4.4. +* A `NoMethodError` was raised when a stubbed method was expecting a `Hash`, but was invoked with an instance of `ActionController::Parameters`. See #662 for the report and #664 for the fix which was released in v2.4.5. + +## 2.4.2 + +### External changes + +* Don't trust `Object#is_a?` in presence of mock objects (#656) - thanks to @casperisfine + +**WARNING: This release includes a regression:** +* Keyword argument and top-level `Hash` matching became more relaxed than intended, e.g. `mock.expects(:method).with(key: "value")` accepted `mock.method(key: "value", key2: "value")` when it should not have done. See #657 & #675 for the reports and #660 for the fix which was released in v2.4.3. + +## 2.4.1 + +### External changes + +* Fix regression in matchers when used with keyword arguments (#654 & #655) - thanks to @ElvinEfendi for reporting + +### Internal changes + +* Reduce duplication & consolidate `#to_matcher` method definitions (600ee2aa, e9de64e4, #655) +* Change `#to_matcher` method to use keyword arguments (3b60b7df, #655) + +**WARNING: This release includes a regression:** +* Keyword argument and top-level `Hash` matching became more relaxed than intended, e.g. `mock.expects(:method).with(key: "value")` accepted `mock.method(key: "value", key2: "value")` when it should not have done. See #657 & #675 for the reports and #660 for the fix which was released in v2.4.3. + +## 2.4.0 + +### External changes + +* Improve rendering of keyword arguments (#652) - thanks to @casperisfine + +### Internal changes + +**WARNING: This release includes a couple of regressions:** +* Nested parameter matching for keyword arguments became more relaxed than intended, e.g. `mock.expects(:method).with(has_entry(:k1, k2: 'v2'))` accepted `mock.method(k1: { k2: 'v2', k3: 'v3' })` when it should not have done. See #654 for the report and #655 for the fix which was released in v2.4.1. +* Keyword argument and top-level `Hash` matching became more relaxed than intended, e.g. `mock.expects(:method).with(key: "value")` accepted `mock.method(key: "value", key2: "value")` when it should not have done. See #657 & #675 for the reports and #660 for the fix which was released in v2.4.3. + +* Improvements to `#mocha_inspect` unit tests (#650) + +## 2.3.0 + +### External changes + +* Fix nested parameter matching for keyword arguments (f94e2504, #648) - thanks to @CodingAnarchy for reporting + +**WARNING: This release inadvertently introduced a couple of regressions:** +* Nested parameter matching for keyword arguments became more relaxed than intended, e.g. `mock.expects(:method).with(has_entry(:k1, k2: 'v2'))` accepted `mock.method(k1: { k2: 'v2', k3: 'v3' })` when it should not have done. See #654 for the report and #655 for the fix which was released in v2.4.1. +* Keyword argument and top-level `Hash` matching became more relaxed than intended, e.g. `mock.expects(:method).with(key: "value")` accepted `mock.method(key: "value", key2: "value")` when it should not have done. See #657 & #675 for the reports and #660 for the fix which was released in v2.4.3. + +## 2.2.0 + +### External changes + +* Support multiple methods in `responds_with` matcher (f086b7e4, #578) - thanks to @vlad-pisanov for the suggestion +* Add block syntax for sequences (93fdffd, #61) +* Improve sequence failure message (0800c6ff, #60) +* Drop support for Ruby v2.0 (85848fb0, #642) +* Include the original test name in expired stub error messages (ca3ff8eb, #641, #642) - thanks to @casperisfine + +* Avoid rubocop directive ending up in YARD docs (2a9ee81a) +* Update docs to fix those for `Mock#method_missing` (cee0bad6) +* Reinstate missing CNAME for GitHub Pages site (da67bb0d) +* Use Ruby v1.9 Hash syntax in docs (6de20726, #625) +* Add missing YARD tag for API#sequence name param (343c5979) +* Add missing YARD tag for API#states name param (f798df83) + +### Internal changes + +* Tidy up Minitest vs MiniTest references (#626, #614, #615) - thanks to @zenspider & @Maimer for their help +* Add Ruby v3.3 to CI build matrix (ce31b544) + +## 2.1.0 + +### External changes + +* Fix compatibility with Minitest (#614) - thanks to @kyrofa & @manewitz for reporting and to @zenspider for his input + +### Internal changes + +* Update URLs for links to Ruby & MIT licenses (d6470af4) + +## 2.0.4 + +### Internal changes + +* Update `README.md` (e8c21e1b) + +## 2.0.3 + +### External changes + +* Fix `BacktraceFilter` to handle special characters (e242033f, #592) - thanks to @casperisfine + +### Internal changes + +* Add Ruby v3.1 to the CircleCI build (3e460489) +* DRY up `regexp_matches` test (ae9fed4a) +* Fix regexp_matches tests in Ruby v3.2 (26b106a5, #590) +* Use Ruby 1.9 hash syntax (8bc0ad2f, #598, #537) - thanks to @herwinw +* Simplify storage of `MOCHA_OPTIONS` (b70507a1, #600) - thanks to @herwinw +* Pin JRuby to v9.3.9.0 in CircleCI builds (b8e6d064, #591) +* Rubocop: enable Style/FormatStringToken cop (089a688e, #603) - thanks to @herwinw +* Remove Ruby version check from `RespondsLikeTest` (21583129) +* Add Ruby v3.2 to CircleCI build (f7e17636, #601) +* Use Ruby v2.6 vs v2.2 to run lint CI job (af40b7db) +* Pin yard version to v0.9.28 to avoid `ArgumentError` (12f1eef7) +* Revert "Pin JRuby to v9.3.9.0 in CircleCI builds" (4f5bb2f0, #591) +* Remove invalid CircleCI token from badge URL (7078e76a) +* Revert "Pin yard version to v0.9.28 to avoid ArgumentError" (7c6c10c5, #609) +* Remove Google Analytics tracking code (2279c49d, #612) +* Update `MIT-LICENSE.md` (48162b4e) +* Update `COPYING.md` (f3152376) + +## 2.0.2 + +### External changes + +* Fix regression in `Mock#responds_like` behaviour - thanks to @adrianna-chang-shopify for reporting (#580,#583,ba4d619e) + +## 2.0.1 + +### External changes + +* Fix `LoadError` when using v2.0.0 with Ruby < v2.7 by moving declaration of runtime dependency on `ruby2_keywords` gem from `Gemfile` to `mocha.gemspec` - thanks to @mishina2228 for reporting (#581, #582, cdeb0356) + +## 2.0.0 + +### External changes + +* Remove support for Ruby v1.9 - thanks to @wasabigeek (#552) +* Support strict keyword argument matching - see docs for `Expectation#with` & `Configuration#strict_keyword_argument_matching=` - thanks to @wasabigeek (#446,#535,#544,#562) +* Deprecate `Hash` args that don't strictly match (#563,981c31be) +* Drop support for older versions of test-unit - gem versions of test-unit earlier than v2.5.1 and versions of test-unit +from the Ruby v1.8 standard library are no longer supported (#540,969f4845) +* Drop support for older versions of minitest - versions of minitest earlier than v3.3.0 are no longer supported (#541,ca69dc9e) +* Remove deprecated `mocha/setup.rb` mechanism (642a0ff4) +* Add missing docs for `API#stub` parameter (257b4cb4) +* Remove optional reinstatement of v1.9 behaviour (#436,#438,#569,1473ee25) +* Remove deprecated methods in `Configuration` (#421,e7ff7528) +* Fail fast when mock receives invocations in another test (#440,#442,cb054d59) +* Improve docs re using matchers in `Expectation#with` (da7237cd) +* Expand `Expectation#with` docs re keyword arguments (fed6808d) +* Improve docs for `strict_keyword_argument_matching` (8d8f881d) +* Remove deprecated Rails plugin `init.rb` file (1c617175) +* Improve strict keyword argument matching deprecation warning by including the source location of the stub definition (77c0d4cc) +* Add README section re semantic versioning (00758246) + +### Internal changes + +* Separate linting from tests in terms of Rake tasks & CircleCI jobs - thanks to @wasabigeek (#556) +* Remove tests specific to Ruby v1.8 behaviour (46fca7ac, 3b369e99) +* Multi-line rubocop disable in `Mock#method_missing` (af2194c4) +* Remove unused arg for `HashMethods#mocha_inspect` (4f59e27f) +* Improve test runner assertions - failure vs error (eec7200a) +* Improve test coverage of `PositionalOrKeywordHash` (c294fe70) +* More consistent Test::Unit & Minitest integration (27dd3817) +* Remove redundant `require` statements (d82218a8,fa17b114) +* Add missing `require` statement (73493761) +* Disable Style/Semicolon cop globally (8cd0b705) + +## 1.16.1 + +### External changes + +* Fix regression in `Mock#responds_like` behaviour - thanks to @adrianna-chang-shopify for reporting (#580,#583,77af2af1) + +## 1.16.0 + +### External changes + +* Default `Configuration#reinstate_undocumented_behaviour_from_v1_9=` to `false` (6fcaf947) +* Deprecate `Configuration#reinstate_undocumented_behaviour_from_v1_9=` (a797c5fd) + +### Internal changes + +* Remove redundant deprecation disabling in MockTest (dc8ca969) + +## 1.15.1 + +### External changes + +* Fix regression in `Mock#responds_like` behaviour - thanks to @adrianna-chang-shopify for reporting (#580,#583,c586a08c) + +## 1.15.0 + +### External changes + +* Fix examples using mock constructor with block (1cc17667) +* Add another example for `API#sequence` (b7a7d233, #59) +* Remove support for Ruby v1.8 (ddb5d672) +* Deprecate support for Ruby versions earlier than v2.0 - thanks to @wasabigeek (#553, #555) + +### Internal changes + +* Update instructions for obtaining Rubygems API key (ed9c040a) +* Consistent definitions for `respond_to?` methods (#533) +* Run test tasks before release tasks (92a1bc6e, #447) +* Fix test:performance Rake task (#538, #539) +* Tidying following removal of support for Ruby v1.8 - thanks to @nitishr (#542) +* Remove `ParametersMatcher` from `Invocation#call_description` - thanks to @wasabigeek (#543) +* Remove unnecessary splatting in Invocation - thanks to @wasabigeek (#549) +* Extract `handle_method_call` from `method_missing` - thanks to @wasabigeek (#550) + +## 1.14.0 + +### External changes + +* Mock#expects,#stubs should return last expectation - thanks to @vlad-pisanov for #524 (b6b637db) + +### Internal changes + +* Avoid breaking change in psych v4 in ruby v3.1 (08b9f4ca) +* Remove broken Dependabot badge from README (d446657a) +* Add Ruby 3.0 to the CI matrix - thanks to @mishina2228 for #526 (65bc626e) +* Move development dependencies from gemspec to Gemfile - thanks to @mishina2228 for #527 (dd127f7b) + +## 1.13.0 + +### External changes + +* Add `ParameterMatchers#has_keys` - thanks to @cstyles for #512 (18d8104) +* Fix misleading exception message in `ParameterMatchers#has_entry` - thanks to @cstyles for #513 (9c4ef13) +* Only add dependency on rubocop if we're actually going to use it (f2f879f) +* Fix rake dependency constraints for older Ruby versions (7ce5f29) + +### Internal changes + +* Check documentation can be generated as part of CircleCI build (b30d9a9) +* Add --fail-on-warning option to yard rake task (53a6ee3) +* Add a weekly scheduled build to the CircleCI build (fd2a4c6) +* Add Ruby v1.8 to the CircleCI build matrix (818ca03) +* Add API token to fix CircleCI badge in README (607c5aa) +* Provide wrapped option for #mocha_inspect on hashes & arrays (d8f44bc) +* Use CircleCI instead of TravisCI for automated builds (c98c6ec) +* Switch to newer default Travis CI build env (c78f75c) +* Use latest Ruby versions in Travis CI builds (9e0043a) +* Use latest JRuby v9.2.18 in Travis CI builds (8c99a1b) +* Use consistent JRuby versions in Travis CI builds (0f849aa) +* Use more recent version of JRuby in Travis CI build matrix (58653db) + +## 1.12.0 + +### External changes + +* Various improvements to README inspired by #207 and #390 - thanks to @nitishr for his work on #390 (fed0eee6) +* Improve documentation related to `StateMachine` classes - thanks to @nitishr (#425 & #427) +* Fix regression in cardinality introduced in v1.10.0 (59454a8) and reported in #473 - thanks to @srvance for reporting and @nitishr for fixing (#474) +* Fix documentation for `Mocha::Expectation#when` - thanks to @olleolleolle (b4f59daa & #477) +* Remove `Mocha::Mock#respond_to?` from documentation - thanks to @nitishr (#480) +* Improvements to documentation for `Expectation#yields` & `#multiple_yields` - thanks to @andyw8 for reporting in #495 (1b6571c) +* Remove documentation & tests from gem to reduce its size by over 50% - thanks to @gabetax (#500) +* Update documentation to point to travis-ci.com instead of travis-ci.org + +### Internal changes + +* Refactor `StateMachine`-related classes - thanks to @nitishr (#425 & #427) +* Remove redundant test - thanks to @nitishr (8e4f1a7c) +* Add Ruby 2.7 to Travis CI matrix - thanks to @bastelfreak (fc5ea2f2) +* Simplify `Mockery` - thanks to @nitishr (#449) +* Update Travis CI badge to point to main vs master branch (bd8028f8) +* Generate docs using newer version of yard (v0.9.25) (c619afac) +* Manually upgrade jquery in docs from v1.7.1 -> v1.9.0 to fix CVE-2017-16011 (211098a5, dd5eeedb & 1b76e4d5; also see #492) +* Remove reference to non-existent jquery source map to fix error in Chrome developer tools (20156555) +* Temporarily ignore Ruby v1.8.7 build failures (e5b9feef) + +## 1.11.2 + +### External changes + +* Fix regression introduced in v1.10.0 that meant `Object#inspect` was called unnecessarily (368abd98) +* Warn when mock object receives invocations in another test - thanks to @nitishr (#442) +* Avoid rubocop comments appearing in YARD-generated docs (d8019eed) + +### Internal changes + +* Replace `StubbedMethod#original_method` & `#original_visibility` attribute reader methods with instance variables - thanks to @nitishr (d917f332) +* Set up `MochaExampleTest` & `StubbaExampleTest` as acceptance tests - thanks to @nitishr (4881cc58) +* Delete unused `PrettyParameters` class - thanks to @nitishr (314ea922) + +## 1.11.1 + +### External changes + +* The `reinstate_undocumented_behaviour_from_v1_9` configuration option is now enabled by default to give people a chance to see and fix the relevant deprecation warnings before the behaviour is removed in a future release (b91b1c9e) + +## 1.11.0 + +### External changes + +* Add `Expectation#with_block_given` & `Expectation#with_no_block_given` (#441). + * Allows non-deprecated solution for #382. Thanks to @yemartin for reporting and to @techbelly & @nitishr for feedback. +* Fix issue with non-Array arguments passed to `Expectation#multiple_yields` (#444). + * The undocumented behaviour is now properly supported and documented. + +### Internal changes + +* Move static YARD options from Rake task to `.yardopts` file - thanks to @nitishr (#429) +* Simplify implementation of yielding functionality - thanks to @nitishr (#439) +* Add missing require statement to `acceptance_test_helper.rb` (1070fc02) +* Add some baseline acceptance tests for yielding behaviour (c2cac911) +* Display a sponsor button on GitHub repo page (9fc5911b) +* Use new Deprecation.warning behaviour in `Invocation#call` (932d1166) + +## 1.10.2 + +* Optionally reinstate undocumented behaviour from v1.9. This introduces a new configuration option (`reinstate_undocumented_behaviour_from_v1_9`) to reinstate a couple of bits of undocumented behaviour from v1.9 which were changed in v1.10 without any prior deprecation warning (#438): + * The behaviour of `API#mock`, `API#stub` and `API#stub_everything` when called with a symbol as the first argument. + * The behaviour of `Expectation#yields` and `Expectation#multiple_yields` when the stubbed method is called without a block. + +## 1.10.1 + +* Ensure ObjectMethods & ClassMethods included when API extended (43778756) +* Fix regression in `any_instance` stubbing of methods on object which has an implementation of `#respond_to?` that depends on the object's internal state - thanks to @rafaelfranca for reporting & @nitishr for fixing (#432, #434, 469d4b17) + +## 1.10.0 + +* Improve deprecation warning when requiring 'mocha/setup' (388f44d7) +* Add documentation for Cucumber integration (13ab797b) +* Add documentation about an undocumented feature of `API#mock`, `API#stub` & `API#stub_everything` being changed (7ed2e4e7, d30c1717) + +**WARNING: This release inadvertently changed some undocumented behaviour:** +* An undocumented feature of `API#mock`, `API#stub` & `API#stub_everything` was changed. Previously when these methods were passed a single symbol, they returned a mock object that responded to the method identified by the symbol. Now Passing a single symbol is equivalent to passing a single string, i.e. it now defines the 'name' of the mock object. + +## 1.10.0.beta.1 + +* Hide `ClassMethods#method_visibility` & `#method_exists?` methods to avoid clash with Rails (#428) + +## 1.10.0.alpha + +### External changes + +* Remove dependency on metaclass gem (#49, #365) +* Accept symbol (as well as a string) as mock/stub name - thanks to @nitishr (#347, #353, #377) +* More realistic examples in documentation for `Expectation#yields` and `#multiple_yields` - thanks to @nitishr (#352, #383) +* Improve documentation for `Mock#responds_like` & `#responds_like_instance_of` - thanks to @nitishr (#337, #384) +* Make `Expectation#yields` & `Expectation#multiple_yields` fail when the caller of the stubbed method does not provide a block. This is a change to an undocumented aspect of the public API's behaviour. If this causes your tests to fail, then fix it by removing the unnecessary call to `Expectation#yields` or `Expectation#multiple_yields` - thanks to @nitishr (#382) +* Document `MOCHA_OPTIONS` in README - thanks to @nitishr (#311, #386) +* Add documentation to explain how Mocha is intended to be used - thanks to @nitishr (#330, #385) +* Deprecation warning if integration using 'mocha/test_unit' or 'mocha/minitest' fails - thanks to @nitishr (#229, #389, c6032d0b) +* Require at least one specified sequence for `Expectation#in_sequence` - thanks to @nitishr (#79, #396, 9020248a) +* Make signatures of `Mock#unstub` & `ObjectMethods#unstub` consistent - thanks to @nitishr (#397, f04d437) +* Deprecate requiring 'mocha/setup' (36adf880) +* Optionally display matching invocations alongside expectations - thanks to @nitishr (#178, #394, 00f0540, #410) +* Put deprecations into effect (#400, #418): + * Remove deprecated 'mocha_standalone.rb' & 'mocha/standalone.rb' + * Fail fast if no test library loaded + * Removed optional block for `Mocha::API#mock`, `#stub` & `#stub_everything` + * Remove deprecated `ParameterMatchers#has_equivalent_query_string` method + * Remove deprecated 'mocha/mini_test.rb' +* Fix typo in docs for `Mocha::Configuration.prevent` (266ce71c) +* New-style configuration (see documentation for `Mocha::Configuration`) (#407, #421) +* Deprecate support for Ruby versions earlier than v1.9 (#325, c5f8496d) +* Deprecate support for versions of test-unit & minitest which need monkey-patching (a34e1a88) +* Deprecate old-style Rails plugin (#403, 2df77134) +* Documentation fixes & improvements which also fix YARD warnings (472d5416, a2c0d64a) + +### Internal changes + +* Pin minitest to v5.11.3 for Ruby v1.8.7 to fix build; minitest no longer supports Ruby v1.8.7 (4a0a580) +* Upgrade JRuby to v9.2.8.0 in Travis CI builds (aa29b3f) +* Only run rubocop for MRI Ruby versions & non-integration test builds (8f1c6af) +* Reduce duplication in any instance method class - thanks to @nitishr (#378) +* Simplify `AnyInstanceMethod`, `ClassMethod`, `InstanceMethod`, `ModuleMethod` class hierarchy - thanks to @nitishr (#381) +* Simplify `ClassMethods#method_exists?` & `ObjectMethods#method_exists?` making them consistent - thanks to @nitishr (#270, #362, #370) +* Don't override definition of `singleton_class` in `ClassMethods` - thanks to @nitishr (#391, #392) +* Do not include 'method_definer' methods into all objects (#268, #402) +* Distinguish different `ObjectMethods` modules (#268, #404) +* Pass invocation to expectation list methods - thanks to @nitishr (#408, #409, #411) +* Consistently use `assert_raises` - thanks to @nitishr (#405, #412, a66b7bed) +* Update Ruby & JRuby versions in Travis CI config (18cb1a93, eb061c53) +* Rubocop improvements (aa16ea67...6f4db70b, 2a1240e6...e95716ae) +* Fix inconsistency in CardinalityTest (aa10e0a8) +* Fix test failures on Mac OSX Catalina - thanks to @nitishr (#413, #417, #419, 8a0f2535) +* Remove default argument in `Expectation#invoke` - thanks to @nitishr (#414, #420) + +## 1.9.0 + +* Add TruffleRuby to Travis CI build matrix - thanks to @deepj (#354) +* Explicitly set Travis CI OS to Ubuntu Trusty 14.04 (ded1fa45) +* Expand explanation of thread-safety concerns - thanks to @techbelly (#357) +* Refactor class method and any instance method - thanks to @chrisroos (#358) +* Rely on default bundler version in Travis CI builds (3352e9c5) +* Fix local build-matrix script (11abe231) +* No need to install latest bundler in build-matrix script (8247a894) + +## 1.8.0 + +* Constrain rubocop version to avoid breaking Travis CI builds (05e507f5) +* Avoid calling Kernel#format from ObjectMethods#mocha_inspect - thanks to @hoffmanilya (#345) +* Fix build matrix script (#346) +* Avoid deprecation warning in gemspec (4976e0bc) +* Removed link to documentation translation (ef428ea2) +* Don't use the new bundler v2 in builds (683ded9b) +* Moved documentation from https://gofreerange.com/mocha/docs to https://mocha.jamesmead.org/ [683ded...a17fde](https://github.com/freerange/mocha/compare/683ded...a17fde) + +## 1.7.0 + +* Update Ruby & JRuby versions in Travis CI config (9bf55631 & 3883af7e) +* Simplify gemspec (63744f86) +* Add rubocop and fix most cop violations (#341) +* Use Kernel#warn for deprecations - thanks to @etiennebarrie (#333, 196970a) + +## 1.6.0 + +* Fix subtle bug in setting correct visibility of stubbed module methods on `Kernel` or `Object` - thanks to @chrisroos (#295) +* Avoid mocks for partial mocking leaking into subsequent tests - thanks to @skliew for reporting (#331) +* Remove OpenCollective badge, backers & sponsors (a283a079) +* Change gem version badge to SVG format and add SemVer stability badge - thanks to @greysteil (#335) +* Improve documentation for Configuration (#236) + +## 1.5.0 + +* Prevent use of Mocha outside the context of a test/example - thanks to @andyw8 & @lzap (#327) + +## 1.4.0 + +* Fix deprecation warning for `assert_nil` in `ClassMethodTest` (#308 & #309) +* Display file and line number in deprecation warning - thanks to @chrisarcand (#310, #312 & #313) +* Rename `mocha/mini_test.rb` to `mocha/minitest.rb` - thanks to @grosser (#320 & #322) +* Fix warning when delegating to mock in Ruby 2.4 - thanks to @tjvc (#321 & #323) +* Updates to Travis CI configuration ([73af600..9732726](https://github.com/freerange/mocha/compare/73af600...9732726) & 0426e5e) + +## 1.3.0 + +* Ensure all tests run individually - thanks to @chrisroos (#267) +* Update Travis CI build status badge to show master branch status (#264) +* Correct RSpec section of the README - thanks to @myronmarston (0cc039c8) +* Fix pretty printing of quotes in `String#mocha_inspect` (#215 & #223) +* Add release instructions to README - thanks to @chrisroos (70a5febd & 3c664df7) +* Require at least Ruby v1.8.7 in gemspec - thanks to @knappe (3e20be8e) +* Remove redundant InstanceMethod#method_exists? - thanks to @chrisroos (8f58eddf) +* Reduce risk of hitting bug 12832 in Ruby v2.3 - thanks to @chrisroos (#277 & eca7560c) +* Fix JRuby build - thanks to @headius (jruby/jruby#4250) & @chrisroos (#274) +* Add latest stable version of JRuby to Travis CI build matrix (#288) +* Fix Ruby v1.8.7 builds on Travis CI (928b5a40 & 460dce5b) +* Deprecate passing block to mock object constructor (#290) +* Add a known issue to README for Ruby bug 12876 (#276) +* Add Ruby 2.4 and ruby-head to Travis CI build matrix - thanks to @junaruga (#297) +* Fix `Mocha::ParameterMatchers#includes` for `Array` values - thanks to @timcraft (#302) +* Use faster container-based virtual environments for Travis CI builds (#305) +* Rename `Mocha::ParameterMatchers::QueryStringMatches` to `QueryString` (#306) +* Handle blank parameter value for query string matcher - thanks to @weynsee (#303 & #304) +* Rename `Mocha::ParameterMatchers::QueryString` -> `EquivalentUri` (#307) +* Use `do ... end` instead of `{ ... }` in acceptance tests - thanks to @chrisroos (#294) + +## 1.2.1 + +* Fixed #272. Workaround Ruby bug 12832 which caused interpreter to hang. See https://bugs.ruby-lang.org/issues/12832. Thanks to @chrisroos & @petems (6f1c8b9b, #273). + +## 1.2.0 + +* Always use prepended module to stub class & instance methods for Ruby v2+ - thanks to @grosser & @chrisroos (43d56671, #244) +* Always use prepended module to stub AnyInstance methods in Ruby v2+ - thanks to @chrisroos (#262) +* Always set visibility of stub method to match stubbed method on included module - thanks to @grosser & @chrisroos (e87c03b0, #248) +* Always set visibility to stub method to match stubbed method on superclass - thanks to @chrisroos (38d902ad) +* Allow stubbing of method to which any instance responds (#200) +* Allow `includes` matcher to take matcher arguments - thanks to @lazyatom (#217) +* Avoid exception in older version of Rubygems - thanks to @chrisroos (78d930a7) +* Add licenses to gemspec as requested by @coreyhaines (#201) +* Fix typo in README - thanks to @jaredbeck (6119460d) +* Added section about using Mocha with RSpec & Rails to README (#221) +* Fix documentation for Mocha::API#stub method - thanks to @raeno (599b1dcd) +* Added backers and sponsors from OpenCollective - thanks to @piamancini (#253) +* Fix typo in docs for equals - thanks to @alexcoco (#254) +* Add known issue for Ruby v1.8 to README - thanks to @chrisroos (2c642096) + +**WARNING: This release inadvertently introduced the possibility of causing the Ruby interpreter to hang:** +* There is a scenario where stubbing a class method originally defined in a module hangs the Ruby interpreter due to [a bug in Ruby v2.3.1](https://bugs.ruby-lang.org/issues/12832). See #272. This was fixed in Mocha v1.2.1. + +## 1.1.0 + +* Set visibility of any instance stub method. +* Stub methods with a prepended method if there are other prepended methods. Thanks to @mrsimo. +* Improve docs for `Mock#responds_like` & `#responds_like_instance_of`. +* Use GitHub convention for instructions on contributing to Mocha. +* Fix typos in docs. Thanks to @10io + +**WARNING: This release inadvertently introduced the possibility of causing the Ruby interpreter to hang:** +* From this release onwards, prepended modules have been used internally for stubbing methods. There is [an obscure Ruby bug](https://bugs.ruby-lang.org/issues/12876) in many (but not all) versions of Ruby between v2.0 & v2.3 which under certain circumstances may cause your Ruby interpreter to hang. See the Ruby bug report for more details. The bug has been fixed in Ruby v2.3.3 & v2.4.0. + +## 1.0.0 + +### External changes +* Assume 'mocha' has been required when requiring 'mocha/setup'. +* Provide shortcuts for integrating with specific test library i.e. `require 'mocha/test_unit'` or `require 'mocha/mini_test'` +as alternatives to `require 'mocha/setup'`. +* Do not automatically try to integrate with test libraries. Since the automatic test library integration functionality +requires the test library to be loaded and this doesn't usually happen until *after* the bundle is loaded, it makes things +simpler if we use `require 'mocha/setup'` to explicitly setup Mocha when we know the test library has been loaded. Fixes #146 & #155. +* Consider stubs on superclasses if none exist on primary receiver. Largely based on changes suggested by @ccutrer in #145. +Note: this may break existing tests which rely on the old behaviour. Stubbing a superclass method and then invoking that +method on a child class would previously cause an unexpected invocation error. By searching up through the inheritance +hierarchy for each of the delegate mock objects, we can provide more intuitive behaviour. Instead of an unexpected invocation +error, invoking the method on the child class will cause the stubbed method on the superclass to be used. +* Avoid recursion when constructing unexpected invocation message. Fixes #168. +* Add explanation of method dispatch. Heavily based on the relevant jMock v1 documentation. Fixes #172. +* Make class_eval line number more accurate. This sets the line number as the line number of the `def` statement. Closes #169. +* Allow nesting of `responds_with` parameter matcher. Closes #166. +* Define `Mocha` module before it's referenced. The test helper defines a class `TestCase` within the `Mocha` module. When +running the tests inside the bundle, the `Mocha` module happens to be defined at this point. However when running the tests outside the bundle, it is not defined and so an exception is raised: `uninitialized constant Mocha (NameError)`. Fixes #163. +* Document lack of thread-safety. Fixes #154. +* Document how to use the build-matrix script. Fixes #160. +* Stubbing non-public method should use same visibility. This will probably break some existing tests that were somehow relying +on the stubbed method being public while the original method was protected or private. Fixes #150. + +### Internal changes +* Use lastest Rubygems in Travis CI builds. +* Run the standard test suite against Ruby 2.1.0 in the build matrix. +* Run integration tests against Ruby 2.0.0 with latest Test::Unit gem in the build matrix. +* Test::Unit is not available in Ruby v1.9.3 standard library, so remove it from the build matrix. +* Force use of Test::Unit runner, etc in relevant integration tests. Prior to this, I don't think we were really testing the +Mocha integration with Test::Unit much, because, although `TestUnitTest` was a subclass of `Test::Unit::TestCase`, the +important test case instances are the temporary ones built by `TestRunner#run_as_test` et al. Prior to this change, these +would only have used Test::Unit where MiniTest was not available *at all* i.e. only in early versions of Ruby and when the +MiniTest gem was not loaded. +* Reset environment variables between build matrix builds. +* Only activate integration with relevant test library for each of the integration tests. +* Include standard build combinations from Travis CI config i.e. builds using standard library versions of test libraries. +* Fix `build-matrix.rb` script. Also use `.travis.yml` to decide what combinations to run. This means we +can now simulate the Travis CI build locally and avoid duplication. Fixes #157. +* Remove Ruby version map from build matrix script. I'm using the `rbenv-aliases` plugin to alias minor versions to the +relevant patch version. + +## 0.14.0 + +* Official support for MiniTest v5. All tests now pass on the continuous integration build. + +## 0.14.0.alpha + +* Add speculative support for Minitest v5. Due to incompatibilities it has not yet been possible to run the Mocha test suite against Minitest v5. However, @zenspider (author of Minitest) provided the patch and he has tested it against Rails v4. Fixes #156. Thanks to @zenspider. +* Documentation updates. + +## 0.13.3 +* Allow `Mocha::ParameterMatchers#includes` to accept multiple items. Thanks to @simao. +* Allow stubbing of *private* `Kernel` methods. Fixes #134. Thanks to @camski for reporting. +* Avoid a warning when `test/unit/version` is required by other libraries in the same project. Fixes #140. Thanks to @tmiller. +* Make auto-activation of Test::Unit integration more resilient. This change is specifically to cope with the nasty re-defining of classes that is done by the `minitest-spec-rails` gem. Fixes #143. Thanks to @tubaxenor for reporting. +* Safer restoration of stubbed method visibility. Fixes #141. Thanks to @tmm1. +* Ensure `Mockery` instance gets reset even if exception raised. Fixes #144. +* Adapt Mocha acceptance tests to cope with changes in output from latest (v4.6.2) of MiniTest. +* Updates to README about Rails compatibility. + +**NOTE: This release inadvertently caused deprecation warnings in some contexts:** +* When used with Rails v3.2.0-v3.2.12, v3.1.0-v3.1.10 & v3.0.0-v3.0.19. + +## 0.13.2 +* Stubbing of methods re-declared with different visibilty. Fixes #109. +* Add `Mock#responds_like_instance_of`. Fixes #119. +* Make `Expectation#inspect` less verbose and more useful. Fixes #122. +* Make unit tests more robust to changes in environment. Fixes #121. +* Update README in an attempt to head Rails-related issues off at the pass. +* Add a Gem Badge to provide a link to Mocha on Rubygems. +* Make documentation example consistent with other examples. + +**NOTE: This release inadvertently caused deprecation warnings in some contexts:** +* When used with Rails v3.2.0-v3.2.12, v3.1.0-v3.1.10 & v3.0.0-v3.0.19. + +## 0.13.1 +* Fix #97 - `Mocha::ParameterMatchers#has_entry` does not work with an Array as the entry's value. Thanks to @ngokli. +* Allow deprecation `:debug` mode to be switched on from `MOCHA_OPTIONS` environment variable. + +**NOTE: This release inadvertently caused deprecation warnings in some contexts:** +* When used with Rails v3.2.0-v3.2.12, v3.1.0-v3.1.10 & v3.0.0-v3.0.19. + +## 0.13.0 +* Major overhaul of MiniTest & Test::Unit integration. Mocha now integrates with later versions of the two test libraries using documented hooks rather than monkey-patching. This should mean that Mocha will integrate with new versions of either library without the need to release a new version of Mocha each time, which was clearly bad and unsustainable. Many thanks to @tenderlove, @zenspider & @kou for their help, suggestions & patience. +* Temporarily deprecated `require 'mocha'`. Use `require 'mocha/setup'` instead. The plan is that eventually `require 'mocha'` will *not* automatically integrate with either of the two test libraries as it does at the moment, and you'll need to explicitly & separately trigger the integration. I think this will provide a lot more flexibility and will hopefully do away with the need for the `require: false` option in the `Gemfile` which has always confused people. +* Deprecated `require 'mocha_standalone'` and `require 'mocha/standalone'`. Use `require 'mocha/api` instead. +* Although these are not part of Mocha's public API, I thought I should mention that the MiniTest and Test::Unit assertion counter classes have been combined into a single class `Mocha::Integration::AssertionCounter`. +* Extracted Mocha::Hooks module from Mocha::API and added documentation for test library authors. +* Improvements to documentation. Much of it has been combined into the README file. +* Fix #101 - Mock#respond_to? doesn't work with a string argument - thanks to @urbanautomaton. +* Fix #105 - Travis link in README - thanks to @cknadler. +* Various improvements to automated testing of integration with test libraries. +* Make deprecation warnings more prominent. + +**NOTE: This release inadvertently caused deprecation warnings in some contexts:** +* When used with Rails v3.2.0-v3.2.12, v3.1.0-v3.1.10 & v3.0.0-v3.0.19. + +## 0.12.7 +* Officially support minitest v4.1.0 (still monkey-patching). + +## 0.12.6 +* Fixes #103. + +## 0.12.5 +* Officially support minitest v3.5.0 (still monkey-patching). + +## 0.12.4 +* Officially support minitest v3.4.0 & test-unit v2.5.2 (still monkey-patching). + +## 0.12.3 +* Revert rename of undocumented internal module since it turns out Rails/ActiveSupport is relying on its existence. + +## 0.12.2 +* Officially support minitest v3.3.0 (still monkey-patching) + +## 0.12.1 +* Deprecation warning (instead of fail fast) if neither Test::Unit nor MiniTest is loaded. Fixes #88. +* Remove deprecated access to `Mocha::Standalone`. +* Remove the deprecated file `stubba.rb`. +* Officially support test-unit v2.5.1 (still monkey-patching). +* Improve the API acceptance test. + +## 0.12.0 +* Fail fast if neither Test::Unit nor MiniTest is loaded. Fixes #40. +* Officially support MiniTest up to v3.2.0 (still monkey-patching). +* Officially support test-unit v2.5.0 (still monkey-patching). +* Do not monkey-patch Test::Unit or MiniTest unless we *know* it's ok. +* Add acceptance tests to demonstrate using a block as a custom parameter matcher. +* Update Travis CI build status image to use the new build under the freerange account. + +## 0.11.4 +* Homepage has moved to http://gofreerange.com/mocha/docs. + +**WARNING: This release inadvertently included a Rails compatibility issue:** +* `TypeError: superclass mismatch for class ExpectationError` raised when using Rails v3.2.13. See #115. + +## 0.11.3 +* Fix for #78 i.e. alias Object#method as Object#_method, not Object#__method__ which already exists as another Ruby method. + +**WARNING: This release inadvertently included a Rails compatibility issue:** +* `TypeError: superclass mismatch for class ExpectationError` raised when using Rails v3.2.13. See #115. + +## 0.11.2 +* Rails has a Request class which defines its own #method method. This broke the new mechanism for stubbing a method. This release includes a slightly modified version of fix #77 provided by @sikachu. See https://github.com/rails/rails/pull/5907 for further info. + +**WARNING: This release inadvertently included a Rails compatibility issue:** +* `TypeError: superclass mismatch for class ExpectationError` raised when using Rails v3.2.13. See #115. + +## 0.11.1 +* In Ruby 1.8.7 methods accepting a block parameter were incorrectly restored without the block parameter after being stubbed. Fix for #76. + +**WARNING: This release inadvertently included a Rails compatibility issue:** +* `TypeError: superclass mismatch for class ExpectationError` raised when using Rails v3.2.13. See #115. + +## 0.11.0 +* Store original method when stubbing rather than using alias_method. This fixes #41, #47, #74 and all tests now pass on both Ruby 1.8.7 and 1.9.3. +* Attempting to stub a method on a frozen object should fail fast. See #68. +* Prevent stubbing a method on nil by default. See #68. +* Generate documentation using YARD instead of Rdoc - removes dependency on Coderay. +* Publish documentation on Github pages instead of Rubyforge - uses rake task written by @tomafro. +* Remove agiledox which has outlived it's usefulness. +* Removed trailing whitespace throughout codebase. +* Add documentation for Mock#unstub. +* Improve documentation for ObjectMethods. +* Provide a way to run multiple tests within a single acceptance test method. + +**WARNING: This release inadvertently included a significant bug - please do not use it!** + +**WARNING: This release inadvertently introduced a Rails compatibility issue:** +* `TypeError: superclass mismatch for class ExpectationError` raised when using Rails v3.2.13. See #115. + +## 0.10.5 +* Fix for issue #66 (hopefully without regressing on issue #63) - Mocha::Mock has Mocha::Mockery as a dependency. Stop trying to pretend otherwise. Thanks to @kennyj for reporting. +* Fix a bunch of warnings in Ruby 1.9. There are still the 6 test failures mentioned in issue #41 which I suspect are due to the introspection gem not being Ruby 1.9-compatible. +* Add links to README for source code & issue tracker. +* Fix for issue #67 - Make the travis-ci badge visible in the README. Thanks to Diego Plentz for pull request. +* Fix for issue #70 - Rename Mock#expectations to Mock#__expectations__ to avoid conflicts. Thanks to Jeremy Stephens for pull request. + +## 0.10.4 +* Fix for issue #65 - expectations not being verified in subsequent tests. +* Fix for issue #63 - require Mocha::Mockery at Mocha::Mock class load time and not on invocation of Mock#method_missing. +* Fix for issue #45 - raise ArgumentError if Mocha::ParameterMatchers#has_entry is given +Hash with wrong number of entries. +* Make global variable name more obscure to avoid clashes with other libraries. +* Move travis-ci-related gemfiles into their own directory. + +## 0.10.3 +* Fix for issue #57. Gem::Requirement#=~ was only added in rubygems v1.8.0, but Object#=~ means the result of various monkey-patching checks is always false/nil for earlier versions of rubygems. However, the method it aliases #satisfied_by? has existed since Gem::Dependency was extracted from Gem::Version in rubygems v0.9.4.4, so it's much safer to use that. Thanks to fguillen for reporting and helping with diagnosis. + +**WARNING: This release inadvertently included a significant bug - please do not use it!** + +## 0.10.2 +* Merge pull request #53. Unstubbing a method should not remove expectations for other stubbed methods. Fixes #52. Thanks to saikat. + +**WARNING: This release inadvertently included a significant bug - please do not use it!** + +## 0.10.1 +* Merge pull request #51. Use Gem::Requirement & Gem::Version for version comparison. Fixes issue #50. Thanks to meineerde. +* Fixed typo in rdoc for Mocha::ObjectMethods. +* Improve README as suggested in issue #46. Explain that Mocha must be loaded after test libraries and how to achieve this using Bundler. +* Merge pull request #43 - nobody expects the spanish inquisition! Thanks to cairo140. +* Fix for issue #39 - improve documentation for Expectation#multiple_yields. +* Fix for issue #38 where a subtle change in test-unit v2.3.0 had been missed - only visible in verbose mode. +* Support for MiniTest up to v2.6.2 has been verified. +* Add explicit development dependency on coderay for generating syntax-highlighted code examples. + +## 0.10.0 +* Add Expectation#throws to allow a stubbed method to use Kernel#throw. +* Updates for versions of Test::Unit up to and including v2.3.3 (including patch by Jens Fahnenbruck). +* Updates for versions of MiniTest up to and including v2.5.1. +* Since the singleton method added by Mocha masks the underlying instance method, there's no need to move it out the way and then back again. This fixes Github issue #20, because the original method is left unchanged - https://github.com/floehopper/mocha/issues/20 (thanks to Nick Lewis). +* Handle stubbing of a singleton method, leaving the original method unchanged after the test. +* When stubbing an instance method that was originally defined as a singleton method, the original method should still exist after the test. +* Fixed mis-print in Mocha::ObjectMethods#unstub documentation (patch by Gleb Pomykalov). +* Improved test coverage around stubbing of methods defined in different ways - this makes use of the newly extracted introspection gem (although this means some tests are now failing in Ruby v1.9.2). +* Added configuration for Travis continuous integration. +* Make the gemspec the canonical reference and stop generating it from the Rakefile. +* Use the built-in Bundler rake tasks for packaging the gem. +* Use the "release" rake task provided by Bundler instead of using the Rake::XForge::Release functionality. +* Extract Object#__metaclass__ into a new metaclass gem. +* Run rake tasks without `bundle exec`. +* Avoid deprecation warning for rdoc rake task. +* Remove the `use_test_unit_gem` MOCHA_OPTION which hasn't worked since we switched to bundler - we can now run the tests specifying a different Gemfile instead. +* Use multiple Gemfiles seems to run Travis CI builds against multiple version of test-unit & minitest. + +## 0.9.12 +* Make Mocha's tests pass under Ruby 1.9.2 i.e. using MiniTest. One of the main issues was that we were not parsing stacktraces on MiniTest errors comprehensively enough. +* Avoid 'circular require considered harmful' warning when running Mocha's tests in Ruby 1.9.2 +* Make performance tests work on Ruby 1.9.2 i.e. using MiniTest. +* Declare rake as a *development* dependency with newer versions of Rubygems since it's only needed to carry out developer-related tasks. + +## 0.9.11 +* Added explicit support for minitest v1.5.0 to v2.0.2. +* Make testable by rubygems-test. +* Update links to my blog and make other links consistent. +* Added a URI parameter matcher that ignores the order of query parameters so that tests can be independent of undefined hash ordering (patch by Paul Battley). +* Include unexpected invocation in failure message and change the language slightly to make the failure message less confusing. See http://floehopper.lighthouseapp.com/projects/22289/tickets/52. +* No need to create regular expression every time the BacktraceFilter#filtered method is called. See http://floehopper.lighthouseapp.com/projects/22289-mocha/tickets/66. + +## 0.9.10 +* Added Mocha::ObjectMethods#unstub method - https://github.com/floehopper/mocha/issues#issue/6 +* Inherit Mocha::ExpectationError from Exception instead of StandardError to reduce the chances of a test passing by accident - thanks to James Sanders (jsanders) - https://github.com/floehopper/mocha/issues#issue/15 +* Fixed bug - GitHub README page to link correctly to code examples - https://github.com/floehopper/mocha/issues/closed#issue/11 +* Fixed bug - PASSTHROUGH_EXCEPTIONS are defined on MiniTest::Unit::TestCase not in Mocha - thanks to Brian Troutwine (blt) - https://github.com/floehopper/mocha/issues/closed#issue/14 + +## 0.9.9 +* Avoid loading bits of the test-unit gem by accident. This is an attempt at a fix for the problem that James Adam reported [1]. By using 'load' instead of 'require' to detect the version of Test::Unit, we can avoid rubygems trying to load bits of the test-unit gem when it's not wanted. [1] http://floehopper.lighthouseapp.com/projects/22289-mocha/tickets/50#ticket-50-13 +* Fix exception when running rake without test-unit gem. When test-unit gem >=v2.0.0 was installed but the "use_test_unit_gem" MOCHA_OPTIONS was not specified, a "comparison of Fixnum with Hash failed" exception was being raised when running the performance tests. This was because bits of the test-unit gem were being loaded accidentally and a Hash was being incorrectly supplied to the TestRunner.run method. +* Explicitly require rubygems for running tests via rake using test-unit gem. +* Handle newer versions of test-unit gem (v2.0.2 to v2.0.9) +* Handle newer versions of minitest gem (v1.4.0 to v1.6.0) +* Added warnings about monkey-patching test-unit and minitest to aid debugging. These are enabled by including "debug" in the MOCHA_OPTIONS environment variable. This is now a comma-separated list, so that we can specify multiple options e.g. MOCHA_OPTIONS=debug,use_test_unit_gem +* Eloy Duran (alloy) made the unit tests run on 1.9.2dev r25249. +* Eloy Duran (alloy) also improved some MiniTest TestResult code I'd written and got the acceptance tests running on Ruby 1.9 HEAD. There are still 4 failures because for some reason the backtrace line numbers are off by one. And the minitest_test test case does not run when the whole suite is run with MiniTest. These issues still need investigation. +* Fixed some acceptance tests to run in Ruby 1.9.2 - it's no longer possible to subvert the protection of a method by calling it via Object#send. +* Fixed "test:performance" rake task so it runs in Ruby 1.9.2. +* Fix test incorrectly failing under Rubinius 1.0. This test imposed too many constraints. It appears that Object#inspect legitimately calls Object#object_id in Rubinius. But we're only interested in what 'id' methods Mocha::ObjectMethods#mocha_inspect calls. By stubbing Object#inspect we can relax the constraints imposed by the test. +* Luke Redpath (lukeredpath) added new shorthand "any" and "all" composite parameter matchers using "&" and "|". This provides an alternative syntax for expecting any or all matchers to pass, e.g. foo.expects(:bar).with(equals(1) | equals(2)). +* Improved documentation for Expectation#raises. A number of people have suggested an extension to the API to cope with custom exceptions that have extra constructor parameters. However, since the arguments supplied to Expectation#raises are just passed on to Kernel#raise, it's possible to pass in an instance of an exception. Thus no change to the API is required, but it does seem worthwhile pointing this out in the docs. +* Corrected RDoc example for Expectation#never thanks to Red David (reddavis). +* Improved RDoc including a change suggested by Rohit Arondekar (rohit). +* Updated gemspec as requested by Sam Woodard (shwoodard). + +## 0.9.8 +* Fixed bug "NameError raised when using Mocha as a Rails plug-in" - http://floehopper.lighthouseapp.com/projects/22289/tickets/53. Since 0.9.6 the Rails plugin has been broken. See bug report for details. You will need to explicitly load Mocha *after* the test framework has been loaded, e.g. by adding "require 'mocha'" at the bottom of test/test_helper.rb. +* Make Mocha::ParameterMatchers#regexp_matches, #includes, #has_value, #has_key more robust. Thanks to Sander Hartlage. +* Allow passing a block to Mocha::Configuration methods to only change configuration for the duration of the block. Thanks to Dan Manges. +* Fixed bug "doc generation fails in 0.9.7 gem" - http://floehopper.lighthouseapp.com/projects/22289/tickets/51. +* Remove rdoc template incorporating google analytics from source control. The file just needs to exist locally and be ignored by source control. This should stop the warning showing up on e.g. RunCodeRun build results. + +## 0.9.7 +* Although I had provided a deprecation warning for people using Mocha::Standalone, I had assumed people wouldn't be explicitly loading the mocha/standalone.rb file. It turns out this assumption was incorrect at least in the case of Rspec. This is now fixed. + +## 0.9.6 +* Version 2.0.1 of the test-unit gem introduced a private 'run_test' method on TestCase which clashed with the public TestRunner#run_test method. So this latter method has been renamed to 'run_as_test'. +* Stop requiring rubygems - this should be an environmental choice for the user. http://gist.github.com/54177 - describes why requiring rubygems in your library code is a bad idea. +* It seems like overkill to vendorize coderay and meta_project when they're only needed to generate the examples for documentation and for publishing files on RubyForge. So I'm removing them and installing them locally as gems when I need them. +* Added support for 'test-unit' gem (version >= 2.0). Note that as with other versions of Test::Unit I'm completely replacing the TestCase#run method. Unfortunately in version 2.0.0 this method differs slightly from the same method in version 2.0.1 & 2.0.2, so we have to provide different implementations to ensure that the internal working of Test::Unit are not compromised by Mocha. Note also that unless the 'test-unit' gem is loaded, requiring 'test/unit' leads to a mixture of stdlib and gem classes being loaded causing errors. To avoid a dependency on rubygems, the gem is loaded only if MOCHA_OPTIONS is set to 'use_test_unit_gem' - this option is only intended for use in running Mocha's own tests. It might be worthwhile to create a shim gem like minitest_tu_shim to allow the test-unit gem to completely replace the stdlib, but that's a job for another day. The changes in the Rakefile are to make the default task run with the 'test-unit' gem (version >= 2.0). +* Renamed Mocha::Standalone to Mocha::API to better reflect its purpose. Added a deprecation warning for those who are referencing Mocha::Standalone. +* Fix exception raised by HasEntry#matches? if first param is not a Hash (thanks to Taylor Barstow). +* Ken Collins reported [1] that Mocha is always loading MiniTest if it is available and loading it causes some Rails/ActionPack tests to break. I've removed the loading of MiniTest, but this now means the user has to ensure that if they want to use MiniTest in conjunction with Mocha, he must load MiniTest before loading Mocha. [1] http://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/2060 +* Implemented Bacon integration (thanks to Ubiratan Pires Alberton), but this was then removed after deciding only to maintain integration with Test::Unit and MiniTest which are both Ruby standard libraries. See mailing list for details. +* Don't monkey-patch MiniTest if it's already been monkey-patched by Mocha. +* Fixed bug: MiniTest integration was counting ExpectationErrors as errors not failures. http://floehopper.lighthouseapp.com/projects/22289-mocha/tickets/41. +* Fixed bug: Some Bacon tests were failing in Ruby 1.9.1. http://floehopper.lighthouseapp.com/projects/22289-mocha/tickets/43. +* Chad Humphries pointed out that in Ruby 1.9.1, if you are not using Test::Unit or MiniTest, Mocha will attempt to load and monkey-patch Test::Unit. Mocha will now only monkey-patch Test::Unit and/or MiniTest if they have already been loaded. MiniTest tests will now run in both Ruby 1.8.6 (with MiniTest gem) and in Ruby 1.9.1 (with MiniTest std lib). See Ligthouse ticket - http://floehopper.lighthouseapp.com/projects/22289/tickets/49. +* Made Mocha compatible with minitest 1.4.0 and above (thanks to Denis Defreyne). + +## 0.9.5 +* Fixed Lighthouse bug #32 - stub_everything should mean mock responds to anything. +* Added Expectation#twice to improve readability. Thanks to pull request from Celestino Gomes. +* In Ruby 1.9.1, requiring 'test/unit' loads a thin wrapper around MiniTest and Test::Unit::TestCase ends up inheriting from MiniTest::Unit::TestCase. So we need to avoid including the Mocha modules more than once to avoid nasty consequences. Thanks to Matthias Hennemeyer for help with this. +* Ruby 1.9 includes rake, but not rake/contrib. For the moment I've moved the sshpublisher require into the only rake task that needs it, so that I can at least run the tests in Ruby 1.9. It looks like I will need to build a rake/contrib gem or similar to get this working properly - http://intertwingly.net/blog/2008/01/07/Rake-Contrib-for-1-9 + +## 0.9.4 +* Added mocha.gemspec file generated with Chad Woolley's new rake task, so that a floehopper-mocha gem will get built on GitHub. +* Add rake task to update mocha.gemspec with unique version, which will cause gem to be auto-built on github +* As Tobias Crawley correctly pointed out in feature request #23055 "stubs(with_hash) not working with existing object" [1], following the principle of least surprise, it should be possible to call ObjectMethods#expects & ObjectMethods#stubs with a Hash of method_names vs return_values like you can with Mock#expects & Mock#stubs. I've also updated & improved the docs to reflect the changes. [1] http://rubyforge.org/tracker/index.php?func=detail&aid=23055&group_id=1917&atid=7480 +* Removed deprecated gem autorequire. + +## 0.9.3 +* Added support for MiniTest thanks to Jeff Smick. +* Fixed a possible bug with some of the non-default Configuration options relating to the argument to Object#respond_to? +* As per Jay Fields recommendations [1] and with further impetus from a talk at Ruby Manor, any methods added to core classes are now added by including a module. This means that Mocha is a better citizen of the Ruby world and it's behaviour is more easily extended. [1] http://blog.jayfields.com/2008/07/ruby-underuse-of-modules.html & http://blog.jayfields.com/2008/07/ruby-redefine-method-behavior.html +* Removed deprecated gem autorequire. + +## 0.9.2 +* Improved documentation to address [#22530] 'Mock methods with multiple return values not possible?' +* respond_with parameter matcher was not available in tests. +* Patch [#22630] Fix for a bug in running Rails tests with Ruby 1.8.7. Array#flatten was being called which in turn was checking whether each element responded to #to_ary. This check was using the two parameter version of #respond_to?, but Mock was only defining a one parameter version. + +## 0.9.1 + +* Fixed bug #21465 - expects & stubs should support method names as strings (as well as symbols) or fail fast. Convert all expectation method names to a symbol in case they were supplied as a string. +* By removing Mock#unexpected_method_called we reduce the number of methods vulnerable to the problem that surfaced in bug #21563. +* Fix bug #21563 - stubbing 'verified?' method is unsafe. Instance method names on the Mock class should be more obscure. +* Performance improvement. StubbaExampleTest goes twice as fast on my local machine. +* Added primitive performance test to default rake task. +* Fix format of case statements which don't work in Ruby 1.9 and make others consistent. +* There is no point in running (potentially expensive) checks if configuration is set to allow such checks to fail. This is a relatively quick fix in response to Chris McGrath's performance problems. +* Fix for bug #21161 - 'uninitialized constant Deprecation in stubba.rb'. +* It's more readable to talk about 'once' and 'twice' rather than '1 time' and '2 times'. +* Fix bug #20883 - never should raise when called to prevent follow up errors. Fail fast when there are no matching invokable expectations and handle the stub_everything case sensibly. This might not be entirely backwards compatible, but I think the benefits outweigh the risks. The most likely change is that a test that was already failing will now fail faster, which doesn't seem so awful. + +## 0.9.0 + +* Configurable warnings or errors + * when a method on a non-public method is stubbed + * when a method on a non-existent method is stubbed + * when a method on a non-mock object is stubbed + * when a method is stubbed unnecessarily (i.e. the stubbed method is not called during the test) + +* Improved error messages + * User-friendly list of unsatisfied expectations, satisfied expectations and state machines. + * Improved readability of cardinality description. + * Display sensible failure message for any_instance expectations e.g. "#.bar - expected calls: 1, actual calls: 0" + +* Parameter matchers + * New to this release + * optionally (allows matching of optional parameters if available) + * yaml_equivalent (allows matching of YAML that represents the specified object) + * responds_with (tests the quack not the duck) + * Nesting of parameter matchers is now supported. + +* Optional block passed into mock initializer is evaluated in the context of the new mock instance and can be used as a shortcut to set up expectations. + +* Added JMock-style sequences for constraining the order of expected invocations. See Standalone#sequence and Expectation#in_sequence. + +* Added JMock-style states for constraining the order of expected invocations. See Standalone#states, Expectation#then, Expectation#when and StateMachine. + +* Compatibility with versions of Ruby + * Compatibility with Ruby v1.9. All test errors and warnings fixed. + * Nasty fix so that TestCaseAdaptor works consistently with earlier versions of Test::Unit as well as more recent versions. + * Added platform to gem specification to avoid bug in rubygems 0.9.5 - see http://www.dcmanges.com/blog/rubygems-0-9-5-platform-bug and http://rubygems.org/read/chapter/20#platform. + * Make ExpectationRaiser deal with subclasses of Interrupt which seem to need a message supplied in the raise statement in Ruby 1.8.6 (but not 1.8.4 or 1.9). Not sure this is really Mocha's responsibility. + +* Added deprecation warning in stubba.rb which is no longer needed and will be removed. + +* Supply positioning information to evals to improve any error messages. See http://ola-bini.blogspot.com/2008/01/ruby-antipattern-using-eval-without.html + +* Bug fixes + * 18914 in revision 296 - http://rubyforge.org/tracker/index.php?func=detail&aid=18914&group_id=1917&atid=7477 + * 18917 in revision 295 - http://rubyforge.org/tracker/index.php?func=detail&aid=18917&group_id=1917&atid=7477 + * 18336 in revision 287 - http://rubyforge.org/tracker/index.php?func=detail&aid=18336&group_id=1917&atid=7477 + * 17835 in revision 255 - http://rubyforge.org/tracker/index.php?func=detail&aid=17835&group_id=1917&atid=7477 + * 17412 in revision 242 - http://rubyforge.org/tracker/index.php?func=detail&aid=17412&group_id=1917&atid=7477 + * 15977 in revision 198 - http://rubyforge.org/tracker/index.php?func=detail&aid=15977&group_id=1917&atid=7477 + * 11885 in revision 156 - http://rubyforge.org/tracker/index.php?func=detail&aid=11885&group_id=1917&atid=7477 + +## 0.5.5 + +- Renamed Matches parameter matcher to RegexpMatches for clarity. +- Added noframes tag to rdoc index to assist Google. + +## 0.5.4 + +- Added matches parameter matcher for matching regular expressions. + +## 0.5.3 + +- Attempt to fix packaging problems by switching to newer version (1.15.1) of gnutar and setting COPY_EXTENDED_ATTRIBUTES_DISABLE environment variable. +- Removed unused ExpectationSequenceError exception. +- Added instance_of and kind_of parameter matchers. +- Added Google Webmaster meta tag to rdoc template header. +- Put Google Webmaster meta tag in the right header i.e. the one for the index page. + +## 0.5.2 + +- Fix bug 11885 - "never doesn't work with stub_everything" submitted by Alexander Lang. In fixing this bug, also fixed undiscoverd bug where expected & actual invocation counts were being incorrectly reported which seems to have been introduced when fixes were added for invocation dispatch (see MockedMethodDispatchAcceptanceTest). +- Previously when an expectation did not allow more invocations, it was treated as not matching. Now we prefer matching expectations which allow more invocations, but still match expectations which cannot allow more invocations. I think this may be overcomplicating things, but let's see how it goes. + +## 0.5.1 + +- Fixed bug #11583 "Mocha 0.5.0 throwing unexpected warnings". Also switched on ruby warning for all rake test tasks. Fixed majority of warnings, but some left to fix. + +## 0.5.0 + +- Parameter Matchers - I've added a few Hamcrest-style parameter matchers which are designed to be used inside Expectation#with. The following matchers are currently available: anything(), includes(), has_key(), has_value(), has_entry(), all_of() & any_of(). More to follow soon. The idea is eventually to get rid of the nasty parameter_block option on Expectation#with. + + object = mock() + object.expects(:method).with(has_key('key_1')) + object.method('key_1' => 1, 'key_2' => 2) + # no verification error raised + + object = mock() + object.expects(:method).with(has_key('key_1')) + object.method('key_2' => 2) + # verification error raised, because method was not called with Hash containing key: 'key_1' + +- Values Returned and Exceptions Raised on Consecutive Invocations - Allow multiple calls to Expectation#returns and Expectation#raises to build up a sequence of responses to invocations on the mock. Added syntactic sugar method Expectation#then to allow more readable expectations. + + object = mock() + object.stubs(:method).returns(1, 2).then.raises(Exception).then.returns(4) + object.method # => 1 + object.method # => 2 + object.method # => raises exception of class Exception + object.method # => 4 + +- Yields on Consecutive Invocations - Allow multiple calls to yields on single expectation to allow yield parameters to be specified for consecutive invocations. + + object = mock() + object.stubs(:method).yields(1, 2).then.yields(3) + object.method { |*values| p values } # => [1, 2] + object.method { |*values| p values } # => [3] + +- Multiple Yields on Single Invocation - Added Expectation#multiple_yields to allow a mocked or stubbed method to yield multiple times for a single invocation. + + object = mock() + object.stubs(:method).multiple_yields([1, 2], [3]) + object.method { |*values| p values } # => [1, 2] # => [3] + +- Invocation Dispatch - Expectations were already being matched in reverse order i.e. the most recently defined one was being found first. This is still the case, but we now stop matching an expectation when its maximum number of expected invocations is reached. c.f. JMock v1. A stub will never stop matching by default. Hopefully this means we can soon get rid of the need to pass a Proc to Expectation#returns. + + object = mock() + object.stubs(:method).returns(2) + object.expects(:method).once.returns(1) + object.method # => 1 + object.method # => 2 + object.method # => 2 + # no verification error raised + + # The following should still work... + + Time.stubs(:now).returns(Time.parse('Mon Jan 01 00:00:00 UTC 2007')) + Time.now # => Mon Jan 01 00:00:00 UTC 2007 + Time.stubs(:now).returns(Time.parse('Thu Feb 01 00:00:00 UTC 2007')) + Time.now # => Thu Feb 01 00:00:00 UTC 2007 + +- Deprecate passing an instance of Proc to Expectation#returns. +- Explicitly include all Rakefile dependencies in project. +- Fixed old Stubba example. +- Fix so that it is possible for a stubbed method to raise an Interrupt exception without a message in Ruby 1.8.6 +- Added responds_like and quacks_like. +- Capture standard object methods before Mocha adds any. +- Added Expectation#once method to make interface less surprising. +- Use Rake::TestTask to run tests. Created three separate tasks to run unit, integration & acceptance tests. Split inspect_test into one file per TestCase. Deleted superfluous all_tests file. +- Fiddled with mocha_inspect and tests to give more sensible results on x86 platform. +- Fixed bug #7834 "infinite_range.rb makes incorrect assumption about to_f" logged by James Moore. + +## 0.4.0 + +- Allow naming of mocks (patch from Chris Roos). +- Specify multiple return values for consecutive calls. +- Improved consistency of expectation error messages. +- Allow mocking of Object instance methods e.g. kind_of?, type. +- Provide aliased versions of #expects and #stubs to allow mocking of these methods. +- Added at_least, at_most, at_most_once methods to expectation. +- Allow expects and stubs to take a hash of method and return values. +- Eliminate warning: "instance variable @yield not initialized" (patch from Xavier Shay). +- Restore instance methods on partial mocks (patch from Chris Roos). +- Allow stubbing of a method with non-word characters in its name (patch from Paul Battley). +- Removed coupling to Test::Unit. +- Allow specified exception instance to be raised (patch from Chris Roos). +- Make mock object_id appear in hex like normal Ruby inspect (patch from Paul Battley). +- Fix path to object.rb in rdoc rake task (patch from Tomas Pospisek). +- Reverse order in which expectations are matched, so that last expectation is matched first. This allows e.g. a call to #stubs to be effectively overridden by a call to #expects (patch from Tobias Lutke). +- Stubba & SmartTestCase modules incorporated into Mocha module so only need to require 'mocha' - no longer need to require 'stubba'. +- AutoMocha removed. + +## 0.3.3 + +- Quick bug fix to restore instance methods on partial mocks (for Kevin Clark). + +## 0.3.2 + +- Examples added. + +## 0.3.1 + +- Dual licensing with MIT license added. + +## 0.3.0 + +* Rails plugin. +* Auto-verify for expectations on concrete classes. +* Include each expectation verification in the test result assertion count. +* Filter out noise from assertion backtraces. +* Point assertion backtrace to line where failing expectation was created. +* New yields method for expectations. +* Create stubs which stub all method calls. +* Mocks now respond_to? expected methods. + +## 0.2.1 + +* Rename MochaAcceptanceTest::Rover#move method to avoid conflict with Rake (in Ruby 1.8.4 only?) + +## 0.2.0 + +* Small change to SetupAndTeardown#teardown_stubs suggested by Luke Redpath (http://www.lukeredpath.co.uk) to allow use of Stubba with RSpec (http://rspec.rubyforge.org). +* Reorganized directory structure and extracted addition of setup and teardown methods into SmartTestCase mini-library. +* Addition of auto-verify for Mocha (but not Stubba). This means there is more significance in the choice of expects or stubs in that any expects on a mock will automatically get verified. + +So instead of... + + wotsit = Mocha.new + wotsit.expects(:thingummy).with(5).returns(10) + doobrey = Doobrey.new(wotsit) + doobrey.hoojamaflip + wotsit.verify + +you need to do... + + wotsit = mock() + wotsit.expects(:thingummy).with(5).returns(10) + doobrey = Doobrey.new(wotsit) + doobrey.hoojamaflip + # no need to verify + +There are also shortcuts as follows... + +instead of... + + wotsit = Mocha.new + wotsit.expects(:thingummy).returns(10) + wotsit.expects(:summat).returns(25) + +you can have... + + wotsit = mock(:thingummy => 5, :summat => 25) + +and instead of... + + wotsit = Mocha.new + wotsit.stubs(:thingummy).returns(10) + wotsit.stubs(:summat).returns(25) + +you can have... + + wotsit = stub(:thingummy => 5, :summat => 25) + +## 0.1.2 + +* Minor tweaks + +## 0.1.1 + +* Initial release. diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/Rakefile b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/Rakefile new file mode 100644 index 0000000..6d589fa --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/Rakefile @@ -0,0 +1,171 @@ +require 'bundler' +namespace 'rubygems' do + Bundler::GemHelper.install_tasks +end +require 'bundler/setup' + +require 'rake/testtask' +begin + # Only available with `gemfiles/Gemfile.rubocop` + require 'rubocop/rake_task' +rescue LoadError + warn "Unable to load 'rubocop/rake_task', but continuing anyway" if $DEBUG +end + +desc 'Run all linters and tests' +task 'default' => ['lint', 'test', 'test:performance'] + +desc 'Run tests' +task 'test' do + if (test_library = ENV['MOCHA_RUN_INTEGRATION_TESTS']) + Rake::Task["test:integration:#{test_library}"].invoke + else + Rake::Task['test:units'].invoke + Rake::Task['test:acceptance'].invoke + end +end + +namespace 'test' do + desc 'Run unit tests' + Rake::TestTask.new('units') do |t| + t.libs << 'test' + t.test_files = FileList['test/unit/**/*_test.rb'] + t.verbose = true + t.warning = true + end + + desc 'Run acceptance tests' + Rake::TestTask.new('acceptance') do |t| + t.libs << 'test' + t.test_files = FileList['test/acceptance/*_test.rb'] + t.verbose = true + t.warning = true + end + + namespace 'integration' do + desc 'Run Minitest integration tests (intended to be run in its own process)' + Rake::TestTask.new('minitest') do |t| + t.libs << 'test' + t.test_files = FileList['test/integration/minitest_test.rb'] + t.verbose = true + t.warning = true + end + + desc 'Run Test::Unit integration tests (intended to be run in its own process)' + Rake::TestTask.new('test-unit') do |t| + t.libs << 'test' + t.test_files = FileList['test/integration/test_unit_test.rb'] + t.verbose = true + t.warning = true + end + end + + # require 'rcov/rcovtask' + # Rcov::RcovTask.new('coverage') do |t| + # t.libs << 'test' + # t.test_files = unit_tests + acceptance_tests + # t.verbose = true + # t.warning = true + # t.rcov_opts << '--sort coverage' + # t.rcov_opts << '--xref' + # end + + desc 'Run performance tests' + task 'performance' do + require File.join(File.dirname(__FILE__), 'test', 'acceptance', 'stubba_example_test') + require File.join(File.dirname(__FILE__), 'test', 'acceptance', 'mocha_example_test') + iterations = 1000 + puts "\nBenchmarking with #{iterations} iterations..." + [MochaExampleTest, StubbaExampleTest].each do |test_case| + puts "#{test_case}: #{benchmark_test_case(test_case, iterations)} seconds." + end + end +end + +desc 'Run linters' +task 'lint' do + if defined?(RuboCop::RakeTask) + RuboCop::RakeTask.new + Rake::Task['rubocop'].invoke + else + puts 'RuboCop not available - skipping linting' + end +end + +def benchmark_test_case(klass, iterations) + require 'benchmark' + require 'mocha/detection/minitest' + + if defined?(Minitest) + minitest_version = Gem::Version.new(Mocha::Detection::Minitest.version) + if Gem::Requirement.new('>= 5.0.0').satisfied_by?(minitest_version) + Minitest.seed = 1 + result = Benchmark.realtime { iterations.times { |_i| klass.run(Minitest::CompositeReporter.new) } } + Minitest::Runnable.runnables.delete(klass) + result + else + Minitest::Unit.output = StringIO.new + Benchmark.realtime { iterations.times { |_i| Minitest::Unit.new.run([klass]) } } + end + else + load 'test/unit/ui/console/testrunner.rb' unless defined?(Test::Unit::UI::Console::TestRunner) + unless @silent_option + begin + load 'test/unit/ui/console/outputlevel.rb' unless defined?(Test::Unit::UI::Console::OutputLevel::SILENT) + @silent_option = { output_level: Test::Unit::UI::Console::OutputLevel::SILENT } + rescue LoadError + @silent_option = Test::Unit::UI::SILENT + end + end + Benchmark.realtime { iterations.times { Test::Unit::UI::Console::TestRunner.run(klass, @silent_option) } } + end +end +if ENV['MOCHA_GENERATE_DOCS'] + require 'yard' + + namespace :docs do + desc 'Remove generated documentation' + task :clobber do + `rm -rf ./docs` + end + + desc 'Generate documentation' + YARD::Rake::YardocTask.new(:generate) do |task| + task.options = ['--title', "Mocha #{Mocha::VERSION}", '--fail-on-warning'] + end + + desc 'Ensure custom domain remains in place for docs on GitHub Pages' + task :ensure_cname do + `git checkout docs/CNAME` + end + + desc 'Ensure custom JavaScript files remain in place for docs on GitHub Pages' + task :ensure_js do + `git checkout docs/js/app.js` + `git checkout docs/js/jquery.js` + end + + desc 'Check documentation coverage' + task :coverage do + stats_output = `yard stats --list-undoc` + puts stats_output + + match = stats_output.match(/(?\d+\.\d+)% documented/); + abort 'Error: Could not determine documentation coverage.' unless match + + coverage_percentage = match[:coverage_percentage].to_f + minimum_percentage = 100.0 + + if coverage_percentage < minimum_percentage + abort "Documentation coverage is #{coverage_percentage}%, which is below the required #{minimum_percentage}%." + else + puts "Documentation coverage is #{coverage_percentage}%, which is at or above the required #{minimum_percentage}%." + end + end + end + + desc 'Prepare documentation for publication on GitHub Pages' + task 'docs' => %w[docs:clobber docs:generate docs:ensure_cname docs:ensure_js] +end + +task 'release' => ['default', 'rubygems:release'] diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/gemfiles/Gemfile.minitest.latest b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/gemfiles/Gemfile.minitest.latest new file mode 100644 index 0000000..84138e4 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/gemfiles/Gemfile.minitest.latest @@ -0,0 +1,8 @@ +source 'https://rubygems.org' + +gemspec path: '../' + +group :development do + gem 'rake' + gem 'minitest' +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/gemfiles/Gemfile.rubocop b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/gemfiles/Gemfile.rubocop new file mode 100644 index 0000000..5bb691f --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/gemfiles/Gemfile.rubocop @@ -0,0 +1,9 @@ +source 'https://rubygems.org' + +gemspec path: '../' + +group :development do + gem 'rake' + gem 'rubocop' + gem 'rubocop-rake' +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/gemfiles/Gemfile.test-unit.latest b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/gemfiles/Gemfile.test-unit.latest new file mode 100644 index 0000000..07f122e --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/gemfiles/Gemfile.test-unit.latest @@ -0,0 +1,8 @@ +source 'https://rubygems.org' + +gemspec path: '../' + +group :development do + gem 'rake' + gem 'test-unit' +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha.rb new file mode 100644 index 0000000..0cede22 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +require 'mocha/version' + +# Mocha's top level namespace, which also provides the ability to {.configure configure} Mocha's behavior. +# +# Methods in the {API} are directly available in +Test::Unit::TestCase+, +Minitest::Unit::TestCase+. +# +# The mock creation methods are {API#mock mock}, {API#stub stub} and {API#stub_everything stub_everything}, all of which return a {Mock} +# +# A {Mock} {Mock#expects expects} or {Mock#stubs stubs} a method, which sets up (returns) an {Expectation}. +# +# An {Expectation} can be further qualified through its {Expectation fluent interface}. +# +# {ParameterMatchers} for {Expectation#with} restrict the parameter values which will match the {Expectation}. +# +# Adapters in {Integration} provide built-in support for +Minitest+ and +Test::Unit+. +# +# Integration {Hooks} enable support for other test frameworks. +module Mocha +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/any_instance_method.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/any_instance_method.rb new file mode 100644 index 0000000..823c439 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/any_instance_method.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +require 'mocha/stubbed_method' + +module Mocha + class AnyInstanceMethod < StubbedMethod + private + + def stubbee + stubba_object.any_instance + end + + def stubbee_method(method_name) + stubba_object.instance_method(method_name) + end + + def original_method_owner + stubba_object + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/any_instance_receiver.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/any_instance_receiver.rb new file mode 100644 index 0000000..8095c30 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/any_instance_receiver.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module Mocha + class AnyInstanceReceiver + def initialize(klass) + @klass = klass + end + + def mocks + klass = @klass + mocks = [] + while klass + mocha = klass.any_instance.mocha(instantiate: false) + mocks << mocha if mocha + klass = klass.superclass + end + mocks + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/api.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/api.rb new file mode 100644 index 0000000..2e6fd73 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/api.rb @@ -0,0 +1,213 @@ +# frozen_string_literal: true + +require 'mocha/ruby_version' +require 'mocha/parameter_matchers' +require 'mocha/hooks' +require 'mocha/mockery' +require 'mocha/sequence' +require 'mocha/object_methods' +require 'mocha/class_methods' + +module Mocha + # Methods added to +Test::Unit::TestCase+, +Minitest::Unit::TestCase+ or equivalent. + # The mock creation methods are {#mock}, {#stub} and {#stub_everything}, all of which return a #{Mock} + # which can be further modified by {Mock#responds_like} and {Mock#responds_like_instance_of} methods, + # both of which return a {Mock}, too, and can therefore, be chained to the original creation methods. + # + # {Mock#responds_like} and {Mock#responds_like_instance_of} force the mock to indicate what it is + # supposed to be mocking, thus making it a safer verifying mock. They check that the underlying +responder+ + # will actually respond to the methods being stubbed, throwing a +NoMethodError+ upon invocation otherwise. + # + # @example Verifying mock using {Mock#responds_like_instance_of} + # class Sheep + # def initialize + # raise "some awkward code we don't want to call" + # end + # def chew(grass); end + # end + # + # sheep = mock('sheep').responds_like_instance_of(Sheep) + # sheep.expects(:chew) + # sheep.expects(:foo) + # sheep.respond_to?(:chew) # => true + # sheep.respond_to?(:foo) # => false + # sheep.chew + # sheep.foo # => raises NoMethodError exception + module API + include ParameterMatchers::Methods + include Hooks + + # @private + def self.included(_mod) + Object.include Mocha::ObjectMethods + Class.include Mocha::ClassMethods + end + + # @private + def self.extended(mod) + included(mod) + end + + # Builds a new mock object + # + # @return [Mock] a new mock object + # + # @overload def mock(name) + # @param [String, Symbol] name identifies mock object in error messages. + # @overload def mock(expected_methods_vs_return_values = {}) + # @param [Hash] expected_methods_vs_return_values expected method name symbols as keys and corresponding return values as values - these expectations are setup as if {Mock#expects} were called multiple times. + # @overload def mock(name, expected_methods_vs_return_values = {}) + # @param [String, Symbol] name identifies mock object in error messages. + # @param [Hash] expected_methods_vs_return_values expected method name symbols as keys and corresponding return values as values - these expectations are setup as if {Mock#expects} were called multiple times. + # + # @example Using expected_methods_vs_return_values Hash to setup expectations. + # def test_motor_starts_and_stops + # motor = mock('motor', start: true, stop: true) + # assert motor.start + # assert motor.stop + # # an error will be raised unless both Motor#start and Motor#stop have been called + # end + # + def mock(*arguments) + name = arguments.shift.to_s if arguments.first.is_a?(String) || arguments.first.is_a?(Symbol) + expectations = arguments.shift || {} + mock = name ? Mockery.instance.named_mock(name) : Mockery.instance.unnamed_mock + mock.expects(expectations) + mock + end + + # Builds a new mock object + # + # @return [Mock] a new mock object + # + # @overload def stub(name) + # @param [String, Symbol] name identifies mock object in error messages. + # @overload def stub(stubbed_methods_vs_return_values = {}) + # @param [Hash] stubbed_methods_vs_return_values stubbed method name symbols as keys and corresponding return values as values - these stubbed methods are setup as if {Mock#stubs} were called multiple times. + # @overload def stub(name, stubbed_methods_vs_return_values = {}) + # @param [String, Symbol] name identifies mock object in error messages. + # @param [Hash] stubbed_methods_vs_return_values stubbed method name symbols as keys and corresponding return values as values - these stubbed methods are setup as if {Mock#stubs} were called multiple times. + # + # @example Using stubbed_methods_vs_return_values Hash to setup stubbed methods. + # def test_motor_starts_and_stops + # motor = stub('motor', start: true, stop: true) + # assert motor.start + # assert motor.stop + # # an error will not be raised even if either Motor#start or Motor#stop has not been called + # end + def stub(*arguments) + name = arguments.shift.to_s if arguments.first.is_a?(String) || arguments.first.is_a?(Symbol) + expectations = arguments.shift || {} + stub = name ? Mockery.instance.named_mock(name) : Mockery.instance.unnamed_mock + stub.stubs(expectations) + stub + end + + # Builds a mock object that accepts calls to any method. By default it will return +nil+ for any method call. + # + # @return [Mock] a new mock object + # + # @overload def stub_everything(name) + # @param [String, Symbol] name identifies mock object in error messages. + # @overload def stub_everything(stubbed_methods_vs_return_values = {}) + # @param [Hash] stubbed_methods_vs_return_values stubbed method name symbols as keys and corresponding return values as values - these stubbed methods are setup as if {Mock#stubs} were called multiple times. + # @overload def stub_everything(name, stubbed_methods_vs_return_values = {}) + # @param [String, Symbol] name identifies mock object in error messages. + # @param [Hash] stubbed_methods_vs_return_values stubbed method name symbols as keys and corresponding return values as values - these stubbed methods are setup as if {Mock#stubs} were called multiple times. + # + # @example Ignore invocations of irrelevant methods. + # def test_motor_stops + # motor = stub_everything('motor', stop: true) + # assert_nil motor.irrelevant_method_1 # => no error raised + # assert_nil motor.irrelevant_method_2 # => no error raised + # assert motor.stop + # end + def stub_everything(*arguments) + name = arguments.shift if arguments.first.is_a?(String) || arguments.first.is_a?(Symbol) + expectations = arguments.shift || {} + stub = name ? Mockery.instance.named_mock(name) : Mockery.instance.unnamed_mock + stub.stub_everything + stub.stubs(expectations) + stub + end + + # Builds a new sequence which can be used to constrain the order in which expectations can occur. + # + # Specify that an expected invocation must occur within a named {Sequence} by calling {Expectation#in_sequence} + # on each expectation or by passing a block within which all expectations should be constrained by the {Sequence}. + # + # @param [String] name name of sequence + # @yield optional block within which expectations should be constrained by the sequence + # @return [Sequence] a new sequence + # + # @see Expectation#in_sequence + # + # @example Ensure methods on egg are invoked in correct order. + # breakfast = sequence('breakfast') + # + # egg = mock('egg') + # egg.expects(:crack).in_sequence(breakfast) + # egg.expects(:fry).in_sequence(breakfast) + # egg.expects(:eat).in_sequence(breakfast) + # + # @example Ensure methods across multiple objects are invoked in correct order. + # sequence = sequence(:task_order) + # + # task_one = mock("task_one") + # task_two = mock("task_two") + # + # task_one.expects(:execute).in_sequence(sequence) + # task_two.expects(:execute).in_sequence(sequence) + # + # task_one.execute + # task_two.execute + # + # @example Ensure methods on egg are invoked in the correct order using a block. + # egg = mock('egg') + # sequence('breakfast') do + # egg.expects(:crack) + # egg.expects(:fry) + # egg.expects(:eat) + # end + def sequence(name) + Sequence.new(name).tap do |seq| + Mockery.instance.sequences.push(seq) + begin + yield if block_given? + ensure + Mockery.instance.sequences.pop + end + end + end + + # Builds a new state machine which can be used to constrain the order in which expectations can occur. + # + # Specify the initial state of the state machine by using {StateMachine#starts_as}. + # + # Specify that an expected invocation should change the state of the state machine by using {Expectation#then}. + # + # Specify that an expected invocation should be constrained to occur within a particular +state+ by using {Expectation#when}. + # + # A test can contain multiple state machines. + # + # @param [String] name name of state machine + # @return [StateMachine] a new state machine + # + # @see Expectation#then + # @see Expectation#when + # @see StateMachine + # @example Constrain expected invocations to occur in particular states. + # power = states('power').starts_as('off') + # + # radio = mock('radio') + # radio.expects(:switch_on).then(power.is('on')) + # radio.expects(:select_channel).with('BBC Radio 4').when(power.is('on')) + # radio.expects(:adjust_volume).with(+5).when(power.is('on')) + # radio.expects(:select_channel).with('BBC World Service').when(power.is('on')) + # radio.expects(:adjust_volume).with(-5).when(power.is('on')) + # radio.expects(:switch_off).then(power.is('off')) + def states(name) + Mockery.instance.new_state_machine(name) + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/argument_iterator.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/argument_iterator.rb new file mode 100644 index 0000000..e3e8afb --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/argument_iterator.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module Mocha + class ArgumentIterator + def initialize(argument) + @argument = argument + end + + def each(&block) + if @argument.is_a?(Hash) + @argument.each(&block) + else + block.call(@argument) + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/backtrace_filter.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/backtrace_filter.rb new file mode 100644 index 0000000..fb31233 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/backtrace_filter.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Mocha + class BacktraceFilter + LIB_DIRECTORY = File.expand_path(File.join(File.dirname(__FILE__), '..')) + File::SEPARATOR + + def initialize(lib_directory = LIB_DIRECTORY) + @lib_directory = lib_directory + end + + def filtered(backtrace) + backtrace.reject { |location| File.expand_path(location).start_with?(@lib_directory) } + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/block_matchers.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/block_matchers.rb new file mode 100644 index 0000000..554c88d --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/block_matchers.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +module Mocha + module BlockMatchers + class OptionalBlock + def match?(_actual_block) + true + end + + def mocha_inspect; end + end + + class BlockGiven + def match?(actual_block) + !actual_block.nil? + end + + def mocha_inspect + 'with block given' + end + end + + class NoBlockGiven + def match?(actual_block) + actual_block.nil? + end + + def mocha_inspect + 'with no block given' + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/cardinality.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/cardinality.rb new file mode 100644 index 0000000..e2fa668 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/cardinality.rb @@ -0,0 +1,110 @@ +# frozen_string_literal: true + +module Mocha + class Cardinality + INFINITY = 1 / 0.0 + + def initialize(required = 0, maximum = INFINITY) + update(required, maximum) + @invocations = [] + end + + def exactly(count) + update(count, count) + end + + def at_least(count) + update(count, INFINITY) + end + + def at_most(count) + update(0, count) + end + + def times(range_or_count) + case range_or_count + when Range then update(range_or_count.first, range_or_count.last) + else update(range_or_count, range_or_count) + end + end + + def <<(invocation) + @invocations << invocation + end + + def invocations_allowed? + @invocations.size < maximum + end + + def invocations_never_allowed? + maximum.zero? + end + + def satisfied? + @invocations.size >= required + end + + def needs_verifying? + !allowed_any_number_of_times? + end + + def verified? + (@invocations.size >= required) && (@invocations.size <= maximum) + end + + def allowed_any_number_of_times? + required.zero? && infinite?(maximum) + end + + def used? + @invocations.any? || maximum.zero? + end + + def anticipated_times + if allowed_any_number_of_times? + 'allowed any number of times' + elsif required.zero? && maximum.zero? + "expected #{count(maximum)}" + elsif required == maximum + "expected exactly #{count(required)}" + elsif infinite?(maximum) + "expected at least #{count(required)}" + elsif required.zero? + "expected at most #{count(maximum)}" + else + "expected between #{required} and #{count(maximum)}" + end + end + + def invoked_times + "invoked #{count(@invocations.size)}" + end + + def actual_invocations + @invocations.map(&:full_description).join + end + + protected + + attr_reader :required, :maximum + + def count(number) + case number + when 0 then 'never' + when 1 then 'once' + when 2 then 'twice' + else "#{number} times" + end + end + + def update(required, maximum) + @required = required + @maximum = maximum + self + end + + def infinite?(number) + number.respond_to?(:infinite?) && number.infinite? + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/central.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/central.rb new file mode 100644 index 0000000..1252aa1 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/central.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +module Mocha + class Central + class Null < self + def initialize(&block) + super + @raise_not_initialized_error = block + end + + def stub(*) + @raise_not_initialized_error.call + end + + def unstub(*) + @raise_not_initialized_error.call + end + end + + attr_accessor :stubba_methods + + def initialize + self.stubba_methods = [] + end + + def stub(method) + return if stubba_methods.detect { |m| m.matches?(method) } + + method.stub + stubba_methods.push(method) + end + + def unstub(method) + return unless (existing = stubba_methods.detect { |m| m.matches?(method) }) + + existing.unstub + stubba_methods.delete(existing) + end + + def unstub_all + while stubba_methods.any? + unstub(stubba_methods.first) + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/change_state_side_effect.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/change_state_side_effect.rb new file mode 100644 index 0000000..c16a576 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/change_state_side_effect.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module Mocha + class ChangeStateSideEffect + def initialize(state) + @state = state + end + + def perform + @state.activate + end + + def mocha_inspect + "then #{@state.mocha_inspect}" + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/class_methods.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/class_methods.rb new file mode 100644 index 0000000..3c02774 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/class_methods.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true + +require 'mocha/mockery' +require 'mocha/any_instance_method' + +module Mocha + # Methods added to all classes to allow mocking and stubbing on real (i.e. non-mock) objects. + module ClassMethods + # @private + class AnyInstance + def initialize(klass) + @stubba_object = klass + end + + def mocha(instantiate: true) + if instantiate + @mocha ||= Mocha::Mockery.instance.mock_impersonating_any_instance_of(@stubba_object) + else + defined?(@mocha) ? @mocha : nil + end + end + + def stubba_method + Mocha::AnyInstanceMethod + end + + def stubba_class + @stubba_object + end + + def stubba_respond_to?(symbol) + @stubba_object.allocate.respond_to?(symbol.to_sym) + end + + attr_reader :stubba_object + end + + # @return [Mock] a mock object which will detect calls to any instance of this class. + # @raise [StubbingError] if attempting to stub method which is not allowed. + # + # @example Return false to invocation of +Product#save+ for any instance of +Product+. + # Product.any_instance.stubs(:save).returns(false) + # product_1 = Product.new + # assert_equal false, product_1.save + # product_2 = Product.new + # assert_equal false, product_2.save + def any_instance + if frozen? + raise StubbingError.new("can't stub method on frozen object: #{mocha_inspect}.any_instance", caller) + end + + @any_instance ||= AnyInstance.new(self) + end + + # @private + def __method_visibility__(method, include_public_methods: true) + if include_public_methods && public_method_defined?(method) + :public + elsif protected_method_defined?(method) + :protected + else + private_method_defined?(method) ? :private : nil + end + end + alias_method :__method_exists__?, :__method_visibility__ + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/configuration.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/configuration.rb new file mode 100644 index 0000000..dfcb944 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/configuration.rb @@ -0,0 +1,338 @@ +# frozen_string_literal: true + +require 'mocha/ruby_version' +require 'mocha/deprecation' + +module Mocha + # Allows setting of configuration options. See {Configuration} for the available options. + # + # Typically the configuration is set globally in a +test_helper.rb+ or +spec_helper.rb+ file. + # + # @see Configuration + # + # @yieldparam configuration [Configuration] the configuration for modification + # + # @example Setting multiple configuration options + # Mocha.configure do |c| + # c.stubbing_method_unnecessarily = :prevent + # c.stubbing_method_on_non_mock_object = :warn + # end + # + def self.configure + yield configuration + end + + # @private + def self.configuration + Configuration.configuration + end + + # This class provides a number of ways to configure the library. + # + # Typically the configuration is set globally in a +test_helper.rb+ or +spec_helper.rb+ file. + # + # @example Setting multiple configuration options + # Mocha.configure do |c| + # c.stubbing_method_unnecessarily = :prevent + # c.stubbing_method_on_non_mock_object = :warn + # end + # + class Configuration + # @private + DEFAULTS = { + stubbing_method_unnecessarily: :allow, + stubbing_method_on_non_mock_object: :allow, + stubbing_non_existent_method: :allow, + stubbing_non_public_method: :allow, + display_matching_invocations_on_failure: false, + strict_keyword_argument_matching: Mocha::RUBY_V30_PLUS + }.freeze + + attr_reader :options + protected :options + + # @private + def initialize(options = {}) + @options = DEFAULTS.merge(options) + end + + # @private + def initialize_copy(other) + @options = other.options.dup + end + + # @private + def merge(other) + self.class.new(@options.merge(other.options)) + end + + # Configure whether stubbing methods unnecessarily is allowed. + # + # This is useful for identifying unused stubs. Unused stubs are often accidentally introduced when code is {http://martinfowler.com/bliki/DefinitionOfRefactoring.html refactored}. + # + # When +value+ is +:allow+, do nothing. This is the default. + # When +value+ is +:warn+, display a warning. + # When +value+ is +:prevent+, raise a {StubbingError}. + # + # @param [Symbol] value one of +:allow+, +:warn+, +:prevent+. + # + # @example Preventing unnecessary stubbing of a method + # Mocha.configure do |c| + # c.stubbing_method_unnecessarily = :prevent + # end + # + # example = mock('example') + # example.stubs(:unused_stub) + # # => Mocha::StubbingError: stubbing method unnecessarily: + # # => #.unused_stub(any_parameters) + # + def stubbing_method_unnecessarily=(value) + @options[:stubbing_method_unnecessarily] = value + end + + # @private + def stubbing_method_unnecessarily + @options[:stubbing_method_unnecessarily] + end + + # Configure whether stubbing methods on non-mock objects is allowed. + # + # If you like the idea of {http://www.jmock.org/oopsla2004.pdf mocking roles not objects} and {http://www.mockobjects.com/2007/04/test-smell-mocking-concrete-classes.html you don't like stubbing concrete classes}, this is the setting for you. However, while this restriction makes a lot of sense in Java with its {http://java.sun.com/docs/books/tutorial/java/concepts/interface.html explicit interfaces}, it may be moot in Ruby where roles are probably best represented as Modules. + # + # When +value+ is +:allow+, do nothing. This is the default. + # When +value+ is +:warn+, display a warning. + # When +value+ is +:prevent+, raise a {StubbingError}. + # + # @param [Symbol] value one of +:allow+, +:warn+, +:prevent+. + # + # @example Preventing stubbing of a method on a non-mock object + # Mocha.configure do |c| + # c.stubbing_method_on_non_mock_object = :prevent + # end + # + # class Example + # def example_method; end + # end + # + # example = Example.new + # example.stubs(:example_method) + # # => Mocha::StubbingError: stubbing method on non-mock object: + # # => #.example_method + # + def stubbing_method_on_non_mock_object=(value) + @options[:stubbing_method_on_non_mock_object] = value + end + + # @private + def stubbing_method_on_non_mock_object + @options[:stubbing_method_on_non_mock_object] + end + + # Configure whether stubbing of non-existent methods is allowed. + # + # This is useful if you want to ensure that methods you're mocking really exist. A common criticism of unit tests with mock objects is that such a test may (incorrectly) pass when an equivalent non-mocking test would (correctly) fail. While you should always have some integration tests, particularly for critical business functionality, this Mocha configuration setting should catch scenarios when mocked methods and real methods have become misaligned. + # + # When +value+ is +:allow+, do nothing. This is the default. + # When +value+ is +:warn+, display a warning. + # When +value+ is +:prevent+, raise a {StubbingError}. + # + # @param [Symbol] value one of +:allow+, +:warn+, +:prevent+. + # + # @example Preventing stubbing of a non-existent method + # + # Mocha.configure do |c| + # c.stubbing_non_existent_method = :prevent + # end + # + # class Example + # end + # + # example = Example.new + # example.stubs(:method_that_doesnt_exist) + # # => Mocha::StubbingError: stubbing non-existent method: + # # => #.method_that_doesnt_exist + # + def stubbing_non_existent_method=(value) + @options[:stubbing_non_existent_method] = value + end + + # @private + def stubbing_non_existent_method + @options[:stubbing_non_existent_method] + end + + # Configure whether stubbing of non-public methods is allowed. + # + # Many people think that it's good practice only to mock public methods. This is one way to prevent your tests being too tightly coupled to the internal implementation of a class. Such tests tend to be very brittle and not much use when refactoring. + # + # When +value+ is +:allow+, do nothing. This is the default. + # When +value+ is +:warn+, display a warning. + # When +value+ is +:prevent+, raise a {StubbingError}. + # + # @param [Symbol] value one of +:allow+, +:warn+, +:prevent+. + # + # @example Preventing stubbing of a non-public method + # Mocha.configure do |c| + # c.stubbing_non_public_method = :prevent + # end + # + # class Example + # def internal_method; end + # private :internal_method + # end + # + # example = Example.new + # example.stubs(:internal_method) + # # => Mocha::StubbingError: stubbing non-public method: + # # => #.internal_method + # + def stubbing_non_public_method=(value) + @options[:stubbing_non_public_method] = value + end + + # @private + def stubbing_non_public_method + @options[:stubbing_non_public_method] + end + + # Display matching invocations alongside expectations on Mocha-related test failure. + # + # @param [Boolean] value +true+ to enable display of matching invocations; disabled by default. + # + # @example Enable display of matching invocations + # Mocha.configure do |c| + # c.display_matching_invocations_on_failure = true + # end + # + # foo = mock('foo') + # foo.expects(:bar) + # foo.stubs(:baz).returns('baz').raises(RuntimeError).throws(:tag, 'value') + # + # foo.baz(1, 2) + # assert_raises(RuntimeError) { foo.baz(3, 4) } + # assert_throws(:tag) { foo.baz(5, 6) } + # + # not all expectations were satisfied + # unsatisfied expectations: + # - expected exactly once, invoked never: #.bar + # satisfied expectations: + # - allowed any number of times, invoked 3 times: #.baz(any_parameters) + # - #.baz(1, 2) # => "baz" + # - #.baz(3, 4) # => raised RuntimeError + # - #.baz(5, 6) # => threw (:tag, "value") + def display_matching_invocations_on_failure=(value) + @options[:display_matching_invocations_on_failure] = value + end + + # @private + def display_matching_invocations_on_failure? + @options[:display_matching_invocations_on_failure] + end + + # Perform strict keyword argument comparison. Only supported in Ruby >= v2.7. + # + # When this option is set to +false+ a positional +Hash+ and a set of keyword arguments are treated the same during comparison, which can lead to misleading passing tests in Ruby >= v3.0 (see examples below). However, a deprecation warning will be displayed if a positional +Hash+ matches a set of keyword arguments or vice versa. + # + # For more details on keyword arguments in Ruby v3, refer to {https://www.ruby-lang.org/en/news/2019/12/12/separation-of-positional-and-keyword-arguments-in-ruby-3-0 this article}. + # + # Note that +Hash+-related matchers such as {ParameterMatchers::Methods#has_value} or {ParameterMatchers::Methods#has_key} will still treat a positional +Hash+ and a set of keyword arguments the same, so misleading passing tests are still possible when they are used. + # + # This configuration option is +false+ by default in Ruby v2.7 to enable gradual adoption, but +true+ by default in Ruby >= v3.0. + # + # @param [Boolean] value +true+ to enable strict keyword argument matching. + # + # @example Loose keyword argument matching (default in Ruby v2.7) + # + # Mocha.configure do |c| + # c.strict_keyword_argument_matching = false + # end + # + # class Example + # def foo(a, bar:); end + # end + # + # example = Example.new + # example.expects(:foo).with('a', bar: 'b') + # example.foo('a', { bar: 'b' }) + # # This passes the test, but would result in an ArgumentError in practice + # + # @example Strict keyword argument matching (default in Ruby >= v3.0) + # + # Mocha.configure do |c| + # c.strict_keyword_argument_matching = true + # end + # + # class Example + # def foo(a, bar:); end + # end + # + # example = Example.new + # example.expects(:foo).with('a', bar: 'b') + # example.foo('a', { bar: 'b' }) + # # This now fails as expected + def strict_keyword_argument_matching=(value) + raise 'Strict keyword argument matching requires Ruby 2.7 and above.' unless Mocha::RUBY_V27_PLUS + + @options[:strict_keyword_argument_matching] = value + end + + # @private + def strict_keyword_argument_matching? + @options[:strict_keyword_argument_matching] + end + + class << self + # @private + def reset_configuration + @configuration = nil + end + + # Temporarily modify {Configuration} options. + # + # The supplied +temporary_options+ will override the current configuration for the duration of the supplied block. + # The configuration will be returned to its original state when the block returns. + # + # @param [Hash] temporary_options the configuration options to apply for the duration of the block. + # @yield block during which the configuration change will be in force. + # + # @example Temporarily prevent stubbing of non-mock object + # Mocha::Configuration.override(stubbing_method_on_non_mock_object: :prevent) do + # 123.stubs(:to_s).returns('456') + # end + def override(temporary_options) + original_configuration = configuration + @configuration = configuration.merge(new(temporary_options)) + yield + ensure + @configuration = original_configuration + end + + # @private + def configuration + @configuration ||= new + end + + private + + # @private + def change_config(action, new_value, &block) + if block_given? + temporarily_change_config action, new_value, &block + else + configuration.send(:"#{action}=", new_value) + end + end + + # @private + def temporarily_change_config(action, new_value) + original_configuration = configuration + new_configuration = configuration.dup + new_configuration.send(:"#{action}=", new_value) + @configuration = new_configuration + yield + ensure + @configuration = original_configuration + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/default_name.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/default_name.rb new file mode 100644 index 0000000..6b691e3 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/default_name.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Mocha + class DefaultName + def initialize(mock) + @mock = mock + end + + def mocha_inspect + address = @mock.__id__ * 2 + address += 0x100000000 if address < 0 + "#x', address: address)}>" + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/default_receiver.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/default_receiver.rb new file mode 100644 index 0000000..00acb45 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/default_receiver.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Mocha + class DefaultReceiver + def initialize(mock) + @mock = mock + end + + def mocks + [@mock] + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/deprecation.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/deprecation.rb new file mode 100644 index 0000000..200a687 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/deprecation.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require 'mocha/backtrace_filter' + +module Mocha + class Deprecation + class Logger + def warning(message) + filter = BacktraceFilter.new + location = filter.filtered(caller)[0] + warn "Mocha deprecation warning at #{location}: #{message}" + end + end + + class << self + attr_writer :logger + + def warning(message) + logger.call(message) + end + + def logger + @logger ||= Logger.new + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/detection/minitest.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/detection/minitest.rb new file mode 100644 index 0000000..e556daf --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/detection/minitest.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Mocha + module Detection + module Minitest + def self.testcase + if defined?(::Minitest::Test) + ::Minitest::Test + elsif defined?(::Minitest::Unit::TestCase) + ::Minitest::Unit::TestCase + end + end + + def self.version + if defined?(::Minitest::Unit::VERSION) + ::Minitest::Unit::VERSION + elsif defined?(::Minitest::VERSION) + ::Minitest::VERSION + else + '0.0.0' + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/detection/test_unit.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/detection/test_unit.rb new file mode 100644 index 0000000..b105cef --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/detection/test_unit.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +module Mocha + module Detection + module TestUnit + def self.testcase + if defined?(::Test::Unit::TestCase) && + !(defined?(::Minitest::Unit::TestCase) && (::Test::Unit::TestCase < ::Minitest::Unit::TestCase)) && + !(defined?(::Minitest::Spec) && (::Test::Unit::TestCase < ::Minitest::Spec)) + ::Test::Unit::TestCase + end + end + + def self.version + version = '1.0.0' + if testcase + begin + require 'test/unit/version' + rescue LoadError + warn "Unable to load 'test/unit/version', but continuing anyway" if $DEBUG + end + if defined?(::Test::Unit::VERSION) + version = ::Test::Unit::VERSION + end + end + version + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/error_with_filtered_backtrace.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/error_with_filtered_backtrace.rb new file mode 100644 index 0000000..7bb12e5 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/error_with_filtered_backtrace.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require 'mocha/backtrace_filter' + +module Mocha + # @private + class ErrorWithFilteredBacktrace < StandardError + # @private + def initialize(message = nil, backtrace = []) + super(message) + filter = BacktraceFilter.new + set_backtrace(filter.filtered(backtrace)) + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/exception_raiser.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/exception_raiser.rb new file mode 100644 index 0000000..7681434 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/exception_raiser.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module Mocha + class ExceptionRaiser + def initialize(exception, message) + @exception = exception + @message = message + end + + def evaluate(invocation) + invocation.raised(@exception) + raise @exception, @exception.to_s if @exception.is_a?(Module) && (@exception < Interrupt) + raise @exception, @message if @message + + raise @exception + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/expectation.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/expectation.rb new file mode 100644 index 0000000..62414ee --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/expectation.rb @@ -0,0 +1,769 @@ +# frozen_string_literal: true + +require 'ruby2_keywords' +require 'mocha/method_matcher' +require 'mocha/parameters_matcher' +require 'mocha/expectation_error' +require 'mocha/return_values' +require 'mocha/exception_raiser' +require 'mocha/thrower' +require 'mocha/yield_parameters' +require 'mocha/in_state_ordering_constraint' +require 'mocha/change_state_side_effect' +require 'mocha/cardinality' +require 'mocha/configuration' +require 'mocha/block_matchers' +require 'mocha/backtrace_filter' + +module Mocha + # Methods on expectations returned from {Mock#expects}, {Mock#stubs}, {ObjectMethods#expects} and {ObjectMethods#stubs}. + class Expectation + # Modifies expectation so that the number of invocations of the expected method must be within a specified range or exactly equal to a specified number. + # + # @param [Range,Integer] range_or_number specifies the allowable range for the number of expected invocations or the specified number of expected invocations. + # @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained. + # + # @example Specifying an exact number of expected invocations. + # object = mock() + # object.expects(:expected_method).times(3) + # 3.times { object.expected_method } + # # => verify succeeds + # + # object = mock() + # object.expects(:expected_method).times(3) + # 2.times { object.expected_method } + # # => verify fails + # + # @example Specifying a range for the number of expected invocations. + # object = mock() + # object.expects(:expected_method).times(2..4) + # 3.times { object.expected_method } + # # => verify succeeds + # + # object = mock() + # object.expects(:expected_method).times(2..4) + # object.expected_method + # # => verify fails + def times(range_or_number) + @cardinality.times(range_or_number) + self + end + + # Modifies expectation so that the expected method must be called exactly three times. This is equivalent to calling {#times} with an argument of +3+. + # + # @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained. + # + # @example Expected method must be invoked exactly three times. + # object = mock() + # object.expects(:expected_method).thrice + # object.expected_method + # object.expected_method + # object.expected_method + # # => verify succeeds + # + # object = mock() + # object.expects(:expected_method).thrice + # object.expected_method + # object.expected_method + # object.expected_method + # object.expected_method # => unexpected invocation + # + # object = mock() + # object.expects(:expected_method).thrice + # object.expected_method + # object.expected_method + # # => verify fails + def thrice + @cardinality.exactly(3) + self + end + + # Modifies expectation so that the expected method must be called exactly twice. This is equivalent to calling {#times} with an argument of +2+. + # + # @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained. + # + # @example Expected method must be invoked exactly twice. + # object = mock() + # object.expects(:expected_method).twice + # object.expected_method + # object.expected_method + # # => verify succeeds + # + # object = mock() + # object.expects(:expected_method).twice + # object.expected_method + # object.expected_method + # object.expected_method # => unexpected invocation + # + # object = mock() + # object.expects(:expected_method).twice + # object.expected_method + # # => verify fails + def twice + @cardinality.exactly(2) + self + end + + # Modifies expectation so that the expected method must be called exactly once. This is equivalent to calling {#times} with an argument of +1+. + # + # Note that this is the default behaviour for an expectation, but you may wish to use it for clarity/emphasis. + # + # @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained. + # + # @example Expected method must be invoked exactly once. + # object = mock() + # object.expects(:expected_method).once + # object.expected_method + # # => verify succeeds + # + # object = mock() + # object.expects(:expected_method).once + # object.expected_method + # object.expected_method # => unexpected invocation + # + # object = mock() + # object.expects(:expected_method).once + # # => verify fails + def once + @cardinality.exactly(1) + self + end + + # Modifies expectation so that the expected method must never be called. + # + # @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained. + # + # @example Expected method must never be called. + # object = mock() + # object.expects(:expected_method).never + # object.expected_method # => unexpected invocation + # + # object = mock() + # object.expects(:expected_method).never + # # => verify succeeds + def never + @cardinality.exactly(0) + self + end + + # Modifies expectation so that the expected method must be called at least a +minimum_number_of_times+. + # + # @param [Integer] minimum_number_of_times minimum number of expected invocations. + # @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained. + # + # @example Expected method must be called at least twice. + # object = mock() + # object.expects(:expected_method).at_least(2) + # 3.times { object.expected_method } + # # => verify succeeds + # + # object = mock() + # object.expects(:expected_method).at_least(2) + # object.expected_method + # # => verify fails + def at_least(minimum_number_of_times) + @cardinality.at_least(minimum_number_of_times) + self + end + + # Modifies expectation so that the expected method must be called at least once. This is equivalent to calling {#at_least} with an argument of +1+. + # + # @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained. + # + # @example Expected method must be called at least once. + # object = mock() + # object.expects(:expected_method).at_least_once + # object.expected_method + # # => verify succeeds + # + # object = mock() + # object.expects(:expected_method).at_least_once + # # => verify fails + def at_least_once + at_least(1) + end + + # Modifies expectation so that the expected method must be called at most a +maximum_number_of_times+. + # + # @param [Integer] maximum_number_of_times maximum number of expected invocations. + # @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained. + # + # @example Expected method must be called at most twice. + # object = mock() + # object.expects(:expected_method).at_most(2) + # 2.times { object.expected_method } + # # => verify succeeds + # + # object = mock() + # object.expects(:expected_method).at_most(2) + # 3.times { object.expected_method } # => unexpected invocation + def at_most(maximum_number_of_times) + @cardinality.at_most(maximum_number_of_times) + self + end + + # Modifies expectation so that the expected method must be called at most once. This is equivalent to calling {#at_most} with an argument of +1+. + # + # @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained. + # + # @example Expected method must be called at most once. + # object = mock() + # object.expects(:expected_method).at_most_once + # object.expected_method + # # => verify succeeds + # + # object = mock() + # object.expects(:expected_method).at_most_once + # 2.times { object.expected_method } # => unexpected invocation + def at_most_once + at_most(1) + end + + # Modifies expectation so that the expected method must be called with +expected_parameters_or_matchers+. + # + # May be used with Ruby literals or variables for exact matching or with parameter matchers for less-specific matching, e.g. {ParameterMatchers::Methods#includes}, {ParameterMatchers::Methods#has_key}, etc. See {ParameterMatchers} for a list of all available parameter matchers. + # + # Alternatively a block argument can be passed to {#with} to implement custom parameter matching. The block receives the +*actual_parameters+ as its arguments and should return +true+ if they are acceptable or +false+ otherwise. See the example below where a method is expected to be called with a value divisible by 4. + # The block argument takes precedence over +expected_parameters_or_matchers+. The block may be called multiple times per invocation of the expected method and so it should be idempotent. + # + # Note that if {#with} is called multiple times on the same expectation, the last call takes precedence; other calls are ignored. + # + # Positional arguments were separated from keyword arguments in Ruby v3 (see {https://www.ruby-lang.org/en/news/2019/12/12/separation-of-positional-and-keyword-arguments-in-ruby-3-0 this article}). In relation to this a new configuration option ({Configuration#strict_keyword_argument_matching=}) is available in Ruby >= 2.7. + # + # When {Configuration#strict_keyword_argument_matching=} is set to +false+ (which is the default in Ruby v2.7), a positional +Hash+ and a set of keyword arguments passed to {#with} are treated the same for the purposes of parameter matching. However, a deprecation warning will be displayed if a positional +Hash+ matches a set of keyword arguments or vice versa. + # + # When {Configuration#strict_keyword_argument_matching=} is set to +true+ (which is the default in Ruby >= v3.0), an actual positional +Hash+ will not match an expected set of keyword arguments; and vice versa, an actual set of keyword arguments will not match an expected positional +Hash+, i.e. the parameter matching is stricter. + # + # @see ParameterMatchers + # @see Configuration#strict_keyword_argument_matching= + # + # @param [Array] expected_parameters_or_matchers expected parameter values or parameter matchers. + # @yield optional block specifying custom matching. + # @yieldparam [Array] actual_parameters parameters with which expected method was invoked. + # @yieldreturn [Boolean] +true+ if +actual_parameters+ are acceptable; +false+ otherwise. + # @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained. + # + # @example Expected method must be called with exact parameter values. + # object = mock() + # object.expects(:expected_method).with(:param1, :param2) + # object.expected_method(:param1, :param2) + # # => verify succeeds + # + # object = mock() + # object.expects(:expected_method).with(:param1, :param2) + # object.expected_method(:param3) + # # => verify fails + # + # @example Expected method must be called with parameters matching parameter matchers. + # object = mock() + # object.expects(:expected_method).with(includes('string2'), anything) + # object.expected_method(['string1', 'string2'], 'any-old-value') + # # => verify succeeds + # + # object = mock() + # object.expects(:expected_method).with(includes('string2'), anything) + # object.expected_method(['string1'], 'any-old-value') + # # => verify fails + # + # @example Loose keyword argument matching (default in Ruby v2.7). + # + # Mocha.configure do |c| + # c.strict_keyword_argument_matching = false + # end + # + # class Example + # def foo(a, bar:); end + # end + # + # example = Example.new + # example.expects(:foo).with('a', bar: 'b') + # example.foo('a', { bar: 'b' }) + # # This passes the test, but would result in an ArgumentError in practice + # + # @example Strict keyword argument matching (default in Ruby >= 3.0). + # + # Mocha.configure do |c| + # c.strict_keyword_argument_matching = true + # end + # + # class Example + # def foo(a, bar:); end + # end + # + # example = Example.new + # example.expects(:foo).with('a', bar: 'b') + # example.foo('a', { bar: 'b' }) + # # This now fails as expected + # + # @example Using a block argument to expect the method to be called with a value divisible by 4. + # object = mock() + # object.expects(:expected_method).with() { |value| value % 4 == 0 } + # object.expected_method(16) + # # => verify succeeds + # + # object = mock() + # object.expects(:expected_method).with() { |value| value % 4 == 0 } + # object.expected_method(17) + # # => verify fails + # + # @example Extracting a custom matcher into an instance method on the test class. + # class MyTest < Minitest::Test + # def test_expected_method_is_called_with_a_value_divisible_by_4 + # object = mock() + # object.expects(:expected_method).with(&method(:divisible_by_4)) + # object.expected_method(16) + # # => verify succeeds + # end + # + # private + # + # def divisible_by_4(value) + # value % 4 == 0 + # end + # end + def with(*expected_parameters_or_matchers, &matching_block) + @parameters_matcher = ParametersMatcher.new(expected_parameters_or_matchers, self, &matching_block) + self + end + ruby2_keywords(:with) + + # Modifies expectation so that the expected method must be called with a block. + # + # @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained. + # + # @example Expected method must be called with a block. + # object = mock() + # object.expects(:expected_method).with_block_given + # object.expected_method { 1 + 1 } + # # => verify succeeds + # + # object = mock() + # object.expects(:expected_method).with_block_given + # object.expected_method + # # => verify fails + def with_block_given + @block_matcher = BlockMatchers::BlockGiven.new + self + end + + # Modifies expectation so that the expected method must be called without a block. + # + # @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained. + # + # @example Expected method must be called without a block. + # object = mock() + # object.expects(:expected_method).with_no_block_given + # object.expected_method + # # => verify succeeds + # + # object = mock() + # object.expects(:expected_method).with_block_given + # object.expected_method { 1 + 1 } + # # => verify fails + def with_no_block_given + @block_matcher = BlockMatchers::NoBlockGiven.new + self + end + + # Modifies expectation so that when the expected method is called, it yields to the block with the specified +parameters+. + # + # If no +parameters+ are specified, it yields to the block without any parameters. + # + # If no block is provided, the method will still attempt to yield resulting in a +LocalJumpError+. Note that this is what would happen if a "real" (non-mock) method implementation tried to yield to a non-existent block. + # + # May be called multiple times on the same expectation for consecutive invocations. + # + # @param [*Array] parameters parameters to be yielded. + # @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained. + # @see #then + # + # @example Yield when expected method is invoked. + # benchmark = mock() + # benchmark.expects(:measure).yields + # yielded = false + # benchmark.measure { yielded = true } + # yielded # => true + # + # @example Yield parameters when expected method is invoked. + # fibonacci = mock() + # fibonacci.expects(:next_pair).yields(0, 1) + # sum = 0 + # fibonacci.next_pair { |first, second| sum = first + second } + # sum # => 1 + # + # @example Yield different parameters on different invocations of the expected method. + # fibonacci = mock() + # fibonacci.expects(:next_pair).yields(0, 1).then.yields(1, 1) + # sum = 0 + # fibonacci.next_pair { |first, second| sum = first + second } + # sum # => 1 + # fibonacci.next_pair { |first, second| sum = first + second } + # sum # => 2 + def yields(*parameters) + multiple_yields(parameters) + end + + # Modifies expectation so that when the expected method is called, it yields multiple times per invocation with the specified +parameter_groups+. + # + # If no block is provided, the method will still attempt to yield resulting in a +LocalJumpError+. Note that this is what would happen if a "real" (non-mock) method implementation tried to yield to a non-existent block. + # + # @param [*Array] parameter_groups each element of +parameter_groups+ should iself be an +Array+ representing the parameters to be passed to the block for a single yield. Any element of +parameter_groups+ that is not an +Array+ is wrapped in an +Array+. + # @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained. + # @see #then + # + # @example When +foreach+ is called, the stub will invoke the block twice, the first time it passes ['row1_col1', 'row1_col2'] as the parameters, and the second time it passes ['row2_col1', ''] as the parameters. + # csv = mock() + # csv.expects(:foreach).with("path/to/file.csv").multiple_yields(['row1_col1', 'row1_col2'], ['row2_col1', '']) + # rows = [] + # csv.foreach { |row| rows << row } + # rows # => [['row1_col1', 'row1_col2'], ['row2_col1', '']] + # + # @example Yield different groups of parameters on different invocations of the expected method. Simulating a situation where the CSV file at 'path/to/file.csv' has been modified between the two calls to +foreach+. + # csv = mock() + # csv.stubs(:foreach).with("path/to/file.csv").multiple_yields(['old_row1_col1', 'old_row1_col2'], ['old_row2_col1', '']).then.multiple_yields(['new_row1_col1', ''], ['new_row2_col1', 'new_row2_col2']) + # rows_from_first_invocation = [] + # rows_from_second_invocation = [] + # csv.foreach { |row| rows_from_first_invocation << row } # first invocation + # csv.foreach { |row| rows_from_second_invocation << row } # second invocation + # rows_from_first_invocation # => [['old_row1_col1', 'old_row1_col2'], ['old_row2_col1', '']] + # rows_from_second_invocation # => [['new_row1_col1', ''], ['new_row2_col1', 'new_row2_col2']] + def multiple_yields(*parameter_groups) + @yield_parameters.add(*parameter_groups) + self + end + + # Modifies expectation so that when the expected method is called, it returns the specified +value+. + # + # @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained. + # @see #then + # + # @overload def returns(value) + # @param [Object] value value to return on invocation of expected method. + # @overload def returns(*values) + # @param [*Array] values values to return on consecutive invocations of expected method. + # + # @example Return the same value on every invocation. + # object = mock() + # object.stubs(:stubbed_method).returns('result') + # object.stubbed_method # => 'result' + # object.stubbed_method # => 'result' + # + # @example Return a different value on consecutive invocations. + # object = mock() + # object.stubs(:stubbed_method).returns(1, 2) + # object.stubbed_method # => 1 + # object.stubbed_method # => 2 + # + # @example Alternative way to return a different value on consecutive invocations. + # object = mock() + # object.stubs(:expected_method).returns(1, 2).then.returns(3) + # object.expected_method # => 1 + # object.expected_method # => 2 + # object.expected_method # => 3 + # + # @example May be called in conjunction with {#raises} on the same expectation. + # object = mock() + # object.stubs(:expected_method).returns(1, 2).then.raises(Exception) + # object.expected_method # => 1 + # object.expected_method # => 2 + # object.expected_method # => raises exception of class Exception1 + # + # @example Note that in Ruby a method returning multiple values is exactly equivalent to a method returning an +Array+ of those values. + # object = mock() + # object.stubs(:expected_method).returns([1, 2]) + # x, y = object.expected_method + # x # => 1 + # y # => 2 + def returns(*values) + @return_values += ReturnValues.build(*values) + self + end + + # Modifies expectation so that when the expected method is called, it raises the specified +exception+ with the specified +message+ i.e. calls +Kernel#raise(exception, message)+. + # + # @param [Class,Exception,String,#exception] exception exception to be raised or message to be passed to RuntimeError. + # @param [String] message exception message. + # @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained. + # + # @see Kernel#raise + # @see #then + # + # @overload def raises + # @overload def raises(exception) + # @overload def raises(exception, message) + # + # @example Raise specified exception if expected method is invoked. + # object = stub() + # object.stubs(:expected_method).raises(Exception, 'message') + # object.expected_method # => raises exception of class Exception and with message 'message' + # + # @example Raise custom exception with extra constructor parameters by passing in an instance of the exception. + # object = stub() + # object.stubs(:expected_method).raises(MyException.new('message', 1, 2, 3)) + # object.expected_method # => raises the specified instance of MyException + # + # @example Raise different exceptions on consecutive invocations of the expected method. + # object = stub() + # object.stubs(:expected_method).raises(Exception1).then.raises(Exception2) + # object.expected_method # => raises exception of class Exception1 + # object.expected_method # => raises exception of class Exception2 + # + # @example Raise an exception on first invocation of expected method and then return values on subsequent invocations. + # object = stub() + # object.stubs(:expected_method).raises(Exception).then.returns(2, 3) + # object.expected_method # => raises exception of class Exception1 + # object.expected_method # => 2 + # object.expected_method # => 3 + def raises(exception = RuntimeError, message = nil) + @return_values += ReturnValues.new(ExceptionRaiser.new(exception, message)) + self + end + + # Modifies expectation so that when the expected method is called, it throws the specified +tag+ with the specific return value +object+ i.e. calls +Kernel#throw(tag, object)+. + # + # @param [Symbol,String] tag tag to throw to transfer control to the active catch block. + # @param [Object] object return value for the catch block. + # @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained. + # + # @see Kernel#throw + # @see #then + # + # @overload def throw(tag) + # @overload def throw(tag, object) + # + # @example Throw tag when expected method is invoked. + # object = stub() + # object.stubs(:expected_method).throws(:done) + # object.expected_method # => throws tag :done + # + # @example Throw tag with return value +object+ c.f. +Kernel#throw+. + # object = stub() + # object.stubs(:expected_method).throws(:done, 'result') + # object.expected_method # => throws tag :done and causes catch block to return 'result' + # + # @example Throw different tags on consecutive invocations of the expected method. + # object = stub() + # object.stubs(:expected_method).throws(:done).then.throws(:continue) + # object.expected_method # => throws :done + # object.expected_method # => throws :continue + # + # @example Throw tag on first invocation of expected method and then return values for subsequent invocations. + # object = stub() + # object.stubs(:expected_method).throws(:done).then.returns(2, 3) + # object.expected_method # => throws :done + # object.expected_method # => 2 + # object.expected_method # => 3 + def throws(tag, object = nil) + @return_values += ReturnValues.new(Thrower.new(tag, object)) + self + end + + # @overload def then + # Used as syntactic sugar to improve readability. It has no effect on state of the expectation. + # @overload def then(state) + # Used to change the +state_machine+ to the specified state when the expected invocation occurs. + # @param [StateMachine::State] state state_machine.is(state_name) provides a mechanism to change the +state_machine+ into the state specified by +state_name+ when the expected method is invoked. + # + # @see API#states + # @see StateMachine + # @see #when + # + # @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained. + # + # @example Using {#then} as syntactic sugar when specifying values to be returned and exceptions to be raised on consecutive invocations of the expected method. + # object = mock() + # object.stubs(:expected_method).returns(1, 2).then.raises(Exception).then.returns(4) + # object.expected_method # => 1 + # object.expected_method # => 2 + # object.expected_method # => raises exception of class Exception + # object.expected_method # => 4 + # + # @example Using {#then} to change the +state+ of a +state_machine+ on the invocation of an expected method. + # power = states('power').starts_as('off') + # + # radio = mock('radio') + # radio.expects(:switch_on).then(power.is('on')) + # radio.expects(:select_channel).with('BBC Radio 4').when(power.is('on')) + # radio.expects(:adjust_volume).with(+5).when(power.is('on')) + # radio.expects(:select_channel).with('BBC World Service').when(power.is('on')) + # radio.expects(:adjust_volume).with(-5).when(power.is('on')) + # radio.expects(:switch_off).then(power.is('off')) + def then(state = nil) + add_side_effect(ChangeStateSideEffect.new(state)) if state + self + end + + # Constrains the expectation to occur only when the +state_machine+ is in the state specified by +state_predicate+. + # + # @param [StateMachine::StatePredicate] state_predicate +state_machine.is(state_name)+ provides a mechanism to determine whether the +state_machine+ is in the state specified by +state_predicate+ when the expected method is invoked. + # @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained. + # + # @see API#states + # @see StateMachine + # @see #then + # + # @example Using {#when} to only allow invocation of methods when "power" state machine is in the "on" state. + # power = states('power').starts_as('off') + # + # radio = mock('radio') + # radio.expects(:switch_on).then(power.is('on')) + # radio.expects(:select_channel).with('BBC Radio 4').when(power.is('on')) + # radio.expects(:adjust_volume).with(+5).when(power.is('on')) + # radio.expects(:select_channel).with('BBC World Service').when(power.is('on')) + # radio.expects(:adjust_volume).with(-5).when(power.is('on')) + # radio.expects(:switch_off).then(power.is('off')) + def when(state_predicate) + add_ordering_constraint(InStateOrderingConstraint.new(state_predicate)) + self + end + + # Constrains the expectation so that it must be invoked at the current point in the +sequence+. + # + # To expect a sequence of invocations, write the expectations in order and add the +in_sequence(sequence)+ clause to each one. + # + # Expectations in a +sequence+ can have any invocation count. + # + # If an expectation in a sequence is stubbed, rather than expected, it can be skipped in the +sequence+. + # + # An expected method can appear in multiple sequences. + # + # @param [Sequence] sequence sequence in which expected method should appear. + # @param [*Array] sequences more sequences in which expected method should appear. + # @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained. + # + # @see API#sequence + # + # @example Ensure methods are invoked in a specified order. + # breakfast = sequence('breakfast') + # + # egg = mock('egg') + # egg.expects(:crack).in_sequence(breakfast) + # egg.expects(:fry).in_sequence(breakfast) + # egg.expects(:eat).in_sequence(breakfast) + def in_sequence(sequence, *sequences) + sequences.unshift(sequence).each { |seq| add_in_sequence_ordering_constraint(seq) } + self + end + + # @private + attr_reader :backtrace + + # @private + def initialize(mock, expected_method_name, backtrace = nil) + @mock = mock + @method_matcher = MethodMatcher.new(expected_method_name.to_sym) + @parameters_matcher = ParametersMatcher.new + @block_matcher = BlockMatchers::OptionalBlock.new + @ordering_constraints = [] + @side_effects = [] + @cardinality = Cardinality.new.exactly(1) + @return_values = ReturnValues.new + @yield_parameters = YieldParameters.new + @backtrace = backtrace || caller + end + + # @private + def add_ordering_constraint(ordering_constraint) + @ordering_constraints << ordering_constraint + end + + # @private + def add_in_sequence_ordering_constraint(sequence) + sequence.constrain_as_next_in_sequence(self) + end + + # @private + def add_side_effect(side_effect) + @side_effects << side_effect + end + + # @private + def perform_side_effects + @side_effects.each(&:perform) + end + + # @private + def in_correct_order? + @ordering_constraints.all?(&:allows_invocation_now?) + end + + # @private + def ordering_constraints_not_allowing_invocation_now + @ordering_constraints.reject(&:allows_invocation_now?) + end + + # @private + def matches_method?(method_name) + @method_matcher.match?(method_name) + end + + # @private + def match?(invocation, ignoring_order: false) + order_independent_match = @method_matcher.match?(invocation.method_name) && @parameters_matcher.match?(invocation.arguments) && @block_matcher.match?(invocation.block) + ignoring_order ? order_independent_match : order_independent_match && in_correct_order? + end + + # @private + def invocations_allowed? + @cardinality.invocations_allowed? + end + + # @private + def invocations_never_allowed? + @cardinality.invocations_never_allowed? + end + + # @private + def satisfied? + @cardinality.satisfied? + end + + # @private + def invoke(invocation) + perform_side_effects + @cardinality << invocation + invocation.call(@yield_parameters, @return_values) + end + + # @private + def verified?(assertion_counter = nil) + assertion_counter.increment if assertion_counter && @cardinality.needs_verifying? + @cardinality.verified? + end + + # @private + def used? + @cardinality.used? + end + + # @private + def inspect + address = __id__ * 2 + address += 0x100000000 if address < 0 + "#x', address: address)} #{mocha_inspect} >" + end + + # @private + def mocha_inspect + strings = ["#{@cardinality.anticipated_times}, #{@cardinality.invoked_times}: #{method_signature}"] + strings << "; #{@ordering_constraints.map(&:mocha_inspect).join('; ')}" unless @ordering_constraints.empty? + if Mocha.configuration.display_matching_invocations_on_failure? + strings << @cardinality.actual_invocations + end + strings.join + end + + # @private + def method_signature + strings = ["#{@mock.mocha_inspect}.#{@method_matcher.mocha_inspect}#{@parameters_matcher.mocha_inspect}"] + strings << " #{@block_matcher.mocha_inspect}" if @block_matcher.mocha_inspect + strings.join + end + + # @private + def definition_location + filter = BacktraceFilter.new + filter.filtered(backtrace)[0] + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/expectation_error.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/expectation_error.rb new file mode 100644 index 0000000..b4cd24e --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/expectation_error.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module Mocha + # Default exception class raised when an unexpected invocation or an unsatisfied expectation occurs. + # + # Authors of test libraries may use +Mocha::ExpectationErrorFactory+ to have Mocha raise a different exception. + # + # @see Mocha::ExpectationErrorFactory + class ExpectationError < Exception; end # rubocop:disable Lint/InheritException +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/expectation_error_factory.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/expectation_error_factory.rb new file mode 100644 index 0000000..a5e752d --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/expectation_error_factory.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +require 'mocha/backtrace_filter' +require 'mocha/expectation_error' + +module Mocha + # This factory determines what class of exception should be raised when Mocha detects a test failure. + # + # This class should only be used by authors of test libraries and not by typical "users" of Mocha. + # + # For example, it is used by +Mocha::Integration::Minitest::Adapter+ in order to have Mocha raise a +Minitest::Assertion+ which can then be sensibly handled by +Minitest::Unit::TestCase+. + # + # @see Mocha::Integration::Minitest::Adapter + class ExpectationErrorFactory + class << self + # @!attribute exception_class + # Determines what class of exception should be raised when Mocha detects a test failure. + # + # This attribute may be set by authors of test libraries in order to have Mocha raise exceptions of a specific class when there is an unexpected invocation or an unsatisfied expectation. + # + # By default a +Mocha::ExpectationError+ will be raised. + # + # @return [Exception] class of exception to be raised when an expectation error occurs + # @see Mocha::ExpectationError + attr_accessor :exception_class + + # @private + def build(message = nil, backtrace = []) + exception = exception_class.new(message) + filter = BacktraceFilter.new + exception.set_backtrace(filter.filtered(backtrace)) + exception + end + end + self.exception_class = ExpectationError + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/expectation_list.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/expectation_list.rb new file mode 100644 index 0000000..2f070dc --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/expectation_list.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +module Mocha + class ExpectationList + def initialize(expectations = []) + @expectations = expectations + end + + def add(expectation) + @expectations.unshift(expectation) + expectation + end + + def remove_all_matching_method(method_name) + @expectations.reject! { |expectation| expectation.matches_method?(method_name) } + end + + def matches_method?(method_name) + @expectations.any? { |expectation| expectation.matches_method?(method_name) } + end + + def match(invocation, ignoring_order: false) + matching_expectations(invocation, ignoring_order: ignoring_order).first + end + + def match_allowing_invocation(invocation) + matching_expectations(invocation).detect(&:invocations_allowed?) + end + + def match_never_allowing_invocation(invocation) + matching_expectations(invocation).detect(&:invocations_never_allowed?) + end + + def verified?(assertion_counter) + @expectations.all? { |expectation| expectation.verified?(assertion_counter) } + end + + def to_a + @expectations + end + + def to_set + @expectations.to_set + end + + def length + @expectations.length + end + + def any? + @expectations.any? + end + + def +(other) + self.class.new(to_a + other.to_a) + end + + def matching_expectations(invocation, ignoring_order: false) + @expectations.select { |e| e.match?(invocation, ignoring_order: ignoring_order) } + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/hooks.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/hooks.rb new file mode 100644 index 0000000..fe37ef2 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/hooks.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +require 'mocha/mockery' + +module Mocha + # Integration hooks for test library authors. + # + # The methods in this module should be called from test libraries wishing to integrate with Mocha. + # + # This module is provided as part of the +Mocha::API+ module and is therefore part of the public API, but should only be used by authors of test libraries and not by typical "users" of Mocha. + # + # Integration with Test::Unit and Minitest are provided as part of Mocha, because they are (or were once) part of the Ruby standard library. Integration with other test libraries is not provided as *part* of Mocha, but is supported by means of the methods in this module. + # + # See the code in the +Adapter+ modules for examples of how to use the methods in this module. +Mocha::ExpectationErrorFactory+ may be used if you want +Mocha+ to raise a different type of exception. + # + # @see Mocha::Integration::TestUnit::Adapter + # @see Mocha::Integration::Minitest::Adapter + # @see Mocha::ExpectationErrorFactory + # @see Mocha::API + module Hooks + # @private + class NullAssertionCounter + def increment; end + end + + # Prepares Mocha before a test (only for use by authors of test libraries). + # + # This method should be called before each individual test starts (including before any "setup" code). + def mocha_setup(assertion_counter = NullAssertionCounter.new) + Mockery.setup(assertion_counter) + end + + # Verifies that all mock expectations have been met (only for use by authors of test libraries). + # + # This is equivalent to a series of "assertions". + # + # This method should be called at the end of each individual test, before it has been determined whether or not the test has passed. + def mocha_verify + Mockery.verify + end + + # Resets Mocha after a test (only for use by authors of test libraries). + # + # This method should be called after each individual test has finished (including after any "teardown" code). + def mocha_teardown(origin = mocha_test_name) + Mockery.teardown(origin) + end + + # Returns a string representing the unit test name, to be included in some Mocha + # to help track down potential bugs. + def mocha_test_name + nil + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/ignoring_warning.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/ignoring_warning.rb new file mode 100644 index 0000000..d435dbc --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/ignoring_warning.rb @@ -0,0 +1,20 @@ +module Mocha + # @private + module IgnoringWarning + def ignoring_warning(pattern, if_: true) + return yield unless if_ + + begin + original_warn = Warning.method(:warn) + Warning.singleton_class.define_method(:warn) do |message| + original_warn.call(message) unless message =~ pattern + end + + yield + ensure + Warning.singleton_class.undef_method(:warn) + Warning.singleton_class.define_method(:warn, original_warn) + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/impersonating_any_instance_name.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/impersonating_any_instance_name.rb new file mode 100644 index 0000000..2e3ffd9 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/impersonating_any_instance_name.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Mocha + class ImpersonatingAnyInstanceName + def initialize(klass) + @klass = klass + end + + def mocha_inspect + "#" + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/impersonating_name.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/impersonating_name.rb new file mode 100644 index 0000000..8345e35 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/impersonating_name.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Mocha + class ImpersonatingName + def initialize(object) + @object = object + end + + def mocha_inspect + @object.mocha_inspect + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/in_state_ordering_constraint.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/in_state_ordering_constraint.rb new file mode 100644 index 0000000..a03af7b --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/in_state_ordering_constraint.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module Mocha + class InStateOrderingConstraint + def initialize(state_predicate) + @state_predicate = state_predicate + end + + def allows_invocation_now? + @state_predicate.active? + end + + def mocha_inspect + "when #{@state_predicate.mocha_inspect}" + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/inspect.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/inspect.rb new file mode 100755 index 0000000..2adb54f --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/inspect.rb @@ -0,0 +1,73 @@ +# frozen_string_literal: true + +require 'date' + +module Mocha + module Inspect + module ObjectMethods + def mocha_inspect + address = __id__ * 2 + address += 0x100000000 if address < 0 + inspect =~ /#x', address: address)}>" : inspect + end + end + + module ArrayMethods + def mocha_inspect(wrapped: true) + unwrapped = collect(&:mocha_inspect).join(', ') + wrapped ? "[#{unwrapped}]" : unwrapped + end + end + + module HashMethods + def mocha_inspect + unwrapped = collect do |key, value| + case key + when Symbol + "#{key}: #{value.mocha_inspect}" + else + "#{key.mocha_inspect} => #{value.mocha_inspect}" + end + end.join(', ') + + if Hash.ruby2_keywords_hash?(self) + empty? ? '**{}' : unwrapped + else + "{#{unwrapped}}" + end + end + end + + module TimeMethods + def mocha_inspect + "#{inspect} (#{to_f} secs)" + end + end + + module DateMethods + def mocha_inspect + to_s + end + end + end +end + +class Object + include Mocha::Inspect::ObjectMethods +end + +class Array + include Mocha::Inspect::ArrayMethods +end + +class Hash + include Mocha::Inspect::HashMethods +end + +class Time + include Mocha::Inspect::TimeMethods +end + +class Date + include Mocha::Inspect::DateMethods +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/instance_method.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/instance_method.rb new file mode 100644 index 0000000..2d9f09a --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/instance_method.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +require 'mocha/stubbed_method' + +module Mocha + class InstanceMethod < StubbedMethod + private + + def stubbee + stubba_object + end + + def stubbee_method(method_name) + stubba_object._method(method_name) + end + + def original_method_owner + stubba_object.singleton_class + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/integration.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/integration.rb new file mode 100644 index 0000000..a54d050 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/integration.rb @@ -0,0 +1,5 @@ +module Mocha + # Contains adapters that provide built-in support for +Minitest+ and +Test::Unit+. + module Integration + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/integration/assertion_counter.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/integration/assertion_counter.rb new file mode 100644 index 0000000..f7420d4 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/integration/assertion_counter.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Mocha + module Integration + class AssertionCounter + def initialize(test_case) + @test_case = test_case + end + + def increment + @test_case.assert(true) + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/integration/minitest.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/integration/minitest.rb new file mode 100644 index 0000000..b795867 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/integration/minitest.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require 'mocha/detection/minitest' +require 'mocha/integration/minitest/adapter' + +module Mocha + module Integration + module Minitest + def self.activate # rubocop:disable Naming/PredicateMethod + target = Detection::Minitest.testcase + return false unless target + + minitest_version = Gem::Version.new(Detection::Minitest.version) + warn "Detected Minitest version: #{minitest_version}" if $DEBUG + + unless Minitest::Adapter.applicable_to?(minitest_version) + raise 'Versions of minitest earlier than v3.3.0 are not supported.' + end + + unless target < Minitest::Adapter + warn "Applying #{Minitest::Adapter.description}" if $DEBUG + target.send(:include, Minitest::Adapter) + end + + true + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/integration/minitest/adapter.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/integration/minitest/adapter.rb new file mode 100644 index 0000000..3facb1f --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/integration/minitest/adapter.rb @@ -0,0 +1,71 @@ +# frozen_string_literal: true + +require 'mocha/api' +require 'mocha/integration/assertion_counter' +require 'mocha/expectation_error_factory' + +module Mocha + module Integration + # Contains {Adapter} that integrates Mocha into recent versions of Minitest. + module Minitest + # Integrates Mocha into recent versions of Minitest. + # + # See the source code for an example of how to integrate Mocha into a test library. + module Adapter + include Mocha::API + + # @private + def self.applicable_to?(minitest_version) + Gem::Requirement.new('>= 3.3.0').satisfied_by?(minitest_version) + end + + # @private + def self.description + 'adapter for Minitest gem >= v3.3.0' + end + + # @private + def self.included(_mod) + Mocha::ExpectationErrorFactory.exception_class = ::Minitest::Assertion + end + + # @private + def before_setup + assertion_counter = Integration::AssertionCounter.new(self) + mocha_setup(assertion_counter) + super + end + + # @private + def before_teardown + return unless passed? + + mocha_verify + ensure + super + end + + # @private + def after_teardown + super + mocha_teardown + end + + # @private + def mocha_test_name + if respond_to?(:name) + test_name = name + elsif respond_to?(:__name__) # Older minitest + test_name = __name__ + end + + if test_name + "#{self.class.name}##{test_name}" + else + self.class.name + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/integration/monkey_patcher.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/integration/monkey_patcher.rb new file mode 100644 index 0000000..59eefee --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/integration/monkey_patcher.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +require 'mocha/api' + +module Mocha + module Integration + module MonkeyPatcher + def self.apply(mod, run_method_patch) + if mod < Mocha::API + warn "Mocha::API already included in #{mod}" if $DEBUG + else + mod.send(:include, Mocha::API) + end + if mod.method_defined?(:run_before_mocha) + warn "#{mod}#run_before_mocha method already defined" if $DEBUG + elsif mod.method_defined?(:run) + mod.send(:alias_method, :run_before_mocha, :run) + mod.send(:remove_method, :run) + mod.send(:include, run_method_patch) + else + raise "Unable to monkey-patch #{mod}, because it does not define a `#run` method" + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/integration/test_unit.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/integration/test_unit.rb new file mode 100644 index 0000000..465a448 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/integration/test_unit.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require 'mocha/detection/test_unit' +require 'mocha/integration/test_unit/adapter' + +module Mocha + module Integration + module TestUnit + def self.activate # rubocop:disable Naming/PredicateMethod + target = Detection::TestUnit.testcase + return false unless target + + test_unit_version = Gem::Version.new(Detection::TestUnit.version) + warn "Detected Test::Unit version: #{test_unit_version}" if $DEBUG + + unless TestUnit::Adapter.applicable_to?(test_unit_version) + raise 'Versions of test-unit earlier than v2.5.1 are not supported.' + end + + unless target < TestUnit::Adapter + warn "Applying #{TestUnit::Adapter.description}" if $DEBUG + target.send(:include, TestUnit::Adapter) + end + + true + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/integration/test_unit/adapter.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/integration/test_unit/adapter.rb new file mode 100644 index 0000000..1f3c659 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/integration/test_unit/adapter.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +require 'mocha/api' +require 'mocha/integration/assertion_counter' +require 'mocha/expectation_error' + +module Mocha + module Integration + # Contains {Adapter} that integrates Mocha into recent versions of Test::Unit. + module TestUnit + # Integrates Mocha into recent versions of Test::Unit. + # + # See the source code for an example of how to integrate Mocha into a test library. + module Adapter + include Mocha::API + + # @private + def self.applicable_to?(test_unit_version) + Gem::Requirement.new('>= 2.5.1').satisfied_by?(test_unit_version) + end + + # @private + def self.description + 'adapter for Test::Unit gem >= v2.5.1' + end + + # @private + def self.included(mod) + mod.setup before: :prepend do + assertion_counter = Integration::AssertionCounter.new(self) + mocha_setup(assertion_counter) + end + + mod.exception_handler(:handle_mocha_expectation_error) + + mod.cleanup after: :append do + mocha_verify + end + + mod.teardown :mocha_teardown, after: :append + end + + private + + # @private + def mocha_test_name + name + end + + # @private + def handle_mocha_expectation_error(exception) # rubocop:disable Naming/PredicateMethod + return false unless exception.is_a?(Mocha::ExpectationError) + + problem_occurred + add_failure(exception.message, exception.backtrace) + true + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/invocation.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/invocation.rb new file mode 100644 index 0000000..a7a32f3 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/invocation.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true + +require 'mocha/parameters_matcher' +require 'mocha/raised_exception' +require 'mocha/return_values' +require 'mocha/thrown_object' +require 'mocha/yield_parameters' + +module Mocha + class Invocation + attr_reader :method_name, :block + + def initialize(mock, method_name, arguments = [], block = nil) + @mock = mock + @method_name = method_name + @arguments = arguments + @block = block + @yields = [] + @result = nil + end + + def call(yield_parameters = YieldParameters.new, return_values = ReturnValues.new) + yield_parameters.next_invocation.each do |yield_args| + @yields << ParametersMatcher.new(yield_args) + raise LocalJumpError unless @block + + @block.call(*yield_args) + end + return_values.next(self) + end + + def returned(value) + @result = value + end + + def raised(exception) + @result = RaisedException.new(exception) + end + + def threw(tag, value) + @result = ThrownObject.new(tag, value) + end + + def arguments + @arguments.dup + end + + def call_description + strings = ["#{@mock.mocha_inspect}.#{@method_name}#{argument_description}"] + strings << ' { ... }' unless @block.nil? + strings.join + end + + def short_call_description + "#{@method_name}(#{@arguments.join(', ')})" + end + + def result_description + strings = ["# => #{@result.mocha_inspect}"] + strings << " after yielding #{@yields.map(&:mocha_inspect).join(', then ')}" if @yields.any? + strings.join + end + + def full_description + "\n - #{call_description} #{result_description}" + end + + private + + def argument_description + signature = arguments.mocha_inspect + signature = signature.gsub(/^\[|\]$/, '') + "(#{signature})" + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/logger.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/logger.rb new file mode 100644 index 0000000..9254e42 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/logger.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Mocha + class Logger + def initialize(io) + @io = io + end + + def warn(message) + @io.puts "WARNING: #{message}" + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/macos_version.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/macos_version.rb new file mode 100644 index 0000000..a1391da --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/macos_version.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +module Mocha + MACOS = /darwin/.match(RUBY_PLATFORM) + MACOS_VERSION = MACOS && /darwin(\d+)/.match(RUBY_PLATFORM)[1].to_i + MACOS_MOJAVE_VERSION = 18 +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/method_matcher.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/method_matcher.rb new file mode 100644 index 0000000..077ea8b --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/method_matcher.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Mocha + class MethodMatcher + attr_reader :expected_method_name + + def initialize(expected_method_name) + @expected_method_name = expected_method_name + end + + def match?(actual_method_name) + @expected_method_name == actual_method_name.to_sym + end + + def mocha_inspect + @expected_method_name.to_s + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/minitest.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/minitest.rb new file mode 100644 index 0000000..dbbe257 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/minitest.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +require 'mocha/integration/minitest' + +unless Mocha::Integration::Minitest.activate + raise "Minitest must be loaded *before* `require 'mocha/minitest'`." +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/mock.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/mock.rb new file mode 100644 index 0000000..26ab70f --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/mock.rb @@ -0,0 +1,427 @@ +# frozen_string_literal: true + +require 'ruby2_keywords' +require 'mocha/expectation' +require 'mocha/expectation_list' +require 'mocha/invocation' +require 'mocha/default_name' +require 'mocha/default_receiver' +require 'mocha/method_matcher' +require 'mocha/parameters_matcher' +require 'mocha/argument_iterator' +require 'mocha/expectation_error_factory' +require 'mocha/deprecation' + +module Mocha + # Traditional mock object. + # + # {expects} and {stubs} return an {Expectation} which can be further modified + # by methods on {Expectation}. + # + # {responds_like} and {responds_like_instance_of} both return a {Mock}, and + # can therefore, be chained to the original creation methods in {API}. + # They force the mock to indicate what it is supposed to be mocking, thus + # making it a safer verifying mock. They check that the underlying +responder+ + # will actually respond to the methods being stubbed, throwing a + # +NoMethodError+ upon invocation otherwise. + # + # Stubs and expectations are basically the same thing. A stub is just an + # expectation of zero or more invocations. The {#stubs} method is syntactic + # sugar to make the intent of the test more explicit. + # + # When a method is invoked on a mock object, the mock object searches through + # its expectations from newest to oldest to find one that matches the + # invocation. After the invocation, the matching expectation might stop + # matching further invocations. For example, an +expects(:foo).once+ + # expectation only matches once and will be ignored on future invocations + # while an +expects(:foo).at_least_once+ expectation will always be matched + # against invocations. + # + # However, note that if the expectation that matches the invocation has a + # cardinality of "never", then an unexpected invocation error is reported. + # + # This scheme allows you to: + # + # - Set up default stubs in your the +setup+ method of your test class and + # override some of those stubs in individual tests. + # - Set up different +once+ expectations for the same method with different + # action per invocation. However, it's better to use the + # {Expectation#returns} method with multiple arguments to do this, as + # described below. + # + # However, there are some possible "gotchas" caused by this scheme: + # + # - if you create an expectation and then a stub for the same method, the + # stub will always override the expectation and the expectation will never + # be met. + # - if you create a stub and then an expectation for the same method, the + # expectation will match, and when it stops matching the stub will be used + # instead, possibly masking test failures. + # - if you create different expectations for the same method, they will be + # invoked in the opposite order than that in which they were specified, + # rather than the same order. + # + # The best thing to do is not set up multiple expectations and stubs for the + # same method with exactly the same matchers. Instead, use the + # {Expectation#returns} method with multiple arguments to create multiple + # actions for a method. You can also chain multiple calls to + # {Expectation#returns} and {Expectation#raises} (along with syntactic sugar + # {Expectation#then} if desired). + # + # @example + # object = mock() + # object.stubs(:expected_method).returns(1, 2).then.raises(Exception) + # object.expected_method # => 1 + # object.expected_method # => 2 + # object.expected_method # => raises exception of class Exception1 + # + # If you want to specify more complex ordering or order invocations across + # different mock objects, use the {Expectation#in_sequence} method to + # explicitly define a total or partial ordering of invocations. + class Mock + # Adds an expectation that the specified method must be called exactly once with any parameters. + # + # @return [Expectation] last-built expectation which can be further modified by methods on {Expectation}. + # + # @overload def expects(method_name) + # @param [Symbol,String] method_name name of expected method + # @overload def expects(expected_methods_vs_return_values) + # @param [Hash] expected_methods_vs_return_values expected method name symbols as keys and corresponding return values as values - these expectations are setup as if {#expects} were called multiple times. + # + # @example Expected method invoked once so no error raised + # object = mock() + # object.expects(:expected_method) + # object.expected_method + # + # @example Expected method not invoked so error raised + # object = mock() + # object.expects(:expected_method) + # # error raised when test completes, because expected_method not called exactly once + # + # @example Expected method invoked twice so error raised + # object = mock() + # object.expects(:expected_method) + # object.expected_method + # object.expected_method # => error raised when expected method invoked second time + # + # @example Setup multiple expectations using +expected_methods_vs_return_values+. + # object = mock() + # object.expects(expected_method_one: :result_one, expected_method_two: :result_two) + # + # # is exactly equivalent to + # + # object = mock() + # object.expects(:expected_method_one).returns(:result_one) + # object.expects(:expected_method_two).returns(:result_two) + def expects(method_name_or_hash, backtrace = nil) + expectation = nil + iterator = ArgumentIterator.new(method_name_or_hash) + iterator.each do |method_name, *args| + ensure_method_not_already_defined(method_name) + expectation = Expectation.new(self, method_name, backtrace) + expectation.in_sequence(@mockery.sequences.last) if @mockery.sequences.any? + expectation.returns(args.shift) unless args.empty? + @expectations.add(expectation) + end + expectation + end + + # Adds an expectation that the specified method may be called any number of times with any parameters. + # + # @return [Expectation] last-built expectation which can be further modified by methods on {Expectation}. + # + # @overload def stubs(method_name) + # @param [Symbol,String] method_name name of stubbed method + # @overload def stubs(stubbed_methods_vs_return_values) + # @param [Hash] stubbed_methods_vs_return_values stubbed method name symbols as keys and corresponding return values as values - these stubbed methods are setup as if {#stubs} were called multiple times. + # + # @example No error raised however many times stubbed method is invoked + # object = mock() + # object.stubs(:stubbed_method) + # object.stubbed_method + # object.stubbed_method + # # no error raised + # + # @example Setup multiple expectations using +stubbed_methods_vs_return_values+. + # object = mock() + # object.stubs(stubbed_method_one: :result_one, stubbed_method_two: :result_two) + # + # # is exactly equivalent to + # + # object = mock() + # object.stubs(:stubbed_method_one).returns(:result_one) + # object.stubs(:stubbed_method_two).returns(:result_two) + def stubs(method_name_or_hash, backtrace = nil) + expectation = nil + iterator = ArgumentIterator.new(method_name_or_hash) + iterator.each do |method_name, *args| + ensure_method_not_already_defined(method_name) + expectation = Expectation.new(self, method_name, backtrace) + expectation.at_least(0) + expectation.in_sequence(@mockery.sequences.last) if @mockery.sequences.any? + expectation.returns(args.shift) unless args.empty? + @expectations.add(expectation) + end + expectation + end + + # Removes the specified stubbed methods (added by calls to {#expects} or {#stubs}) and all expectations associated with them. + # + # @param [Array] method_names names of methods to unstub. + # + # @example Invoking an unstubbed method causes error to be raised + # object = mock('mock') + # object.stubs(:stubbed_method).returns(:result_one) + # object.stubbed_method # => :result_one + # object.unstub(:stubbed_method) + # object.stubbed_method # => unexpected invocation: #.stubbed_method() + # + # @example Unstubbing multiple methods. + # multiplier.unstub(:double, :triple) + # + # # exactly equivalent to + # + # multiplier.unstub(:double) + # multiplier.unstub(:triple) + def unstub(*method_names) + method_names.each do |method_name| + @expectations.remove_all_matching_method(method_name) + end + end + + # Constrains the {Mock} instance so that it can only expect or stub methods to which +responder+ responds publicly. The constraint is only applied at method invocation time. + # + # A +NoMethodError+ will be raised if the +responder+ does not publicly +#respond_to?+ the invoked method (even if the method has been expected or stubbed). + # + # The {Mock} instance will delegate its +#respond_to?+ method to the +responder+. However, the +include_all+ parameter is not passed through, so only public methods on the +responder+ will be considered. + # + # Note that the methods on +responder+ are never actually invoked. + # + # @param [Object, #respond_to?] responder an object used to determine whether {Mock} instance should +#respond_to?+ to an invocation. + # @return [Mock] the same {Mock} instance, thereby allowing invocations of other {Mock} methods to be chained. + # @see #responds_like_instance_of + # + # @example Normal mocking + # sheep = mock('sheep') + # sheep.expects(:chew) + # sheep.expects(:foo) + # sheep.respond_to?(:chew) # => true + # sheep.respond_to?(:foo) # => true + # sheep.chew + # sheep.foo + # # no error raised + # + # @example Using {#responds_like} with an instance method + # class Sheep + # def chew(grass); end + # end + # + # sheep = mock('sheep') + # sheep.responds_like(Sheep.new) + # sheep.expects(:chew) + # sheep.expects(:foo) + # sheep.respond_to?(:chew) # => true + # sheep.respond_to?(:foo) # => false + # sheep.chew + # sheep.foo # => raises NoMethodError exception + # + # @example Using {#responds_like} with a class method + # class Sheep + # def self.number_of_legs; end + # end + # + # sheep_class = mock('sheep_class') + # sheep_class.responds_like(Sheep) + # sheep_class.stubs(:number_of_legs).returns(4) + # sheep_class.expects(:foo) + # sheep_class.respond_to?(:number_of_legs) # => true + # sheep_class.respond_to?(:foo) # => false + # sheep_class.number_of_legs # => 4 + # sheep_class.foo # => raises NoMethodError exception + def responds_like(responder) + @responder = responder + self + end + + # Constrains the {Mock} instance so that it can only expect or stub methods to which an instance of the +responder_class+ responds publicly. The constraint is only applied at method invocation time. Note that the responder instance is instantiated using +Class#allocate+. + # + # A +NoMethodError+ will be raised if the responder instance does not publicly +#respond_to?+ the invoked method (even if the method has been expected or stubbed). + # + # The {Mock} instance will delegate its +#respond_to?+ method to the responder instance. However, the +include_all+ parameter is not passed through, so only public methods on the +responder+ will be considered. + # + # Note that the methods on the responder instance are never actually invoked. + # + # @param [Class] responder_class a class used to determine whether {Mock} instance should +#respond_to?+ to an invocation. + # @return [Mock] the same {Mock} instance, thereby allowing invocations of other {Mock} methods to be chained. + # @see #responds_like + # + # @example Using {#responds_like_instance_of} + # class Sheep + # def initialize + # raise "some awkward code we don't want to call" + # end + # def chew(grass); end + # end + # + # sheep = mock('sheep') + # sheep.responds_like_instance_of(Sheep) + # sheep.expects(:chew) + # sheep.expects(:foo) + # sheep.respond_to?(:chew) # => true + # sheep.respond_to?(:foo) # => false + # sheep.chew + # sheep.foo # => raises NoMethodError exception + def responds_like_instance_of(responder_class) + responds_like(responder_class.allocate) + end + + # @private + def initialize(mockery, assertion_counter, name = nil, receiver = nil) + @mockery = mockery + @assertion_counter = assertion_counter + @name = name || DefaultName.new(self) + @receiver = receiver || DefaultReceiver.new(self) + @expectations = ExpectationList.new + @everything_stubbed = false + @responder = nil + @unexpected_invocation = nil + @expired = false + end + + # @private + attr_reader :everything_stubbed + + alias_method :__expects__, :expects + + alias_method :__stubs__, :stubs + + alias_method :__singleton_class__, :singleton_class + + alias_method :quacks_like, :responds_like + alias_method :quacks_like_instance_of, :responds_like_instance_of + + # @private + def __expectations__ + @expectations + end + + # @private + def stub_everything + @everything_stubbed = true + end + + # @private + def all_expectations + @receiver.mocks.inject(ExpectationList.new) { |e, m| e + m.__expectations__ } + end + + # @private + def method_missing(symbol, *arguments, &block) + handle_method_call(symbol, arguments, block) + end + ruby2_keywords(:method_missing) + + # @private + def handle_method_call(symbol, arguments, block) + check_expiry + check_responder_responds_to(symbol) + invocation = Invocation.new(self, symbol, arguments, block) + + matching_expectations = all_expectations.matching_expectations(invocation) + + index = 0 + while index < matching_expectations.length + matching_expectation = matching_expectations[index] + if matching_expectation.invocations_never_allowed? + raise_unexpected_invocation_error(invocation, matching_expectation) + elsif matching_expectation.invocations_allowed? + return matching_expectation.invoke(invocation) + end + index += 1 + end + + matching_expectation_ignoring_order = all_expectations.match(invocation, ignoring_order: true) + if matching_expectation_ignoring_order || (!matching_expectation_ignoring_order && !@everything_stubbed) # rubocop:disable Style/GuardClause + raise_unexpected_invocation_error(invocation, matching_expectation_ignoring_order) + end + end + + # @private + def respond_to_missing?(symbol, _include_all) + if @responder + @responder.respond_to?(symbol) + else + @everything_stubbed || all_expectations.matches_method?(symbol) + end + end + + # @private + def __verified__? + @expectations.verified?(@assertion_counter) + end + + # @private + def __expire__(origin) + @expired = origin || true + end + + # @private + def mocha_inspect + @name.mocha_inspect + end + + # @private + def inspect + mocha_inspect + end + + # @private + def ensure_method_not_already_defined(method_name) + __singleton_class__.send(:undef_method, method_name) if __singleton_class__.method_defined?(method_name) || __singleton_class__.private_method_defined?(method_name) + end + + # @private + def any_expectations? + @expectations.any? + end + + private + + def raise_unexpected_invocation_error(invocation, matching_expectation) + if @unexpected_invocation.nil? + @unexpected_invocation = invocation + if matching_expectation + matching_expectation.invoke(invocation) + @assertion_counter.increment + end + call_description = @unexpected_invocation.call_description + if matching_expectation && !matching_expectation.in_correct_order? + call_description += ' invoked out of order' + end + message = "#{call_description}\n#{@mockery.mocha_inspect}" + else + message = @unexpected_invocation.short_call_description + end + raise ExpectationErrorFactory.build("unexpected invocation: #{message}", caller) + end + + def check_responder_responds_to(symbol) + if @responder && !@responder.respond_to?(symbol) # rubocop:disable Style/GuardClause + raise NoMethodError, "undefined method `#{symbol}' for #{mocha_inspect} which responds like #{@responder.mocha_inspect}" + end + end + + def check_expiry + return unless @expired + + origin = @expired == true ? 'one test' : @expired + + sentences = [ + "#{mocha_inspect} was instantiated in #{origin} but it is receiving invocations within another test.", + 'This can lead to unintended interactions between tests and hence unexpected test failures.', + 'Ensure that every test correctly cleans up any state that it introduces.' + ] + raise StubbingError.new(sentences.join(' '), caller) + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/mockery.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/mockery.rb new file mode 100644 index 0000000..ae69756 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/mockery.rb @@ -0,0 +1,199 @@ +# frozen_string_literal: true + +require 'mocha/central' +require 'mocha/mock' +require 'mocha/name' +require 'mocha/impersonating_name' +require 'mocha/impersonating_any_instance_name' +require 'mocha/object_receiver' +require 'mocha/any_instance_receiver' +require 'mocha/state_machine' +require 'mocha/logger' +require 'mocha/configuration' +require 'mocha/stubbing_error' +require 'mocha/not_initialized_error' +require 'mocha/expectation_error_factory' + +module Mocha + class Mockery + class Null < self + def self.build + new(nil) + end + + def add_mock(*) + raise_not_initialized_error + end + + def add_state_machine(*) + raise_not_initialized_error + end + + def stubba + Central::Null.new(&method(:raise_not_initialized_error)) + end + + private + + def raise_not_initialized_error + message = 'Mocha methods cannot be used outside the context of a test' + raise NotInitializedError.new(message, caller) + end + end + + class << self + def instance + @instances.last || Null.build + end + + def setup(assertion_counter) + @instances ||= [] + mockery = new(assertion_counter) + mockery.logger = instance.logger unless @instances.empty? + @instances.push(mockery) + end + + def verify + instance.verify + end + + def teardown(origin = nil) + if @instances.nil? + raise NotInitializedError, 'Mocha::Mockery.teardown called before Mocha::Mockery.setup' + end + + instance.teardown(origin) + ensure + @instances.pop unless @instances.nil? + end + end + + def initialize(assertion_counter) + @assertion_counter = assertion_counter + end + + def named_mock(name) + add_mock(Mock.new(self, @assertion_counter, Name.new(name))) + end + + def unnamed_mock + add_mock(Mock.new(self, @assertion_counter)) + end + + def mock_impersonating(object) + add_mock(Mock.new(self, @assertion_counter, ImpersonatingName.new(object), ObjectReceiver.new(object))) + end + + def mock_impersonating_any_instance_of(klass) + add_mock(Mock.new(self, @assertion_counter, ImpersonatingAnyInstanceName.new(klass), AnyInstanceReceiver.new(klass))) + end + + def new_state_machine(name) + add_state_machine(StateMachine.new(name)) + end + + def verify + unless mocks.all?(&:__verified__?) + message = "not all expectations were satisfied\n#{mocha_inspect}" + backtrace = if unsatisfied_expectations.empty? + caller + else + unsatisfied_expectations[0].backtrace + end + raise ExpectationErrorFactory.build(message, backtrace) + end + expectations.reject(&:used?).each do |expectation| + signature_proc = lambda { expectation.method_signature } + check(:stubbing_method_unnecessarily, 'method unnecessarily', signature_proc, expectation.backtrace) + end + end + + def teardown(origin = nil) + stubba.unstub_all + mocks.each { |m| m.__expire__(origin) } + reset + end + + def stubba + @stubba ||= Central.new + end + + def mocks + @mocks ||= [] + end + + def state_machines + @state_machines ||= [] + end + + def sequences + @sequences ||= [] + end + + def mocha_inspect + lines = [] + lines << "unsatisfied expectations:\n- #{unsatisfied_expectations.map(&:mocha_inspect).join("\n- ")}\n" if unsatisfied_expectations.any? + lines << "satisfied expectations:\n- #{satisfied_expectations.map(&:mocha_inspect).join("\n- ")}\n" if satisfied_expectations.any? + lines << "states:\n- #{state_machines.map(&:mocha_inspect).join("\n- ")}\n" if state_machines.any? + lines.join + end + + def on_stubbing(object, method) + signature_proc = lambda { "#{object.mocha_inspect}.#{method}" } + check(:stubbing_non_existent_method, 'non-existent method', signature_proc) do + !(object.stubba_class.__method_exists__?(method) || object.stubba_respond_to?(method)) + end + check(:stubbing_non_public_method, 'non-public method', signature_proc) do + object.stubba_class.__method_exists__?(method, include_public_methods: false) + end + check(:stubbing_method_on_non_mock_object, 'method on non-mock object', signature_proc) + end + + attr_writer :logger + + def logger + @logger ||= Logger.new($stderr) + end + + private + + def check(action, description, signature_proc, backtrace = caller) + treatment = Mocha.configuration.send(action) + return if (treatment == :allow) || (block_given? && !yield) + + method_signature = signature_proc.call + message = "stubbing #{description}: #{method_signature}" + raise StubbingError.new(message, backtrace) if treatment == :prevent + + logger.warn(message) if treatment == :warn + end + + def expectations + mocks.map { |mock| mock.__expectations__.to_a }.flatten + end + + def unsatisfied_expectations + expectations.reject(&:verified?) + end + + def satisfied_expectations + expectations.select(&:verified?) + end + + def add_mock(mock) + mocks << mock + mock + end + + def add_state_machine(state_machine) + state_machines << state_machine + state_machine + end + + def reset + @stubba = nil + @mocks = nil + @state_machines = nil + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/name.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/name.rb new file mode 100644 index 0000000..dfaa601 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/name.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Mocha + class Name + def initialize(name) + @name = name.to_s + end + + def mocha_inspect + "#" + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/not_initialized_error.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/not_initialized_error.rb new file mode 100644 index 0000000..e2b4ec4 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/not_initialized_error.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +require 'mocha/error_with_filtered_backtrace' + +module Mocha + # Exception raised when Mocha has not been initialized, e.g. outside the + # context of a test. + class NotInitializedError < ErrorWithFilteredBacktrace; end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/object_methods.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/object_methods.rb new file mode 100644 index 0000000..cf63bb5 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/object_methods.rb @@ -0,0 +1,183 @@ +# frozen_string_literal: true + +require 'mocha/mockery' +require 'mocha/instance_method' +require 'mocha/argument_iterator' +require 'mocha/expectation_error_factory' +require 'mocha/ignoring_warning' + +module Mocha + # Methods added to all objects to allow mocking and stubbing on real (i.e. non-mock) objects. + # + # Both {#expects} and {#stubs} return an {Expectation} which can be further modified by methods on {Expectation}. + module ObjectMethods + extend IgnoringWarning + + # @private + JRUBY_ALIAS_SPECIAL_METHODS_WARNING = /accesses caller method's state and should not be aliased/.freeze + + ignoring_warning(JRUBY_ALIAS_SPECIAL_METHODS_WARNING, if_: RUBY_ENGINE == 'jruby') do + alias_method :_method, :method + end + + # @private + def mocha(instantiate: true) + if instantiate + @mocha ||= Mocha::Mockery.instance.mock_impersonating(self) + else + defined?(@mocha) ? @mocha : nil + end + end + + # @private + def reset_mocha + @mocha = nil + end + + # @private + def stubba_method + Mocha::InstanceMethod + end + + # @private + def stubba_object + self + end + + # @private + def stubba_class + singleton_class + end + + # @private + def stubba_respond_to?(symbol) + respond_to?(symbol) + end + + # Adds an expectation that the specified method must be called exactly once with any parameters. + # + # The original implementation of the method is replaced during the test and then restored at the end of the test. The temporary replacement method has the same visibility as the original method. + # + # @return [Expectation] last-built expectation which can be further modified by methods on {Expectation}. + # @raise [StubbingError] if attempting to stub method which is not allowed. + # + # @overload def expects(method_name) + # @param [Symbol,String] method_name name of expected method + # @overload def expects(expected_methods_vs_return_values) + # @param [Hash] expected_methods_vs_return_values expected method name symbols as keys and corresponding return values as values - these expectations are setup as if {#expects} were called multiple times. + # + # @example Setting up an expectation on a non-mock object. + # product = Product.new + # product.expects(:save).returns(true) + # assert_equal true, product.save + # + # @example Setting up multiple expectations on a non-mock object. + # product = Product.new + # product.expects(valid?: true, save: true) + # + # # exactly equivalent to + # + # product = Product.new + # product.expects(:valid?).returns(true) + # product.expects(:save).returns(true) + # + # @see Mock#expects + def expects(expected_methods_vs_return_values) + if expected_methods_vs_return_values.to_s =~ /the[^a-z]*spanish[^a-z]*inquisition/i + raise ExpectationErrorFactory.build('NOBODY EXPECTS THE SPANISH INQUISITION!') + end + if frozen? + raise StubbingError.new("can't stub method on frozen object: #{mocha_inspect}", caller) + end + + expectation = nil + mockery = Mocha::Mockery.instance + iterator = ArgumentIterator.new(expected_methods_vs_return_values) + iterator.each do |method_name, *args| + mockery.on_stubbing(self, method_name) + method = stubba_method.new(stubba_object, method_name) + mockery.stubba.stub(method) + expectation = mocha.expects(method_name, caller) + expectation.returns(args.shift) unless args.empty? + end + expectation + end + + # Adds an expectation that the specified method may be called any number of times with any parameters. + # + # The original implementation of the method is replaced during the test and then restored at the end of the test. The temporary replacement method has the same visibility as the original method. + # + # @return [Expectation] last-built expectation which can be further modified by methods on {Expectation}. + # @raise [StubbingError] if attempting to stub method which is not allowed. + # + # @overload def stubs(method_name) + # @param [Symbol,String] method_name name of stubbed method + # @overload def stubs(stubbed_methods_vs_return_values) + # @param [Hash] stubbed_methods_vs_return_values stubbed method name symbols as keys and corresponding return values as values - these stubbed methods are setup as if {#stubs} were called multiple times. + # + # @example Setting up a stubbed methods on a non-mock object. + # product = Product.new + # product.stubs(:save).returns(true) + # assert_equal true, product.save + # + # @example Setting up multiple stubbed methods on a non-mock object. + # product = Product.new + # product.stubs(valid?: true, save: true) + # + # # exactly equivalent to + # + # product = Product.new + # product.stubs(:valid?).returns(true) + # product.stubs(:save).returns(true) + # + # @see Mock#stubs + def stubs(stubbed_methods_vs_return_values) + if frozen? + raise StubbingError.new("can't stub method on frozen object: #{mocha_inspect}", caller) + end + + expectation = nil + mockery = Mocha::Mockery.instance + iterator = ArgumentIterator.new(stubbed_methods_vs_return_values) + iterator.each do |method_name, *args| + mockery.on_stubbing(self, method_name) + method = stubba_method.new(stubba_object, method_name) + mockery.stubba.stub(method) + expectation = mocha.stubs(method_name, caller) + expectation.returns(args.shift) unless args.empty? + end + expectation + end + + # Removes the specified stubbed methods (added by calls to {#expects} or {#stubs}) and all expectations associated with them. + # + # Restores the original behaviour of the methods before they were stubbed. This is normally done automatically at the end of each test, but in some circumstances you may want to do it *before* the end of the test. + # + # WARNING: If you {#unstub} a method which still has unsatisfied expectations, you may be removing the only way those expectations can be satisfied. Use {#unstub} with care. + # + # @param [Array] method_names names of methods to unstub. + # + # @example Stubbing and unstubbing a method on a real (non-mock) object. + # multiplier = Multiplier.new + # multiplier.double(2) # => 4 + # multiplier.stubs(:double).raises # new behaviour defined + # multiplier.double(2) # => raises exception + # multiplier.unstub(:double) # original behaviour restored + # multiplier.double(2) # => 4 + # + # @example Unstubbing multiple methods on a real (non-mock) object. + # multiplier.unstub(:double, :triple) + # + # # exactly equivalent to + # + # multiplier.unstub(:double) + # multiplier.unstub(:triple) + def unstub(*method_names) + mockery = Mocha::Mockery.instance + method_names.each do |method_name| + method = stubba_method.new(stubba_object, method_name) + mockery.stubba.unstub(method) + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/object_receiver.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/object_receiver.rb new file mode 100644 index 0000000..4c408a4 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/object_receiver.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module Mocha + class ObjectReceiver + def initialize(object) + @object = object + end + + def mocks + object = @object + mocks = [] + while object + mocha = object.mocha(instantiate: false) + mocks << mocha if mocha + object = object.is_a?(Class) ? object.superclass : nil + end + mocks + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers.rb new file mode 100644 index 0000000..9849482 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module Mocha + # Matcher classes used as parameters for {Expectation#with} to restrict the parameter values which will match the expectation. Can be nested. Build matcher instances in tests using methods in {Methods}, e.g. {Methods#includes}. + module ParameterMatchers + # These methods build instances of the {ParameterMatchers} classes which are used with {Expectation#with} to restrict the parameter values. Can be nested, e.g. see {Methods#all_of} examples. + module Methods; end + end +end + +require 'mocha/parameter_matchers/instance_methods' + +require 'mocha/parameter_matchers/all_of' +require 'mocha/parameter_matchers/any_of' +require 'mocha/parameter_matchers/any_parameters' +require 'mocha/parameter_matchers/anything' +require 'mocha/parameter_matchers/equals' +require 'mocha/parameter_matchers/has_entry' +require 'mocha/parameter_matchers/has_entries' +require 'mocha/parameter_matchers/has_key' +require 'mocha/parameter_matchers/has_keys' +require 'mocha/parameter_matchers/has_value' +require 'mocha/parameter_matchers/includes' +require 'mocha/parameter_matchers/instance_of' +require 'mocha/parameter_matchers/is_a' +require 'mocha/parameter_matchers/kind_of' +require 'mocha/parameter_matchers/not' +require 'mocha/parameter_matchers/optionally' +require 'mocha/parameter_matchers/regexp_matches' +require 'mocha/parameter_matchers/responds_with' +require 'mocha/parameter_matchers/yaml_equivalent' +require 'mocha/parameter_matchers/equivalent_uri' diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/all_of.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/all_of.rb new file mode 100644 index 0000000..d37b056 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/all_of.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +require 'mocha/parameter_matchers/base_methods' + +module Mocha + module ParameterMatchers + module Methods + # Matches if all +matchers+ match. + # + # @param [*Array] matchers parameter matchers. + # @return [AllOf] parameter matcher. + # + # @see Expectation#with + # + # @example All parameter matchers match. + # object = mock() + # object.expects(:method_1).with(all_of(includes(1), includes(3))) + # object.method_1([1, 3]) + # # no error raised + # + # @example One of the parameter matchers does not match. + # object = mock() + # object.expects(:method_1).with(all_of(includes(1), includes(3))) + # object.method_1([1, 2]) + # # error raised, because method_1 was not called with object including 1 and 3 + def all_of(*matchers) + AllOf.new(*matchers) + end + end + + # Parameter matcher which combines a number of other matchers using a logical AND. + class AllOf + include BaseMethods + + # @private + def initialize(*matchers) + @matchers = matchers + end + + # @private + def matches?(available_parameters) + parameter = available_parameters.shift + @matchers.all? { |matcher| matcher.to_matcher.matches?([parameter]) } + end + + # @private + def mocha_inspect + "all_of(#{@matchers.map(&:mocha_inspect).join(', ')})" + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/any_of.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/any_of.rb new file mode 100644 index 0000000..c4dc673 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/any_of.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +require 'mocha/parameter_matchers/base_methods' + +module Mocha + module ParameterMatchers + module Methods + # Matches if any +matchers+ match. + # + # @param [*Array] matchers parameter matchers. + # @return [AnyOf] parameter matcher. + # + # @see Expectation#with + # + # @example One parameter matcher matches. + # object = mock() + # object.expects(:method_1).with(any_of(1, 3)) + # object.method_1(1) + # # no error raised + # + # @example The other parameter matcher matches. + # object = mock() + # object.expects(:method_1).with(any_of(1, 3)) + # object.method_1(3) + # # no error raised + # + # @example Neither parameter matcher matches. + # object = mock() + # object.expects(:method_1).with(any_of(1, 3)) + # object.method_1(2) + # # error raised, because method_1 was not called with 1 or 3 + def any_of(*matchers) + AnyOf.new(*matchers) + end + end + + # Parameter matcher which combines a number of other matchers using a logical OR. + class AnyOf + include BaseMethods + + # @private + def initialize(*matchers) + @matchers = matchers + end + + # @private + def matches?(available_parameters) + parameter = available_parameters.shift + @matchers.any? { |matcher| matcher.to_matcher.matches?([parameter]) } + end + + # @private + def mocha_inspect + "any_of(#{@matchers.map(&:mocha_inspect).join(', ')})" + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/any_parameters.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/any_parameters.rb new file mode 100644 index 0000000..ffabed6 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/any_parameters.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +require 'mocha/parameter_matchers/base_methods' + +module Mocha + module ParameterMatchers + module Methods + # Matches any parameters. This is used as the default for a newly built expectation. + # + # @return [AnyParameters] parameter matcher. + # + # @see Expectation#with + # + # @example Any parameters will match. + # object = mock() + # object.expects(:method_1).with(any_parameters) + # object.method_1(1, 2, 3, 4) + # # no error raised + # + # object = mock() + # object.expects(:method_1).with(any_parameters) + # object.method_1(5, 6, 7, 8, 9, 0) + # # no error raised + def any_parameters + AnyParameters.new + end + end + + # Parameter matcher which always matches whatever the parameters. + class AnyParameters + include BaseMethods + + # @private + def matches?(available_parameters) + until available_parameters.empty? + available_parameters.shift + end + true + end + + # @private + def mocha_inspect + 'any_parameters' + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/anything.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/anything.rb new file mode 100644 index 0000000..348ed9f --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/anything.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require 'mocha/parameter_matchers/base_methods' + +module Mocha + module ParameterMatchers + module Methods + # Matches any object. + # + # @return [Anything] parameter matcher. + # + # @see Expectation#with + # + # @example Any object will match. + # object = mock() + # object.expects(:method_1).with(anything) + # object.method_1('foo') + # object.method_1(789) + # object.method_1(:bar) + # # no error raised + def anything + Anything.new + end + end + + # Parameter matcher which always matches a single parameter. + class Anything + include BaseMethods + + # @private + def matches?(available_parameters) + available_parameters.shift + true + end + + # @private + def mocha_inspect + 'anything' + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/base_methods.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/base_methods.rb new file mode 100644 index 0000000..f0d877c --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/base_methods.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +require 'mocha/deprecation' + +module Mocha + module ParameterMatchers + # @abstract Include and implement +#matches?+ and +#mocha_inspect+ to define a custom matcher. Also add a suitably named instance method to {Methods} to build an instance of the new matcher c.f. {Methods#equals}. + module BaseMethods + # A shorthand way of combining two matchers when both must match. + # + # Returns a new {AllOf} parameter matcher combining two matchers using a logical AND. + # + # This shorthand will not work with an implicit equals match. Instead, an explicit {Equals} matcher should be used. + # + # @param [BaseMethods] other parameter matcher. + # @return [AllOf] parameter matcher. + # + # @see Expectation#with + # + # @example Alternative ways to combine matchers with a logical AND. + # object = mock() + # object.expects(:run).with(all_of(has_key(:foo), has_key(:bar))) + # object.run(foo: 'foovalue', bar: 'barvalue') + # + # # is exactly equivalent to + # + # object.expects(:run).with(has_key(:foo) & has_key(:bar)) + # object.run(foo: 'foovalue', bar: 'barvalue) + def &(other) + AllOf.new(self, other) + end + + # A shorthand way of combining two matchers when at least one must match. + # + # Returns a new +AnyOf+ parameter matcher combining two matchers using a logical OR. + # + # This shorthand will not work with an implicit equals match. Instead, an explicit {Equals} matcher should be used. + # + # @param [BaseMethods] other parameter matcher. + # @return [AnyOf] parameter matcher. + # + # @see Expectation#with + # + # @example Alternative ways to combine matchers with a logical OR. + # object = mock() + # object.expects(:run).with(any_of(has_key(:foo), has_key(:bar))) + # object.run(foo: 'foovalue') + # + # # is exactly equivalent to + # + # object.expects(:run).with(has_key(:foo) | has_key(:bar)) + # object.run(foo: 'foovalue') + # + # @example Using an explicit {Equals} matcher in combination with {#|}. + # object.expects(:run).with(equals(1) | equals(2)) + # object.run(1) # passes + # object.run(2) # passes + # object.run(3) # fails + def |(other) + AnyOf.new(self, other) + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/equals.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/equals.rb new file mode 100644 index 0000000..24d5b49 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/equals.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +require 'mocha/parameter_matchers/base_methods' + +module Mocha + module ParameterMatchers + module Methods + # Matches any +Object+ equalling +value+. + # + # @param [Object] value expected value. + # @return [Equals] parameter matcher. + # + # @see Expectation#with + # @see Object#== + # + # @example Actual parameter equals expected parameter. + # object = mock() + # object.expects(:method_1).with(equals(2)) + # object.method_1(2) + # # no error raised + # + # @example Actual parameter does not equal expected parameter. + # object = mock() + # object.expects(:method_1).with(equals(2)) + # object.method_1(3) + # # error raised, because method_1 was not called with an +Object+ that equals 2 + def equals(value) + Equals.new(value) + end + end + + # Parameter matcher which matches when actual parameter equals expected value. + class Equals + include BaseMethods + + # @private + def initialize(value) + @value = value + end + + # @private + def matches?(available_parameters) + parameter = available_parameters.shift + parameter == @value + end + + # @private + def mocha_inspect + @value.mocha_inspect + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/equivalent_uri.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/equivalent_uri.rb new file mode 100644 index 0000000..36fdad1 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/equivalent_uri.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +require 'mocha/parameter_matchers/base_methods' +require 'uri' + +module Mocha + module ParameterMatchers + module Methods + # Matches a URI without regard to the ordering of parameters in the query string. + # + # @param [String] uri URI to match. + # @return [EquivalentUri] parameter matcher. + # + # @see Expectation#with + # + # @example Actual URI is equivalent. + # object = mock() + # object.expects(:method_1).with(equivalent_uri('http://example.com/foo?a=1&b=2')) + # object.method_1('http://example.com/foo?b=2&a=1') + # # no error raised + # + # @example Actual URI is not equivalent. + # object = mock() + # object.expects(:method_1).with(equivalent_uri('http://example.com/foo?a=1&b=2')) + # object.method_1('http://example.com/foo?a=1&b=3') + # # error raised, because the query parameters were different + def equivalent_uri(uri) + EquivalentUri.new(uri) + end + end + + # Parameter matcher which matches URIs with equivalent query strings. + class EquivalentUri + include BaseMethods + + # @private + def initialize(uri) + @uri = URI.parse(uri) + end + + # @private + def matches?(available_parameters) + actual = explode(URI.parse(available_parameters.shift)) + expected = explode(@uri) + actual == expected + end + + # @private + def mocha_inspect + "equivalent_uri(#{@uri.mocha_inspect})" + end + + private + + # @private + def explode(uri) + query_hash = Hash.new { |hash, key| hash[key] = [] } + URI.decode_www_form(uri.query || '').each do |key, value| + query_hash[key] << value + end + URI::Generic::COMPONENT.inject({}) { |h, k| h.merge(k => uri.__send__(k)) }.merge(query: query_hash) + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/has_entries.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/has_entries.rb new file mode 100644 index 0000000..cae6e0c --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/has_entries.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +require 'mocha/parameter_matchers/base_methods' +require 'mocha/parameter_matchers/all_of' +require 'mocha/parameter_matchers/has_entry' + +module Mocha + module ParameterMatchers + module Methods + # Matches +Hash+ containing all +entries+. + # + # @param [Hash] entries expected +Hash+ entries. + # @return [HasEntries] parameter matcher. + # + # @see Expectation#with + # + # @example Actual parameter contains all expected entries. + # object = mock() + # object.expects(:method_1).with(has_entries('key_1' => 1, 'key_2' => 2)) + # object.method_1('key_1' => 1, 'key_2' => 2, 'key_3' => 3) + # # no error raised + # + # @example Actual parameter does not contain all expected entries. + # object = mock() + # object.expects(:method_1).with(has_entries('key_1' => 1, 'key_2' => 2)) + # object.method_1('key_1' => 1, 'key_2' => 99) + # # error raised, because method_1 was not called with Hash containing entries: 'key_1' => 1, 'key_2' => 2 + # + def has_entries(entries) # rubocop:disable Naming/PredicatePrefix + HasEntries.new(entries) + end + end + + # Parameter matcher which matches when actual parameter contains all expected +Hash+ entries. + class HasEntries + include BaseMethods + + # @private + def initialize(entries, exact: false) + @entries = entries + @exact = exact + end + + # @private + def matches?(available_parameters) + parameter = available_parameters.shift + return false unless parameter + return false unless parameter.respond_to?(:keys) + return false if @exact && @entries.length != parameter.keys.length + + has_entry_matchers = @entries.map { |key, value| HasEntry.new(key, value) } + AllOf.new(*has_entry_matchers).matches?([parameter]) + end + + # @private + def mocha_inspect + @exact ? @entries.mocha_inspect : "has_entries(#{@entries.mocha_inspect})" + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/has_entry.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/has_entry.rb new file mode 100644 index 0000000..4be6c92 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/has_entry.rb @@ -0,0 +1,103 @@ +# frozen_string_literal: true + +require 'mocha/parameter_matchers/base_methods' + +module Mocha + module ParameterMatchers + module Methods + # Matches +Hash+ containing entry with +key+ and +value+. + # + # @overload def has_entry(key, value) + # @param [Object] key key for entry. + # @param [Object] value value for entry. + # @overload def has_entry(single_entry_hash) + # @param [Hash] single_entry_hash +Hash+ with single entry. + # @raise [ArgumentError] if +single_entry_hash+ does not contain exactly one entry. + # + # @return [HasEntry] parameter matcher. + # + # @see Expectation#with + # + # @example Actual parameter contains expected entry supplied as key and value. + # object = mock() + # object.expects(:method_1).with(has_entry('key_1', 1)) + # object.method_1('key_1' => 1, 'key_2' => 2) + # # no error raised + # + # @example Actual parameter contains expected entry supplied as +Hash+ entry. + # object = mock() + # object.expects(:method_1).with(has_entry('key_1' => 1)) + # object.method_1('key_1' => 1, 'key_2' => 2) + # # no error raised + # + # @example Actual parameter does not contain expected entry supplied as key and value. + # object = mock() + # object.expects(:method_1).with(has_entry('key_1', 1)) + # object.method_1('key_1' => 2, 'key_2' => 1) + # # error raised, because method_1 was not called with Hash containing entry: 'key_1' => 1 + # + # @example Actual parameter does not contain expected entry supplied as +Hash+ entry. + # + # object = mock() + # object.expects(:method_1).with(has_entry('key_1' => 1)) + # object.method_1('key_1' => 2, 'key_2' => 1) + # # error raised, because method_1 was not called with Hash containing entry: 'key_1' => 1 + # + def has_entry(*options) # rubocop:disable Naming/PredicatePrefix + case options.length + when 0 + raise ArgumentError, 'No arguments. Expecting at least one.' + when 1 + key, value = HasEntry.parse_option(options[0]) + when 2 + key, value = options + else + raise ArgumentError, 'Too many arguments; use either a single argument (must be a Hash) or two arguments (a key and a value).' + end + HasEntry.new(key, value) + end + end + + # Parameter matcher which matches when actual parameter contains expected +Hash+ entry. + class HasEntry + include BaseMethods + + # @private + def initialize(key, value) + @key = key + @value = value + end + + # @private + def matches?(available_parameters) + parameter = available_parameters.shift + return false unless parameter.respond_to?(:keys) && parameter.respond_to?(:[]) + + matching_keys = parameter.keys.select { |key| @key.to_matcher.matches?([key]) } + matching_keys.any? { |key| @value.to_matcher.matches?([parameter[key]]) } + end + + # @private + def mocha_inspect + "has_entry(#{@key.mocha_inspect} => #{@value.mocha_inspect})" + end + + # @private + def self.parse_option(option) + case option + when Hash + case option.length + when 0 + raise ArgumentError, 'Argument has no entries.' + when 1 + option.first + else + raise ArgumentError, 'Argument has multiple entries. Use Mocha::ParameterMatchers#has_entries instead.' + end + else + raise ArgumentError, 'Argument is not a Hash.' + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/has_key.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/has_key.rb new file mode 100644 index 0000000..7e582d7 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/has_key.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +require 'mocha/parameter_matchers/base_methods' + +module Mocha + module ParameterMatchers + module Methods + # Matches +Hash+ containing +key+. + # + # @param [Object] key expected key. + # @return [HasKey] parameter matcher. + # + # @see Expectation#with + # + # @example Actual parameter contains entry with expected key. + # object = mock() + # object.expects(:method_1).with(has_key('key_1')) + # object.method_1('key_1' => 1, 'key_2' => 2) + # # no error raised + # + # @example Actual parameter does not contain entry with expected key. + # object = mock() + # object.expects(:method_1).with(has_key('key_1')) + # object.method_1('key_2' => 2) + # # error raised, because method_1 was not called with Hash containing key: 'key_1' + # + def has_key(key) # rubocop:disable Naming/PredicatePrefix + HasKey.new(key) + end + end + + # Parameter matcher which matches when actual parameter contains +Hash+ entry with expected key. + class HasKey + include BaseMethods + + # @private + def initialize(key) + @key = key + end + + # @private + def matches?(available_parameters) + parameter = available_parameters.shift + return false unless parameter.respond_to?(:keys) + + parameter.keys.any? { |key| @key.to_matcher.matches?([key]) } + end + + # @private + def mocha_inspect + "has_key(#{@key.mocha_inspect})" + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/has_keys.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/has_keys.rb new file mode 100644 index 0000000..d9a3bd5 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/has_keys.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +require 'mocha/parameter_matchers/base_methods' + +module Mocha + module ParameterMatchers + module Methods + # Matches +Hash+ containing +keys+. + # + # @param [*Array] keys expected keys. + # @return [HasKeys] parameter matcher. + # + # @see Expectation#with + # + # @example Actual parameter contains entry with expected keys. + # object = mock() + # object.expects(:method_1).with(has_keys(:key_1, :key_2)) + # object.method_1(:key_1 => 1, :key_2 => 2, :key_3 => 3) + # # no error raised + # + # @example Actual parameter does not contain all expected keys. + # object = mock() + # object.expects(:method_1).with(has_keys(:key_1, :key_2)) + # object.method_1(:key_2 => 2) + # # error raised, because method_1 was not called with Hash containing key: :key_1 + # + def has_keys(*keys) # rubocop:disable Naming/PredicatePrefix + HasKeys.new(*keys) + end + end + + # Parameter matcher which matches when actual parameter contains +Hash+ with all expected keys. + class HasKeys + include BaseMethods + + # @private + def initialize(*keys) + raise ArgumentError, 'No arguments. Expecting at least one.' if keys.empty? + + @keys = keys + end + + # @private + def matches?(available_parameters) + parameter = available_parameters.shift + return false unless parameter.respond_to?(:keys) + + @keys.map(&:to_matcher).all? do |matcher| + parameter.keys.any? { |key| matcher.matches?([key]) } + end + end + + # @private + def mocha_inspect + "has_keys(#{@keys.mocha_inspect(wrapped: false)})" + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/has_value.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/has_value.rb new file mode 100644 index 0000000..eb602c4 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/has_value.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +require 'mocha/parameter_matchers/base_methods' + +module Mocha + module ParameterMatchers + module Methods + # Matches +Hash+ containing +value+. + # + # @param [Object] value expected value. + # @return [HasValue] parameter matcher. + # + # @see Expectation#with + # + # @example Actual parameter contains entry with expected value. + # object = mock() + # object.expects(:method_1).with(has_value(1)) + # object.method_1('key_1' => 1, 'key_2' => 2) + # # no error raised + # + # @example Actual parameter does not contain entry with expected value. + # object = mock() + # object.expects(:method_1).with(has_value(1)) + # object.method_1('key_2' => 2) + # # error raised, because method_1 was not called with Hash containing value: 1 + # + def has_value(value) # rubocop:disable Naming/PredicatePrefix + HasValue.new(value) + end + end + + # Parameter matcher which matches when actual parameter contains +Hash+ entry with expected value. + class HasValue + include BaseMethods + + # @private + def initialize(value) + @value = value + end + + # @private + def matches?(available_parameters) + parameter = available_parameters.shift + return false unless parameter.respond_to?(:values) + + parameter.values.any? { |value| @value.to_matcher.matches?([value]) } + end + + # @private + def mocha_inspect + "has_value(#{@value.mocha_inspect})" + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/includes.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/includes.rb new file mode 100644 index 0000000..a0f7d63 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/includes.rb @@ -0,0 +1,105 @@ +# frozen_string_literal: true + +require 'mocha/parameter_matchers/all_of' +require 'mocha/parameter_matchers/base_methods' + +module Mocha + module ParameterMatchers + module Methods + # Matches any object that responds with +true+ to +include?(item)+ + # for all items. + # + # @param [*Array] items expected items. + # @return [Includes] parameter matcher. + # + # @see Expectation#with + # + # @example Actual parameter includes all items. + # object = mock() + # object.expects(:method_1).with(includes('foo', 'bar')) + # object.method_1(['foo', 'bar', 'baz']) + # # no error raised + # + # @example Actual parameter does not include all items. + # object.method_1(['foo', 'baz']) + # # error raised, because ['foo', 'baz'] does not include 'bar'. + # + # @example Actual parameter includes item which matches nested matcher. + # object = mock() + # object.expects(:method_1).with(includes(has_key(:key))) + # object.method_1(['foo', 'bar', {key: 'baz'}]) + # # no error raised + # + # @example Actual parameter does not include item matching nested matcher. + # object.method_1(['foo', 'bar', {:other_key => 'baz'}]) + # # error raised, because no element matches `has_key(:key)` matcher + # + # @example Actual parameter is a String including substring. + # object = mock() + # object.expects(:method_1).with(includes('bar')) + # object.method_1('foobarbaz') + # # no error raised + # + # @example Actual parameter is a String not including substring. + # object.method_1('foobaz') + # # error raised, because 'foobaz' does not include 'bar' + # + # @example Actual parameter is a Hash including the given key. + # object = mock() + # object.expects(:method_1).with(includes(:bar)) + # object.method_1({foo: 1, bar: 2}) + # # no error raised + # + # @example Actual parameter is a Hash without the given key. + # object.method_1({foo: 1, baz: 2}) + # # error raised, because hash does not include key 'bar' + # + # @example Actual parameter is a Hash with a key matching the given matcher. + # object = mock() + # object.expects(:method_1).with(includes(regexp_matches(/ar/))) + # object.method_1({'foo' => 1, 'bar' => 2}) + # # no error raised + # + # @example Actual parameter is a Hash no key matching the given matcher. + # object.method_1({'foo' => 1, 'baz' => 3}) + # # error raised, because hash does not include a key matching /ar/ + def includes(*items) + Includes.new(*items) + end + end + + # Parameter matcher which matches when actual parameter includes expected values. + class Includes + include BaseMethods + + # @private + def initialize(*items) + @items = items + end + + # @private + def matches?(available_parameters) + parameter = available_parameters.shift + return false unless parameter.respond_to?(:include?) + + if @items.size == 1 + if parameter.respond_to?(:any?) && !parameter.is_a?(String) + parameter = parameter.keys if parameter.is_a?(Hash) + parameter.any? { |p| @items.first.to_matcher.matches?([p]) } + else + parameter.include?(@items.first) + end + else + includes_matchers = @items.map { |item| Includes.new(item) } + AllOf.new(*includes_matchers).matches?([parameter]) + end + end + + # @private + def mocha_inspect + item_descriptions = @items.map(&:mocha_inspect) + "includes(#{item_descriptions.join(', ')})" + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/instance_methods.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/instance_methods.rb new file mode 100644 index 0000000..58ce52d --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/instance_methods.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require 'mocha/parameter_matchers/base_methods' +require 'mocha/parameter_matchers/equals' +require 'mocha/parameter_matchers/positional_or_keyword_hash' + +module Mocha + module ParameterMatchers + # @private + module InstanceMethods + # @private + def to_matcher(expectation: nil, top_level: false, last: false) + if is_a?(BaseMethods) + self + elsif Hash === self && top_level + Mocha::ParameterMatchers::PositionalOrKeywordHash.new(self, expectation, last) + else + Mocha::ParameterMatchers::Equals.new(self) + end + end + end + end +end + +# @private +class Object + include Mocha::ParameterMatchers::InstanceMethods +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/instance_of.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/instance_of.rb new file mode 100644 index 0000000..fdf4c5e --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/instance_of.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +require 'mocha/parameter_matchers/base_methods' + +module Mocha + module ParameterMatchers + module Methods + # Matches any object that is an instance of +klass+ + # + # @param [Class] klass expected class. + # @return [InstanceOf] parameter matcher. + # + # @see Expectation#with + # @see Kernel#instance_of? + # + # @example Actual parameter is an instance of +String+. + # object = mock() + # object.expects(:method_1).with(instance_of(String)) + # object.method_1('string') + # # no error raised + # + # @example Actual parameter is not an instance of +String+. + # object = mock() + # object.expects(:method_1).with(instance_of(String)) + # object.method_1(99) + # # error raised, because method_1 was not called with an instance of String + def instance_of(klass) + InstanceOf.new(klass) + end + end + + # Parameter matcher which matches when actual parameter is an instance of the specified class. + class InstanceOf + include BaseMethods + + # @private + def initialize(klass) + @klass = klass + end + + # @private + def matches?(available_parameters) + parameter = available_parameters.shift + parameter.instance_of?(@klass) + end + + # @private + def mocha_inspect + "instance_of(#{@klass.mocha_inspect})" + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/is_a.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/is_a.rb new file mode 100644 index 0000000..43a7a91 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/is_a.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +require 'mocha/parameter_matchers/base_methods' + +module Mocha + module ParameterMatchers + module Methods + # Matches any object that is a +klass+. + # + # @param [Class] klass expected class. + # @return [IsA] parameter matcher. + # + # @see Expectation#with + # @see Kernel#is_a? + # + # @example Actual parameter is a +Integer+. + # object = mock() + # object.expects(:method_1).with(is_a(Integer)) + # object.method_1(99) + # # no error raised + # + # @example Actual parameter is not a +Integer+. + # object = mock() + # object.expects(:method_1).with(is_a(Integer)) + # object.method_1('string') + # # error raised, because method_1 was not called with an Integer + # + def is_a(klass) # rubocop:disable Naming/PredicatePrefix + IsA.new(klass) + end + end + + # Parameter matcher which matches when actual parameter is a specific class. + class IsA + include BaseMethods + + # @private + def initialize(klass) + @klass = klass + end + + # @private + def matches?(available_parameters) + parameter = available_parameters.shift + parameter.is_a?(@klass) + end + + # @private + def mocha_inspect + "is_a(#{@klass.mocha_inspect})" + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/kind_of.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/kind_of.rb new file mode 100644 index 0000000..b769878 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/kind_of.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +require 'mocha/parameter_matchers/base_methods' + +module Mocha + module ParameterMatchers + module Methods + # Matches any +Object+ that is a kind of +klass+. + # + # @param [Class] klass expected class. + # @return [KindOf] parameter matcher. + # + # @see Expectation#with + # @see Kernel#kind_of? + # + # @example Actual parameter is a kind of +Integer+. + # object = mock() + # object.expects(:method_1).with(kind_of(Integer)) + # object.method_1(99) + # # no error raised + # + # @example Actual parameter is not a kind of +Integer+. + # object = mock() + # object.expects(:method_1).with(kind_of(Integer)) + # object.method_1('string') + # # error raised, because method_1 was not called with a kind of Integer + def kind_of(klass) + KindOf.new(klass) + end + end + + # Parameter matcher which matches when actual parameter is a kind of specified class. + class KindOf + include BaseMethods + + # @private + def initialize(klass) + @klass = klass + end + + # @private + def matches?(available_parameters) + parameter = available_parameters.shift + # rubocop:disable Style/ClassCheck + parameter.kind_of?(@klass) + # rubocop:enable Style/ClassCheck + end + + # @private + def mocha_inspect + "kind_of(#{@klass.mocha_inspect})" + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/not.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/not.rb new file mode 100644 index 0000000..2c1f0b3 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/not.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +require 'mocha/parameter_matchers/base_methods' + +module Mocha + module ParameterMatchers + module Methods + # Matches if +matcher+ does *not* match. + # + # @param [BaseMethods] matcher matcher whose logic to invert. + # @return [Not] parameter matcher. + # + # @see Expectation#with + # + # @example Actual parameter does not include the value +1+. + # object = mock() + # object.expects(:method_1).with(Not(includes(1))) + # object.method_1([0, 2, 3]) + # # no error raised + # + # @example Actual parameter does include the value +1+. + # object = mock() + # object.expects(:method_1).with(Not(includes(1))) + # object.method_1([0, 1, 2, 3]) + # # error raised, because method_1 was not called with object not including 1 + # + def Not(matcher) # rubocop:disable Naming/MethodName + Not.new(matcher) + end + end + + # Parameter matcher which inverts the logic of the specified matcher using a logical NOT operation. + class Not + include BaseMethods + + # @private + def initialize(matcher) + @matcher = matcher + end + + # @private + def matches?(available_parameters) + parameter = available_parameters.shift + !@matcher.matches?([parameter]) + end + + # @private + def mocha_inspect + "Not(#{@matcher.mocha_inspect})" + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/optionally.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/optionally.rb new file mode 100644 index 0000000..0236b22 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/optionally.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: true + +module Mocha + module ParameterMatchers + module Methods + # Matches optional parameters if available. + # + # @param [*Array] matchers matchers for optional parameters. + # @return [Optionally] parameter matcher. + # + # @see Expectation#with + # + # @example Only the two required parameters are supplied and they both match their expected value. + # object = mock() + # object.expects(:method_1).with(1, 2, optionally(3, 4)) + # object.method_1(1, 2) + # # no error raised + # + # @example Both required parameters and one of the optional parameters are supplied and they all match their expected value. + # object = mock() + # object.expects(:method_1).with(1, 2, optionally(3, 4)) + # object.method_1(1, 2, 3) + # # no error raised + # + # @example Both required parameters and both of the optional parameters are supplied and they all match their expected value. + # object = mock() + # object.expects(:method_1).with(1, 2, optionally(3, 4)) + # object.method_1(1, 2, 3, 4) + # # no error raised + # + # @example One of the actual optional parameters does not match the expected value. + # object = mock() + # object.expects(:method_1).with(1, 2, optionally(3, 4)) + # object.method_1(1, 2, 3, 5) + # # error raised, because optional parameters did not match + def optionally(*matchers) + Optionally.new(*matchers) + end + end + + # Parameter matcher which allows optional parameters to be specified. + class Optionally + include BaseMethods + + # @private + def initialize(*parameters) + @matchers = parameters.map(&:to_matcher) + end + + # @private + def matches?(available_parameters) + index = 0 + while !available_parameters.empty? && (index < @matchers.length) + matcher = @matchers[index] + return false unless matcher.matches?(available_parameters) + + index += 1 + end + true + end + + # @private + def mocha_inspect + "optionally(#{@matchers.map(&:mocha_inspect).join(', ')})" + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/positional_or_keyword_hash.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/positional_or_keyword_hash.rb new file mode 100644 index 0000000..2742675 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/positional_or_keyword_hash.rb @@ -0,0 +1,91 @@ +# frozen_string_literal: true + +require 'mocha/configuration' +require 'mocha/deprecation' +require 'mocha/ruby_version' +require 'mocha/parameter_matchers/base_methods' +require 'mocha/parameter_matchers/has_entries' + +module Mocha + module ParameterMatchers + # @private + class PositionalOrKeywordHash + include BaseMethods + + def initialize(expected_value, expectation, last_expected_value) + @expected_value = expected_value + @expectation = expectation + @last_expected_value = last_expected_value + end + + def matches?(actual_values) + actual_value, is_last_actual_value = extract_actual_value(actual_values) + + if !matches_entries_exactly?(actual_value) + false + elsif is_last_actual_value + matches_last_actual_value?(actual_value) + else + true + end + end + + def mocha_inspect + @expected_value.mocha_inspect + end + + private + + def matches_entries_exactly?(actual_value) + HasEntries.new(@expected_value, exact: true).matches?([actual_value]) + end + + def matches_last_actual_value?(actual_value) + if same_type_of_hash?(actual_value, @expected_value) + true + elsif last_expected_value_is_positional_hash? # rubocop:disable Lint/DuplicateBranch + true + elsif Mocha.configuration.strict_keyword_argument_matching? + false + else + deprecation_warning(actual_value, @expected_value) if Mocha::RUBY_V27_PLUS + true + end + end + + def last_expected_value_is_positional_hash? + @last_expected_value && !ruby2_keywords_hash?(@expected_value) + end + + def extract_actual_value(actual_values) + [actual_values.shift, actual_values.empty?] + end + + def same_type_of_hash?(actual, expected) + ruby2_keywords_hash?(actual) == ruby2_keywords_hash?(expected) + end + + def deprecation_warning(actual, expected) + details1 = "Expectation #{expectation_definition} expected #{hash_type(expected)} (#{expected.mocha_inspect}),".squeeze(' ') + details2 = "but received #{hash_type(actual)} (#{actual.mocha_inspect})." + sentence1 = 'These will stop matching when strict keyword argument matching is enabled.' + sentence2 = 'See the documentation for Mocha::Configuration#strict_keyword_argument_matching=.' + Deprecation.warning([details1, details2, sentence1, sentence2].join(' ')) + end + + def hash_type(hash) + ruby2_keywords_hash?(hash) ? 'keyword arguments' : 'positional hash' + end + + def ruby2_keywords_hash?(hash) + hash.is_a?(Hash) && ::Hash.ruby2_keywords_hash?(hash) + end + + def expectation_definition + return nil unless @expectation + + "defined at #{@expectation.definition_location}" + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/regexp_matches.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/regexp_matches.rb new file mode 100644 index 0000000..5af3482 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/regexp_matches.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +require 'mocha/parameter_matchers/base_methods' + +module Mocha + module ParameterMatchers + module Methods + # Matches any object that matches +regexp+. + # + # @param [Regexp] regexp regular expression to match. + # @return [RegexpMatches] parameter matcher. + # + # @see Expectation#with + # + # @example Actual parameter is matched by specified regular expression. + # object = mock() + # object.expects(:method_1).with(regexp_matches(/e/)) + # object.method_1('hello') + # # no error raised + # + # @example Actual parameter is not matched by specified regular expression. + # object = mock() + # object.expects(:method_1).with(regexp_matches(/a/)) + # object.method_1('hello') + # # error raised, because method_1 was not called with a parameter that matched the + # # regular expression + def regexp_matches(regexp) + RegexpMatches.new(regexp) + end + end + + # Parameter matcher which matches if specified regular expression matches actual paramter. + class RegexpMatches + include BaseMethods + + # @private + def initialize(regexp) + @regexp = regexp + end + + # @private + def matches?(available_parameters) + parameter = available_parameters.shift + return false unless parameter.respond_to?(:=~) + + parameter =~ @regexp + end + + # @private + def mocha_inspect + "regexp_matches(#{@regexp.mocha_inspect})" + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/responds_with.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/responds_with.rb new file mode 100644 index 0000000..67a7ba7 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/responds_with.rb @@ -0,0 +1,82 @@ +# frozen_string_literal: true + +require 'mocha/parameter_matchers/base_methods' +require 'mocha/parameter_matchers/all_of' +require 'yaml' + +module Mocha + module ParameterMatchers + module Methods + # @overload def responds_with(message, result) + # Matches any object that responds to +message+ with +result+. To put it another way, it tests the quack, not the duck. + # @param [Symbol] message method to invoke. + # @param [Object] result expected result of sending +message+. + # @overload def responds_with(messages_vs_results) + # Matches any object that responds to all the messages with the corresponding results as specified by +messages_vs_results+. + # @param [Hash] messages_vs_results +Hash+ of messages vs results. + # @raise [ArgumentError] if +messages_vs_results+ does not contain at least one entry. + # + # @return [RespondsWith] parameter matcher. + # + # @see Expectation#with + # + # @example Actual parameter responds with "FOO" when :upcase is invoked. + # object = mock() + # object.expects(:method_1).with(responds_with(:upcase, "FOO")) + # object.method_1("foo") + # # no error raised, because "foo".upcase == "FOO" + # + # @example Actual parameter does not respond with "FOO" when :upcase is invoked. + # object = mock() + # object.expects(:method_1).with(responds_with(:upcase, "BAR")) + # object.method_1("foo") + # # error raised, because "foo".upcase != "BAR" + # + # @example Actual parameter responds with "FOO" when :upcase is invoked and "oof" when :reverse is invoked. + # object = mock() + # object.expects(:method_1).with(responds_with(upcase: "FOO", reverse: "oof")) + # object.method_1("foo") + # # no error raised, because "foo".upcase == "FOO" and "foo".reverse == "oof" + def responds_with(*options) + case options.length + when 0 + raise ArgumentError, 'No arguments. Expecting at least one.' + when 1 + option = options.first + raise ArgumentError, 'Argument is not a Hash.' unless option.is_a?(Hash) + raise ArgumentError, 'Argument has no entries.' if option.empty? + + matchers = option.map { |message, result| RespondsWith.new(message, result) } + AllOf.new(*matchers) + when 2 + message, result = options + RespondsWith.new(message, result) + else + raise ArgumentError, 'Too many arguments; use either a single argument (must be a Hash) or two arguments (a message and a result).' + end + end + end + + # Parameter matcher which matches if actual parameter returns expected result when specified method is invoked. + class RespondsWith + include BaseMethods + + # @private + def initialize(message, result) + @message = message + @result = result + end + + # @private + def matches?(available_parameters) + parameter = available_parameters.shift + @result.to_matcher.matches?([parameter.__send__(@message)]) + end + + # @private + def mocha_inspect + "responds_with(#{@message.mocha_inspect}, #{@result.mocha_inspect})" + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/yaml_equivalent.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/yaml_equivalent.rb new file mode 100644 index 0000000..e7377fb --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameter_matchers/yaml_equivalent.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +require 'mocha/parameter_matchers/base_methods' +require 'yaml' + +module Mocha + module ParameterMatchers + module Methods + # Matches any YAML that represents the specified +object+ + # + # @param [Object] object object whose YAML to compare. + # @return [YamlEquivalent] parameter matcher. + # + # @see Expectation#with + # + # @example Actual parameter is YAML equivalent of specified +object+. + # object = mock() + # object.expects(:method_1).with(yaml_equivalent(1, 2, 3)) + # object.method_1("--- \n- 1\n- 2\n- 3\n") + # # no error raised + # + # @example Actual parameter is not YAML equivalent of specified +object+. + # object = mock() + # object.expects(:method_1).with(yaml_equivalent(1, 2, 3)) + # object.method_1("--- \n- 1\n- 2\n") + # # error raised, because method_1 was not called with YAML representing the specified Array + def yaml_equivalent(object) + YamlEquivalent.new(object) + end + end + + # Parameter matcher which matches if actual parameter is YAML equivalent of specified object. + class YamlEquivalent + include BaseMethods + + # @private + def initialize(object) + @object = object + end + + # @private + def matches?(available_parameters) + parameter = available_parameters.shift + # rubocop:disable Security/YAMLLoad + @object == YAML.load(parameter) + # rubocop:enable Security/YAMLLoad + end + + # @private + def mocha_inspect + "yaml_equivalent(#{@object.mocha_inspect})" + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameters_matcher.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameters_matcher.rb new file mode 100644 index 0000000..cdff78c --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/parameters_matcher.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +require 'mocha/inspect' +require 'mocha/parameter_matchers' + +module Mocha + class ParametersMatcher + def initialize(expected_parameters = [ParameterMatchers::AnyParameters.new], expectation = nil, &matching_block) + @expected_parameters = expected_parameters + @expectation = expectation + @matching_block = matching_block + end + + def match?(actual_parameters = []) + if @matching_block + @matching_block.call(*actual_parameters) + else + parameters_match?(actual_parameters) + end + end + + def parameters_match?(actual_parameters) + matchers.all? { |matcher| matcher.matches?(actual_parameters) } && actual_parameters.empty? + end + + def mocha_inspect + if @matching_block + '(arguments_accepted_by_custom_matching_block)' + else + signature = matchers.mocha_inspect + signature = signature.gsub(/^\[|\]$/, '') + "(#{signature})" + end + end + + def matchers + @expected_parameters.map.with_index do |parameter, index| + parameter.to_matcher( + expectation: @expectation, + top_level: true, + last: index == @expected_parameters.length - 1 + ) + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/raised_exception.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/raised_exception.rb new file mode 100644 index 0000000..3efe485 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/raised_exception.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Mocha + class RaisedException + def initialize(exception) + @exception = exception + end + + def mocha_inspect + "raised #{@exception}" + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/return_values.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/return_values.rb new file mode 100644 index 0000000..7f20c30 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/return_values.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require 'mocha/single_return_value' + +module Mocha + class ReturnValues + def self.build(*values) + new(*values.map { |value| SingleReturnValue.new(value) }) + end + + attr_accessor :values + + def initialize(*values) + @values = values + end + + def next(invocation) + case @values.length + when 0 then nil + when 1 then @values.first.evaluate(invocation) + else @values.shift.evaluate(invocation) + end + end + + def +(other) + self.class.new(*(@values + other.values)) + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/ruby_version.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/ruby_version.rb new file mode 100644 index 0000000..88e7a90 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/ruby_version.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +module Mocha + RUBY_V27_PLUS = Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('2.7') + RUBY_V30_PLUS = Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('3.0') + RUBY_V34_PLUS = Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('3.4') +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/sequence.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/sequence.rb new file mode 100644 index 0000000..b1a685e --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/sequence.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +module Mocha + # Used to constrain the order in which expectations can occur. + # + # @see API#sequence + # @see Expectation#in_sequence + class Sequence + # @private + class InSequenceOrderingConstraint + def initialize(sequence, index) + @sequence = sequence + @index = index + end + + def allows_invocation_now? + @sequence.satisfied_to_index?(@index) + end + + def mocha_inspect + "in sequence #{@sequence.mocha_inspect}" + end + end + + # @private + def initialize(name) + @name = name + @expectations = [] + end + + # @private + def constrain_as_next_in_sequence(expectation) + index = @expectations.length + @expectations << expectation + expectation.add_ordering_constraint(InSequenceOrderingConstraint.new(self, index)) + end + + # @private + def satisfied_to_index?(index) + @expectations[0...index].all?(&:satisfied?) + end + + # @private + def mocha_inspect + @name.mocha_inspect.to_s + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/single_return_value.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/single_return_value.rb new file mode 100644 index 0000000..9f5d16d --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/single_return_value.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module Mocha + class SingleReturnValue + def initialize(value) + @value = value + end + + def evaluate(invocation) + invocation.returned(@value) + @value + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/state_machine.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/state_machine.rb new file mode 100644 index 0000000..75db692 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/state_machine.rb @@ -0,0 +1,95 @@ +# frozen_string_literal: true + +module Mocha + # A state machine that is used to constrain the order of invocations. + # An invocation can be constrained to occur when a state {#is}, or {#is_not}, active. + class StateMachine + # Provides the ability to determine whether a {StateMachine} is in a specified state at some point in the future. + class StatePredicate + # @private + def initialize(state_machine, state, description, &active_check) + @state_machine = state_machine + @state = state + @description = description + @active_check = active_check + end + + # @private + def active? + @active_check.call(@state_machine.current_state, @state) + end + + # @private + def mocha_inspect + "#{@state_machine.name} #{@description} #{@state.mocha_inspect}" + end + end + + # Provides a mechanism to change the state of a {StateMachine} at some point in the future. + class State < StatePredicate + # @private + def activate + @state_machine.current_state = @state + end + end + + # @private + attr_reader :name + + # @private + attr_accessor :current_state + + # @private + def initialize(name) + @name = name + @current_state = nil + end + + # Put the {StateMachine} into the state specified by +initial_state_name+. + # + # @param [String] initial_state_name name of initial state + # @return [StateMachine] state machine, thereby allowing invocations of other {StateMachine} methods to be chained. + def starts_as(initial_state_name) + become(initial_state_name) + self + end + + # Put the {StateMachine} into the +next_state_name+. + # + # @param [String] next_state_name name of new state + def become(next_state_name) + @current_state = next_state_name + end + + # Provides mechanisms to (a) determine whether the {StateMachine} is in a given state; or (b) to change the {StateMachine} into the given state. + # + # @param [String] state_name name of expected/desired state. + # @return [StatePredicate,State] (a) state predicate which, when queried, will indicate whether the {StateMachine} is in the given state; or (b) state which, when activated, will change the {StateMachine} into the given state. + # + # @overload def is(expected_state_name) + # Provides a mechanism to determine whether the {StateMachine} is in the state specified by +expected_state_name+ at some point in the future + # @param [String] expected_state_name name of expected state. + # @return [StatePredicate] state predicate which, when queried, will indicate whether the {StateMachine} is in the state specified by +expected_state_name+ + # + # @overload def is(desired_state_name) + # Provides a mechanism to change the {StateMachine} into the state specified by +desired_state_name+ at some point in the future. + # @param [String] desired_state_name name of desired new state. + # @return [State] state which, when activated, will change the {StateMachine} into the state with the specified +desired_state_name+. + def is(state_name) + State.new(self, state_name, 'is') { |current, given| current == given } + end + + # Provides a mechanism to determine whether the {StateMachine} is *not* in the state specified by +unexpected_state_name+ at some point in the future. + # + # @param [String] unexpected_state_name name of unexpected state. + # @return [StatePredicate] state predicate which, when queried, will indicate whether the {StateMachine} is *not* in the state specified by +unexpected_state_name+. + def is_not(unexpected_state_name) # rubocop:disable Naming/PredicatePrefix + StatePredicate.new(self, unexpected_state_name, 'is not') { |current, given| current != given } + end + + # @private + def mocha_inspect + %(#{@name} #{@current_state ? "is #{@current_state.mocha_inspect}" : 'has no current state'}) + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/stubbed_method.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/stubbed_method.rb new file mode 100644 index 0000000..8f543a3 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/stubbed_method.rb @@ -0,0 +1,95 @@ +# frozen_string_literal: true + +require 'ruby2_keywords' +require 'mocha/ruby_version' + +module Mocha + class StubbedMethod + PrependedModule = Class.new(Module) + + attr_reader :stubba_object, :method_name + + def initialize(stubba_object, method_name) + @stubba_object = stubba_object + @original_method = nil + @original_visibility = nil + @method_name = method_name.to_sym + end + + def stub + hide_original_method + define_new_method + end + + def unstub + remove_new_method + mock.unstub(method_name.to_sym) + return if mock.any_expectations? + + reset_mocha + end + + def mock + stubbee.mocha + end + + def reset_mocha + stubbee.reset_mocha + end + + def hide_original_method + return unless original_method_owner.__method_exists__?(method_name) + + store_original_method_visibility + use_prepended_module_for_stub_method + end + + def define_new_method + self_in_scope = self + method_name_in_scope = method_name + stub_method_owner.send(:define_method, method_name) do |*args, &block| + self_in_scope.mock.handle_method_call(method_name_in_scope, args, block) + end + stub_method_owner.send(:ruby2_keywords, method_name) + retain_original_visibility(stub_method_owner) + end + + def remove_new_method + stub_method_owner.send(:remove_method, method_name) + end + + def matches?(other) + return false unless other.instance_of?(self.class) + + (stubba_object.object_id == other.stubba_object.object_id) && # rubocop:disable Lint/IdentityComparison + (method_name == other.method_name) + end + + alias_method :==, :eql? + + def to_s + "#{stubba_object}.#{method_name}" + end + + private + + def retain_original_visibility(method_owner) + return unless @original_visibility + + Module.instance_method(@original_visibility).bind(method_owner).call(method_name) + end + + def store_original_method_visibility + @original_visibility = original_method_owner.__method_visibility__(method_name) + end + + def use_prepended_module_for_stub_method + @stub_method_owner = PrependedModule.new + original_method_owner.__send__ :prepend, @stub_method_owner + end + + def stub_method_owner + @stub_method_owner ||= original_method_owner + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/stubbing_error.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/stubbing_error.rb new file mode 100644 index 0000000..53a790d --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/stubbing_error.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +require 'mocha/error_with_filtered_backtrace' + +module Mocha + # Exception raised when stubbing a particular method is not allowed. + # + # @see Configuration.prevent + class StubbingError < ErrorWithFilteredBacktrace; end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/test_unit.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/test_unit.rb new file mode 100644 index 0000000..a012a55 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/test_unit.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +require 'mocha/integration/test_unit' + +unless Mocha::Integration::TestUnit.activate + raise "Test::Unit must be loaded *before* `require 'mocha/test_unit'`." +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/thrower.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/thrower.rb new file mode 100644 index 0000000..679b81f --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/thrower.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Mocha + class Thrower + def initialize(tag, object = nil) + @tag = tag + @object = object + end + + def evaluate(invocation) + invocation.threw(@tag, @object) + throw @tag, @object + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/thrown_object.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/thrown_object.rb new file mode 100644 index 0000000..a069bf3 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/thrown_object.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module Mocha + class ThrownObject + def initialize(tag, value = nil) + @tag = tag + @value = value + end + + def mocha_inspect + "threw (#{@tag.mocha_inspect}, #{@value.mocha_inspect})" + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/version.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/version.rb new file mode 100644 index 0000000..06e92c6 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/version.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +module Mocha + VERSION = '3.0.1' +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/yield_parameters.rb b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/yield_parameters.rb new file mode 100644 index 0000000..6a2cc94 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/lib/mocha/yield_parameters.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Mocha + class YieldParameters + def initialize + @parameter_groups = [] + end + + def next_invocation + case @parameter_groups.length + when 0 then [] + when 1 then @parameter_groups.first + else @parameter_groups.shift + end + end + + def add(*parameter_groups) + @parameter_groups << parameter_groups.map do |pg| + pg.is_a?(Array) ? pg : [pg] + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/mocha.gemspec b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/mocha.gemspec new file mode 100644 index 0000000..5ce10bb --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/mocha-3.0.1/mocha.gemspec @@ -0,0 +1,40 @@ +lib = File.expand_path('../lib/', __FILE__) +$LOAD_PATH.unshift lib unless $LOAD_PATH.include?(lib) +require 'mocha/version' + +DESCRIPTION = + 'A library for mocking and stubbing with a unified, simple and readable syntax ' \ + 'for both full & partial mocking. Built-in support for Minitest and Test::Unit. ' \ + 'Supported by many other test frameworks, e.g. RSpec.'.freeze + +Gem::Specification.new do |s| + s.name = 'mocha' + s.version = Mocha::VERSION + s.licenses = ['MIT', 'BSD-2-Clause'] + s.required_ruby_version = '>= 2.2' + + s.authors = ['James Mead'] + s.description = DESCRIPTION + s.email = 'mocha-developer@googlegroups.com' + + s.files = Dir.chdir(File.expand_path('..', __FILE__)) do + `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(docs|test)/}) } + end + s.files.delete('.circleci/config.yml') + s.files.delete('.gitignore') + + s.homepage = 'https://mocha.jamesmead.org' + s.require_paths = ['lib'] + s.summary = 'Mocking and stubbing library' + s.metadata = { + 'bug_tracker_uri' => 'https://github.com/freerange/mocha/issues', + 'changelog_uri' => 'https://github.com/freerange/mocha/blob/main/RELEASE.md', + 'documentation_uri' => 'https://mocha.jamesmead.org/', + 'funding_uri' => 'https://github.com/sponsors/floehopper', + 'homepage_uri' => s.homepage, + 'source_code_uri' => 'https://github.com/freerange/mocha', + 'rubygems_mfa_required' => 'true' + } + + s.add_dependency 'ruby2_keywords', '>= 0.0.5' +end diff --git a/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/.document b/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/.document new file mode 100644 index 0000000..3f3f85d --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/.document @@ -0,0 +1,5 @@ +BSDL +COPYING +README.md +doc/ +lib/ diff --git a/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/BSDL b/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/BSDL new file mode 100644 index 0000000..66d9359 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/BSDL @@ -0,0 +1,22 @@ +Copyright (C) 1993-2013 Yukihiro Matsumoto. 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. diff --git a/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/COPYING b/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/COPYING new file mode 100644 index 0000000..48e5a96 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/COPYING @@ -0,0 +1,56 @@ +Ruby is copyrighted free software by Yukihiro Matsumoto . +You can redistribute it and/or modify it under either the terms of the +2-clause BSDL (see the file BSDL), or the conditions below: + +1. You may make and give away verbatim copies of the source form of the + software without restriction, provided that you duplicate all of the + original copyright notices and associated disclaimers. + +2. You may modify your copy of the software in any way, provided that + you do at least ONE of the following: + + a. place your modifications in the Public Domain or otherwise + make them Freely Available, such as by posting said + modifications to Usenet or an equivalent medium, or by allowing + the author to include your modifications in the software. + + b. use the modified software only within your corporation or + organization. + + c. give non-standard binaries non-standard names, with + instructions on where to get the original software distribution. + + d. make other distribution arrangements with the author. + +3. You may distribute the software in object code or binary form, + provided that you do at least ONE of the following: + + a. distribute the binaries and library files of the software, + together with instructions (in the manual page or equivalent) + on where to get the original distribution. + + b. accompany the distribution with the machine-readable source of + the software. + + c. give non-standard binaries non-standard names, with + instructions on where to get the original software distribution. + + d. make other distribution arrangements with the author. + +4. You may modify and include the part of the software into any other + software (possibly commercial). But some files in the distribution + are not written by the author, so that they are not under these terms. + + For the list of those files and their copying conditions, see the + file LEGAL. + +5. The scripts and library files supplied as input to or produced as + output from the software do not automatically fall under the + copyright of the software, but belong to whomever generated them, + and may be sold commercially, and may be aggregated with this + software. + +6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE. diff --git a/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/README.md b/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/README.md new file mode 100644 index 0000000..fcc1f39 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/README.md @@ -0,0 +1,93 @@ +# Net::HTTP + +Net::HTTP provides a rich library which can be used to build HTTP +user-agents. For more details about HTTP see +[RFC9110 HTTP Semantics](https://www.ietf.org/rfc/rfc9110.html) and +[RFC9112 HTTP/1.1](https://www.ietf.org/rfc/rfc9112.html). + +Net::HTTP is designed to work closely with URI. URI::HTTP#host, +URI::HTTP#port and URI::HTTP#request_uri are designed to work with +Net::HTTP. + +If you are only performing a few GET requests you should try OpenURI. + +## Installation + +Add this line to your application's Gemfile: + +```ruby +gem 'net-http' +``` + +And then execute: + + $ bundle install + +Or install it yourself as: + + $ gem install net-http + +## Usage + +All examples assume you have loaded Net::HTTP with: + +```ruby +require 'net/http' +``` + +This will also require 'uri' so you don't need to require it separately. + +The Net::HTTP methods in the following section do not persist +connections. They are not recommended if you are performing many HTTP +requests. + +### GET + +```ruby +Net::HTTP.get('example.com', '/index.html') # => String +``` + +### GET by URI + +```ruby +uri = URI('http://example.com/index.html?count=10') +Net::HTTP.get(uri) # => String +``` + +### GET with Dynamic Parameters + +```ruby +uri = URI('http://example.com/index.html') +params = { :limit => 10, :page => 3 } +uri.query = URI.encode_www_form(params) + +res = Net::HTTP.get_response(uri) +puts res.body if res.is_a?(Net::HTTPSuccess) +``` + +### POST + +```ruby +uri = URI('http://www.example.com/search.cgi') +res = Net::HTTP.post_form(uri, 'q' => 'ruby', 'max' => '50') +puts res.body +``` + +### POST with Multiple Values + +```ruby +uri = URI('http://www.example.com/search.cgi') +res = Net::HTTP.post_form(uri, 'q' => ['ruby', 'perl'], 'max' => '50') +puts res.body +``` + +## Development + +After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. + +To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). + +## Contributing + +Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/net-http. + diff --git a/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/doc/net-http/examples.rdoc b/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/doc/net-http/examples.rdoc new file mode 100644 index 0000000..c1366e7 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/doc/net-http/examples.rdoc @@ -0,0 +1,31 @@ +Examples here assume that net/http has been required +(which also requires +uri+): + + require 'net/http' + +Many code examples here use these example websites: + +- https://jsonplaceholder.typicode.com. +- http://example.com. + +Some examples also assume these variables: + + uri = URI('https://jsonplaceholder.typicode.com/') + uri.freeze # Examples may not modify. + hostname = uri.hostname # => "jsonplaceholder.typicode.com" + path = uri.path # => "/" + port = uri.port # => 443 + +So that example requests may be written as: + + Net::HTTP.get(uri) + Net::HTTP.get(hostname, '/index.html') + Net::HTTP.start(hostname) do |http| + http.get('/todos/1') + http.get('/todos/2') + end + +An example that needs a modified URI first duplicates +uri+, then modifies the duplicate: + + _uri = uri.dup + _uri.path = '/todos/1' diff --git a/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/doc/net-http/included_getters.rdoc b/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/doc/net-http/included_getters.rdoc new file mode 100644 index 0000000..7ac327f --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/doc/net-http/included_getters.rdoc @@ -0,0 +1,3 @@ +This class also includes (indirectly) module Net::HTTPHeader, +which gives access to its +{methods for getting headers}[rdoc-ref:Net::HTTPHeader@Getters]. diff --git a/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/lib/net/http.rb b/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/lib/net/http.rb new file mode 100644 index 0000000..9359a6f --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/lib/net/http.rb @@ -0,0 +1,2608 @@ +# 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 . +# HTTPS support added by GOTOU Yuuzou . +# +# 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 Net::HTTP for an overview and examples. +# + +require 'net/protocol' +require 'uri' +require 'resolv' +autoload :OpenSSL, 'openssl' + +module Net #:nodoc: + + # :stopdoc: + class HTTPBadResponse < StandardError; end + class HTTPHeaderSyntaxError < StandardError; end + # :startdoc: + + # \Class \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}[https://docs.ruby-lang.org/en/master/OpenURI.html]. + # - 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:Net::HTTP@Sessions] that sends a single request: + # + # # Return string response body. + # Net::HTTP.get(hostname, path) + # Net::HTTP.get(uri) + # + # # Write string response body to $stdout. + # Net::HTTP.get_print(hostname, path) + # Net::HTTP.get_print(uri) + # + # # Return response as Net::HTTPResponse object. + # Net::HTTP.get_response(hostname, path) + # Net::HTTP.get_response(uri) + # data = '{"title": "foo", "body": "bar", "userId": 1}' + # Net::HTTP.post(uri, data) + # params = {title: 'foo', body: 'bar', userId: 1} + # Net::HTTP.post_form(uri, params) + # data = '{"title": "foo", "body": "bar", "userId": 1}' + # Net::HTTP.put(uri, data) + # + # - If performance is important, consider using sessions, which lower request overhead. + # This {session}[rdoc-ref: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]: + # + # 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: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 {URI::Generic}[https://docs.ruby-lang.org/en/master/URI/Generic.html] object + # represents an internet URI. + # It provides, among others, methods + # +scheme+, +hostname+, +path+, +query+, and +fragment+. + # + # === Schemes + # + # An internet \URI has + # a {scheme}[https://en.wikipedia.org/wiki/List_of_URI_schemes]. + # + # The two schemes supported in \Net::HTTP are 'https' and 'http': + # + # uri.scheme # => "https" + # 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" + # 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 + # 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 = URI.encode_www_form(params) + # _uri # => # + # Net::HTTP.get(_uri) + # + # === Fragments + # + # A {URI fragment}[https://en.wikipedia.org/wiki/URI_fragment] has no effect + # in \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 \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'} + # 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 Net::HTTP#start. + # - May contain any number of requests. + # - Is ended by instance method Net::HTTP#finish. + # + # See example sessions at {Strategies}[rdoc-ref:Net::HTTP@Strategies]. + # + # === Session Using \Net::HTTP.start + # + # If you have many requests to make to a single host (and port), + # consider using singleton method 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 \Net::HTTP.start and \Net::HTTP.finish + # + # You can manage a session manually using methods #start and #finish: + # + # http = 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 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 \Net::HTTPRequest and \Net::HTTPResponse objects. + # + # You can, however, directly create a request object, send the request, + # and retrieve the response object; see: + # + # - Net::HTTPRequest. + # - Net::HTTPResponse. + # + # == Following Redirection + # + # Each returned response is an instance of a subclass of Net::HTTPResponse. + # See the {response class hierarchy}[rdoc-ref:Net::HTTPResponse@Response+Subclasses]. + # + # In particular, class 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 = Net::HTTP.get_response(URI(uri)) + # case res + # when Net::HTTPSuccess # Any success class. + # res + # when 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 = Net::HTTP::Get.new(uri) + # req.basic_auth('user', 'pass') + # res = Net::HTTP.start(hostname) do |http| + # http.request(req) + # end + # + # == Streaming Response Bodies + # + # By default \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. + # + # Net::HTTP.start(hostname) do |http| + # req = 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 Net::HTTP#use_ssl=: + # + # Net::HTTP.start(hostname, :use_ssl => true) do |http| + # req = 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. \Net::HTTP automatically turns on TLS + # verification if the URI object has a 'https' URI scheme: + # + # uri # => # + # 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 Net::HTTP.new or method Net::HTTP.start. + # + # The proxy may be defined either by argument +p_addr+ + # or by environment variable 'http_proxy'. + # + # === 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 = 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 = Net::HTTP.new(hostname, nil, 'proxy.example', 8000, 'pname', 'ppass') + # # => # + # 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 'ENV['http_proxy']' + # + # When environment variable 'http_proxy' + # is set to a \URI string, + # the returned +http+ will have the server at that URI as its proxy; + # note that the \URI string must have a protocol + # such as 'http' or 'https': + # + # ENV['http_proxy'] = 'http://example.com' + # http = 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 \URI string may include proxy username, password, and port number: + # + # ENV['http_proxy'] = 'http://pname:ppass@example.com:8000' + # http = 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 Net::HTTP.new (but not Net::HTTP.start), + # you can use argument +p_no_proxy+ to filter proxies: + # + # - Reject a certain address: + # + # http = Net::HTTP.new('example.com', nil, 'proxy.example', 8000, 'pname', 'ppass', 'proxy.example') + # http.proxy_address # => nil + # + # - Reject certain domains or subdomains: + # + # http = 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 = Net::HTTP.new('example.com', nil, 'proxy.example', 8000, 'pname', 'ppass', 'proxy.example:1234') + # http.proxy_address # => "proxy.example" + # + # http = 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 = Net::HTTP.new('example.com', nil, 'proxy.example', 8000, 'pname', 'ppass', 'my.proxy,proxy.example:8000') + # http.proxy_address # => nil + # + # http = Net::HTTP.new('example.com', nil, 'my.proxy', 8000, 'pname', 'ppass', 'my.proxy,proxy.example:8000') + # http.proxy_address # => nil + # + # == Compression and Decompression + # + # \Net::HTTP does not compress the body of a request before sending. + # + # By default, \Net::HTTP adds header 'Accept-Encoding' + # to a new {request object}[rdoc-ref:Net::HTTPRequest]: + # + # 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. + # + # \Net::HTTP does not automatically decompress a response body + # if the response has header 'Content-Range'. + # + # 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]: + # + # - 'deflate', 'gzip', or 'x-gzip': + # decompresses the body and deletes the header. + # - 'none' or 'identity': + # does not decompress the body, but deletes the header. + # - Any other value: + # leaves the body and header unchanged. + # + # == What's Here + # + # First, what's elsewhere. Class Net::HTTP: + # + # - Inherits from {class Object}[https://docs.ruby-lang.org/en/master/Object.html#class-Object-label-What-27s+Here]. + # + # This is a categorized summary of methods and attributes. + # + # === \Net::HTTP Objects + # + # - {::new}[rdoc-ref:Net::HTTP.new]: + # Creates a new instance. + # - {#inspect}[rdoc-ref:Net::HTTP#inspect]: + # Returns a string representation of +self+. + # + # === Sessions + # + # - {::start}[rdoc-ref:Net::HTTP.start]: + # Begins a new session in a new \Net::HTTP object. + # - {#started?}[rdoc-ref:Net::HTTP#started?]: + # Returns whether in a session. + # - {#finish}[rdoc-ref:Net::HTTP#finish]: + # Ends an active session. + # - {#start}[rdoc-ref:Net::HTTP#start]: + # Begins a new session in an existing \Net::HTTP object (+self+). + # + # === Connections + # + # - {:continue_timeout}[rdoc-ref:Net::HTTP#continue_timeout]: + # Returns the continue timeout. + # - {#continue_timeout=}[rdoc-ref:Net::HTTP#continue_timeout=]: + # Sets the continue timeout seconds. + # - {:keep_alive_timeout}[rdoc-ref:Net::HTTP#keep_alive_timeout]: + # Returns the keep-alive timeout. + # - {:keep_alive_timeout=}[rdoc-ref:Net::HTTP#keep_alive_timeout=]: + # Sets the keep-alive timeout. + # - {:max_retries}[rdoc-ref:Net::HTTP#max_retries]: + # Returns the maximum retries. + # - {#max_retries=}[rdoc-ref:Net::HTTP#max_retries=]: + # Sets the maximum retries. + # - {:open_timeout}[rdoc-ref:Net::HTTP#open_timeout]: + # Returns the open timeout. + # - {:open_timeout=}[rdoc-ref:Net::HTTP#open_timeout=]: + # Sets the open timeout. + # - {:read_timeout}[rdoc-ref:Net::HTTP#read_timeout]: + # Returns the open timeout. + # - {:read_timeout=}[rdoc-ref:Net::HTTP#read_timeout=]: + # Sets the read timeout. + # - {:ssl_timeout}[rdoc-ref:Net::HTTP#ssl_timeout]: + # Returns the ssl timeout. + # - {:ssl_timeout=}[rdoc-ref:Net::HTTP#ssl_timeout=]: + # Sets the ssl timeout. + # - {:write_timeout}[rdoc-ref:Net::HTTP#write_timeout]: + # Returns the write timeout. + # - {write_timeout=}[rdoc-ref:Net::HTTP#write_timeout=]: + # Sets the write timeout. + # + # === Requests + # + # - {::get}[rdoc-ref:Net::HTTP.get]: + # Sends a GET request and returns the string response body. + # - {::get_print}[rdoc-ref:Net::HTTP.get_print]: + # Sends a GET request and write the string response body to $stdout. + # - {::get_response}[rdoc-ref:Net::HTTP.get_response]: + # Sends a GET request and returns a response object. + # - {::post_form}[rdoc-ref:Net::HTTP.post_form]: + # Sends a POST request with form data and returns a response object. + # - {::post}[rdoc-ref:Net::HTTP.post]: + # Sends a POST request with data and returns a response object. + # - {::put}[rdoc-ref:Net::HTTP.put]: + # Sends a PUT request with data and returns a response object. + # - {#copy}[rdoc-ref:Net::HTTP#copy]: + # Sends a COPY request and returns a response object. + # - {#delete}[rdoc-ref:Net::HTTP#delete]: + # Sends a DELETE request and returns a response object. + # - {#get}[rdoc-ref:Net::HTTP#get]: + # Sends a GET request and returns a response object. + # - {#head}[rdoc-ref:Net::HTTP#head]: + # Sends a HEAD request and returns a response object. + # - {#lock}[rdoc-ref:Net::HTTP#lock]: + # Sends a LOCK request and returns a response object. + # - {#mkcol}[rdoc-ref:Net::HTTP#mkcol]: + # Sends a MKCOL request and returns a response object. + # - {#move}[rdoc-ref:Net::HTTP#move]: + # Sends a MOVE request and returns a response object. + # - {#options}[rdoc-ref:Net::HTTP#options]: + # Sends a OPTIONS request and returns a response object. + # - {#patch}[rdoc-ref:Net::HTTP#patch]: + # Sends a PATCH request and returns a response object. + # - {#post}[rdoc-ref:Net::HTTP#post]: + # Sends a POST request and returns a response object. + # - {#propfind}[rdoc-ref:Net::HTTP#propfind]: + # Sends a PROPFIND request and returns a response object. + # - {#proppatch}[rdoc-ref:Net::HTTP#proppatch]: + # Sends a PROPPATCH request and returns a response object. + # - {#put}[rdoc-ref:Net::HTTP#put]: + # Sends a PUT request and returns a response object. + # - {#request}[rdoc-ref:Net::HTTP#request]: + # Sends a request and returns a response object. + # - {#request_get}[rdoc-ref:Net::HTTP#request_get]: + # 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:Net::HTTP#request_head]: + # 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:Net::HTTP#request_post]: + # 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:Net::HTTP#send_request]: + # Sends a request and returns a response object. + # - {#trace}[rdoc-ref:Net::HTTP#trace]: + # Sends a TRACE request and returns a response object. + # - {#unlock}[rdoc-ref:Net::HTTP#unlock]: + # Sends an UNLOCK request and returns a response object. + # + # === Responses + # + # - {:close_on_empty_response}[rdoc-ref:Net::HTTP#close_on_empty_response]: + # Returns whether to close connection on empty response. + # - {:close_on_empty_response=}[rdoc-ref:Net::HTTP#close_on_empty_response=]: + # Sets whether to close connection on empty response. + # - {:ignore_eof}[rdoc-ref:Net::HTTP#ignore_eof]: + # Returns whether to ignore end-of-file when reading a response body + # with Content-Length headers. + # - {:ignore_eof=}[rdoc-ref:Net::HTTP#ignore_eof=]: + # Sets whether to ignore end-of-file when reading a response body + # with Content-Length headers. + # - {:response_body_encoding}[rdoc-ref:Net::HTTP#response_body_encoding]: + # Returns the encoding to use for the response body. + # - {#response_body_encoding=}[rdoc-ref:Net::HTTP#response_body_encoding=]: + # Sets the response body encoding. + # + # === Proxies + # + # - {:proxy_address}[rdoc-ref:Net::HTTP#proxy_address]: + # Returns the proxy address. + # - {:proxy_address=}[rdoc-ref:Net::HTTP#proxy_address=]: + # Sets the proxy address. + # - {::proxy_class?}[rdoc-ref:Net::HTTP.proxy_class?]: + # Returns whether +self+ is a proxy class. + # - {#proxy?}[rdoc-ref:Net::HTTP#proxy?]: + # Returns whether +self+ has a proxy. + # - {#proxy_address}[rdoc-ref:Net::HTTP#proxy_address]: + # Returns the proxy address. + # - {#proxy_from_env?}[rdoc-ref:Net::HTTP#proxy_from_env?]: + # Returns whether the proxy is taken from an environment variable. + # - {:proxy_from_env=}[rdoc-ref:Net::HTTP#proxy_from_env=]: + # Sets whether the proxy is to be taken from an environment variable. + # - {:proxy_pass}[rdoc-ref:Net::HTTP#proxy_pass]: + # Returns the proxy password. + # - {:proxy_pass=}[rdoc-ref:Net::HTTP#proxy_pass=]: + # Sets the proxy password. + # - {:proxy_port}[rdoc-ref:Net::HTTP#proxy_port]: + # Returns the proxy port. + # - {:proxy_port=}[rdoc-ref:Net::HTTP#proxy_port=]: + # Sets the proxy port. + # - {#proxy_user}[rdoc-ref:Net::HTTP#proxy_user]: + # Returns the proxy user name. + # - {:proxy_user=}[rdoc-ref:Net::HTTP#proxy_user=]: + # Sets the proxy user. + # + # === Security + # + # - {:ca_file}[rdoc-ref:Net::HTTP#ca_file]: + # Returns the path to a CA certification file. + # - {:ca_file=}[rdoc-ref:Net::HTTP#ca_file=]: + # Sets the path to a CA certification file. + # - {:ca_path}[rdoc-ref:Net::HTTP#ca_path]: + # Returns the path of to CA directory containing certification files. + # - {:ca_path=}[rdoc-ref:Net::HTTP#ca_path=]: + # Sets the path of to CA directory containing certification files. + # - {:cert}[rdoc-ref:Net::HTTP#cert]: + # Returns the OpenSSL::X509::Certificate object to be used for client certification. + # - {:cert=}[rdoc-ref:Net::HTTP#cert=]: + # Sets the OpenSSL::X509::Certificate object to be used for client certification. + # - {:cert_store}[rdoc-ref:Net::HTTP#cert_store]: + # Returns the X509::Store to be used for verifying peer certificate. + # - {:cert_store=}[rdoc-ref:Net::HTTP#cert_store=]: + # Sets the X509::Store to be used for verifying peer certificate. + # - {:ciphers}[rdoc-ref:Net::HTTP#ciphers]: + # Returns the available SSL ciphers. + # - {:ciphers=}[rdoc-ref:Net::HTTP#ciphers=]: + # Sets the available SSL ciphers. + # - {:extra_chain_cert}[rdoc-ref:Net::HTTP#extra_chain_cert]: + # Returns the extra X509 certificates to be added to the certificate chain. + # - {:extra_chain_cert=}[rdoc-ref:Net::HTTP#extra_chain_cert=]: + # Sets the extra X509 certificates to be added to the certificate chain. + # - {:key}[rdoc-ref:Net::HTTP#key]: + # Returns the OpenSSL::PKey::RSA or OpenSSL::PKey::DSA object. + # - {:key=}[rdoc-ref:Net::HTTP#key=]: + # Sets the OpenSSL::PKey::RSA or OpenSSL::PKey::DSA object. + # - {:max_version}[rdoc-ref:Net::HTTP#max_version]: + # Returns the maximum SSL version. + # - {:max_version=}[rdoc-ref:Net::HTTP#max_version=]: + # Sets the maximum SSL version. + # - {:min_version}[rdoc-ref:Net::HTTP#min_version]: + # Returns the minimum SSL version. + # - {:min_version=}[rdoc-ref:Net::HTTP#min_version=]: + # Sets the minimum SSL version. + # - {#peer_cert}[rdoc-ref:Net::HTTP#peer_cert]: + # Returns the X509 certificate chain for the session's socket peer. + # - {:ssl_version}[rdoc-ref:Net::HTTP#ssl_version]: + # Returns the SSL version. + # - {:ssl_version=}[rdoc-ref:Net::HTTP#ssl_version=]: + # Sets the SSL version. + # - {#use_ssl=}[rdoc-ref:Net::HTTP#use_ssl=]: + # Sets whether a new session is to use Transport Layer Security. + # - {#use_ssl?}[rdoc-ref:Net::HTTP#use_ssl?]: + # Returns whether +self+ uses SSL. + # - {:verify_callback}[rdoc-ref:Net::HTTP#verify_callback]: + # Returns the callback for the server certification verification. + # - {:verify_callback=}[rdoc-ref:Net::HTTP#verify_callback=]: + # Sets the callback for the server certification verification. + # - {:verify_depth}[rdoc-ref:Net::HTTP#verify_depth]: + # Returns the maximum depth for the certificate chain verification. + # - {:verify_depth=}[rdoc-ref:Net::HTTP#verify_depth=]: + # Sets the maximum depth for the certificate chain verification. + # - {:verify_hostname}[rdoc-ref:Net::HTTP#verify_hostname]: + # Returns the flags for server the certification verification at the beginning of the SSL/TLS session. + # - {:verify_hostname=}[rdoc-ref:Net::HTTP#verify_hostname=]: + # Sets he flags for server the certification verification at the beginning of the SSL/TLS session. + # - {:verify_mode}[rdoc-ref:Net::HTTP#verify_mode]: + # Returns the flags for server the certification verification at the beginning of the SSL/TLS session. + # - {:verify_mode=}[rdoc-ref: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:Net::HTTP#address]: + # Returns the string host name or host IP. + # - {::default_port}[rdoc-ref:Net::HTTP.default_port]: + # Returns integer 80, the default port to use for HTTP requests. + # - {::http_default_port}[rdoc-ref:Net::HTTP.http_default_port]: + # Returns integer 80, the default port to use for HTTP requests. + # - {::https_default_port}[rdoc-ref:Net::HTTP.https_default_port]: + # Returns integer 443, the default port to use for HTTPS requests. + # - {#ipaddr}[rdoc-ref:Net::HTTP#ipaddr]: + # Returns the IP address for the connection. + # - {#ipaddr=}[rdoc-ref:Net::HTTP#ipaddr=]: + # Sets the IP address for the connection. + # - {:local_host}[rdoc-ref:Net::HTTP#local_host]: + # Returns the string local host used to establish the connection. + # - {:local_host=}[rdoc-ref:Net::HTTP#local_host=]: + # Sets the string local host used to establish the connection. + # - {:local_port}[rdoc-ref:Net::HTTP#local_port]: + # Returns the integer local port used to establish the connection. + # - {:local_port=}[rdoc-ref:Net::HTTP#local_port=]: + # Sets the integer local port used to establish the connection. + # - {:port}[rdoc-ref:Net::HTTP#port]: + # Returns the integer port number. + # + # === \HTTP Version + # + # - {::version_1_2?}[rdoc-ref:Net::HTTP.version_1_2?] + # (aliased as {::version_1_2}[rdoc-ref:Net::HTTP.version_1_2]): + # Returns true; retained for compatibility. + # + # === Debugging + # + # - {#set_debug_output}[rdoc-ref:Net::HTTP#set_debug_output]: + # Sets the output stream for debugging. + # + class HTTP < Protocol + + # :stopdoc: + VERSION = "0.9.1" + 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: + # Net::HTTP.get_print(hostname, path, port = 80) -> nil + # Net::HTTP:get_print(uri, headers = {}, port = uri.port) -> nil + # + # Like 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: + # Net::HTTP.get(hostname, path, port = 80) -> body + # 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 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 = URI('https://jsonplaceholder.typicode.com/todos/1') + # headers = {'Content-type' => 'application/json; charset=UTF-8'} + # Net::HTTP.get(uri, headers) + # + # Related: + # + # - Net::HTTP::Get: request class for \HTTP method +GET+. + # - 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: + # Net::HTTP.get_response(hostname, path, port = 80) -> http_response + # Net::HTTP:get_response(uri, headers = {}, port = uri.port) -> http_response + # + # Like Net::HTTP.get, but returns a 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 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 = Net::HTTP.post(_uri, data, headers) # => # + # puts res.body + # + # Output: + # + # { + # "title": "foo", + # "body": "bar", + # "userId": 1, + # "id": 101 + # } + # + # Related: + # + # - Net::HTTP::Post: request class for \HTTP method +POST+. + # - 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 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 = Net::HTTP.post_form(_uri, data) # => # + # 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 + + # Sends a PUT request to the server; returns a 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 = Net::HTTP.put(_uri, data, headers) # => # + # puts res.body + # + # Output: + # + # { + # "title": "foo", + # "body": "bar", + # "userId": 1, + # "id": 101 + # } + # + # Related: + # + # - Net::HTTP::Put: request class for \HTTP method +PUT+. + # - Net::HTTP#put: convenience method for \HTTP method +PUT+. + # + def HTTP.put(url, data, header = nil) + start(url.hostname, url.port, + :use_ssl => url.scheme == 'https' ) {|http| + http.put(url, data, header) + } + end + + # + # \HTTP session management + # + + # Returns integer +80+, the default port to use for \HTTP requests: + # + # Net::HTTP.default_port # => 80 + # + def HTTP.default_port + http_default_port() + end + + # Returns integer +80+, the default port to use for \HTTP requests: + # + # Net::HTTP.http_default_port # => 80 + # + def HTTP.http_default_port + 80 + end + + # Returns integer +443+, the default port to use for HTTPS requests: + # + # 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 \Net::HTTP object, +http+, via \Net::HTTP.new: + # + # - For arguments +address+ and +port+, see Net::HTTP.new. + # - For proxy-defining arguments +p_addr+ through +p_pass+, + # see {Proxy Server}[rdoc-ref:Net::HTTP@Proxy+Server]. + # - For argument +opts+, see below. + # + # With no block given: + # + # - Calls http.start 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 = Net::HTTP.start(hostname) + # http.started? # => true + # http.finish + # http.started? # => false + # + # With a block given: + # + # - Calls http.start 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' + # 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 opts[:use_ssl] is a truthy value, + # the value passed to +new+ is 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 \Net::HTTP object +http+ + # (but does not open a TCP connection or \HTTP session). + # + # With only string argument +address+ given + # (and ENV['http_proxy'] undefined or +nil+), + # the returned +http+: + # + # - Has the given address. + # - Has the default port number, Net::HTTP.default_port (80). + # - Has no proxy. + # + # Example: + # + # http = Net::HTTP.new(hostname) + # # => # + # 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 = Net::HTTP.new(hostname, 8000) + # # => # + # http.port # => 8000 + # + # For proxy-defining arguments +p_addr+ through +p_no_proxy+, + # see {Proxy Server}[rdoc-ref: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, p_use_ssl = nil) + http = super address, port + + if proxy_class? then # from 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 + http.proxy_use_ssl = @proxy_use_ssl + elsif p_addr == :ENV then + http.proxy_from_env = true + else + if p_addr && p_no_proxy && !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 + http.proxy_use_ssl = p_use_ssl + end + + http + end + + class << HTTP + # Allows to set the default configuration that will be used + # when creating a new connection. + # + # Example: + # + # Net::HTTP.default_configuration = { + # read_timeout: 1, + # write_timeout: 1 + # } + # http = Net::HTTP.new(hostname) + # http.open_timeout # => 60 + # http.read_timeout # => 1 + # http.write_timeout # => 1 + # + attr_accessor :default_configuration + end + + # Creates a new \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: + defaults = { + keep_alive_timeout: 2, + close_on_empty_response: 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 + } + options = defaults.merge(self.class.default_configuration || {}) + + @address = address + @port = (port || HTTP.default_port) + @ipaddr = nil + @local_host = nil + @local_port = nil + @curr_http_version = HTTPVersion + @keep_alive_timeout = options[:keep_alive_timeout] + @last_communicated = nil + @close_on_empty_response = options[:close_on_empty_response] + @socket = nil + @started = false + @open_timeout = options[:open_timeout] + @read_timeout = options[:read_timeout] + @write_timeout = options[:write_timeout] + @continue_timeout = options[:continue_timeout] + @max_retries = options[:max_retries] + @debug_output = options[:debug_output] + @response_body_encoding = options[:response_body_encoding] + @ignore_eof = options[:ignore_eof] + @tcpsocket_supports_open_timeout = nil + + @proxy_from_env = false + @proxy_uri = nil + @proxy_address = nil + @proxy_port = nil + @proxy_user = nil + @proxy_pass = nil + @proxy_use_ssl = 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+: + # + # Net::HTTP.new(hostname).inspect + # # => "#" + # + 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 = 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 '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}[https://docs.ruby-lang.org/en/master/Encoding.html]. + # + # Examples: + # + # http = Net::HTTP.new(hostname) + # http.response_body_encoding = 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 + # 'ENV['http_proxy']'; + # see {Proxy Using ENV['http_proxy']}[rdoc-ref: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:Net::HTTP@Proxy+Server]. + attr_writer :proxy_address + + # Sets the proxy port; + # see {Proxy Server}[rdoc-ref:Net::HTTP@Proxy+Server]. + attr_writer :proxy_port + + # Sets the proxy user; + # see {Proxy Server}[rdoc-ref:Net::HTTP@Proxy+Server]. + attr_writer :proxy_user + + # Sets the proxy password; + # see {Proxy Server}[rdoc-ref:Net::HTTP@Proxy+Server]. + attr_writer :proxy_pass + + # Sets whether the proxy uses SSL; + # see {Proxy Server}[rdoc-ref:Net::HTTP@Proxy+Server]. + attr_writer :proxy_use_ssl + + # 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 = 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 = 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 = 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 + # \Net::ReadTimeout, IOError, EOFError, Errno::ECONNRESET, + # Errno::ECONNABORTED, Errno::EPIPE, OpenSSL::SSL::SSLError, + # Timeout::Error. + # The initial value is 1. + # + # Argument +retries+ must be a non-negative numeric value: + # + # http = 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 = Net::HTTP.new(hostname) + # http.read_timeout # => 60 + # http.get('/todos/1') # => # + # http.read_timeout = 0 + # http.get('/todos/1') # Raises 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 = < 60 + # http.post(_uri.path, data, headers) + # # => # + # http.write_timeout = 0 + # http.post(_uri.path, data, headers) # Raises 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 Content-Length headers; + # initially +true+. + attr_accessor :ignore_eof + + # Returns +true+ if the \HTTP session has been started: + # + # http = Net::HTTP.new(hostname) + # http.started? # => false + # http.start + # http.started? # => true + # http.finish # => nil + # http.started? # => false + # + # 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 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_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, + ].freeze # :nodoc: + + SSL_IVNAMES = SSL_ATTRIBUTES.map { |a| "@#{a}".to_sym }.freeze # :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=}[OpenSSL::SSL::SSL::Context#ciphers=]. + attr_accessor :ciphers + + # Sets or returns the extra X509 certificates to be added to the certificate chain. + # See {OpenSSL::SSL::SSLContext#add_certificate}[OpenSSL::SSL::SSL::Context#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=}[OpenSSL::SSL::SSL::Context#ssl_version=]. + attr_accessor :ssl_version + + # Sets or returns the minimum SSL version. + # See {OpenSSL::SSL::SSLContext#min_version=}[OpenSSL::SSL::SSL::Context#min_version=]. + attr_accessor :min_version + + # Sets or returns the maximum SSL version. + # See {OpenSSL::SSL::SSLContext#max_version=}[OpenSSL::SSL::SSL::Context#max_version=]. + 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=}[OpenSSL::SSL::SSL::Context#verify_hostname=]. + 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 = Net::HTTP.new(hostname) + # # => # + # http.start + # # => # + # 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 + # # => # + # 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 + + # Finishes the \HTTP session: + # + # http = 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 + + # :stopdoc: + 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}..." + begin + s = timeouted_connect(conn_addr, conn_port) + rescue => e + if (defined?(IO::TimeoutError) && e.is_a?(IO::TimeoutError)) || e.is_a?(Errno::ETIMEDOUT) # for compatibility with previous versions + e = Net::OpenTimeout.new(e) + end + 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? + if @proxy_use_ssl + proxy_sock = OpenSSL::SSL::SSLSocket.new(s) + ssl_socket_connect(proxy_sock, @open_timeout) + else + proxy_sock = s + end + proxy_sock = BufferedIO.new(proxy_sock, 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" + proxy_sock.write(buf) + HTTPResponse.read_new(proxy_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 Resolv::IPv4::Regex, 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 + + tcp_socket_parameters = TCPSocket.instance_method(:initialize).parameters + TCP_SOCKET_NEW_HAS_OPEN_TIMEOUT = if tcp_socket_parameters != [[:rest]] + tcp_socket_parameters.include?([:key, :open_timeout]) + else + # Use Socket.tcp to find out since there is no parameters information for TCPSocket#initialize + # See discussion in https://github.com/ruby/net-http/pull/224 + Socket.method(:tcp).parameters.include?([:key, :open_timeout]) + end + private_constant :TCP_SOCKET_NEW_HAS_OPEN_TIMEOUT + + def timeouted_connect(conn_addr, conn_port) + if TCP_SOCKET_NEW_HAS_OPEN_TIMEOUT + TCPSocket.open(conn_addr, conn_port, @local_host, @local_port, open_timeout: @open_timeout) + else + Timeout.timeout(@open_timeout, Net::OpenTimeout) { + TCPSocket.open(conn_addr, conn_port, @local_host, @local_port) + } + end + end + private :timeouted_connect + + def on_connect + end + private :on_connect + + 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 + @proxy_use_ssl = nil + + # Creates an \HTTP proxy class which behaves like \Net::HTTP, but + # performs all access via the specified proxy. + # + # This class is obsolete. You may pass these same parameters directly to + # \Net::HTTP.new. See Net::HTTP.new for details of the arguments. + def HTTP.Proxy(p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil, p_use_ssl = 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 + @proxy_use_ssl = p_use_ssl + } + end + + # :startdoc: + + 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 Net::HTTP@Proxy+Server. + attr_reader :proxy_address + + # Returns the port number of the proxy host, or +nil+ if none; + # see Net::HTTP@Proxy+Server. + attr_reader :proxy_port + + # Returns the user name for accessing the proxy, or +nil+ if none; + # see Net::HTTP@Proxy+Server. + attr_reader :proxy_user + + # Returns the password for accessing the proxy, or +nil+ if none; + # see Net::HTTP@Proxy+Server. + attr_reader :proxy_pass + + # Use SSL when talking to the proxy. If Net::HTTP does not use a proxy, nil. + attr_reader :proxy_use_ssl + end + + # Returns +true+ if a proxy server is defined, +false+ otherwise; + # see {Proxy Server}[rdoc-ref: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: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 ||= 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: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: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: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: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 + # :stopdoc: + + def unescape(value) + require 'cgi/escape' + require 'cgi/util' unless defined?(CGI::EscapeExt) + 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 + # :startdoc: + + # + # HTTP operations + # + + public + + # :call-seq: + # get(path, initheader = nil) {|res| ... } + # + # Sends a GET request to the server; + # returns an instance of a subclass of Net::HTTPResponse. + # + # The request is based on the 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 = Net::HTTP.new(hostname) + # http.get('/todos/1') do |res| + # p res + # end # => # + # + # 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('/') # => # + # + # Related: + # + # - Net::HTTP::Get: request class for \HTTP method GET. + # - 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 Net::HTTPResponse. + # + # The request is based on the Net::HTTP::Head object + # created from string +path+ and initial headers hash +initheader+: + # + # res = http.head('/todos/1') # => # + # 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 Net::HTTPResponse. + # + # The request is based on the 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 = Net::HTTP.new(hostname) + # http.post('/todos', data) do |res| + # p res + # end # => # + # + # 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) # => # + # + # Related: + # + # - Net::HTTP::Post: request class for \HTTP method POST. + # - 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 Net::HTTPResponse. + # + # The request is based on the 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 = Net::HTTP.new(hostname) + # http.patch('/todos/1', data) do |res| + # p res + # end # => # + # + # 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) # => # + # + 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 Net::HTTPResponse. + # + # The request is based on the 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 = Net::HTTP.new(hostname) + # http.put('/todos/1', data) # => # + # + # Related: + # + # - Net::HTTP::Put: request class for \HTTP method PUT. + # - Net::HTTP.put: sends PUT request, returns response body. + # + 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 Net::HTTPResponse. + # + # The request is based on the 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 = 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 Net::HTTPResponse. + # + # The request is based on the 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 = 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 Net::HTTPResponse. + # + # The request is based on the 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 = 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 Net::HTTPResponse. + # + # The request is based on the Net::HTTP::Options object + # created from string +path+ and initial headers hash +initheader+. + # + # http = 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 Net::HTTPResponse. + # + # The request is based on the 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 = 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 Net::HTTPResponse. + # + # The request is based on the Net::HTTP::Delete object + # created from string +path+ and initial headers hash +initheader+. + # + # http = 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 Net::HTTPResponse. + # + # The request is based on the Net::HTTP::Move object + # created from string +path+ and initial headers hash +initheader+. + # + # http = 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 Net::HTTPResponse. + # + # The request is based on the Net::HTTP::Copy object + # created from string +path+ and initial headers hash +initheader+. + # + # http = 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 Net::HTTPResponse. + # + # The request is based on the 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 = 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 Net::HTTPResponse. + # + # The request is based on the Net::HTTP::Trace object + # created from string +path+ and initial headers hash +initheader+. + # + # http = 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 Net::HTTPResponse object. + # + # The request is based on the Net::HTTP::Get object + # created from string +path+ and initial headers hash +initheader+. + # + # With no block given, returns the response object: + # + # http = Net::HTTP.new(hostname) + # http.request_get('/todos') # => # + # + # 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 # => # + # + # Output: + # + # # + # + 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 Net::HTTPResponse. + # + # The request is based on the Net::HTTP::Head object + # created from string +path+ and initial headers hash +initheader+. + # + # http = Net::HTTP.new(hostname) + # http.head('/todos/1') # => # + # + 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 Net::HTTPResponse object. + # + # The request is based on the Net::HTTP::Post object + # created from string +path+, string +data+, and initial headers hash +initheader+. + # + # With no block given, returns the response object: + # + # http = Net::HTTP.new(hostname) + # http.post('/todos', 'xyzzy') + # # => # + # + # 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 # => # + # + # 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 Net::HTTPResponse. + # + # The request is based on the Net::HTTP::Put object + # created from string +path+, string +data+, and initial headers hash +initheader+. + # + # http = Net::HTTP.new(hostname) + # http.put('/todos/1', 'xyzzy') + # # => # + # + 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 Net::HTTPResponse. + # + # The request is based on the Net::HTTPRequest object + # created from string +path+, string +data+, and initial headers hash +header+. + # That object is an instance of the + # {subclass of Net::HTTPRequest}[rdoc-ref: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 = Net::HTTP.new(hostname) + # http.send_request('GET', '/todos/1') + # # => # + # http.send_request('POST', '/todos', 'xyzzy') + # # => # + # + 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 Net::HTTPResponse object. + # + # The given +req+ must be an instance of a + # {subclass of Net::HTTPRequest}[rdoc-ref:Net::HTTPRequest@Request+Subclasses]. + # Argument +body+ should be given only if needed for the request. + # + # With no block given, returns the response object: + # + # http = Net::HTTP.new(hostname) + # + # req = Net::HTTP::Get.new('/todos/1') + # http.request(req) + # # => # + # + # req = Net::HTTP::Post.new('/todos') + # http.request(req, 'xyzzy') + # # => # + # + # With a block given, calls the block with the response and returns the response: + # + # req = Net::HTTP::Get.new('/todos/1') + # http.request(req) do |res| + # p res + # end # => # + # + # Output: + # + # # + # + 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 + + # :stopdoc: + + IDEMPOTENT_METHODS_ = %w/GET HEAD PUT DELETE OPTIONS TRACE/.freeze # :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?) { + if block_given? + count = max_retries # Don't restart in the middle of a download + yield res + end + } + rescue Net::OpenTimeout + raise + rescue Net::ReadTimeout, IOError, EOFError, + Errno::ECONNRESET, Errno::ECONNABORTED, Errno::EPIPE, Errno::ETIMEDOUT, + # avoid a dependency on OpenSSL + defined?(OpenSSL::SSL) ? OpenSSL::SSL::SSLError : IOError, + 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 + + # for backward compatibility until Ruby 4.0 + # https://bugs.ruby-lang.org/issues/20900 + # https://github.com/bblimke/webmock/pull/1081 + HTTPSession = HTTP + deprecate_constant :HTTPSession +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' diff --git a/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/lib/net/http/exceptions.rb b/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/lib/net/http/exceptions.rb new file mode 100644 index 0000000..4342cfc --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/lib/net/http/exceptions.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true +module Net + # Net::HTTP exception class. + # You cannot use Net::HTTPExceptions directly; instead, you must use + # its subclasses. + module HTTPExceptions # :nodoc: + def initialize(msg, res) #:nodoc: + super msg + @response = res + end + attr_reader :response + alias data response #:nodoc: obsolete + end + + # :stopdoc: + 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/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/lib/net/http/generic_request.rb b/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/lib/net/http/generic_request.rb new file mode 100644 index 0000000..5b01ea4 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/lib/net/http/generic_request.rb @@ -0,0 +1,429 @@ +# frozen_string_literal: true +# +# \HTTPGenericRequest is the parent of the Net::HTTPRequest class. +# +# Do not use this directly; instead, use a subclass of Net::HTTPRequest. +# +# == About the Examples +# +# :include: doc/net-http/examples.rdoc +# +class Net::HTTPGenericRequest + + include Net::HTTPHeader + + def initialize(m, reqbody, resbody, uri_or_path, initheader = nil) # :nodoc: + @method = m + @request_has_body = reqbody + @response_has_body = resbody + + if URI === uri_or_path then + raise ArgumentError, "not an HTTP URI" unless URI::HTTP === uri_or_path + hostname = uri_or_path.host + raise ArgumentError, "no host component for URI" unless (hostname && hostname.length > 0) + @uri = uri_or_path.dup + @path = uri_or_path.request_uri + raise ArgumentError, "no HTTP request path given" unless @path + else + @uri = 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 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'] ||= @uri.authority if @uri + @body = nil + @body_stream = nil + @body_data = nil + end + + # Returns the string method name for the request: + # + # Net::HTTP::Get.new(uri).method # => "GET" + # Net::HTTP::Post.new(uri).method # => "POST" + # + attr_reader :method + + # Returns the string path for the request: + # + # Net::HTTP::Get.new(uri).path # => "/" + # Net::HTTP::Post.new('example.com').path # => "example.com" + # + attr_reader :path + + # Returns the URI object for the request, or +nil+ if none: + # + # Net::HTTP::Get.new(uri).uri + # # => # + # Net::HTTP::Get.new('example.com').uri # => nil + # + attr_reader :uri + + # Returns +false+ if the request's header 'Accept-Encoding' + # has been set manually or deleted + # (indicating that the user intends to handle encoding in the response), + # +true+ otherwise: + # + # req = Net::HTTP::Get.new(uri) # => # + # 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: + # + # Net::HTTP::Post.new(uri).inspect # => "#" + # + def inspect + "\#<#{self.class} #{@method}>" + end + + # Returns a string representation of the request with the details for pp: + # + # require 'pp' + # post = Net::HTTP::Post.new(uri) + # post.inspect # => "#" + # post.pretty_inspect + # # => # ["gzip;q=1.0,deflate;q=0.6,identity;q=0.3"], + # "accept" => ["*/*"], + # "user-agent" => ["Ruby"], + # "host" => ["www.ruby-lang.org"]}> + # + def pretty_print(q) + q.object_group(self) { + q.breakable + q.text @method + q.breakable + q.text "path="; q.pp @path + q.breakable + q.text "headers="; q.pp to_hash + } + 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: + # + # Net::HTTP::Post.new(uri).request_body_permitted? # => true + # 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: + # + # Net::HTTP::Post.new(uri).response_body_permitted? # => true + # Net::HTTP::Head.new(uri).response_body_permitted? # => false + # + def response_body_permitted? + @response_has_body + end + + def body_exist? # :nodoc: + warn "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 = 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 = 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 = Net::HTTP::Post.new(uri) # => # + # req.body_stream # => nil + # require 'stringio' + # req.body_stream = StringIO.new('xyzzy') # => # + # req.body_stream # => # + # + attr_reader :body_stream + + # Sets the body stream for the request: + # + # req = Net::HTTP::Post.new(uri) # => # + # req.body_stream # => nil + # require 'stringio' + # req.body_stream = StringIO.new('xyzzy') # => # + # req.body_stream # => # + # + 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 = URI::HTTPS + else + scheme = 'http' + klass = URI::HTTP + end + + if host = self['host'] + host = URI.parse("//#{host}").host # Remove a port component from the existing Host header + elsif host = @uri.host + else + host = addr + end + # convert the class of the 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 + + # :stopdoc: + + 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' + 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 + 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, 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 + + ## + # 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 = Net::HTTPResponse.read_new(sock) + unless res.kind_of?(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/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/lib/net/http/header.rb b/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/lib/net/http/header.rb new file mode 100644 index 0000000..797a3be --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/lib/net/http/header.rb @@ -0,0 +1,985 @@ +# frozen_string_literal: true +# +# The \HTTPHeader module provides access to \HTTP headers. +# +# The module is included in: +# +# - Net::HTTPGenericRequest (and therefore Net::HTTPRequest). +# - 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 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:Net::HTTPHeader@Setters]. +# - A Net::HTTPResponse object: +# the objects headers are usually those returned from the host. +# Fields may be retrieved from the object; +# see {Getters}[rdoc-ref:Net::HTTPHeader@Getters] +# and {Iterators}[rdoc-ref: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 'Accept' is treated as if it were +# 'Accept'.downcase; i.e., 'accept'. +# - A symbol: Key :Accept is treated as if it were +# :Accept.to_s.downcase; i.e., 'accept'. +# +# Examples: +# +# req = 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 +# self[key.downcase.to_s].join(', ')): +# +# - #[]: 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 'Authorization'. +# - #content_length=: Sets the integer length for field 'Content-Length. +# - #content_type=: Sets the string value for field 'Content-Type'. +# - #proxy_basic_auth: Sets the string authorization header for 'Proxy-Authorization'. +# - #set_range: Sets the value for field 'Range'. +# +# === 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 'Content-Length'. +# - #content_range: Returns the Range value of field 'Content-Range'. +# - #content_type: Returns the string value of field 'Content-Type'. +# - #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 'Content-Type'. +# - #sub_type: Returns second part of the string value of field 'Content-Type'. +# - #range: Returns an array of Range objects of field 'Range', or +nil+. +# - #range_length: Returns the integer length of the range given in field 'Content-Range'. +# - #type_params: Returns the string parameters for 'Content-Type'. +# +# === Queries +# +# - #chunked?: Returns whether field 'Transfer-Encoding' is set to 'chunked'. +# - #connection_close?: Returns whether field 'Connection' is set to 'close'. +# - #connection_keep_alive?: Returns whether field 'Connection' is set to 'keep-alive'. +# - #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 Net::HTTPHeader + # The maximum length of HTTP header keys. + MAX_KEY_LENGTH = 1024 + # The maximum length of HTTP header values. + 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:Net::HTTPHeader@Fields]: + # + # res = 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: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:Net::HTTPHeader@Fields]: + # + # req = 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: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:Net::HTTPHeader@Fields]: + # + # req = 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 + + # :stopdoc: + 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 + # :startdoc: + + # Returns the array field value for the given +key+, + # or +nil+ if there is no such field; + # see {Fields}[rdoc-ref:Net::HTTPHeader@Fields]: + # + # res = 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:Net::HTTPHeader@Fields]: + # + # res = 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 = 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. + # + # Net::HTTPHeader#each is an alias for 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 = 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. + # + # Net::HTTPHeader#each_name is an alias for 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 = 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 = 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:Net::HTTPHeader@Fields]); + # returns the deleted value, or +nil+ if no such field exists: + # + # req = 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 = 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 = 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. + # + # Net::HTTPHeader#canonical_each is an alias for 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) # :nodoc: + name.to_s.split('-'.freeze).map {|s| s.capitalize }.join('-'.freeze) + end + private :capitalize + + # Returns an array of Range objects that represent + # the value of field 'Range', + # 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 = 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 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 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 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 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 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 'Range'; + # see {Range request header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#range-request-header]: + # + # With argument +length+: + # + # req = 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" + # + # Net::HTTPHeader#range= is an alias for 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 Net::HTTPHeaderSyntaxError, 'range.first is negative' if first < 0 + raise Net::HTTPHeaderSyntaxError, 'range.last is negative' if last < 0 + raise 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 'Content-Length' 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 = Net::HTTP.get_response(hostname, '/nosuch/1') + # res.content_length # => 2 + # res = 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 Net::HTTPHeaderSyntaxError, 'wrong Content-Length format' + len.to_i + end + + # Sets the value of field 'Content-Length' 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 = Net::HTTP::Post.new(_uri) # => # + # req.body = '{"title": "foo","body": "bar","userId": 1}' + # req.content_length = req.body.size # => 42 + # req.content_type = 'application/json' + # res = Net::HTTP.start(hostname) do |http| + # http.request(req) + # end # => # + # + 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 'Transfer-Encoding' + # exists and has value 'chunked', + # +false+ otherwise; + # see {Transfer-Encoding response header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#transfer-encoding-response-header]: + # + # res = 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 + # 'Content-Range', 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 = 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 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 + # 'Content-Range', 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 = 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 'Content-Type', + # 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 = 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 'Content-Type', + # 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 = 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 'Content-Type', + # 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 = 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 'Content-Type', + # 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 = 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 'Content-Type'; + # returns the new value; + # see {Content-Type request header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-type-request-header]: + # + # req = Net::HTTP::Get.new(uri) + # req.set_content_type('application/json') # => ["application/json"] + # + # Net::HTTPHeader#content_type= is an alias for 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 'Content-Type' + # to 'application/x-www-form-urlencoded'. + # + # The resulting request is suitable for HTTP request +POST+ or +PUT+. + # + # Argument +params+ must be suitable for use as argument +enum+ to + # {URI.encode_www_form}[https://docs.ruby-lang.org/en/master/URI.html#method-c-encode_www_form]. + # + # With only argument +params+ given, + # sets the body to a URL-encoded string with the default separator '&': + # + # req = 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" + # + # Net::HTTPHeader#form_data= is an alias for Net::HTTPHeader#set_form_data. + def set_form_data(params, sep = '&') + query = 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 params.map will be called), + # and is often an array or hash. + # + # First, we set up a request: + # + # _uri = uri.dup + # _uri.path ='/posts' + # req = Net::HTTP::Post.new(_uri) + # + # Argument +params+ As an Array + # + # 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 + # 'multipart/form-data': + # + # - 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]]) + # + # Argument +params+ As a Hash + # + # 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 + # 'multipart/form-data'). + # + # 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 'Content-Type', and must be one of: + # + # - 'application/x-www-form-urlencoded' (the default). + # - 'multipart/form-data'; + # see {RFC 7578}[https://www.rfc-editor.org/rfc/rfc7578]. + # + # Optional argument +formopt+ is a hash of options + # (applicable only when argument +enctype+ + # is 'multipart/form-data') + # 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 'Authorization' 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 'Proxy-Authorization' 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) # :nodoc: + '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/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/lib/net/http/proxy_delta.rb b/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/lib/net/http/proxy_delta.rb new file mode 100644 index 0000000..e7d30de --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/lib/net/http/proxy_delta.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true +module 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/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/lib/net/http/request.rb b/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/lib/net/http/request.rb new file mode 100644 index 0000000..4a13857 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/lib/net/http/request.rb @@ -0,0 +1,88 @@ +# frozen_string_literal: true + +# This class is the base class for \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 URI or a string hostname: +# +# require 'net/http' +# uri = URI('https://jsonplaceholder.typicode.com/') +# req = Net::HTTP::Get.new(uri) # => # +# req = Net::HTTP::Get.new(uri.hostname) # => # +# +# And with any of the subclasses: +# +# req = Net::HTTP::Head.new(uri) # => # +# req = Net::HTTP::Post.new(uri) # => # +# req = Net::HTTP::Put.new(uri) # => # +# # ... +# +# The new instance is suitable for use as the argument to 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: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 = Net::HTTP::Get.new(uri, {'foo' => '0', 'bar' => '1'}) +# +# This class (and therefore its subclasses) also includes (indirectly) +# module Net::HTTPHeader, which gives access to its +# {methods for setting headers}[rdoc-ref:Net::HTTPHeader@Setters]. +# +# == Request Subclasses +# +# Subclasses for HTTP requests: +# +# - Net::HTTP::Get +# - Net::HTTP::Head +# - Net::HTTP::Post +# - Net::HTTP::Put +# - Net::HTTP::Delete +# - Net::HTTP::Options +# - Net::HTTP::Trace +# - Net::HTTP::Patch +# +# Subclasses for WebDAV requests: +# +# - Net::HTTP::Propfind +# - Net::HTTP::Proppatch +# - Net::HTTP::Mkcol +# - Net::HTTP::Copy +# - Net::HTTP::Move +# - Net::HTTP::Lock +# - Net::HTTP::Unlock +# +class Net::HTTPRequest < Net::HTTPGenericRequest + # Creates an HTTP request object for +path+. + # + # +initheader+ are the default headers to use. 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/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/lib/net/http/requests.rb b/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/lib/net/http/requests.rb new file mode 100644 index 0000000..939d413 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/lib/net/http/requests.rb @@ -0,0 +1,444 @@ +# 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 'net/http' +# uri = URI('http://example.com') +# hostname = uri.hostname # => "example.com" +# req = Net::HTTP::Get.new(uri) # => # +# res = Net::HTTP.start(hostname) do |http| +# http.request(req) +# end +# +# See {Request Headers}[rdoc-ref: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: +# +# - Net::HTTP.get: sends +GET+ request, returns response body. +# - Net::HTTP#get: sends +GET+ request, returns response object. +# +class Net::HTTP::Get < Net::HTTPRequest + # :stopdoc: + 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 'net/http' +# uri = URI('http://example.com') +# hostname = uri.hostname # => "example.com" +# req = Net::HTTP::Head.new(uri) # => # +# res = Net::HTTP.start(hostname) do |http| +# http.request(req) +# end +# +# See {Request Headers}[rdoc-ref: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: +# +# - Net::HTTP#head: sends +HEAD+ request, returns response object. +# +class Net::HTTP::Head < Net::HTTPRequest + # :stopdoc: + 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 'net/http' +# uri = URI('http://example.com') +# hostname = uri.hostname # => "example.com" +# uri.path = '/posts' +# req = Net::HTTP::Post.new(uri) # => # +# req.body = '{"title": "foo","body": "bar","userId": 1}' +# req.content_type = 'application/json' +# res = Net::HTTP.start(hostname) do |http| +# http.request(req) +# end +# +# See {Request Headers}[rdoc-ref: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: +# +# - Net::HTTP.post: sends +POST+ request, returns response object. +# - Net::HTTP#post: sends +POST+ request, returns response object. +# +class Net::HTTP::Post < Net::HTTPRequest + # :stopdoc: + 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 'net/http' +# uri = URI('http://example.com') +# hostname = uri.hostname # => "example.com" +# uri.path = '/posts' +# req = Net::HTTP::Put.new(uri) # => # +# req.body = '{"title": "foo","body": "bar","userId": 1}' +# req.content_type = 'application/json' +# res = Net::HTTP.start(hostname) do |http| +# http.request(req) +# end +# +# See {Request Headers}[rdoc-ref: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. +# +# Related: +# +# - Net::HTTP.put: sends +PUT+ request, returns response object. +# - Net::HTTP#put: sends +PUT+ request, returns response object. +# +class Net::HTTP::Put < Net::HTTPRequest + # :stopdoc: + 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 'net/http' +# uri = URI('http://example.com') +# hostname = uri.hostname # => "example.com" +# uri.path = '/posts/1' +# req = Net::HTTP::Delete.new(uri) # => # +# res = Net::HTTP.start(hostname) do |http| +# http.request(req) +# end +# +# See {Request Headers}[rdoc-ref: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: +# +# - Net::HTTP#delete: sends +DELETE+ request, returns response object. +# +class Net::HTTP::Delete < Net::HTTPRequest + # :stopdoc: + 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 'net/http' +# uri = URI('http://example.com') +# hostname = uri.hostname # => "example.com" +# req = Net::HTTP::Options.new(uri) # => # +# res = Net::HTTP.start(hostname) do |http| +# http.request(req) +# end +# +# See {Request Headers}[rdoc-ref: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: +# +# - Net::HTTP#options: sends +OPTIONS+ request, returns response object. +# +class Net::HTTP::Options < Net::HTTPRequest + # :stopdoc: + 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 'net/http' +# uri = URI('http://example.com') +# hostname = uri.hostname # => "example.com" +# req = Net::HTTP::Trace.new(uri) # => # +# res = Net::HTTP.start(hostname) do |http| +# http.request(req) +# end +# +# See {Request Headers}[rdoc-ref: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: +# +# - Net::HTTP#trace: sends +TRACE+ request, returns response object. +# +class Net::HTTP::Trace < Net::HTTPRequest + # :stopdoc: + 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 'net/http' +# uri = URI('http://example.com') +# hostname = uri.hostname # => "example.com" +# uri.path = '/posts' +# req = Net::HTTP::Patch.new(uri) # => # +# req.body = '{"title": "foo","body": "bar","userId": 1}' +# req.content_type = 'application/json' +# res = Net::HTTP.start(hostname) do |http| +# http.request(req) +# end +# +# See {Request Headers}[rdoc-ref: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: +# +# - Net::HTTP#patch: sends +PATCH+ request, returns response object. +# +class Net::HTTP::Patch < Net::HTTPRequest + # :stopdoc: + 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 'net/http' +# uri = URI('http://example.com') +# hostname = uri.hostname # => "example.com" +# req = Net::HTTP::Propfind.new(uri) # => # +# res = Net::HTTP.start(hostname) do |http| +# http.request(req) +# end +# +# See {Request Headers}[rdoc-ref:Net::HTTPRequest@Request+Headers]. +# +# Related: +# +# - Net::HTTP#propfind: sends +PROPFIND+ request, returns response object. +# +class Net::HTTP::Propfind < Net::HTTPRequest + # :stopdoc: + 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 'net/http' +# uri = URI('http://example.com') +# hostname = uri.hostname # => "example.com" +# req = Net::HTTP::Proppatch.new(uri) # => # +# res = Net::HTTP.start(hostname) do |http| +# http.request(req) +# end +# +# See {Request Headers}[rdoc-ref:Net::HTTPRequest@Request+Headers]. +# +# Related: +# +# - Net::HTTP#proppatch: sends +PROPPATCH+ request, returns response object. +# +class Net::HTTP::Proppatch < Net::HTTPRequest + # :stopdoc: + 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 'net/http' +# uri = URI('http://example.com') +# hostname = uri.hostname # => "example.com" +# req = Net::HTTP::Mkcol.new(uri) # => # +# res = Net::HTTP.start(hostname) do |http| +# http.request(req) +# end +# +# See {Request Headers}[rdoc-ref:Net::HTTPRequest@Request+Headers]. +# +# Related: +# +# - Net::HTTP#mkcol: sends +MKCOL+ request, returns response object. +# +class Net::HTTP::Mkcol < Net::HTTPRequest + # :stopdoc: + 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 'net/http' +# uri = URI('http://example.com') +# hostname = uri.hostname # => "example.com" +# req = Net::HTTP::Copy.new(uri) # => # +# res = Net::HTTP.start(hostname) do |http| +# http.request(req) +# end +# +# See {Request Headers}[rdoc-ref:Net::HTTPRequest@Request+Headers]. +# +# Related: +# +# - Net::HTTP#copy: sends +COPY+ request, returns response object. +# +class Net::HTTP::Copy < Net::HTTPRequest + # :stopdoc: + 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 'net/http' +# uri = URI('http://example.com') +# hostname = uri.hostname # => "example.com" +# req = Net::HTTP::Move.new(uri) # => # +# res = Net::HTTP.start(hostname) do |http| +# http.request(req) +# end +# +# See {Request Headers}[rdoc-ref:Net::HTTPRequest@Request+Headers]. +# +# Related: +# +# - Net::HTTP#move: sends +MOVE+ request, returns response object. +# +class Net::HTTP::Move < Net::HTTPRequest + # :stopdoc: + 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 'net/http' +# uri = URI('http://example.com') +# hostname = uri.hostname # => "example.com" +# req = Net::HTTP::Lock.new(uri) # => # +# res = Net::HTTP.start(hostname) do |http| +# http.request(req) +# end +# +# See {Request Headers}[rdoc-ref:Net::HTTPRequest@Request+Headers]. +# +# Related: +# +# - Net::HTTP#lock: sends +LOCK+ request, returns response object. +# +class Net::HTTP::Lock < Net::HTTPRequest + # :stopdoc: + 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 'net/http' +# uri = URI('http://example.com') +# hostname = uri.hostname # => "example.com" +# req = Net::HTTP::Unlock.new(uri) # => # +# res = Net::HTTP.start(hostname) do |http| +# http.request(req) +# end +# +# See {Request Headers}[rdoc-ref:Net::HTTPRequest@Request+Headers]. +# +# Related: +# +# - Net::HTTP#unlock: sends +UNLOCK+ request, returns response object. +# +class Net::HTTP::Unlock < Net::HTTPRequest + # :stopdoc: + METHOD = 'UNLOCK' + REQUEST_HAS_BODY = true + RESPONSE_HAS_BODY = true +end diff --git a/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/lib/net/http/response.rb b/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/lib/net/http/response.rb new file mode 100644 index 0000000..bea4346 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/lib/net/http/response.rb @@ -0,0 +1,739 @@ +# frozen_string_literal: true + +# This class is the base class for \Net::HTTP response classes. +# +# == About the Examples +# +# :include: doc/net-http/examples.rdoc +# +# == Returned Responses +# +# \Method Net::HTTP.get_response returns +# an instance of one of the subclasses of \Net::HTTPResponse: +# +# Net::HTTP.get_response(uri) +# # => # +# Net::HTTP.get_response(hostname, '/nosuch') +# # => # +# +# As does method Net::HTTP#request: +# +# req = Net::HTTP::Get.new(uri) +# Net::HTTP.start(hostname) do |http| +# http.request(req) +# end # => # +# +# \Class \Net::HTTPResponse includes module Net::HTTPHeader, +# which provides access to response header values via (among others): +# +# - \Hash-like method []. +# - Specific reader methods, such as +content_type+. +# +# Examples: +# +# res = Net::HTTP.get_response(uri) # => # +# res['Content-Type'] # => "text/html; charset=UTF-8" +# res.content_type # => "text/html" +# +# == Response Subclasses +# +# \Class \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: +# +# Net::HTTPResponse::CODE_TO_OBJ['200'] # => Net::HTTPOK +# Net::HTTPResponse::CODE_TO_OBJ['400'] # => Net::HTTPBadRequest +# Net::HTTPResponse::CODE_TO_OBJ['404'] # => Net::HTTPNotFound +# +# And you can retrieve the status code for a response object: +# +# Net::HTTP.get_response(uri).code # => "200" +# Net::HTTP.get_response(hostname, '/nosuch').code # => "404" +# +# The response subclasses (indentation shows class hierarchy): +# +# - Net::HTTPUnknownResponse (for unhandled \HTTP extensions). +# +# - Net::HTTPInformation: +# +# - Net::HTTPContinue (100) +# - Net::HTTPSwitchProtocol (101) +# - Net::HTTPProcessing (102) +# - Net::HTTPEarlyHints (103) +# +# - Net::HTTPSuccess: +# +# - Net::HTTPOK (200) +# - Net::HTTPCreated (201) +# - Net::HTTPAccepted (202) +# - Net::HTTPNonAuthoritativeInformation (203) +# - Net::HTTPNoContent (204) +# - Net::HTTPResetContent (205) +# - Net::HTTPPartialContent (206) +# - Net::HTTPMultiStatus (207) +# - Net::HTTPAlreadyReported (208) +# - Net::HTTPIMUsed (226) +# +# - Net::HTTPRedirection: +# +# - Net::HTTPMultipleChoices (300) +# - Net::HTTPMovedPermanently (301) +# - Net::HTTPFound (302) +# - Net::HTTPSeeOther (303) +# - Net::HTTPNotModified (304) +# - Net::HTTPUseProxy (305) +# - Net::HTTPTemporaryRedirect (307) +# - Net::HTTPPermanentRedirect (308) +# +# - Net::HTTPClientError: +# +# - Net::HTTPBadRequest (400) +# - Net::HTTPUnauthorized (401) +# - Net::HTTPPaymentRequired (402) +# - Net::HTTPForbidden (403) +# - Net::HTTPNotFound (404) +# - Net::HTTPMethodNotAllowed (405) +# - Net::HTTPNotAcceptable (406) +# - Net::HTTPProxyAuthenticationRequired (407) +# - Net::HTTPRequestTimeOut (408) +# - Net::HTTPConflict (409) +# - Net::HTTPGone (410) +# - Net::HTTPLengthRequired (411) +# - Net::HTTPPreconditionFailed (412) +# - Net::HTTPRequestEntityTooLarge (413) +# - Net::HTTPRequestURITooLong (414) +# - Net::HTTPUnsupportedMediaType (415) +# - Net::HTTPRequestedRangeNotSatisfiable (416) +# - Net::HTTPExpectationFailed (417) +# - Net::HTTPMisdirectedRequest (421) +# - Net::HTTPUnprocessableEntity (422) +# - Net::HTTPLocked (423) +# - Net::HTTPFailedDependency (424) +# - Net::HTTPUpgradeRequired (426) +# - Net::HTTPPreconditionRequired (428) +# - Net::HTTPTooManyRequests (429) +# - Net::HTTPRequestHeaderFieldsTooLarge (431) +# - Net::HTTPUnavailableForLegalReasons (451) +# +# - Net::HTTPServerError: +# +# - Net::HTTPInternalServerError (500) +# - Net::HTTPNotImplemented (501) +# - Net::HTTPBadGateway (502) +# - Net::HTTPServiceUnavailable (503) +# - Net::HTTPGatewayTimeOut (504) +# - Net::HTTPVersionNotSupported (505) +# - Net::HTTPVariantAlsoNegotiates (506) +# - Net::HTTPInsufficientStorage (507) +# - Net::HTTPLoopDetected (508) +# - Net::HTTPNotExtended (510) +# - Net::HTTPNetworkAuthenticationRequired (511) +# +# There is also the Net::HTTPBadResponse exception which is raised when +# there is a protocol error. +# +class 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 + # :stopdoc: + + def read_status_line(sock) + str = sock.readline + m = /\AHTTP(?:\/(\d+\.\d+))?\s+(\d\d\d)(?:\s+(.*))?\z/in.match(str) or + raise 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 + 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 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 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 URI used to fetch this response. The response URI is only available + # if a 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 = Net::HTTP.new(hostname) + # req = Net::HTTP::Get.new('/') + # + # http.request(req) do |res| + # p res.body.encoding # => # + # end + # + # http.request(req) do |res| + # res.body_encoding = "UTF-8" + # p res.body.encoding # => # + # 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 # :nodoc: + "#<#{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?(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 "Net::HTTPResponse#response is obsolete", uplevel: 1 if $VERBOSE + self + end + + def header #:nodoc: + warn "Net::HTTPResponse#header is obsolete", uplevel: 1 if $VERBOSE + self + end + + def read_header #:nodoc: + warn "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 dest#<< method (it could be String or IO, or any + # other object responding to <<). + # + # 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' + # 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' + 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 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 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 + Net::ReadAdapter.new(block) + else + dest || +'' + end + end + + ## + # Inflater is a wrapper around 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 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 + + Net::ReadAdapter.new(block) + end + + ## + # Reads +clen+ bytes from the socket, inflates them, then writes them to + # +dest+. +ignore_eof+ is passed down to Net::BufferedIO#read + # + # Unlike Net::BufferedIO#read, this method returns more than +clen+ bytes. + # At this time there is no way for a user of 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/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/lib/net/http/responses.rb b/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/lib/net/http/responses.rb new file mode 100644 index 0000000..941a6fe --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/lib/net/http/responses.rb @@ -0,0 +1,1242 @@ +# frozen_string_literal: true +#-- +# https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml + +module Net + + # Unknown HTTP response + class HTTPUnknownResponse < HTTPResponse + # :stopdoc: + 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 + # :stopdoc: + 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 + # :stopdoc: + 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 + # :stopdoc: + 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 + # :stopdoc: + 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 + # :stopdoc: + 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 + # :stopdoc: + HAS_BODY = false + end + + # Response class for Switching Protocol responses (status code 101). + # + # The Switching Protocol 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 + # :stopdoc: + 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 + # :stopdoc: + HAS_BODY = false + end + + # Response class for Early Hints responses (status code 103). + # + # The Early Hints 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 + # :stopdoc: + 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 + # :stopdoc: + 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 + # :stopdoc: + 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 + # :stopdoc: + HAS_BODY = true + end + + # Response class for Non-Authoritative Information responses (status code 203). + # + # The Non-Authoritative Information 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 + # :stopdoc: + HAS_BODY = true + end + + # Response class for No Content responses (status code 204). + # + # The No Content 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 + # :stopdoc: + HAS_BODY = false + end + + # Response class for Reset Content responses (status code 205). + # + # The Reset Content 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 + # :stopdoc: + HAS_BODY = false + end + + # Response class for Partial Content responses (status code 206). + # + # The Partial Content 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 + # :stopdoc: + HAS_BODY = true + end + + # Response class for Multi-Status (WebDAV) responses (status code 207). + # + # The Multi-Status (WebDAV) 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 + # :stopdoc: + HAS_BODY = true + end + + # Response class for Already Reported (WebDAV) responses (status code 208). + # + # The Already Reported (WebDAV) 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 + # :stopdoc: + HAS_BODY = true + end + + # Response class for IM Used responses (status code 226). + # + # The IM Used 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 + # :stopdoc: + HAS_BODY = true + end + + # Response class for Multiple Choices responses (status code 300). + # + # The Multiple Choices 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 + # :stopdoc: + HAS_BODY = true + end + HTTPMultipleChoice = HTTPMultipleChoices + + # Response class for Moved Permanently responses (status code 301). + # + # The Moved Permanently 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 + # :stopdoc: + HAS_BODY = true + end + + # Response class for Found responses (status code 302). + # + # The Found 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 + # :stopdoc: + HAS_BODY = true + end + HTTPMovedTemporarily = HTTPFound + + # Response class for See Other responses (status code 303). + # + # The response to the request can be found under another 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 + # :stopdoc: + HAS_BODY = true + end + + # Response class for Not Modified 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 + # :stopdoc: + HAS_BODY = false + end + + # Response class for Use Proxy 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 + # :stopdoc: + HAS_BODY = false + end + + # Response class for Temporary Redirect responses (status code 307). + # + # The request should be repeated with another URI; + # however, future requests should still use the original 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 + # :stopdoc: + HAS_BODY = true + end + + # Response class for Permanent Redirect responses (status code 308). + # + # This and all future requests should be directed to the given 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 + # :stopdoc: + HAS_BODY = true + end + + # Response class for Bad Request 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 + # :stopdoc: + HAS_BODY = true + end + + # Response class for Unauthorized 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 + # :stopdoc: + HAS_BODY = true + end + + # Response class for Payment Required 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 + # :stopdoc: + HAS_BODY = true + end + + # Response class for Forbidden 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 + # :stopdoc: + HAS_BODY = true + end + + # Response class for Not Found 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 + # :stopdoc: + HAS_BODY = true + end + + # Response class for Method Not Allowed 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 + # :stopdoc: + HAS_BODY = true + end + + # Response class for Not Acceptable 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 + # :stopdoc: + HAS_BODY = true + end + + # Response class for Proxy Authentication Required 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 + # :stopdoc: + HAS_BODY = true + end + + # Response class for Request Timeout 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 + # :stopdoc: + HAS_BODY = true + end + HTTPRequestTimeOut = HTTPRequestTimeout + + # Response class for Conflict 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 + # :stopdoc: + HAS_BODY = true + end + + # Response class for Gone 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 + # :stopdoc: + HAS_BODY = true + end + + # Response class for Length Required 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 + # :stopdoc: + HAS_BODY = true + end + + # Response class for Precondition Failed 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 + # :stopdoc: + HAS_BODY = true + end + + # Response class for Payload Too Large 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 + # :stopdoc: + HAS_BODY = true + end + HTTPRequestEntityTooLarge = HTTPPayloadTooLarge + + # Response class for URI Too Long responses (status code 414). + # + # The 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 + # :stopdoc: + HAS_BODY = true + end + HTTPRequestURITooLong = HTTPURITooLong + HTTPRequestURITooLarge = HTTPRequestURITooLong + + # Response class for Unsupported Media Type 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 + # :stopdoc: + HAS_BODY = true + end + + # Response class for Range Not Satisfiable 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 + # :stopdoc: + HAS_BODY = true + end + HTTPRequestedRangeNotSatisfiable = HTTPRangeNotSatisfiable + + # Response class for Expectation Failed 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 + # :stopdoc: + 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 Misdirected Request 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 + # :stopdoc: + HAS_BODY = true + end + + # Response class for Unprocessable Entity 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 + # :stopdoc: + HAS_BODY = true + end + + # Response class for Locked (WebDAV) 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 + # :stopdoc: + HAS_BODY = true + end + + # Response class for Failed Dependency (WebDAV) 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 + # :stopdoc: + HAS_BODY = true + end + + # 425 Too Early + # https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#425. + + # Response class for Upgrade Required 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 + # :stopdoc: + HAS_BODY = true + end + + # Response class for Precondition Required 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 + # :stopdoc: + HAS_BODY = true + end + + # Response class for Too Many Requests 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 + # :stopdoc: + HAS_BODY = true + end + + # Response class for Request Header Fields Too Large 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 + # :stopdoc: + HAS_BODY = true + end + + # Response class for Unavailable For Legal Reasons 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 + # :stopdoc: + 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 Internal Server Error 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 + # :stopdoc: + HAS_BODY = true + end + + # Response class for Not Implemented 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 + # :stopdoc: + HAS_BODY = true + end + + # Response class for Bad Gateway 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 + # :stopdoc: + HAS_BODY = true + end + + # Response class for Service Unavailable 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 + # :stopdoc: + HAS_BODY = true + end + + # Response class for Gateway Timeout 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 + # :stopdoc: + HAS_BODY = true + end + HTTPGatewayTimeOut = HTTPGatewayTimeout + + # Response class for HTTP Version Not Supported 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 + # :stopdoc: + HAS_BODY = true + end + + # Response class for Variant Also Negotiates 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 + # :stopdoc: + HAS_BODY = true + end + + # Response class for Insufficient Storage (WebDAV) 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 + # :stopdoc: + HAS_BODY = true + end + + # Response class for Loop Detected (WebDAV) 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 + # :stopdoc: + HAS_BODY = true + end + # 509 Bandwidth Limit Exceeded - Apache bw/limited extension + + # Response class for Not Extended 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 + # :stopdoc: + HAS_BODY = true + end + + # Response class for Network Authentication Required 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 + # :stopdoc: + HAS_BODY = true + end + +end + +class Net::HTTPResponse + # :stopdoc: + CODE_CLASS_TO_OBJ = { + '1' => Net::HTTPInformation, + '2' => Net::HTTPSuccess, + '3' => Net::HTTPRedirection, + '4' => Net::HTTPClientError, + '5' => Net::HTTPServerError + }.freeze + CODE_TO_OBJ = { + '100' => Net::HTTPContinue, + '101' => Net::HTTPSwitchProtocol, + '102' => Net::HTTPProcessing, + '103' => Net::HTTPEarlyHints, + + '200' => Net::HTTPOK, + '201' => Net::HTTPCreated, + '202' => Net::HTTPAccepted, + '203' => Net::HTTPNonAuthoritativeInformation, + '204' => Net::HTTPNoContent, + '205' => Net::HTTPResetContent, + '206' => Net::HTTPPartialContent, + '207' => Net::HTTPMultiStatus, + '208' => Net::HTTPAlreadyReported, + '226' => Net::HTTPIMUsed, + + '300' => Net::HTTPMultipleChoices, + '301' => Net::HTTPMovedPermanently, + '302' => Net::HTTPFound, + '303' => Net::HTTPSeeOther, + '304' => Net::HTTPNotModified, + '305' => Net::HTTPUseProxy, + '307' => Net::HTTPTemporaryRedirect, + '308' => Net::HTTPPermanentRedirect, + + '400' => Net::HTTPBadRequest, + '401' => Net::HTTPUnauthorized, + '402' => Net::HTTPPaymentRequired, + '403' => Net::HTTPForbidden, + '404' => Net::HTTPNotFound, + '405' => Net::HTTPMethodNotAllowed, + '406' => Net::HTTPNotAcceptable, + '407' => Net::HTTPProxyAuthenticationRequired, + '408' => Net::HTTPRequestTimeout, + '409' => Net::HTTPConflict, + '410' => Net::HTTPGone, + '411' => Net::HTTPLengthRequired, + '412' => Net::HTTPPreconditionFailed, + '413' => Net::HTTPPayloadTooLarge, + '414' => Net::HTTPURITooLong, + '415' => Net::HTTPUnsupportedMediaType, + '416' => Net::HTTPRangeNotSatisfiable, + '417' => Net::HTTPExpectationFailed, + '421' => Net::HTTPMisdirectedRequest, + '422' => Net::HTTPUnprocessableEntity, + '423' => Net::HTTPLocked, + '424' => Net::HTTPFailedDependency, + '426' => Net::HTTPUpgradeRequired, + '428' => Net::HTTPPreconditionRequired, + '429' => Net::HTTPTooManyRequests, + '431' => Net::HTTPRequestHeaderFieldsTooLarge, + '451' => Net::HTTPUnavailableForLegalReasons, + + '500' => Net::HTTPInternalServerError, + '501' => Net::HTTPNotImplemented, + '502' => Net::HTTPBadGateway, + '503' => Net::HTTPServiceUnavailable, + '504' => Net::HTTPGatewayTimeout, + '505' => Net::HTTPVersionNotSupported, + '506' => Net::HTTPVariantAlsoNegotiates, + '507' => Net::HTTPInsufficientStorage, + '508' => Net::HTTPLoopDetected, + '510' => Net::HTTPNotExtended, + '511' => Net::HTTPNetworkAuthenticationRequired, + }.freeze +end diff --git a/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/lib/net/http/status.rb b/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/lib/net/http/status.rb new file mode 100644 index 0000000..e70b47d --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/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 "Net::HTTP::STATUS_CODES = {" + url = "https://www.iana.org/assignments/http-status-codes/http-status-codes-1.csv" + 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 + +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/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/lib/net/https.rb b/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/lib/net/https.rb new file mode 100644 index 0000000..0f23e1f --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/net-http-0.9.1/lib/net/https.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true +=begin + += net/https -- SSL/TLS enhancement for Net::HTTP. + + This file has been merged with net/http. There is no longer any need to + require 'net/https' to use HTTPS. + + See Net::HTTP for details on how to make HTTPS connections. + +== Info + 'OpenSSL for Ruby 2' project + Copyright (C) 2001 GOTOU Yuuzou + 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/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/.document b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/.document new file mode 100644 index 0000000..0ad1bde --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/.document @@ -0,0 +1,5 @@ +lib/**/*.rb +bin/* +features/**/*.feature +- +LICENSE.md diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/CONTRIBUTING.md b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/CONTRIBUTING.md new file mode 100644 index 0000000..669e816 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/CONTRIBUTING.md @@ -0,0 +1,23 @@ +## Submitting a Pull Request + +0. Read our [Code of Conduct](CODE_OF_CONDUCT.md). +1. Check out [Hacking on Octokit](README.md#hacking-on-octokitrb) in the + README for bootstrapping the project for local development. +2. [Fork the repository.][fork] +3. [Create a topic branch.][branch] +4. Add specs for your unimplemented feature or bug fix. +5. Run `script/test`. If your specs pass, return to step 3. +6. Implement your feature or bug fix. +7. Run `script/test`. If your specs fail, return to step 5. +8. Run `open coverage/index.html`. If your changes are not completely covered + by your tests, return to step 4. +9. Add documentation for your feature or bug fix. +10. Run `bundle exec rake doc:yard`. If your changes are not 100% documented, go + back to step 8. +11. Add, commit, and push your changes. For documentation-only fixes, please + add "[ci skip]" to your commit message to avoid needless CI builds. +12. [Submit a pull request.][pr] + +[fork]: https://help.github.com/articles/fork-a-repo +[branch]: https://help.github.com/articles/creating-and-deleting-branches-within-your-repository/ +[pr]: https://help.github.com/articles/using-pull-requests diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/LICENSE.md b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/LICENSE.md new file mode 100644 index 0000000..f198331 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/LICENSE.md @@ -0,0 +1,20 @@ +Copyright (c) 2009-2017 Wynn Netherland, Adam Stacoviak, Erik Michaels-Ober + +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/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/README.md b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/README.md new file mode 100644 index 0000000..bffecfe --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/README.md @@ -0,0 +1,817 @@ +# Octokit + +> **Note** +> We've recently renamed the `4-stable` branch to `main`. This might affect you if you're making changes to Octokit's code locally. For more details and for the steps to reconfigure your local clone for the new branch name, check out [this post](https://github.com/octokit/octokit.rb/discussions/1455). + +Ruby toolkit for the GitHub API. + +![logo](https://docs.github.com/assets/images/gundamcat.png) + +Upgrading? Check the [Upgrade Guide](#upgrading-guide) before bumping to a new +[major version][semver]. + +## Table of Contents + +1. [Philosophy](#philosophy) +2. [Installation](#installation) +3. [Making requests](#making-requests) + 1. [Additional Query Parameters](#additional-query-parameters) +4. [Consuming resources](#consuming-resources) +5. [Accessing HTTP responses](#accessing-http-responses) +6. [Handling errors](#handling-errors) +7. [Authentication](#authentication) + 1. [Basic Authentication](#basic-authentication) + 2. [OAuth access tokens](#oauth-access-tokens) + 3. [Two-Factor Authentication](#two-factor-authentication) + 4. [Using a .netrc file](#using-a-netrc-file) + 5. [Application authentication](#application-authentication) + 6. [GitHub App](#github-app) +8. [Default results per_page](#default-results-per_page) +9. [Pagination](#pagination) + 1. [Auto pagination](#auto-pagination) +10. [Working with GitHub Enterprise](#working-with-github-enterprise) + 1. [Interacting with the GitHub.com APIs in GitHub Enterprise](#interacting-with-the-githubcom-apis-in-github-enterprise) + 2. [Interacting with the GitHub Enterprise Admin APIs](#interacting-with-the-github-enterprise-admin-apis) + 3. [Interacting with the GitHub Enterprise Management Console APIs](#interacting-with-the-github-enterprise-management-console-apis) + 4. [SSL Connection Errors](#ssl-connection-errors) +11. [Configuration and defaults](#configuration-and-defaults) + 1. [Configuring module defaults](#configuring-module-defaults) + 2. [Using ENV variables](#using-env-variables) + 3. [Timeouts](#timeouts) +12. [Hypermedia agent](#hypermedia-agent) + 1. [Hypermedia in Octokit](#hypermedia-in-octokit) + 2. [URI templates](#uri-templates) + 3. [The Full Hypermedia Experience™](#the-full-hypermedia-experience) +13. [Upgrading guide](#upgrading-guide) + 1. [Upgrading from 1.x.x](#upgrading-from-1xx) +14. [Advanced usage](#advanced-usage) + 1. [Debugging](#debugging) + 2. [Caching](#caching) +15. [Hacking on Octokit.rb](#hacking-on-octokitrb) + 1. [Code of Conduct](#code-of-conduct) + 2. [Running and writing new tests](#running-and-writing-new-tests) +16. [Supported Ruby Versions](#supported-ruby-versions) +17. [Versioning](#versioning) +18. [Making Repeating Requests](#making-repeating-requests) +19. [License](#license) + +## Philosophy + +API wrappers [should reflect the idioms of the language in which they were +written][wrappers]. Octokit.rb wraps the [GitHub API][github-api] in a flat API +client that follows Ruby conventions and requires little knowledge of REST. +Most methods have positional arguments for required input and an options hash +for optional parameters, headers, or other options: + +```ruby +client = Octokit::Client.new + +# Fetch a README with Accept header for HTML format +client.readme 'al3x/sovereign', :accept => 'application/vnd.github.html' +``` + +[wrappers]: http://wynnnetherland.com/journal/what-makes-a-good-api-wrapper +[github-api]: https://developer.github.com/v3/ + +## Installation + +Install via Rubygems + + gem install octokit + +... or add to your Gemfile + + gem "octokit" + +Access the library in Ruby: + + require 'octokit' + +## Making requests + +[API methods][] are available as client instance methods. + +```ruby +# Provide authentication credentials +client = Octokit::Client.new(:access_token => 'personal_access_token') + +# You can still use the username/password syntax by replacing the password value with your PAT. +# client = Octokit::Client.new(:login => 'defunkt', :password => 'personal_access_token') + +# Fetch the current user +client.user +``` + +### Additional query parameters + +When passing additional parameters to GET based request use the following syntax: + +```ruby + # query: { parameter_name: 'value' } + # Example: Get repository listing by owner in ascending order + client.repos({}, query: {type: 'owner', sort: 'asc'}) + + # Example: Get contents of a repository by ref + # https://api.github.com/repos/octokit/octokit.rb/contents/path/to/file.rb?ref=some-other-branch + client.contents('octokit/octokit.rb', path: 'path/to/file.rb', query: {ref: 'some-other-branch'}) +``` + +[api methods]: http://octokit.github.io/octokit.rb/method_list.html + +## Consuming resources + +Most methods return a `Resource` object which provides dot notation and `[]` +access for fields returned in the API response. + +```ruby +client = Octokit::Client.new + +# Fetch a user +user = client.user 'jbarnette' +puts user.name +# => "John Barnette" +puts user.fields +# => +puts user[:company] +# => "GitHub" +user.rels[:gists].href +# => "https://api.github.com/users/jbarnette/gists" +``` + +**Note:** URL fields are culled into a separate `.rels` collection for easier +[Hypermedia](#hypermedia-agent) support. + +## Accessing HTTP responses + +While most methods return a `Resource` object or a Boolean, sometimes you may +need access to the raw HTTP response headers. You can access the last HTTP +response with `Client#last_response`: + +```ruby +user = client.user 'andrewpthorp' +response = client.last_response +etag = response.headers[:etag] +``` + +## Handling errors + +When the API returns an error response, Octokit will raise a Ruby exception. + +A range of different exceptions can be raised depending on the error returned +by the API - for example: + +* A `400 Bad Request` response will lead to an `Octokit::BadRequest` error +* A `403 Forbidden` error with a "rate limited exceeded" message will lead + to a `Octokit::TooManyRequests` error + +All of the different exception classes inherit from `Octokit::Error` and +expose the `#response_status`, `#response_headers` and `#response_body`. +For validation errors, `#errors` will return an `Array` of `Hash`es +with the detailed information +[returned by the API](https://docs.github.com/en/rest/overview/resources-in-the-rest-api#client-errors). + +## Authentication + +Octokit supports the various [authentication methods supported by the GitHub +API][auth]: + +### Basic Authentication + +Using your GitHub username and password is the easiest way to get started +making authenticated requests: + +```ruby +client = Octokit::Client.new(:login => 'defunkt', :password => 'c0d3b4ssssss!') + +user = client.user +user.login +# => "defunkt" +``` + +While Basic Authentication allows you to get started quickly, OAuth access +tokens are the preferred way to authenticate on behalf of users. + +### OAuth access tokens + +[OAuth access tokens][oauth] provide two main benefits over using your username +and password: + +- **Revocable access**. Access tokens can be revoked, removing access for only + that token without having to change your password everywhere. +- **Limited access**. Access tokens have [access scopes][] which allow for more + granular access to API resources. For instance, you can grant a third party + access to your gists but not your private repositories. + +To use an access token with the Octokit client, pass your token in the +`:access_token` options parameter in lieu of your username and password: + +```ruby +client = Octokit::Client.new(:access_token => "") + +user = client.user +user.login +# => "defunkt" +``` + +You can [create access tokens through your GitHub Account Settings](https://help.github.com/articles/creating-an-access-token-for-command-line-use). + +### Two-Factor Authentication + +[Two-Factor Authentication](https://help.github.com/articles/about-two-factor-authentication) brings added security to the account by requiring more information to login. + +Using two-factor authentication for API calls is as simple as adding the [required header](http://developer.github.com/v3/auth/#working-with-two-factor-authentication) as an option: + +```ruby +client = Octokit::Client.new \ + :login => 'defunkt', + :password => 'c0d3b4ssssss!' + +user = client.user("defunkt", :headers => { "X-GitHub-OTP" => "" }) +``` + +### Using a .netrc file + +Octokit supports reading credentials from a netrc file (defaulting to +`~/.netrc`). Given these lines in your netrc: + +``` +machine api.github.com + login defunkt + password c0d3b4ssssss! +``` + +You can now create a client with those credentials: + +```ruby +client = Octokit::Client.new(:netrc => true) +client.login +# => "defunkt" +``` + +But _I want to use OAuth_ you say. Since the GitHub API supports using an OAuth +token as a Basic password, you totally can: + +``` +machine api.github.com + login defunkt + password +``` + +**Note:** Support for netrc requires adding the [netrc gem][] to your Gemfile +or `.gemspec`. + +### Application authentication + +Octokit also supports application-only authentication [using OAuth application client +credentials][app-creds]. Using application credentials will result in making +anonymous API calls on behalf of an application in order to take advantage of +the higher rate limit. + +```ruby +client = Octokit::Client.new \ + :client_id => "", + :client_secret => "" + +user = client.user 'defunkt' +``` + +[auth]: http://developer.github.com/v3/#authentication +[oauth]: http://developer.github.com/v3/oauth/ +[access scopes]: http://developer.github.com/v3/oauth/#scopes +[app-creds]: http://developer.github.com/v3/#increasing-the-unauthenticated-rate-limit-for-oauth-applications + +### GitHub App +Octokit.rb also supports authentication [using a GitHub App](https://docs.github.com/en/developers/apps/managing-github-apps/installing-github-apps), which [requires a generated JWT token](https://docs.github.com/en/developers/apps/building-github-apps/authenticating-with-github-apps#authenticating-as-a-github-app). + +```ruby +client = Octokit::Client.new(:bearer_token => "") +client.app +# => about GitHub App info +``` + +## Default results per_page + +Default results from the GitHub API are 30, if you wish to add more you must do so during Octokit configuration. + +```ruby +Octokit::Client.new(access_token: "", per_page: 100) +``` + +## Pagination + +Many GitHub API resources are [paginated][]. While you may be tempted to start +adding `:page` parameters to your calls, the API returns links to the next, +previous, and last pages for you in the `Link` response header as [Hypermedia +link relations](#hypermedia-agent). + +```ruby +issues = client.issues 'rails/rails' +issues.concat client.get(client.last_response.rels[:next].href) +``` + +### Auto pagination + +For smallish resource lists, Octokit provides auto pagination. When this is +enabled, calls for paginated resources will fetch and concatenate the results +from every page into a single array: + +```ruby +client.auto_paginate = true +issues = client.issues 'rails/rails' +issues.length + +# => 702 +``` + +You can also enable auto pagination for all Octokit client instances: + +```ruby +Octokit.configure do |c| + c.auto_paginate = true +end +``` + +**Note:** While Octokit auto pagination will set the page size to the maximum +`100`, and seek to not overstep your rate limit, you probably want to use a +custom pattern for traversing large lists. + +[paginated]: http://developer.github.com/v3/#pagination + +## Working with GitHub Enterprise + +With a bit of setup, you can also use Octokit with your GitHub Enterprise instance. + +### Interacting with the GitHub.com APIs in GitHub Enterprise + +To interact with the "regular" GitHub.com APIs in GitHub Enterprise, simply configure the `api_endpoint` to match your hostname. For example: + +```ruby +Octokit.configure do |c| + c.api_endpoint = "https:///api/v3/" +end + +client = Octokit::Client.new(:access_token => "") +``` + +### Interacting with the GitHub Enterprise Admin APIs + +The GitHub Enterprise Admin APIs are under a different client: `EnterpriseAdminClient`. You'll need to have an administrator account in order to use these APIs. + +```ruby +admin_client = Octokit::EnterpriseAdminClient.new( + :access_token => "", + :api_endpoint => "https:///api/v3/" +) + +# or +Octokit.configure do |c| + c.api_endpoint = "https:///api/v3/" + c.access_token = "" +end + +admin_client = Octokit.enterprise_admin_client.new +``` + +### Interacting with the GitHub Enterprise Management Console APIs + +The GitHub Enterprise Management Console APIs are also under a separate client: `EnterpriseManagementConsoleClient`. In order to use it, you'll need to provide both your management console password as well as the endpoint to your management console. This is different from the API endpoint provided above. + +```ruby +management_console_client = Octokit::EnterpriseManagementConsoleClient.new( + :management_console_password => "secret", + :management_console_endpoint = "https://hostname:8633" +) + +# or +Octokit.configure do |c| + c.management_console_endpoint = "https://hostname:8633" + c.management_console_password = "secret" +end + +management_console_client = Octokit.enterprise_management_console_client.new +``` + +### SSL Connection Errors + +You _may_ need to disable SSL temporarily while first setting up your GitHub Enterprise install. You can do that with the following configuration: + +```ruby +client.connection_options[:ssl] = { :verify => false } +``` + +Do remember to turn `:verify` back to `true`, as it's important for secure communication. + +## Configuration and defaults + +While `Octokit::Client` accepts a range of options when creating a new client +instance, Octokit's configuration API allows you to set your configuration +options at the module level. This is particularly handy if you're creating a +number of client instances based on some shared defaults. Changing options +affects new instances only and will not modify existing `Octokit::Client` +instances created with previous options. + +### Configuring module defaults + +Every writable attribute in {Octokit::Configurable} can be set one at a time: + +```ruby +Octokit.api_endpoint = 'http://api.github.dev' +Octokit.web_endpoint = 'http://github.dev' +``` + +or in batch: + +```ruby +Octokit.configure do |c| + c.api_endpoint = 'http://api.github.dev' + c.web_endpoint = 'http://github.dev' +end +``` + +### Using ENV variables + +Default configuration values are specified in {Octokit::Default}. Many +attributes will look for a default value from the ENV before returning +Octokit's default. + +```ruby +# Given $OCTOKIT_API_ENDPOINT is "http://api.github.dev" +client.api_endpoint + +# => "http://api.github.dev" +``` + +Deprecation warnings and API endpoints in development preview warnings are +printed to STDOUT by default, these can be disabled by setting the ENV +`OCTOKIT_SILENT=true`. + +### Timeouts + +By default, Octokit does not timeout network requests. To set a timeout, pass in Faraday timeout settings to Octokit's `connection_options` setting. + +```ruby +Octokit.configure do |c| + c.api_endpoint = ENV.fetch('GITHUB_API_ENDPOINT', 'https://api.github.com/') + c.connection_options = { + request: { + open_timeout: 5, + timeout: 5 + } + } +end +``` + +You should set a timeout in order to avoid Ruby’s Timeout module, which can hose your server. Here are some resources for more information on this: + +- [The Oldest Bug In Ruby - Why Rack::Timeout Might Hose your Server](https://www.schneems.com/2017/02/21/the-oldest-bug-in-ruby-why-racktimeout-might-hose-your-server/) +- [Timeout: Ruby's Most Dangerous API](https://www.mikeperham.com/2015/05/08/timeout-rubys-most-dangerous-api/) +- [The Ultimate Guide to Ruby Timeouts](https://github.com/ankane/the-ultimate-guide-to-ruby-timeouts) + +## Hypermedia agent + +Starting in version 2.0, Octokit is [hypermedia][]-enabled. Under the hood, +{Octokit::Client} uses [Sawyer][], a hypermedia client built on [Faraday][]. + +### Hypermedia in Octokit + +Resources returned by Octokit methods contain not only data but hypermedia +link relations: + +```ruby +user = client.user 'technoweenie' + +# Get the repos rel, returned from the API +# as repos_url in the resource +user.rels[:repos].href +# => "https://api.github.com/users/technoweenie/repos" + +repos = user.rels[:repos].get.data +repos.last.name +# => "faraday-zeromq" +``` + +When processing API responses, all `*_url` attributes are culled into the link +relations collection. Any `url` attribute becomes `.rels[:self]`. + +### URI templates + +You might notice many link relations have variable placeholders. Octokit +supports [URI Templates][uri-templates] for parameterized URI expansion: + +```ruby +repo = client.repo 'pengwynn/pingwynn' +rel = repo.rels[:issues] +# => # + +# Get a page of issues +rel.get.data + +# Get issue #2 +rel.get(:uri => {:number => 2}).data +``` + +### The Full Hypermedia Experience™ + +If you want to use Octokit as a pure hypermedia API client, you can start at +the API root and follow link relations from there: + +```ruby +root = client.root +root.rels[:repository].get :uri => {:owner => "octokit", :repo => "octokit.rb" } +root.rels[:user_repositories].get :uri => { :user => "octokit" }, + :query => { :type => "owner" } +``` + +Octokit 3.0 aims to be hypermedia-driven, removing the internal URL +construction currently used throughout the client. + +[hypermedia]: http://en.wikipedia.org/wiki/Hypermedia +[sawyer]: https://github.com/lostisland/sawyer +[faraday]: https://github.com/lostisland/faraday +[uri-templates]: http://tools.ietf.org/html/rfc6570 + +## Upgrading guide + +Version 4.0 + +- **removes support for a [long-deprecated overload][list-pulls] for + passing state as a positional argument** when listing pull requests. Instead, + pass `state` in the method options. +- **drops support for Ruby < 2.0**. +- adds support for new [Enterprise-only APIs](#working-with-github-enterprise). +- adds support for [Repository redirects][redirects]. + +[list-pulls]: https://github.com/octokit/octokit.rb/commit/e48e91f736d5fce51e3bf74d7c9022aaa52f5c5c +[redirects]: https://developer.github.com/changes/2015-05-26-repository-redirects-are-coming/ + +Version 3.0 includes a couple breaking changes when upgrading from v2.x.x: + +The [default media type][default-media-type] is now `v3` instead of `beta`. If +you need to request the older media type, you can set the default media type +for the client: + +```ruby +Octokit.default_media_type = "application/vnd.github.beta+json" +``` + +or per-request + +```ruby +client.emails(:accept => "application/vnd.github.beta+json") +``` + +The long-deprecated `Octokit::Client#create_download` method has been removed. + +[default-media-type]: https://developer.github.com/changes/2014-01-07-upcoming-change-to-default-media-type/ + +### Upgrading from 1.x.x + +Version 2.0 includes a completely rewritten `Client` factory that now memoizes +client instances based on unique configuration options. Breaking changes also +include: + +- `:oauth_token` is now `:access_token` +- `:auto_traversal` is now `:auto_paginate` +- `Hashie::Mash` has been removed. Responses now return a `Sawyer::Resource` + object. This new type behaves mostly like a Ruby `Hash`, but does not fully + support the `Hashie::Mash` API. +- Two new client error types are raised where appropriate: + `Octokit::TooManyRequests` and `Octokit::TooManyLoginAttempts` +- The `search_*` methods from v1.x are now found at `legacy_search_*` +- Support for netrc requires including the [netrc gem][] in your Gemfile or + gemspec. +- DateTime fields are now proper `DateTime` objects. Previous versions outputted DateTime fields as 'String' objects. + +[netrc gem]: https://rubygems.org/gems/netrc + +## Advanced usage + +Since Octokit employs [Faraday][faraday] under the hood, some behavior can be +extended via middleware. + +### Debugging + +Often, it helps to know what Octokit is doing under the hood. You can add a +logger to the middleware that enables you to peek into the underlying HTTP +traffic: + +```ruby +stack = Faraday::RackBuilder.new do |builder| + builder.use Faraday::Retry::Middleware, exceptions: Faraday::Retry::Middleware::DEFAULT_EXCEPTIONS + [Octokit::ServerError] # or Faraday::Request::Retry for Faraday < 2.0 + builder.use Octokit::Middleware::FollowRedirects + builder.use Octokit::Response::RaiseError + builder.use Octokit::Response::FeedParser + builder.response :logger do |logger| + logger.filter(/(Authorization: "(token|Bearer) )(\w+)/, '\1[REMOVED]') + end + builder.adapter Faraday.default_adapter +end +Octokit.middleware = stack + +client = Octokit::Client.new +client.user 'pengwynn' +``` + +``` +I, [2013-08-22T15:54:38.583300 #88227] INFO -- : get https://api.github.com/users/pengwynn +D, [2013-08-22T15:54:38.583401 #88227] DEBUG -- request: Accept: "application/vnd.github.beta+json" +User-Agent: "Octokit Ruby Gem 2.0.0.rc4" +I, [2013-08-22T15:54:38.843313 #88227] INFO -- Status: 200 +D, [2013-08-22T15:54:38.843459 #88227] DEBUG -- response: server: "GitHub.com" +date: "Thu, 22 Aug 2013 20:54:40 GMT" +content-type: "application/json; charset=utf-8" +transfer-encoding: "chunked" +connection: "close" +status: "200 OK" +x-ratelimit-limit: "60" +x-ratelimit-remaining: "39" +x-ratelimit-reset: "1377205443" +... +``` + +See the [Faraday README][faraday] for more middleware magic. + +### Caching + +If you want to boost performance, stretch your API rate limit, or avoid paying +the hypermedia tax, you can use [Faraday Http Cache][cache]. + +Add the gem to your Gemfile + + gem 'faraday-http-cache' + +Next, construct your own Faraday middleware: + +```ruby +stack = Faraday::RackBuilder.new do |builder| + builder.use Faraday::HttpCache, serializer: Marshal, shared_cache: false + builder.use Octokit::Response::RaiseError + builder.adapter Faraday.default_adapter +end +Octokit.middleware = stack +``` + +Once configured, the middleware will store responses in cache based on ETag +fingerprint and serve those back up for future `304` responses for the same +resource. See the [project README][cache] for advanced usage. + +[cache]: https://github.com/sourcelevel/faraday-http-cache +[faraday]: https://github.com/lostisland/faraday + +## Hacking on Octokit.rb + +If you want to hack on Octokit locally, we try to make [bootstrapping the +project][bootstrapping] as painless as possible. To start hacking, clone and run: + + script/bootstrap + +This will install project dependencies and get you up and running. If you want +to run a Ruby console to poke on Octokit, you can crank one up with: + + script/console + +Using the scripts in `./script` instead of `bundle exec rspec`, `bundle console`, etc. ensures your dependencies are up-to-date. + +### Code of Conduct + +We want both the Octokit.rb and larger Octokit communities to be open +and welcoming environments. Please read and follow both in spirit and +letter [Code of Conduct](CODE_OF_CONDUCT.md). + +### Running and writing new tests + +Octokit uses [VCR][] for recording and playing back API fixtures during test +runs. These cassettes (fixtures) are part of the Git project in the `spec/cassettes` +folder. If you're not recording new cassettes you can run the specs with existing +cassettes with: + + script/test + +Octokit uses environmental variables for storing credentials used in testing. +If you are testing an API endpoint that doesn't require authentication, you +can get away without any additional configuration. For the most part, tests +use an authenticated client, using a token stored in `ENV['OCTOKIT_TEST_GITHUB_TOKEN']`. +There are several different authentication methods used across the api. +Here is the full list of configurable environmental variables for testing +Octokit: + +| ENV Variable | Description | +| :----------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `OCTOKIT_TEST_GITHUB_LOGIN` | GitHub login name (preferably one created specifically for testing against). | +| `OCTOKIT_TEST_GITHUB_PASSWORD` | Password for the test GitHub login. | +| `OCTOKIT_TEST_GITHUB_TOKEN` | [Personal Access Token](https://github.com/blog/1509-personal-api-tokens) for the test GitHub login. | +| `OCTOKIT_TEST_GITHUB_CLIENT_ID` | Test OAuth application client id. | +| `OCTOKIT_TEST_GITHUB_CLIENT_SECRET` | Test OAuth application client secret. | +| `OCTOKIT_TEST_GITHUB_REPOSITORY` | Test repository to perform destructive actions against, this should not be set to any repository of importance. **Automatically created by the test suite if nonexistent** Default: `api-sandbox` | +| `OCTOKIT_TEST_GITHUB_ORGANIZATION` | Test organization. | +| `OCTOKIT_TEST_GITHUB_ENTERPRISE_LOGIN` | GitHub Enterprise login name. | +| `OCTOKIT_TEST_GITHUB_ENTERPRISE_TOKEN` | GitHub Enterprise token. | +| `OCTOKIT_TEST_GITHUB_ENTERPRISE_MANAGEMENT_CONSOLE_PASSWORD` | GitHub Enterprise management console password. | +| `OCTOKIT_TEST_GITHUB_ENTERPRISE_ENDPOINT` | GitHub Enterprise hostname. | +| `OCTOKIT_TEST_GITHUB_ENTERPRISE_MANAGEMENT_CONSOLE_ENDPOINT` | GitHub Enterprise Management Console endpoint. | +| `OCTOKIT_TEST_GITHUB_MANAGE_GHES_ENDPOINT` | GitHub Enterprise Server GHES Manage Endpoint. | +| `OCTOKIT_TEST_GITHUB_MANAGE_GHES_USERNAME` | GitHub Enterprise Server GHES Manage Username. | +| `OCTOKIT_TEST_GITHUB_MANAGE_GHES_PASSWORD` | GitHub Enterprise Server GHES Manage Password. | +| `OCTOKIT_TEST_GITHUB_INTEGRATION` | [GitHub Integration](https://developer.github.com/early-access/integrations/) owned by your test organization. | +| `OCTOKIT_TEST_GITHUB_INTEGRATION_INSTALLATION` | Installation of the GitHub Integration specified above. | +| `OCTOKIT_TEST_INTEGRATION_PEM_KEY` | File path to the private key generated from your integration. | + +Since we periodically refresh our cassettes, please keep some points in mind +when writing new specs. + +- **Specs should be idempotent**. The HTTP calls made during a spec should be + able to be run over and over. This means deleting a known resource prior to + creating it if the name has to be unique. +- **Specs should be able to be run in random order.** If a spec depends on + another resource as a fixture, make sure that's created in the scope of the + spec and not depend on a previous spec to create the data needed. +- **Do not depend on authenticated user info.** Instead of asserting + actual values in resources, try to assert the existence of a key or that a + response is an Array. We're testing the client, not the API. + +[bootstrapping]: http://wynnnetherland.com/linked/2013012801/bootstrapping-consistency +[vcr]: https://github.com/vcr/vcr + +## Supported Ruby Versions + +This library aims to support and is [tested against][actions] the following Ruby +implementations: + +* Ruby 2.7 +* Ruby 3.0 +* Ruby 3.1 +* Ruby 3.2 +* Ruby 3.3 + +If something doesn't work on one of these Ruby versions, it's a bug. + +This library may inadvertently work (or seem to work) on other Ruby +implementations, but support will only be provided for the versions listed +above. + +If you would like this library to support another Ruby version, you may +volunteer to be a maintainer. Being a maintainer entails making sure all tests +run and pass on that implementation. When something breaks on your +implementation, you will be responsible for providing patches in a timely +fashion. If critical issues for a particular implementation exist at the time +of a major release, support for that Ruby version may be dropped. + +[actions]: https://github.com/octokit/octokit.rb/actions + +## Versioning + +This library aims to adhere to [Semantic Versioning 2.0.0][semver]. Violations +of this scheme should be reported as bugs. Specifically, if a minor or patch +version is released that breaks backward compatibility, that version should be +immediately yanked and/or a new version should be immediately released that +restores compatibility. Breaking changes to the public API will only be +introduced with new major versions. As a result of this policy, you can (and +should) specify a dependency on this gem using the [Pessimistic Version +Constraint][pvc] with two digits of precision. For example: + + spec.add_dependency 'octokit', '~> 3.0' + +The changes made between versions can be seen on the [project releases page][releases]. + +[semver]: http://semver.org/ +[pvc]: http://guides.rubygems.org/patterns/#pessimistic-version-constraint +[releases]: https://github.com/octokit/octokit.rb/releases + +## Making Repeating Requests + +In most cases it would be best to use [webhooks](https://developer.github.com/webhooks/), but sometimes webhooks don't provide all of the information needed. In those cases where one might need to poll for progress or retry a request on failure, we designed [Octopoller](https://github.com/octokit/octopoller.rb). Octopoller is a micro gem perfect for making repeating requests. + +```ruby +Octopoller.poll(timeout: 15.seconds) do + begin + client.request_progress # ex. request a long running job's status + rescue Error + :re_poll + end +end +``` + +This is useful when making requests for a long running job's progress (ex. requesting a [Source Import's progress](https://developer.github.com/v3/migrations/source_imports/#get-import-progress)). + +## License + +Copyright (c) 2009-2014 Wynn Netherland, Adam Stacoviak, Erik Michaels-Ober + +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/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/Rakefile b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/Rakefile new file mode 100644 index 0000000..8d212a5 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/Rakefile @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require 'bundler' +Bundler::GemHelper.install_tasks + +task test: :spec +task default: :spec + +desc 'Run RSpec' +task :spec do + if Process.respond_to?(:fork) + sh('rspec-queue') + else + sh('rspec') + end +end + +namespace :doc do + require 'yard' + YARD::Rake::YardocTask.new do |task| + task.files = ['README.md', 'LICENSE.md', 'lib/**/*.rb'] + task.options = [ + '--output-dir', 'doc/yard', + '--markup', 'markdown' + ] + end +rescue LoadError +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/ext/sawyer/relation.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/ext/sawyer/relation.rb new file mode 100644 index 0000000..84e975b --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/ext/sawyer/relation.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require 'sawyer' + +patch = Module.new do + def href(options = nil) + # Temporary workaround for: https://github.com/octokit/octokit.rb/issues/727 + name.to_s == 'ssh' ? @href : super + end +end + +Sawyer::Relation.send(:prepend, patch) diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit.rb new file mode 100644 index 0000000..b029d8b --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit.rb @@ -0,0 +1,81 @@ +# frozen_string_literal: true + +require 'octokit/default' +require 'octokit/client' +require 'octokit/enterprise_admin_client' +require 'octokit/enterprise_management_console_client' +require 'octokit/manage_ghes_client' + +# Ruby toolkit for the GitHub API +module Octokit + class << self + include Octokit::Configurable + + # API client based on configured options {Configurable} + # + # @return [Octokit::Client] API wrapper + def client + return @client if defined?(@client) && @client.same_options?(options) + + @client = Octokit::Client.new(options) + end + + # EnterpriseAdminClient client based on configured options {Configurable} + # + # @return [Octokit::EnterpriseAdminClient] API wrapper + def enterprise_admin_client + if defined?(@enterprise_admin_client) && @enterprise_admin_client.same_options?(options) + return @enterprise_admin_client + end + + @enterprise_admin_client = Octokit::EnterpriseAdminClient.new(options) + end + + # EnterpriseManagementConsoleClient client based on configured options {Configurable} + # + # @return [Octokit::EnterpriseManagementConsoleClient] API wrapper + def enterprise_management_console_client + if defined?(@enterprise_management_console_client) && @enterprise_management_console_client.same_options?(options) + return @enterprise_management_console_client + end + + @enterprise_management_console_client = Octokit::EnterpriseManagementConsoleClient.new(options) + end + + # ManageGHESClient client based on configured options {Configurable} + # + # @return [Octokit::ManageGHESClient] API wrapper + def manage_ghes_client + if defined?(@manage_ghes_client) && @manage_ghes_client.same_options?(options) + return @manage_ghes_client + end + + @manage_ghes_client = Octokit::ManageGHESClient.new(options) + end + + private + + def respond_to_missing?(method_name, include_private = false) + client.respond_to?(method_name, include_private) || + enterprise_admin_client.respond_to?(method_name, include_private) || + enterprise_management_console_client.respond_to?(method_name, include_private) || + manage_ghes_client.respond_to?(method_name, include_private) + end + + def method_missing(method_name, *args, &block) + if client.respond_to?(method_name) + return client.send(method_name, *args, &block) + elsif enterprise_admin_client.respond_to?(method_name) + return enterprise_admin_client.send(method_name, *args, &block) + elsif enterprise_management_console_client.respond_to?(method_name) + return enterprise_management_console_client.send(method_name, *args, &block) + elsif manage_ghes_client.respond_to?(method_name) + return manage_ghes_client.send(method_name, *args, &block) + end + + super + end + end +end + +Octokit.setup diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/arguments.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/arguments.rb new file mode 100644 index 0000000..3794e55 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/arguments.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module Octokit + # Extracts options from method arguments + # @private + class Arguments < Array + attr_reader :options + + def initialize(args) + @options = args.last.is_a?(::Hash) ? args.pop : {} + super + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/authentication.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/authentication.rb new file mode 100644 index 0000000..48a4c3e --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/authentication.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: true + +module Octokit + # Authentication methods for {Octokit::Client} + module Authentication + # In Faraday 2.x, the authorization middleware uses new interface + FARADAY_BASIC_AUTH_KEYS = + if Gem::Version.new(Faraday::VERSION) >= Gem::Version.new('2.0') + %i[authorization basic] + else + [:basic_auth] + end + + # Indicates if the client was supplied Basic Auth + # username and password + # + # @see https://developer.github.com/v3/#authentication + # @return [Boolean] + def basic_authenticated? + !!(@login && @password) + end + + # Indicates if the client was supplied an OAuth + # access token + # + # @see https://developer.github.com/v3/#authentication + # @return [Boolean] + def token_authenticated? + !!@access_token + end + + # Indicates if the client was supplied a bearer token + # + # @see https://developer.github.com/early-access/integrations/authentication/#as-an-integration + # @return [Boolean] + def bearer_authenticated? + !!@bearer_token + end + + # Indicates if the client was supplied an OAuth + # access token or Basic Auth username and password + # + # @see https://developer.github.com/v3/#authentication + # @return [Boolean] + def user_authenticated? + basic_authenticated? || token_authenticated? + end + + # Indicates if the client has OAuth Application + # client_id and secret credentials to make anonymous + # requests at a higher rate limit + # + # @see https://developer.github.com/v3/#unauthenticated-rate-limited-requests + # @return [Boolean] + def application_authenticated? + !!(@client_id && @client_secret) + end + + private + + def login_from_netrc + return unless netrc? + + require 'netrc' + info = Netrc.read netrc_file + netrc_host = URI.parse(api_endpoint).host + creds = info[netrc_host] + if creds.nil? + # creds will be nil if there is no netrc for this end point + octokit_warn "Error loading credentials from netrc file for #{api_endpoint}" + else + creds = creds.to_a + self.login = creds.shift + self.password = creds.shift + end + rescue LoadError + octokit_warn 'Please install netrc gem for .netrc support' + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client.rb new file mode 100644 index 0000000..cb15dc0 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client.rb @@ -0,0 +1,278 @@ +# frozen_string_literal: true + +require 'octokit/connection' +require 'octokit/warnable' +require 'octokit/arguments' +require 'octokit/repo_arguments' +require 'octokit/configurable' +require 'octokit/authentication' +require 'octokit/gist' +require 'octokit/rate_limit' +require 'octokit/repository' +require 'octokit/user' +require 'octokit/organization' +require 'octokit/client/actions_artifacts' +require 'octokit/client/actions_secrets' +require 'octokit/client/actions_workflows' +require 'octokit/client/actions_workflow_jobs' +require 'octokit/client/actions_workflow_runs' +require 'octokit/client/apps' +require 'octokit/client/checks' +require 'octokit/client/code_scanning' +require 'octokit/client/codespaces_secrets' +require 'octokit/client/commits' +require 'octokit/client/commit_comments' +require 'octokit/client/commit_pulls' +require 'octokit/client/commit_branches' +require 'octokit/client/community_profile' +require 'octokit/client/contents' +require 'octokit/client/downloads' +require 'octokit/client/dependabot_secrets' +require 'octokit/client/deployments' +require 'octokit/client/environments' +require 'octokit/client/emojis' +require 'octokit/client/events' +require 'octokit/client/feeds' +require 'octokit/client/gists' +require 'octokit/client/gitignore' +require 'octokit/client/hooks' +require 'octokit/client/issues' +require 'octokit/client/labels' +require 'octokit/client/legacy_search' +require 'octokit/client/licenses' +require 'octokit/client/meta' +require 'octokit/client/markdown' +require 'octokit/client/marketplace' +require 'octokit/client/milestones' +require 'octokit/client/notifications' +require 'octokit/client/oauth_applications' +require 'octokit/client/objects' +require 'octokit/client/organizations' +require 'octokit/client/pages' +require 'octokit/client/projects' +require 'octokit/client/pull_requests' +require 'octokit/client/rate_limit' +require 'octokit/client/reactions' +require 'octokit/client/refs' +require 'octokit/client/releases' +require 'octokit/client/repositories' +require 'octokit/client/repository_invitations' +require 'octokit/client/reviews' +require 'octokit/client/say' +require 'octokit/client/search' +require 'octokit/client/service_status' +require 'octokit/client/source_import' +require 'octokit/client/stats' +require 'octokit/client/statuses' +require 'octokit/client/tokens' +require 'octokit/client/traffic' +require 'octokit/client/users' +require 'ext/sawyer/relation' + +module Octokit + # Client for the GitHub API + # + # @see https://developer.github.com + class Client + include Octokit::Authentication + include Octokit::Configurable + include Octokit::Connection + include Octokit::Warnable + include Octokit::Client::ActionsArtifacts + include Octokit::Client::ActionsSecrets + include Octokit::Client::Checks + include Octokit::Client::CodeScanning + include Octokit::Client::CodespacesSecrets + include Octokit::Client::Commits + include Octokit::Client::CommitComments + include Octokit::Client::CommitPulls + include Octokit::Client::CommitBranches + include Octokit::Client::CommunityProfile + include Octokit::Client::Contents + include Octokit::Client::DependabotSecrets + include Octokit::Client::Deployments + include Octokit::Client::Downloads + include Octokit::Client::Environments + include Octokit::Client::Emojis + include Octokit::Client::Events + include Octokit::Client::Feeds + include Octokit::Client::Gists + include Octokit::Client::Gitignore + include Octokit::Client::Hooks + include Octokit::Client::ActionsWorkflows + include Octokit::Client::ActionsWorkflowJobs + include Octokit::Client::ActionsWorkflowRuns + include Octokit::Client::Apps + include Octokit::Client::Issues + include Octokit::Client::Labels + include Octokit::Client::LegacySearch + include Octokit::Client::Licenses + include Octokit::Client::Meta + include Octokit::Client::Markdown + include Octokit::Client::Marketplace + include Octokit::Client::Milestones + include Octokit::Client::Notifications + include Octokit::Client::OauthApplications + include Octokit::Client::Objects + include Octokit::Client::Organizations + include Octokit::Client::Pages + include Octokit::Client::Projects + include Octokit::Client::PullRequests + include Octokit::Client::RateLimit + include Octokit::Client::Reactions + include Octokit::Client::Refs + include Octokit::Client::Releases + include Octokit::Client::Repositories + include Octokit::Client::RepositoryInvitations + include Octokit::Client::Reviews + include Octokit::Client::Say + include Octokit::Client::Search + include Octokit::Client::ServiceStatus + include Octokit::Client::SourceImport + include Octokit::Client::Stats + include Octokit::Client::Statuses + include Octokit::Client::Tokens + include Octokit::Client::Traffic + include Octokit::Client::Users + + # Header keys that can be passed in options hash to {#get},{#head} + CONVENIENCE_HEADERS = Set.new(%i[accept content_type]) + + def initialize(options = {}) + # Use options passed in, but fall back to module defaults + # + # rubocop:disable Style/HashEachMethods + # + # This may look like a `.keys.each` which should be replaced with `#each_key`, but + # this doesn't actually work, since `#keys` is just a method we've defined ourselves. + # The class doesn't fulfill the whole `Enumerable` contract. + Octokit::Configurable.keys.each do |key| + # rubocop:enable Style/HashEachMethods + value = options[key].nil? ? Octokit.instance_variable_get(:"@#{key}") : options[key] + instance_variable_set(:"@#{key}", value) + end + + login_from_netrc unless user_authenticated? || application_authenticated? + end + + # Text representation of the client, masking tokens and passwords + # + # @return [String] + def inspect + inspected = super + + # mask password + inspected.gsub! @password, '*******' if @password + if @management_console_password + inspected.gsub! @management_console_password, '*******' + end + inspected.gsub! @bearer_token, '********' if @bearer_token + # Only show last 4 of token, secret + if @access_token + inspected.gsub! @access_token, "#{'*' * 36}#{@access_token[36..]}" + end + if @client_secret + inspected.gsub! @client_secret, "#{'*' * 36}#{@client_secret[36..]}" + end + + inspected + end + + # Duplicate client using client_id and client_secret as + # Basic Authentication credentials. + # @example + # Octokit.client_id = "foo" + # Octokit.client_secret = "bar" + # + # # GET https://api.github.com/?client_id=foo&client_secret=bar + # Octokit.get "/" + # + # Octokit.client.as_app do |client| + # # GET https://foo:bar@api.github.com/ + # client.get "/" + # end + def as_app(key = client_id, secret = client_secret) + if key.to_s.empty? || secret.to_s.empty? + raise ApplicationCredentialsRequired, 'client_id and client_secret required' + end + + app_client = dup + app_client.client_id = app_client.client_secret = nil + app_client.login = key + app_client.password = secret + + yield app_client if block_given? + end + + # Set username for authentication + # + # @param value [String] GitHub username + def login=(value) + reset_agent + @login = value + end + + # Set password for authentication + # + # @param value [String] GitHub password + def password=(value) + reset_agent + @password = value + end + + # Set OAuth access token for authentication + # + # @param value [String] 40 character GitHub OAuth access token + def access_token=(value) + reset_agent + @access_token = value + end + + # Set Bearer Token for authentication + # + # @param value [String] JWT + def bearer_token=(value) + reset_agent + @bearer_token = value + end + + # Set OAuth app client_id + # + # @param value [String] 20 character GitHub OAuth app client_id + def client_id=(value) + reset_agent + @client_id = value + end + + # Set OAuth app client_secret + # + # @param value [String] 40 character GitHub OAuth app client_secret + def client_secret=(value) + reset_agent + @client_secret = value + end + + def client_without_redirects(options = {}) + conn_opts = @connection_options + conn_opts[:url] = @api_endpoint + conn_opts[:builder] = @middleware.dup if @middleware + conn_opts[:proxy] = @proxy if @proxy + conn_opts[:ssl] = { verify_mode: @ssl_verify_mode } if @ssl_verify_mode + conn = Faraday.new(conn_opts) do |http| + http_cache_middleware = http.builder.handlers.delete(Faraday::HttpCache) if Faraday.const_defined?(:HttpCache) + if basic_authenticated? + http.request(*FARADAY_BASIC_AUTH_KEYS, @login, @password) + elsif token_authenticated? + http.request :authorization, 'token', @access_token + elsif bearer_authenticated? + http.request :authorization, 'Bearer', @bearer_token + end + http.builder.handlers.push(http_cache_middleware) unless http_cache_middleware.nil? + http.headers['accept'] = options[:accept] if options.key?(:accept) + end + conn.builder.delete(Octokit::Middleware::FollowRedirects) + + conn + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/actions_artifacts.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/actions_artifacts.rb new file mode 100644 index 0000000..d79cdb9 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/actions_artifacts.rb @@ -0,0 +1,71 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the Actions Artifacts API + # + # @see https://developer.github.com/v3/actions/artifacts + module ActionsArtifacts + # List all artifacts for a repository + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # + # @return [Sawyer::Resource] the total count and an array of artifacts + # @see https://developer.github.com/v3/actions/artifacts#list-artifacts-for-a-repository + def repository_artifacts(repo, options = {}) + paginate "#{Repository.path repo}/actions/artifacts", options do |data, last_response| + data.artifacts.concat last_response.data.artifacts + end + end + + # List all artifacts for a workflow run + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param workflow_run_id [Integer] Id of a workflow run + # + # @return [Sawyer::Resource] the total count and an array of artifacts + # @see https://docs.github.com/en/rest/actions/artifacts#list-workflow-run-artifacts + def workflow_run_artifacts(repo, workflow_run_id, options = {}) + paginate "#{Repository.path repo}/actions/runs/#{workflow_run_id}/artifacts", options do |data, last_response| + data.artifacts.concat last_response.data.artifacts + end + end + + # Get an artifact + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param id [Integer] Id of an artifact + # + # @return [Sawyer::Resource] Artifact information + # @see https://docs.github.com/en/rest/actions/artifacts#get-an-artifact + def artifact(repo, id, options = {}) + get "#{Repository.path repo}/actions/artifacts/#{id}", options + end + + # Get a download URL for an artifact + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param id [Integer] Id of an artifact + # + # @return [String] URL to the .zip archive of the artifact + # @see https://docs.github.com/en/rest/actions/artifacts#download-an-artifact + def artifact_download_url(repo, id, options = {}) + url = "#{Repository.path repo}/actions/artifacts/#{id}/zip" + + response = client_without_redirects.head(url, options) + response.headers['Location'] + end + + # Delete an artifact + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param id [Integer] Id of an artifact + # + # @return [Boolean] Return true if the artifact was successfully deleted + # @see https://docs.github.com/en/rest/actions/artifacts#delete-an-artifact + def delete_artifact(repo, id, options = {}) + boolean_from_response :delete, "#{Repository.path repo}/actions/artifacts/#{id}", options + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/actions_secrets.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/actions_secrets.rb new file mode 100644 index 0000000..1abf2a7 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/actions_secrets.rb @@ -0,0 +1,161 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the Actions Secrets API + # + # @see https://developer.github.com/v3/actions/secrets/ + module ActionsSecrets + # Get public key for secrets encryption + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @return [Hash] key_id and key + # @see https://developer.github.com/v3/actions/secrets/#get-your-public-key + def get_actions_public_key(repo) + get "#{Repository.path repo}/actions/secrets/public-key" + end + + # Get public key for secrets encryption + # + # @param org [String] A GitHub organization + # @return [Hash] key_id and key + # @see https://developer.github.com/v3/actions/secrets/#get-your-public-key + def get_org_actions_public_key(org) + get "#{Organization.path org}/actions/secrets/public-key" + end + + # List secrets + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @return [Hash] total_count and list of secrets (each item is hash with name, created_at and updated_at) + # @see https://developer.github.com/v3/actions/secrets/#list-secrets-for-a-repository + def list_actions_secrets(repo) + paginate "#{Repository.path repo}/actions/secrets" do |data, last_response| + data.secrets.concat last_response.data.secrets + end + end + + # List org secrets + # + # @param org [String] A GitHub organization + # @return [Hash] total_count and list of secrets (each item is hash with name, created_at and updated_at) + # @see https://developer.github.com/v3/actions/secrets/#list-organization-secrets + def list_org_actions_secrets(org) + paginate "#{Organization.path org}/actions/secrets" do |data, last_response| + data.secrets.concat last_response.data.secrets + end + end + + # Get a secret + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param name [String] Name of secret + # @return [Hash] name, created_at and updated_at + # @see https://developer.github.com/v3/actions/secrets/#get-a-secret + def get_actions_secret(repo, name) + get "#{Repository.path repo}/actions/secrets/#{name}" + end + + # Get an org secret + # + # @param org [String] A GitHub organization + # @param name [String] Name of secret + # @return [Hash] name, created_at and updated_at + # @see https://developer.github.com/v3/actions/secrets/#get-a-secret + def get_org_actions_secret(org, name) + get "#{Organization.path org}/actions/secrets/#{name}" + end + + # Create or update secrets + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param name [String] Name of secret + # @param options [Hash] encrypted_value and key_id + # @see https://developer.github.com/v3/actions/secrets/#create-or-update-a-secret-for-a-repository + def create_or_update_actions_secret(repo, name, options) + put "#{Repository.path repo}/actions/secrets/#{name}", options + end + + # Create or update org secrets + # + # @param org [String] A GitHub organization + # @param name [String] Name of secret + # @param options [Hash] encrypted_value and key_id + # @see https://developer.github.com/v3/actions/secrets/#create-or-update-a-secret + def create_or_update_org_actions_secret(org, name, options) + put "#{Organization.path org}/actions/secrets/#{name}", options + end + + # Delete a secret + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param name [String] Name of secret + # @see https://developer.github.com/v3/actions/secrets/#delete-a-secret-from-a-repository + def delete_actions_secret(repo, name) + boolean_from_response :delete, "#{Repository.path repo}/actions/secrets/#{name}" + end + + # Delete an org secret + # + # @param org [String] A GitHub organization + # @param name [String] Name of secret + # @see https://developer.github.com/v3/actions/secrets/#delete-a-secret + def delete_org_actions_secret(org, name) + boolean_from_response :delete, "#{Organization.path org}/actions/secrets/#{name}" + end + + # Get environment public key for secrets encryption + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param environment [String] Name of environment + # @return [Hash] key_id and key + # @see https://docs.github.com/en/rest/actions/secrets#get-an-environment-public-key + def get_actions_environment_public_key(repo, environment) + get "#{Repository.path repo}/environments/#{environment}/secrets/public-key" + end + + # List environment secrets + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param environment [String] Name of environment + # @return [Hash] total_count and list of secrets (each item is hash with name, created_at and updated_at) + # @see https://developer.github.com/v3/actions/secrets/#list-environment-secrets + def list_actions_environment_secrets(repo, environment) + paginate "#{Repository.path repo}/environments/#{environment}/secrets" do |data, last_response| + data.secrets.concat last_response.data.secrets + end + end + + # Get an environment secret + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param environment [String] Name of environment + # @param name [String] Name of secret + # @return [Hash] name, created_at and updated_at + # @see https://docs.github.com/en/rest/actions/secrets#get-an-environment-secret + def get_actions_environment_secret(repo, environment, name) + get "#{Repository.path repo}/environments/#{environment}/secrets/#{name}" + end + + # Create or update an environment secret + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param environment [String] Name of environment + # @param name [String] Name of secret + # @param options [Hash] encrypted_value and key_id + # @see https://docs.github.com/en/rest/actions/secrets#create-or-update-an-environment-secret + def create_or_update_actions_environment_secret(repo, environment, name, options) + put "#{Repository.path repo}/environments/#{environment}/secrets/#{name}", options + end + + # Delete environment secret + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param environment [String] Name of environment + # @param name [String] Name of secret + # @see https://docs.github.com/en/rest/actions/secrets#delete-an-environment-secret + def delete_actions_environment_secret(repo, environment, name) + boolean_from_response :delete, "#{Repository.path repo}/environments/#{environment}/secrets/#{name}" + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/actions_workflow_jobs.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/actions_workflow_jobs.rb new file mode 100644 index 0000000..d7a34bf --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/actions_workflow_jobs.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the Actions Workflows jobs API + # + # @see https://docs.github.com/rest/actions/workflow-jobs + module ActionsWorkflowJobs + # Get a job for a workflow run + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param job_id [Integer, String] Id of the job + # + # @return [Sawyer::Resource] Job information + # @see https://docs.github.com/rest/actions/workflow-jobs#get-a-job-for-a-workflow-run + def workflow_run_job(repo, job_id, options = {}) + get "#{Repository.path repo}/actions/jobs/#{job_id}", options + end + + # Download job logs for a workflow run + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param job_id [Integer, String] Id of the job + # + # @return [String] URL to the archived log files of the job + # @see https://docs.github.com/rest/actions/workflow-jobs#download-job-logs-for-a-workflow-run + def workflow_run_job_logs(repo, job_id, options = {}) + url = "#{Repository.path repo}/actions/jobs/#{job_id}/logs" + + response = client_without_redirects.head(url, options) + response.headers['Location'] + end + + # List jobs for a workflow run attempt + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param run_id [Integer, String] Id of the workflow run + # @param attempt_number [Integer, String] Attempt number of the workflow run + # + # @return [Sawyer::Resource] Jobs information + # @see https://docs.github.com/rest/actions/workflow-jobs#list-jobs-for-a-workflow-run-attempt + def workflow_run_attempt_jobs(repo, run_id, attempt_number, options = {}) + paginate "#{Repository.path repo}/actions/runs/#{run_id}/attempts/#{attempt_number}/jobs", options do |data, last_response| + data.jobs.concat last_response.data.jobs + end + end + alias list_workflow_run_attempt_jobs workflow_run_attempt_jobs + + # List jobs for a workflow run + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param run_id [Integer, String] Id of the workflow run + # @option options [String] :filter Optional filtering by a `completed_at` timestamp + # + # @return [Sawyer::Resource] Jobs information + # @see https://docs.github.com/rest/actions/workflow-jobs#list-jobs-for-a-workflow-run + def workflow_run_jobs(repo, run_id, options = {}) + paginate "#{Repository.path repo}/actions/runs/#{run_id}/jobs", options do |data, last_response| + data.jobs.concat last_response.data.jobs + end + end + alias list_workflow_run_jobs workflow_run_jobs + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/actions_workflow_runs.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/actions_workflow_runs.rb new file mode 100644 index 0000000..6048af0 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/actions_workflow_runs.rb @@ -0,0 +1,125 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the Actions Workflows runs API + # + # @see https://docs.github.com/rest/actions/workflow-runs + module ActionsWorkflowRuns + # List all runs for a repository workflow + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param workflow [Integer, String] Id or file name of the workflow + # @option options [String] :actor Optional filtering by a user + # @option options [String] :branch Optional filtering by a branch + # @option options [String] :event Optional filtering by the event type + # @option options [String] :status Optional filtering by a status or conclusion + # + # @return [Sawyer::Resource] the total count and an array of workflows + # @see https://developer.github.com/v3/actions/workflow-runs/#list-workflow-runs + def workflow_runs(repo, workflow, options = {}) + paginate "#{Repository.path repo}/actions/workflows/#{workflow}/runs", options do |data, last_response| + data.workflow_runs.concat last_response.data.workflow_runs + end + end + alias list_workflow_runs workflow_runs + + # List all workflow runs for a repository + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @option options [String] :actor Optional filtering by the login of a user + # @option options [String] :branch Optional filtering by a branch + # @option options [String] :event Optional filtering by the event type (e.g. push, pull_request, issue) + # @option options [String] :status Optional filtering by a status or conclusion (e.g. success, completed...) + # + # @return [Sawyer::Resource] the total count and an array of workflows + # @see https://developer.github.com/v3/actions/workflow-runs/#list-repository-workflow-runs + def repository_workflow_runs(repo, options = {}) + paginate "#{Repository.path repo}/actions/runs", options do |data, last_response| + data.workflow_runs.concat last_response.data.workflow_runs + end + end + alias list_repository_workflow_runs repository_workflow_runs + + # Get a workflow run + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param id [Integer] Id of a workflow run + # + # @return [Sawyer::Resource] Run information + # @see https://developer.github.com/v3/actions/workflow-runs/#get-a-workflow-run + def workflow_run(repo, id, options = {}) + get "#{Repository.path repo}/actions/runs/#{id}", options + end + + # Re-runs a workflow run + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param id [Integer] Id of a workflow run + # + # @return [Boolean] Returns true if the re-run request was accepted + # @see https://developer.github.com/v3/actions/workflow-runs/#re-run-a-workflow + def rerun_workflow_run(repo, id, options = {}) + boolean_from_response :post, "#{Repository.path repo}/actions/runs/#{id}/rerun", options + end + + # Cancels a workflow run + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param id [Integer] Id of a workflow run + # + # @return [Boolean] Returns true if the cancellation was accepted + # @see https://developer.github.com/v3/actions/workflow-runs/#cancel-a-workflow-run + def cancel_workflow_run(repo, id, options = {}) + boolean_from_response :post, "#{Repository.path repo}/actions/runs/#{id}/cancel", options + end + + # Deletes a workflow run + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param id [Integer] Id of a workflow run + # + # @return [Boolean] Returns true if the run is deleted + # @see https://docs.github.com/en/rest/reference/actions#delete-a-workflow-run + def delete_workflow_run(repo, id, options = {}) + boolean_from_response :delete, "#{Repository.path repo}/actions/runs/#{id}", options + end + + # Get a download url for archived log files of a workflow run + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param id [Integer] Id of a workflow run + # + # @return [String] URL to the archived log files of the run + # @see https://developer.github.com/v3/actions/workflow-runs/#download-workflow-run-logs + def workflow_run_logs(repo, id, options = {}) + url = "#{Repository.path repo}/actions/runs/#{id}/logs" + + response = client_without_redirects.head(url, options) + response.headers['Location'] + end + + # Delete all log files of a workflow run + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param id [Integer] Id of a workflow run + # + # @return [Boolean] Returns true if the logs are deleted + # @see https://developer.github.com/v3/actions/workflow-runs/#delete-workflow-run-logs + def delete_workflow_run_logs(repo, id, options = {}) + boolean_from_response :delete, "#{Repository.path repo}/actions/runs/#{id}/logs", options + end + + # Get workflow run usage + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param id [Integer] Id of a workflow run + # + # @return [Sawyer::Resource] Run usage + # @see https://developer.github.com/v3/actions/workflow-runs/#get-workflow-run-usage + def workflow_run_usage(repo, id, options = {}) + get "#{Repository.path repo}/actions/runs/#{id}/timing", options + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/actions_workflows.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/actions_workflows.rb new file mode 100644 index 0000000..4832510 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/actions_workflows.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the Actions Workflows API + # + # @see https://developer.github.com/v3/actions/workflows + module ActionsWorkflows + # Get the workflows in a repository + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # + # @return [Sawyer::Resource] the total count and an array of workflows + # @see https://developer.github.com/v3/actions/workflows/#list-repository-workflows + def workflows(repo, options = {}) + paginate "#{Repository.path repo}/actions/workflows", options do |data, last_response| + data.workflows.concat last_response.data.workflows + end + end + alias list_workflows workflows + + # Get single workflow in a repository + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param id [Integer, String] Id or file name of the workflow + # + # @return [Sawyer::Resource] A single workflow + # @see https://developer.github.com/v3/actions/workflows/#get-a-workflow + def workflow(repo, id, options = {}) + get "#{Repository.path repo}/actions/workflows/#{id}", options + end + + # Create a workflow dispatch event + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param id [Integer, String] Id or file name of the workflow + # @param ref [String] A SHA, branch name, or tag name + # + # @return [Boolean] True if event was dispatched, false otherwise + # @see https://docs.github.com/en/rest/reference/actions#create-a-workflow-dispatch-event + def workflow_dispatch(repo, id, ref, options = {}) + boolean_from_response :post, "#{Repository.path repo}/actions/workflows/#{id}/dispatches", options.merge({ ref: ref }) + end + + # Enable a workflow + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param id [Integer, String] Id or file name of the workflow + # + # @return [Boolean] True if workflow was enabled, false otherwise + # @see https://docs.github.com/en/rest/actions/workflows#enable-a-workflow + def workflow_enable(repo, id, options = {}) + boolean_from_response :put, "#{Repository.path repo}/actions/workflows/#{id}/enable", options + end + + # Disable a workflow + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param id [Integer, String] Id or file name of the workflow + # + # @return [Boolean] True if workflow was disabled, false otherwise + # @see https://docs.github.com/en/rest/actions/workflows#disable-a-workflow + def workflow_disable(repo, id, options = {}) + boolean_from_response :put, "#{Repository.path repo}/actions/workflows/#{id}/disable", options + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/apps.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/apps.rb new file mode 100644 index 0000000..235652c --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/apps.rb @@ -0,0 +1,211 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the Apps API + module Apps + # Get the authenticated App + # + # @param options [Hash] A customizable set of options + # + # @see https://docs.github.com/en/rest/apps/apps#get-the-authenticated-app + # + # @return [Sawyer::Resource] App information + def app(options = {}) + get 'app', options + end + + # List all installations that belong to an App + # + # @param options [Hash] A customizable set of options + # + # @see https://docs.github.com/en/rest/apps/apps#list-installations-for-the-authenticated-app + # + # @return [Array] the total_count and an array of installations + def list_app_installations(options = {}) + paginate 'app/installations', options + end + alias find_installations list_app_installations + alias find_app_installations list_app_installations + + # List all installations that are accessible to the authenticated user + # + # @param options [Hash] A customizable set of options + # + # @see https://docs.github.com/en/rest/apps/installations#list-app-installations-accessible-to-the-user-access-token + # + # @return [Sawyer::Resource] the total_count and an array of installations + def list_user_installations(options = {}) + paginate('user/installations', options) do |data, last_response| + data.installations.concat last_response.data.installations + end + end + alias find_user_installations list_user_installations + + # Get a single installation + # + # @param id [Integer] Installation id + # + # @see https://docs.github.com/en/rest/apps/apps#get-an-installation-for-the-authenticated-app + # + # @return [Sawyer::Resource] Installation information + def installation(id, options = {}) + get "app/installations/#{id}", options + end + + # Create a new installation token + # + # @param installation [Integer] The id of a GitHub App Installation + # @param options [Hash] A customizable set of options + # + # @see https://docs.github.com/en/rest/apps/apps#create-an-installation-access-token-for-an-app + # + # @return [] An installation token + def create_app_installation_access_token(installation, options = {}) + post "app/installations/#{installation}/access_tokens", options + end + alias create_installation_access_token create_app_installation_access_token + + # Enables an app to find the organization's installation information. + # + # @param organization [String] Organization GitHub login + # @param options [Hash] A customizable set of options + # + # @see https://docs.github.com/en/rest/apps/apps#get-an-organization-installation-for-the-authenticated-app + # + # @return [Sawyer::Resource] Installation information + def find_organization_installation(organization, options = {}) + get "#{Organization.path(organization)}/installation", options + end + + # Enables an app to find the repository's installation information. + # + # @param repo [String] A GitHub repository + # @param options [Hash] A customizable set of options + # + # @see https://docs.github.com/en/rest/apps/apps#get-a-repository-installation-for-the-authenticated-app + # + # @return [Sawyer::Resource] Installation information + def find_repository_installation(repo, options = {}) + get "#{Repository.path(repo)}/installation", options + end + + # Enables an app to find the user's installation information. + # + # @param user [String] GitHub user login + # @param options [Hash] A customizable set of options + # + # @see https://docs.github.com/en/rest/apps/apps#get-a-user-installation-for-the-authenticated-app + # + # @return [Sawyer::Resource] Installation information + def find_user_installation(user, options = {}) + get "#{User.path(user)}/installation", options + end + + # List repositories that are accessible to the authenticated installation + # + # @param options [Hash] A customizable set of options + # + # @see https://docs.github.com/en/rest/apps/installations#list-repositories-accessible-to-the-app-installation + # + # @return [Sawyer::Resource] the total_count and an array of repositories + def list_app_installation_repositories(options = {}) + paginate('installation/repositories', options) do |data, last_response| + data.repositories.concat last_response.data.repositories + end + end + alias list_installation_repos list_app_installation_repositories + + # Add a single repository to an installation + # + # @param installation [Integer] The id of a GitHub App Installation + # @param repo [Integer] The id of the GitHub repository + # @param options [Hash] A customizable set of options + # + # @see https://docs.github.com/en/rest/apps/installations#add-a-repository-to-an-app-installation + # + # @return [Boolean] Success + def add_repository_to_app_installation(installation, repo, options = {}) + boolean_from_response :put, "user/installations/#{installation}/repositories/#{repo}", options + end + alias add_repo_to_installation add_repository_to_app_installation + + # Remove a single repository to an installation + # + # @param installation [Integer] The id of a GitHub App Installation + # @param repo [Integer] The id of the GitHub repository + # @param options [Hash] A customizable set of options + # + # @see https://docs.github.com/en/rest/apps/installations#remove-a-repository-from-an-app-installation + # + # @return [Boolean] Success + def remove_repository_from_app_installation(installation, repo, options = {}) + boolean_from_response :delete, "user/installations/#{installation}/repositories/#{repo}", options + end + alias remove_repo_from_installation remove_repository_from_app_installation + + # List repositories accessible to the user for an installation + # + # @param installation [Integer] The id of a GitHub App Installation + # @param options [Hash] A customizable set of options + # + # @see https://docs.github.com/en/rest/apps/installations#list-repositories-accessible-to-the-user-access-token + # + # @return [Sawyer::Resource] the total_count and an array of repositories + def find_installation_repositories_for_user(installation, options = {}) + paginate("user/installations/#{installation}/repositories", options) do |data, last_response| + data.repositories.concat last_response.data.repositories + end + end + + # Delete an installation and uninstall a GitHub App + # + # @param installation [Integer] The id of a GitHub App Installation + # @param options [Hash] A customizable set of options + # + # @see https://docs.github.com/en/rest/apps/apps#delete-an-installation-for-the-authenticated-app + # + # @return [Boolean] Success + def delete_installation(installation, options = {}) + boolean_from_response :delete, "app/installations/#{installation}", options + end + + # Returns a list of webhook deliveries for the webhook configured for a GitHub App. + # + # @param options [Hash] A customizable set of options + # + # @see https://docs.github.com/en/rest/apps/webhooks#list-deliveries-for-an-app-webhook + # + # @return [Array] an array of hook deliveries + def list_app_hook_deliveries(options = {}) + paginate('app/hook/deliveries', options) do |data, last_response| + data.concat last_response.data + end + end + + # Returns a delivery for the webhook configured for a GitHub App. + # + # @param delivery_id [String] The id of a GitHub App Hook Delivery + # @param options [Hash] A customizable set of options + # + # @see https://docs.github.com/en/rest/apps/webhooks#get-a-delivery-for-an-app-webhook + # + # @return [] The webhook delivery + def app_hook_delivery(delivery_id, options = {}) + get "/app/hook/deliveries/#{delivery_id}", options + end + + # Redeliver a delivery for the webhook configured for a GitHub App. + # + # @param delivery_id [Integer] The id of a GitHub App Hook Delivery + # @param options [Hash] A customizable set of options + # + # @see https://docs.github.com/en/rest/apps/webhooks#redeliver-a-delivery-for-an-app-webhook + # + # @return [Boolean] Success + def deliver_app_hook(delivery_id, options = {}) + boolean_from_response :post, "app/hook/deliveries/#{delivery_id}/attempts", options + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/checks.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/checks.rb new file mode 100644 index 0000000..c22c6be --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/checks.rb @@ -0,0 +1,200 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the Checks API + # + # @see https://developer.github.com/v3/checks/ + module Checks + # Methods for Check Runs + # + # @see https://developer.github.com/v3/checks/runs/ + + # Create a check run + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param name [String] The name of the check + # @param head_sha [String] The SHA of the commit to check + # @return [Sawyer::Resource] A hash representing the new check run + # @see https://developer.github.com/v3/checks/runs/#create-a-check-run + # @example Create a check run + # check_run = @client.create_check_run("octocat/Hello-World", "my-check", "7638417db6d59f3c431d3e1f261cc637155684cd") + # check_run.name # => "my-check" + # check_run.head_sha # => "7638417db6d59f3c431d3e1f261cc637155684cd" + # check_run.status # => "queued" + def create_check_run(repo, name, head_sha, options = {}) + options[:name] = name + options[:head_sha] = head_sha + + post "#{Repository.path repo}/check-runs", options + end + + # Update a check run + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param id [Integer] The ID of the check run + # @return [Sawyer::Resource] A hash representing the updated check run + # @see https://developer.github.com/v3/checks/runs/#update-a-check-run + # @example Update a check run + # check_run = @client.update_check_run("octocat/Hello-World", 51295429, status: "in_progress") + # check_run.id # => 51295429 + # check_run.status # => "in_progress" + def update_check_run(repo, id, options = {}) + patch "#{Repository.path repo}/check-runs/#{id}", options + end + + # List check runs for a specific ref + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param ref [String] A SHA, branch name, or tag name + # @param options [Hash] A set of optional filters + # @option options [String] :check_name Returns check runs with the specified name + # @option options [String] :status Returns check runs with the specified status + # @option options [String] :filter Filters check runs by their completed_at timestamp + # @return [Sawyer::Resource] A hash representing a collection of check runs + # @see https://developer.github.com/v3/checks/runs/#list-check-runs-for-a-specific-ref + # @example List check runs for a specific ref + # result = @client.check_runs_for_ref("octocat/Hello-World", "7638417db6d59f3c431d3e1f261cc637155684cd", status: "in_progress") + # result.total_count # => 1 + # result.check_runs.count # => 1 + # result.check_runs[0].id # => 51295429 + # result.check_runs[0].status # => "in_progress" + def check_runs_for_ref(repo, ref, options = {}) + paginate "#{Repository.path repo}/commits/#{ref}/check-runs", options do |data, last_response| + data.check_runs.concat last_response.data.check_runs + data.total_count += last_response.data.total_count + end + end + alias list_check_runs_for_ref check_runs_for_ref + + # List check runs in a check suite + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param id [Integer] The ID of the check suite + # @param options [Hash] A set of optional filters + # @option options [String] :check_name Returns check runs with the specified name + # @option options [String] :status Returns check runs with the specified status + # @option options [String] :filter Filters check runs by their completed_at timestamp + # @return [Sawyer::Resource] A hash representing a collection of check runs + # @see https://developer.github.com/v3/checks/runs/#list-check-runs-in-a-check-suite + # @example List check runs in a check suite + # result = @client.check_runs_for_check_suite("octocat/Hello-World", 50440400, status: "in_progress") + # result.total_count # => 1 + # result.check_runs.count # => 1 + # result.check_runs[0].check_suite.id # => 50440400 + # result.check_runs[0].status # => "in_progress" + def check_runs_for_check_suite(repo, id, options = {}) + paginate "#{Repository.path repo}/check-suites/#{id}/check-runs", options do |data, last_response| + data.check_runs.concat last_response.data.check_runs + data.total_count += last_response.data.total_count + end + end + alias list_check_runs_for_check_suite check_runs_for_check_suite + + # Get a single check run + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param id [Integer] The ID of the check run + # @return [Sawyer::Resource] A hash representing the check run + # @see https://developer.github.com/v3/checks/runs/#get-a-single-check-run + def check_run(repo, id, options = {}) + get "#{Repository.path repo}/check-runs/#{id}", options + end + + # List annotations for a check run + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param id [Integer] The ID of the check run + # @return [Array] An array of hashes representing check run annotations + # @see https://developer.github.com/v3/checks/runs/#list-annotations-for-a-check-run + # @example List annotations for a check run + # annotations = @client.check_run_annotations("octocat/Hello-World", 51295429) + # annotations.count # => 1 + # annotations[0].path # => "README.md" + # annotations[0].message # => "Looks good!" + def check_run_annotations(repo, id, options = {}) + paginate "#{Repository.path repo}/check-runs/#{id}/annotations", options + end + + # Methods for Check Suites + # + # @see https://developer.github.com/v3/checks/suites/ + + # Get a single check suite + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param id [Integer] The ID of the check suite + # @return [Sawyer::Resource] A hash representing the check suite + # @see https://developer.github.com/v3/checks/suites/#get-a-single-check-suite + def check_suite(repo, id, options = {}) + get "#{Repository.path repo}/check-suites/#{id}", options + end + + # List check suites for a specific ref + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param ref [String] A SHA, branch name, or tag name + # @param options [Hash] A set of optional filters + # @option options [Integer] :app_id Filters check suites by GitHub App id + # @option options [String] :check_name Filters checks suites by the name of the check run + # @return [Sawyer::Resource] A hash representing a collection of check suites + # @see https://developer.github.com/v3/checks/suites/#list-check-suites-for-a-specific-ref + # @example List check suites for a specific ref + # result = @client.check_suites_for_ref("octocat/Hello-World", "7638417db6d59f3c431d3e1f261cc637155684cd", app_id: 76765) + # result.total_count # => 1 + # result.check_suites.count # => 1 + # result.check_suites[0].id # => 50440400 + # result.check_suites[0].app.id # => 76765 + def check_suites_for_ref(repo, ref, options = {}) + paginate "#{Repository.path repo}/commits/#{ref}/check-suites", options do |data, last_response| + data.check_suites.concat last_response.data.check_suites + data.total_count += last_response.data.total_count + end + end + alias list_check_suites_for_ref check_suites_for_ref + + # Set preferences for check suites on a repository + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param options [Hash] Preferences to set + # @return [Sawyer::Resource] A hash representing the repository's check suite preferences + # @see https://developer.github.com/v3/checks/suites/#set-preferences-for-check-suites-on-a-repository + # @example Set preferences for check suites on a repository + # result = @client.set_check_suite_preferences("octocat/Hello-World", auto_trigger_checks: [{ app_id: 76765, setting: false }]) + # result.preferences.auto_trigger_checks.count # => 1 + # result.preferences.auto_trigger_checks[0].app_id # => 76765 + # result.preferences.auto_trigger_checks[0].setting # => false + # result.repository.full_name # => "octocat/Hello-World" + def set_check_suite_preferences(repo, options = {}) + patch "#{Repository.path repo}/check-suites/preferences", options + end + + # Create a check suite + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param head_sha [String] The SHA of the commit to check + # @return [Sawyer::Resource] A hash representing the new check suite + # @see https://developer.github.com/v3/checks/suites/#create-a-check-suite + # @example Create a check suite + # check_suite = @client.create_check_suite("octocat/Hello-World", "7638417db6d59f3c431d3e1f261cc637155684cd") + # check_suite.head_sha # => "7638417db6d59f3c431d3e1f261cc637155684cd" + # check_suite.status # => "queued" + def create_check_suite(repo, head_sha, options = {}) + options[:head_sha] = head_sha + + post "#{Repository.path repo}/check-suites", options + end + + # Rerequest check suite + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param id [Integer] The ID of the check suite + # @return [Boolean] True if successful, raises an error otherwise + # @see https://developer.github.com/v3/checks/suites/#rerequest-check-suite + def rerequest_check_suite(repo, id, options = {}) + post "#{Repository.path repo}/check-suites/#{id}/rerequest", options + true + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/code_scanning.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/code_scanning.rb new file mode 100644 index 0000000..a03a075 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/code_scanning.rb @@ -0,0 +1,190 @@ +# frozen_string_literal: true + +require 'tempfile' +require 'zlib' + +module Octokit + class Client + # Methods for the code scanning alerts API + # + # @see https://docs.github.com/rest/code-scanning + module CodeScanning + # Updates a code scanning default setup configuration + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param state [String] The desired state of code scanning default setup + # @param query_suite [String] CodeQL query suite to be used + # @param languages [Array] List of CodeQL languages to be analyzed + # + # @return [Sawyer::Resource] Action Run information + # @see https://docs.github.com/en/rest/code-scanning/code-scanning#update-a-code-scanning-default-setup-configuration + def update_code_scanning_default_config(repo, state, query_suite = nil, languages = nil, options = {}) + options[:state] = state + options[:query_suite] = query_suite if query_suite + options[:languages] = languages if languages + + patch "#{Repository.path repo}/code-scanning/default-setup", options + end + + # Get Code Scanning Default Configuration + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # + # @return [Sawyer::Resource] CodeQl Default Setup Configuration Information + # @see https://docs.github.com/en/rest/code-scanning/code-scanning#get-a-code-scanning-default-setup-configuration + def get_code_scanning_default_config(repo, options = {}) + get "#{Repository.path repo}/code-scanning/default-setup", options + end + + # Gets a CodeQL database for a language in a repository + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param language [String] + # + # @return [Sawyer::Resource] CodeQl Default Setup Configuration Information + # @see https://docs.github.com/en/rest/code-scanning/code-scanning#get-a-codeql-database-for-a-repository + def get_codeql_database_for_repo(repo, language, options = {}) + get "#{Repository.path repo}/code-scanning/codeql/databases/#{language}", options + end + + # Lists the CodeQL databases that are available in a repository + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # + # @return [Array] List of CodeQL Databases + # @see https://docs.github.com/en/rest/code-scanning/code-scanning#list-codeql-databases-for-a-repository + def list_codeql_database_for_repo(repo, options = {}) + get "#{Repository.path repo}/code-scanning/codeql/databases", options + end + + # Delete a specified code scanning analysis from a repository + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param analysis_id [Integer] ID of the code scanning analysis + # + # @return [Sawyer::Resource] Next Code Scanning Analysis Information + # @see https://docs.github.com/en/rest/code-scanning/code-scanning#delete-a-code-scanning-analysis-from-a-repository + def delete_code_scanning_analysis(repo, analysis_id, options = {}) + delete "#{Repository.path repo}/code-scanning/analyses/#{analysis_id}", options + end + + # Get a code scanning analysis for a repository + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param analysis_id [Integer] ID of the code scanning analysis + # + # @return [Sawyer::Resource] Code Scanning Analysis + # @see https://docs.github.com/en/rest/code-scanning/code-scanning#get-a-code-scanning-analysis-for-a-repository + def get_code_scanning_analysis(repo, analysis_id, options = {}) + get "#{Repository.path repo}/code-scanning/analyses/#{analysis_id}", options + end + + # List code scanning analyses for a repository + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # + # @return [Array] List of Code Scanning Analyses + # @see https://docs.github.com/en/rest/code-scanning/code-scanning#list-code-scanning-analyses-for-a-repository + def list_code_scanning_analysis(repo, options = {}) + paginate "#{Repository.path repo}/code-scanning/analyses", options + end + + # List instances of a code scanning alert + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param alert_number [Integer] The number that identifies an alert + # + # @return [Array] List of Code Scanning Alerts + # @see https://docs.github.com/en/rest/code-scanning/code-scanning#list-instances-of-a-code-scanning-alert + def list_instances_of_code_scanning_alert(repo, alert_number, options = {}) + paginate "#{Repository.path repo}/code-scanning/alerts/#{alert_number}/instances", options + end + + # Update a code scanning alert + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param alert_number [Integer] The number that identifies an alert + # @param state [String] The reason for dismissing or closing the alert. Required when the state is dismissed + # + # @return [Sawyer::Resource] Code Scanning Alert information + # @see https://docs.github.com/en/rest/code-scanning/code-scanning#update-a-code-scanning-alert + def update_code_scanning_alert(repo, alert_number, state, reason, comment = nil, options = {}) + options[:state] = state + options[:dismissed_reason] = reason + options[:dismissed_comment] = comment if comment + + patch "#{Repository.path repo}/code-scanning/alerts/#{alert_number}", options + end + + # Gets a single code scanning alert + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param alert_number [Integer] The number that identifies an alert + # + # @return [Sawyer::Resource] Code Scanning Alert + # @see https://docs.github.com/en/rest/code-scanning/code-scanning#get-a-code-scanning-alert + def get_code_scanning_alert(repo, alert_number, options = {}) + get "#{Repository.path repo}/code-scanning/alerts/#{alert_number}", options + end + + # List code scanning alerts for a repository + # + # @param org [String] A GitHub organization + # + # @return [Array] Code Scanning Alert information + # @see https://docs.github.com/en/rest/code-scanning/code-scanning#list-code-scanning-alerts-for-a-repository + def list_code_scanning_alerts_for_repo(repo, options = {}) + paginate "#{Repository.path repo}/code-scanning/alerts", options + end + + # List code scanning alerts for an organization + # + # @param org [String] A GitHub organization + # + # @return [Array] Code Scanning Alert information + # @see https://docs.github.com/en/rest/code-scanning/code-scanning#list-code-scanning-alerts-for-an-organization + def list_code_scanning_alerts_for_org(org, options = {}) + paginate "orgs/#{org}/code-scanning/alerts", options + end + + # Uploads SARIF data containing the results of a code scanning analysis + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param file [String] Path to the SARIF file to upload + # @param sha [String] The SHA of the commit to which the analysis you are uploading relates + # @param ref [String] The full Git reference, formatted as `refs/heads/`, `refs/pull//merge`, or `refs/pull//head` + # + # @return [Sawyer::Resource] SARIF upload information + # @see https://docs.github.com/rest/code-scanning#upload-an-analysis-as-sarif-data + def upload_sarif_data(repo, file, sha, ref, options = {}) + options[:sarif] = compress_sarif_data(file) + options[:commit_sha] = sha + options[:ref] = ref + + post "#{Repository.path repo}/code-scanning/sarifs", options + end + + # Gets information about a SARIF upload + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param sarif_id [String] The SARIF ID obtained after uploading + # + # @return [Sawyer::Resource] SARIF upload information + # @see https://docs.github.com/rest/code-scanning#get-information-about-a-sarif-upload + def get_sarif_upload_information(repo, sarif_id, options = {}) + get "#{Repository.path repo}/code-scanning/sarifs/#{sarif_id}", options + end + + private + + def compress_sarif_data(file) + Tempfile.create('sarif.gz') do |tempfile| + Zlib::GzipWriter.open(tempfile) do |gz_file| + gz_file.write File.binread(file) + end + [tempfile.read].pack('m0') # Base64.strict_encode64 + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/codespaces_secrets.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/codespaces_secrets.rb new file mode 100644 index 0000000..08fa2f5 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/codespaces_secrets.rb @@ -0,0 +1,108 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the Codespaces Secrets API + # + # @see https://docs.github.com/en/rest/codespaces/ + module CodespacesSecrets + # Get public key for secrets encryption + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @return [Hash] key_id and key + # @see https://docs.github.com/en/rest/codespaces/repository-secrets#get-a-repository-public-key + def get_codespaces_public_key(repo) + get "#{Repository.path repo}/codespaces/secrets/public-key" + end + + # Get public key for secrets encryption + # + # @param org [String] A GitHub organization + # @return [Hash] key_id and key + # @see https://docs.github.com/en/rest/codespaces/organization-secrets?apiVersion=2022-11-28#get-an-organization-public-key + def get_org_codespaces_public_key(org) + get "#{Organization.path org}/codespaces/secrets/public-key" + end + + # List secrets + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @return [Hash] total_count and list of secrets (each item is hash with name, created_at and updated_at) + # @see https://docs.github.com/en/rest/codespaces/repository-secrets?apiVersion=2022-11-28#list-repository-secrets + def list_codespaces_secrets(repo) + paginate "#{Repository.path repo}/codespaces/secrets" do |data, last_response| + data.secrets.concat last_response.data.secrets + end + end + + # List org secrets + # + # @param org [String] A GitHub organization + # @return [Hash] total_count and list of secrets (each item is hash with name, created_at and updated_at) + # @see https://docs.github.com/en/rest/codespaces/organization-secrets?apiVersion=2022-11-28#list-organization-secrets + def list_org_codespaces_secrets(org) + paginate "#{Organization.path org}/codespaces/secrets" do |data, last_response| + data.secrets.concat last_response.data.secrets + end + end + + # Get a secret + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param name [String] Name of secret + # @return [Hash] name, created_at, updated_at, and visibility + # @see https://docs.github.com/en/rest/codespaces/repository-secrets?apiVersion=2022-11-28#get-a-repository-secret + def get_codespaces_secret(repo, name) + get "#{Repository.path repo}/codespaces/secrets/#{name}" + end + + # Get an org secret + # + # @param org [String] A GitHub organization + # @param name [String] Name of secret + # @return [Hash] name, created_at, updated_at, and visibility + # @see https://docs.github.com/en/rest/codespaces/organization-secrets?apiVersion=2022-11-28#get-an-organization-secret + def get_org_codespaces_secret(org, name) + get "#{Organization.path org}/codespaces/secrets/#{name}" + end + + # Create or update secrets + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param name [String] Name of secret + # @param options [Hash] encrypted_value and key_id + # @see https://docs.github.com/en/rest/codespaces/repository-secrets?apiVersion=2022-11-28#create-or-update-a-repository-secret + def create_or_update_codespaces_secret(repo, name, options) + put "#{Repository.path repo}/codespaces/secrets/#{name}", options + end + + # Create or update org secrets + # + # @param org [String] A GitHub organization + # @param name [String] Name of secret + # @param options [Hash] encrypted_value and key_id + # @see https://docs.github.com/en/rest/codespaces/organization-secrets?apiVersion=2022-11-28#create-or-update-an-organization-secret + def create_or_update_org_codespaces_secret(org, name, options) + put "#{Organization.path org}/codespaces/secrets/#{name}", options + end + + # Delete a secret + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param name [String] Name of secret + # @see https://docs.github.com/en/rest/codespaces/repository-secrets?apiVersion=2022-11-28#delete-a-repository-secret + def delete_codespaces_secret(repo, name) + boolean_from_response :delete, "#{Repository.path repo}/codespaces/secrets/#{name}" + end + + # Delete an org secret + # + # @param org [String] A GitHub organization + # @param name [String] Name of secret + # @see https://docs.github.com/en/rest/codespaces/organization-secrets?apiVersion=2022-11-28#delete-an-organization-secret + def delete_org_codespaces_secret(org, name) + boolean_from_response :delete, "#{Organization.path org}/codespaces/secrets/#{name}" + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/commit_branches.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/commit_branches.rb new file mode 100644 index 0000000..fb060db --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/commit_branches.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the Branches for HEAD API + # + # @see https://developer.github.com/v3/repos/commits/ + module CommitBranches + # List branches for a single HEAD commit + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param sha [String] The SHA of the commit whose branches will be fetched + # @return [Array] List of branches + # @see https://developer.github.com/v3/repos/commits/#list-branches-for-head-commit + def commit_branches(repo, sha, options = {}) + paginate "#{Repository.path repo}/commits/#{sha}/branches-where-head", options + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/commit_comments.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/commit_comments.rb new file mode 100644 index 0000000..36af538 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/commit_comments.rb @@ -0,0 +1,95 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the Commit Comments API + # + # @see https://developer.github.com/v3/repos/comments/ + module CommitComments + # List all commit comments + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @return [Array] List of commit comments + # @see https://developer.github.com/v3/repos/comments/#list-commit-comments-for-a-repository + def list_commit_comments(repo, options = {}) + paginate "#{Repository.path repo}/comments", options + end + + # List comments for a single commit + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param sha [String] The SHA of the commit whose comments will be fetched + # @return [Array] List of commit comments + # @see https://developer.github.com/v3/repos/comments/#list-comments-for-a-single-commit + def commit_comments(repo, sha, options = {}) + paginate "#{Repository.path repo}/commits/#{sha}/comments", options + end + + # Get a single commit comment + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param id [String] The ID of the comment to fetch + # @return [Sawyer::Resource] Commit comment + # @see https://developer.github.com/v3/repos/comments/#get-a-single-commit-comment + def commit_comment(repo, id, options = {}) + get "#{Repository.path repo}/comments/#{id}", options + end + + # Create a commit comment + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param sha [String] Sha of the commit to comment on + # @param body [String] Message + # @param path [String] Relative path of file to comment on + # @param line [Integer] Line number in the file to comment on + # @param position [Integer] Line index in the diff to comment on + # @return [Sawyer::Resource] Commit comment + # @see https://developer.github.com/v3/repos/comments/#create-a-commit-comment + # @example Create a commit comment + # comment = Octokit.create_commit_comment("octocat/Hello-World", "827efc6d56897b048c772eb4087f854f46256132", "My comment message", "README.md", 10, 1) + # comment.commit_id # => "827efc6d56897b048c772eb4087f854f46256132" + # comment.id # => 54321 + # comment.body # => "My comment message" + # comment.path # => "README.md" + # comment.line # => 10 + # comment.position # => 1 + def create_commit_comment(repo, sha, body, path = nil, line = nil, position = nil, options = {}) + params = { + body: body, + path: path, + line: line, + position: position + } + post "#{Repository.path repo}/commits/#{sha}/comments", options.merge(params) + end + + # Update a commit comment + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param id [String] The ID of the comment to update + # @param body [String] Message + # @return [Sawyer::Resource] Updated commit comment + # @see https://developer.github.com/v3/repos/comments/#update-a-commit-comment + # @example Update a commit comment + # comment = Octokit.update_commit_comment("octocat/Hello-World", "860296", "Updated commit comment") + # comment.id # => 860296 + # comment.body # => "Updated commit comment" + def update_commit_comment(repo, id, body, options = {}) + params = { + body: body + } + patch "#{Repository.path repo}/comments/#{id}", options.merge(params) + end + + # Delete a commit comment + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param id [String] The ID of the comment to delete + # @return [Boolean] Success + # @see https://developer.github.com/v3/repos/comments/#delete-a-commit-comment + def delete_commit_comment(repo, id, options = {}) + boolean_from_response :delete, "#{Repository.path repo}/comments/#{id}", options + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/commit_pulls.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/commit_pulls.rb new file mode 100644 index 0000000..1e5a16f --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/commit_pulls.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the Commit Pulls API + # + # @see https://developer.github.com/v3/repos/comments/ + module CommitPulls + # List pulls for a single commit + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param sha [String] The SHA of the commit whose pulls will be fetched + # @return [Array] List of commit pulls + # @see https://developer.github.com/v3/repos/commits/#list-pull-requests-associated-with-commit + def commit_pulls(repo, sha, options = {}) + paginate "#{Repository.path repo}/commits/#{sha}/pulls", options + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/commits.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/commits.rb new file mode 100644 index 0000000..fa73b7e --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/commits.rb @@ -0,0 +1,236 @@ +# frozen_string_literal: true + +require 'date' + +module Octokit + class Client + # Methods for the Commits API + # + # @see https://developer.github.com/v3/repos/commits/ + module Commits + # List commits + # + # @overload commits(repo, sha_or_branch, options = {}) + # @deprecated + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param sha_or_branch [String] A commit SHA or branch name + # @param options [String] :sha Commit SHA or branch name from which to start the list + # @overload commits(repo, options = {}) + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param options [String] :sha Commit SHA or branch name from which to start the list + # @return [Array] An array of hashes representing commits + # @see https://developer.github.com/v3/repos/commits/#list-commits-on-a-repository + def commits(*args) + arguments = Octokit::RepoArguments.new(args) + sha_or_branch = arguments.pop + arguments.options[:sha] = sha_or_branch if sha_or_branch + paginate "#{Repository.new(arguments.repo).path}/commits", arguments.options + end + alias list_commits commits + + # Get commits after a specified date + # + # @overload commits_since(repo, date, options = {}) + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param date [String] Date on which we want to compare + # @param options [String] :sha Commit SHA or branch name from which to start the list + # @overload commits_since(repo, date, sha_or_branch, options = {}) + # @deprecated + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param date [String] Date on which we want to compare + # @param sha_or_branch [String] A commit SHA or branch name + # @param options [String] :sha Commit SHA or branch name from which to start the list + # @return [Array] An array of hashes representing commits + # @see https://developer.github.com/v3/repos/commits/#list-commits-on-a-repository + # @example + # Octokit.commits_since('octokit/octokit.rb', '2012-10-01') + def commits_since(*args) + arguments = Octokit::RepoArguments.new(args) + date = parse_date(arguments.shift) + params = arguments.options + params.merge!(since: iso8601(date)) + sha_or_branch = arguments.pop + params[:sha] = sha_or_branch if sha_or_branch + commits(arguments.repo, params) + end + + # Get commits before a specified date + # + # @overload commits_before(repo, date, options = {}) + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param date [String] Date on which we want to compare + # @overload commits_before(repo, date, sha_or_branch, options = {}) + # @deprecated + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param date [String] Date on which we want to compare + # @param sha_or_branch [String] Commit SHA or branch name from which to start the list + # @return [Array] An array of hashes representing commits + # @see https://developer.github.com/v3/repos/commits/#list-commits-on-a-repository + # @example + # Octokit.commits_before('octokit/octokit.rb', '2012-10-01') + def commits_before(*args) + arguments = Octokit::RepoArguments.new(args) + date = parse_date(arguments.shift) + params = arguments.options + params.merge!(until: iso8601(date)) + sha_or_branch = arguments.pop + params[:sha] = sha_or_branch if sha_or_branch + commits(arguments.repo, params) + end + + # Get commits on a specified date + # + # @overload commits_on(repo, date, options = {}) + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param date [String] Date on which we want to compare + # @overload commits_on(repo, date, sha_or_branch, options = {}) + # @deprecated + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param date [String] Date on which we want to compare + # @param sha_or_branch [String] Commit SHA or branch name from which to start the list + # @return [Array] An array of hashes representing commits + # @see https://developer.github.com/v3/repos/commits/#list-commits-on-a-repository + # @example + # Octokit.commits_on('octokit/octokit.rb', '2012-10-01') + def commits_on(*args) + arguments = Octokit::RepoArguments.new(args) + date = parse_date(arguments.shift) + params = arguments.options + end_date = date + 1 + params.merge!(since: iso8601(date), until: iso8601(end_date)) + sha_or_branch = arguments.pop + params[:sha] = sha_or_branch if sha_or_branch + commits(arguments.repo, params) + end + + # Get commits made between two nominated dates + # + # @overload commits_between(repo, start_date, end_date, options = {}) + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param start_date [String] Start Date on which we want to compare + # @param end_date [String] End Date on which we want to compare + # @overload commits_between(repo, start_date, end_date, sha_or_branch, options = {}) + # @deprecated + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param start_date [String] Start Date on which we want to compare + # @param end_date [String] End Date on which we want to compare + # @param sha_or_branch [String] Commit SHA or branch name from which to start the list + # @return [Array] An array of hashes representing commits + # @see https://developer.github.com/v3/repos/commits/#list-commits-on-a-repository + # @example + # Octokit.commits_between('octokit/octokit.rb', '2012-10-01', '2012-11-01') + def commits_between(*args) + arguments = Octokit::RepoArguments.new(args) + date = parse_date(arguments.shift) + end_date = parse_date(arguments.shift) + if date > end_date + raise ArgumentError, "Start date #{date} does not precede #{end_date}" + end + + params = arguments.options + params.merge!(since: iso8601(date), until: iso8601(end_date)) + sha_or_branch = arguments.pop + params[:sha] = sha_or_branch if sha_or_branch + commits(arguments.repo, params) + end + + # Get a single commit + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param sha [String] The SHA of the commit to fetch + # @return [Sawyer::Resource] A hash representing the commit + # @see https://developer.github.com/v3/repos/commits/#get-a-single-commit + def commit(repo, sha, options = {}) + get "#{Repository.path repo}/commits/#{sha}", options + end + + # Get a detailed git commit + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param sha [String] The SHA of the commit to fetch + # @return [Sawyer::Resource] A hash representing the commit + # @see https://developer.github.com/v3/git/commits/#get-a-commit + def git_commit(repo, sha, options = {}) + get "#{Repository.path repo}/git/commits/#{sha}", options + end + + # Create a commit + # + # Optionally pass author and committer hashes in options + # if you'd like manual control over those parameters. If absent, details will be + # inferred from the authenticated user. See GitHub's documentation + # for details about how to format committer identities. + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param message [String] The commit message + # @param tree [String] The SHA of the tree object the new commit will point to + # @param parents [String, Array] One SHA (for a normal commit) or an array of SHAs (for a merge) of the new commit's parent commits. If ommitted or empty, a root commit will be created + # @return [Sawyer::Resource] A hash representing the new commit + # @see https://developer.github.com/v3/git/commits/#create-a-commit + # @example Create a commit + # commit = Octokit.create_commit("octocat/Hello-World", "My commit message", "827efc6d56897b048c772eb4087f854f46256132", "7d1b31e74ee336d15cbd21741bc88a537ed063a0") + # commit.sha # => "7638417db6d59f3c431d3e1f261cc637155684cd" + # commit.tree.sha # => "827efc6d56897b048c772eb4087f854f46256132" + # commit.message # => "My commit message" + # commit.committer # => { "name" => "Wynn Netherland", "email" => "wynn@github.com", ... } + def create_commit(repo, message, tree, parents = nil, options = {}) + params = { message: message, tree: tree } + params[:parents] = [parents].flatten if parents + post "#{Repository.path repo}/git/commits", options.merge(params) + end + + # Compare two commits + # + # When using auto_pagination, commits from all pages will be concatenated + # into the commits attribute of the first page's response. + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param start [String] The sha of the starting commit + # @param endd [String] The sha of the ending commit + # @return [Sawyer::Resource] A hash representing the comparison + # @see https://developer.github.com/v3/repos/commits/#compare-two-commits + def compare(repo, start, endd, options = {}) + paginate "#{Repository.path repo}/compare/#{start}...#{endd}", options do |data, last_response| + data.commits.concat last_response.data.commits + end + end + + # Merge a branch or sha + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param base [String] The name of the base branch to merge into + # @param head [String] The branch or SHA1 to merge + # @option options [String] :commit_message The commit message for the merge + # @return [Sawyer::Resource] A hash representing the comparison + # @see https://developer.github.com/v3/repos/merging/#perform-a-merge + def merge(repo, base, head, options = {}) + params = { + base: base, + head: head + }.merge(options) + post "#{Repository.path repo}/merges", params + end + + protected + + def iso8601(date) + if date.respond_to?(:iso8601) + date.iso8601 + else + date.strftime('%Y-%m-%dT%H:%M:%S%Z') + end + end + + # Parses the given string representation of a date, throwing a meaningful exception + # (containing the date that failed to parse) in case of failure. + # + # @param date [String] String representation of a date + # @return [DateTime] + def parse_date(date) + date = DateTime.parse(date.to_s) + rescue ArgumentError + raise ArgumentError, "#{date} is not a valid date" + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/community_profile.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/community_profile.rb new file mode 100644 index 0000000..e50b47c --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/community_profile.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the Community Profile API + # + # @see https://developer.github.com/v3/repos/community/ + module CommunityProfile + # Get community profile metrics for a repository + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @return [Sawyer::Resource] Community profile metrics + # @see https://developer.github.com/v3/repos/community/#retrieve-community-profile-metrics + # @example Get community profile metrics for octokit/octokit.rb + # @client.community_profile('octokit/octokit.rb') + def community_profile(repo, options = {}) + get "#{Repository.path repo}/community/profile", options + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/contents.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/contents.rb new file mode 100644 index 0000000..031d601 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/contents.rb @@ -0,0 +1,165 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the Repo Contents API + # + # @see https://developer.github.com/v3/repos/contents/ + module Contents + # Receive the default Readme for a repository + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @option options [String] :ref name of the Commit/Branch/Tag. Defaults to “master”. + # @return [Sawyer::Resource] The detail of the readme + # @see https://developer.github.com/v3/repos/contents/#get-the-readme + # @example Get the readme file for a repo + # Octokit.readme("octokit/octokit.rb") + # @example Get the readme file for a particular branch of the repo + # Octokit.readme("octokit/octokit.rb", :query => {:ref => 'some-other-branch'}) + def readme(repo, options = {}) + get "#{Repository.path repo}/readme", options + end + + # Receive a listing of a repository folder or the contents of a file + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @option options [String] :path A folder or file path + # @option options [String] :ref name of the Commit/Branch/Tag. Defaults to “master”. + # @return [Sawyer::Resource] The contents of a file or list of the files in the folder + # @see https://developer.github.com/v3/repos/contents/#get-contents + # @example List the contents of lib/octokit.rb + # Octokit.contents("octokit/octokit.rb", :path => 'lib/octokit.rb') + # @example Lists the contents of lib /octokit.rb on a particular branch + # Octokit.contents("octokit/octokit.rb", :path => 'lib/octokit.rb', :query => {:ref => 'some-other-branch'}) + def contents(repo, options = {}) + options = options.dup + repo_path = options.delete :path + url = "#{Repository.path repo}/contents/#{repo_path}" + get url, options + end + alias content contents + + # Add content to a repository + # + # @overload create_contents(repo, path, message, content = nil, options = {}) + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param path [String] A path for the new content + # @param message [String] A commit message for adding the content + # @param optional content [String] The content for the file + # @option options [String] :branch The branch on which to add the content + # @option options [String] :file Path or Ruby File object for content + # @return [Sawyer::Resource] The contents and commit info for the addition + # @see https://developer.github.com/v3/repos/contents/#create-a-file + # @example Add content at lib/octokit.rb + # Octokit.create_contents("octokit/octokit.rb", + # "lib/octokit.rb", + # "Adding content", + # "File content", + # :branch => "my-new-feature") + def create_contents(*args) + args = args.map { |item| item&.dup } + options = args.last.is_a?(Hash) ? args.pop : {} + repo = args.shift + path = args.shift + message = args.shift + content = args.shift + if content.nil? && file = options.delete(:file) + case file + when String + if File.exist?(file) + file = File.open(file, 'r') + content = file.read + file.close + end + when File, Tempfile + content = file.read + file.close + end + end + raise ArgumentError, 'content or :file option required' if content.nil? + + options[:content] = [content].pack('m0') # Base64.strict_encode64 + options[:message] = message + url = "#{Repository.path repo}/contents/#{path}" + put url, options + end + alias create_content create_contents + alias add_content create_contents + alias add_contents create_contents + + # Update content in a repository + # + # @overload update_contents(repo, path, message, sha, content = nil, options = {}) + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param path [String] A path for the content to update + # @param message [String] A commit message for updating the content + # @param sha [String] The _blob sha_ of the content to update + # @param content [String] The content for the file + # @option options [String] :branch The branch on which to update the content + # @option options [String] :file Path or Ruby File object for content + # @return [Sawyer::Resource] The contents and commit info for the update + # @see https://developer.github.com/v3/repos/contents/#update-a-file + # @example Update content at lib/octokit.rb + # Octokit.update_contents("octokit/octokit.rb", + # "lib/octokit.rb", + # "Updating content", + # "7eb95f97e1a0636015df3837478d3f15184a5f49", + # "File content", + # :branch => "my-new-feature") + def update_contents(*args) + options = args.last.is_a?(Hash) ? args.pop : {} + repo = args.shift + path = args.shift + message = args.shift + sha = args.shift + content = args.shift + options.merge!(sha: sha) + create_contents(repo, path, message, content, options) + end + alias update_content update_contents + + # Delete content in a repository + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param path [String] A path for the content to delete + # @param message [String] A commit message for deleting the content + # @param sha [String] The _blob sha_ of the content to delete + # @option options [String] :branch The branch on which to delete the content + # @return [Sawyer::Resource] The commit info for the delete + # @see https://developer.github.com/v3/repos/contents/#delete-a-file + # @example Delete content at lib/octokit.rb + # Octokit.delete_contents("octokit/octokit.rb", + # "lib/octokit.rb", + # "Deleting content", + # "7eb95f97e1a0636015df3837478d3f15184a5f49", + # :branch => "my-new-feature") + def delete_contents(repo, path, message, sha, options = {}) + options[:message] = message + options[:sha] = sha + url = "#{Repository.path repo}/contents/#{path}" + delete url, options + end + alias delete_content delete_contents + alias remove_content delete_contents + alias remove_contents delete_contents + + # This method will provide a URL to download a tarball or zipball archive for a repository. + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository. + # @option options format [String] Either tarball (default) or zipball. + # @option options [String] :ref Optional valid Git reference, defaults to master. + # @return [String] Location of the download + # @see https://developer.github.com/v3/repos/contents/#get-archive-link + # @example Get archive link for octokit/octokit.rb + # Octokit.archive_link("octokit/octokit.rb") + def archive_link(repo, options = {}) + repo_ref = ERB::Util.url_encode(options.delete(:ref)) + format = (options.delete :format) || 'tarball' + url = "#{Repository.path repo}/#{format}/#{repo_ref}" + + response = client_without_redirects.head(url, options) + response.headers['Location'] + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/dependabot_secrets.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/dependabot_secrets.rb new file mode 100644 index 0000000..5ac765a --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/dependabot_secrets.rb @@ -0,0 +1,108 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the dependabot Secrets API + # + # @see https://docs.github.com/en/rest/dependabot/ + module DependabotSecrets + # Get public key for secrets encryption + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @return [Hash] key_id and key + # @see https://docs.github.com/en/rest/dependabot/repository-secrets#get-a-repository-public-key + def get_dependabot_public_key(repo) + get "#{Repository.path repo}/dependabot/secrets/public-key" + end + + # Get public key for secrets encryption + # + # @param org [String] A GitHub organization + # @return [Hash] key_id and key + # @see https://docs.github.com/en/rest/dependabot/organization-secrets?apiVersion=2022-11-28#get-an-organization-public-key + def get_org_dependabot_public_key(org) + get "#{Organization.path org}/dependabot/secrets/public-key" + end + + # List secrets + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @return [Hash] total_count and list of secrets (each item is hash with name, created_at and updated_at) + # @see https://docs.github.com/en/rest/dependabot/repository-secrets?apiVersion=2022-11-28#list-repository-secrets + def list_dependabot_secrets(repo) + paginate "#{Repository.path repo}/dependabot/secrets" do |data, last_response| + data.secrets.concat last_response.data.secrets + end + end + + # List org secrets + # + # @param org [String] A GitHub organization + # @return [Hash] total_count and list of secrets (each item is hash with name, created_at and updated_at) + # @see https://docs.github.com/en/rest/dependabot/organization-secrets?apiVersion=2022-11-28#list-organization-secrets + def list_org_dependabot_secrets(org) + paginate "#{Organization.path org}/dependabot/secrets" do |data, last_response| + data.secrets.concat last_response.data.secrets + end + end + + # Get a secret + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param name [String] Name of secret + # @return [Hash] name, created_at, updated_at, and visibility + # @see https://docs.github.com/en/rest/dependabot/repository-secrets?apiVersion=2022-11-28#get-a-repository-secret + def get_dependabot_secret(repo, name) + get "#{Repository.path repo}/dependabot/secrets/#{name}" + end + + # Get an org secret + # + # @param org [String] A GitHub organization + # @param name [String] Name of secret + # @return [Hash] name, created_at, updated_at, and visibility + # @see https://docs.github.com/en/rest/dependabot/organization-secrets?apiVersion=2022-11-28#get-an-organization-secret + def get_org_dependabot_secret(org, name) + get "#{Organization.path org}/dependabot/secrets/#{name}" + end + + # Create or update secrets + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param name [String] Name of secret + # @param options [Hash] encrypted_value and key_id + # @see https://docs.github.com/en/rest/dependabot/repository-secrets?apiVersion=2022-11-28#create-or-update-a-repository-secret + def create_or_update_dependabot_secret(repo, name, options) + put "#{Repository.path repo}/dependabot/secrets/#{name}", options + end + + # Create or update org secrets + # + # @param org [String] A GitHub organization + # @param name [String] Name of secret + # @param options [Hash] encrypted_value and key_id + # @see https://docs.github.com/en/rest/dependabot/organization-secrets?apiVersion=2022-11-28#create-or-update-an-organization-secret + def create_or_update_org_dependabot_secret(org, name, options) + put "#{Organization.path org}/dependabot/secrets/#{name}", options + end + + # Delete a secret + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param name [String] Name of secret + # @see https://docs.github.com/en/rest/dependabot/repository-secrets?apiVersion=2022-11-28#delete-a-repository-secret + def delete_dependabot_secret(repo, name) + boolean_from_response :delete, "#{Repository.path repo}/dependabot/secrets/#{name}" + end + + # Delete an org secret + # + # @param org [String] A GitHub organization + # @param name [String] Name of secret + # @see https://docs.github.com/en/rest/dependabot/organization-secrets?apiVersion=2022-11-28#delete-an-organization-secret + def delete_org_dependabot_secret(org, name) + boolean_from_response :delete, "#{Organization.path org}/dependabot/secrets/#{name}" + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/deployments.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/deployments.rb new file mode 100644 index 0000000..7302609 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/deployments.rb @@ -0,0 +1,82 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the Deployments API + # + # @see https://developer.github.com/v3/repos/commits/deployments/ + module Deployments + # Fetch a single deployment for a repository + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param deployment_id [Integer, String, Repository, Hash] A GitHub repository + # @return A single deployment + # @see https://developer.github.com/v3/repos/deployments/#get-a-single-deployment + def deployment(repo, deployment_id, options = {}) + get("#{Repository.path repo}/deployments/#{deployment_id}", options) + end + + # List all deployments for a repository + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @return [Array] A list of deployments + # @see https://developer.github.com/v3/repos/deployments/#list-deployments + def deployments(repo, options = {}) + paginate("#{Repository.path repo}/deployments", options) + end + alias list_deployments deployments + + # Create a deployment for a ref + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param ref [String] The ref to deploy + # @option options [String] :task Used by the deployment system to allow different execution paths. Defaults to "deploy". + # @option options [String] :payload Meta info about the deployment + # @option options [Boolean] :auto_merge Optional parameter to merge the default branch into the requested deployment branch if necessary. Default: true + # @option options [Array] :required_contexts Optional array of status contexts verified against commit status checks. + # @option options [String] :environment Optional name for the target deployment environment (e.g., production, staging, qa). Default: "production" + # @option options [String] :description Optional short description. + # @return [Sawyer::Resource] A deployment + # @see https://developer.github.com/v3/repos/deployments/#create-a-deployment + def create_deployment(repo, ref, options = {}) + options[:ref] = ref + post("#{Repository.path repo}/deployments", options) + end + + # Delete a Deployment + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param deployment_id [Integer, String, Repository, Hash] A GitHub repository + # @return [No Content] + # @see https://developer.github.com/v3/repos/deployments/#delete-a-deployment + def delete_deployment(repo, deployment_id, options = {}) + delete("#{Repository.path repo}/deployments/#{deployment_id}", options) + end + + # List all statuses for a Deployment + # + # @param deployment_url [String] A URL for a deployment resource + # @return [Array] A list of deployment statuses + # @see https://developer.github.com/v3/repos/deployments/#list-deployment-statuses + def deployment_statuses(deployment_url, options = {}) + deployment = get(deployment_url, accept: options[:accept]) + paginate(deployment.rels[:statuses].href, options) + end + alias list_deployment_statuses deployment_statuses + + # Create a deployment status for a Deployment + # + # @param deployment_url [String] A URL for a deployment resource + # @param state [String] The state: pending, success, failure, error + # @option options [String] :target_url The target URL to associate with this status. Default: "" + # @option options [String] :description A short description of the status. Maximum length of 140 characters. Default: "" + # @return [Sawyer::Resource] A deployment status + # @see https://developer.github.com/v3/repos/deployments/#create-a-deployment-status + def create_deployment_status(deployment_url, state, options = {}) + deployment = get(deployment_url, accept: options[:accept]) + options[:state] = state.to_s.downcase + post(deployment.rels[:statuses].href, options) + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/downloads.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/downloads.rb new file mode 100644 index 0000000..2543e6f --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/downloads.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the Repo Downloads API + # + # @see https://developer.github.com/v3/repos/downloads/ + module Downloads + # List available downloads for a repository + # + # @param repo [Integer, String, Repository, Hash] A Github Repository + # @return [Array] A list of available downloads + # @deprecated As of December 11th, 2012: https://github.com/blog/1302-goodbye-uploads + # @see https://developer.github.com/v3/repos/downloads/#list-downloads-for-a-repository + # @example List all downloads for Github/Hubot + # Octokit.downloads("github/hubot") + def downloads(repo, options = {}) + paginate "#{Repository.path repo}/downloads", options + end + alias list_downloads downloads + + # Get single download for a repository + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param id [Integer] ID of the download + # @return [Sawyer::Resource] A single download from the repository + # @deprecated As of December 11th, 2012: https://github.com/blog/1302-goodbye-uploads + # @see https://developer.github.com/v3/repos/downloads/#get-a-single-download + # @example Get the "Robawt" download from Github/Hubot + # Octokit.download("github/hubot") + def download(repo, id, options = {}) + get "#{Repository.path repo}/downloads/#{id}", options + end + + # Delete a single download for a repository + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param id [Integer] ID of the download + # @deprecated As of December 11th, 2012: https://github.com/blog/1302-goodbye-uploads + # @see https://developer.github.com/v3/repos/downloads/#delete-a-download + # @return [Boolean] Status + # @example Get the "Robawt" download from Github/Hubot + # Octokit.delete_download("github/hubot", 1234) + def delete_download(repo, id, options = {}) + boolean_from_response :delete, "#{Repository.path repo}/downloads/#{id}", options + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/emojis.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/emojis.rb new file mode 100644 index 0000000..969383f --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/emojis.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the Emojis API + module Emojis + # List all emojis used on GitHub + # + # @return [Sawyer::Resource] A list of all emojis on GitHub + # @see https://developer.github.com/v3/emojis/#emojis + # @example List all emojis + # Octokit.emojis + def emojis(options = {}) + get 'emojis', options + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/environments.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/environments.rb new file mode 100644 index 0000000..248e9d9 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/environments.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the Environments API + # + # @see https://docs.github.com/en/rest/deployments/environments + module Environments + # Fetch a single environment for a repository + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param environment_name [String] The name of the environment + # @return A single environment + # @see https://docs.github.com/en/rest/deployments/environments#get-an-environment + def environment(repo, environment_name, options = {}) + get("#{Repository.path repo}/environments/#{environment_name}", options) + end + + # Lists the environments for a repository + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @option options [Integer] :per_page The number of results per page (max 100). Default: 30 + # @option options [Integer] :page Page number of the results to fetch. Default: 1 + # @return [Sawyer::Resource] Total count of environments and list of environments + # @see https://docs.github.com/en/rest/deployments/environments#list-environments + def environments(repo, options = {}) + paginate("#{Repository.path repo}/environments", options) do |data, last_response| + data.environments.concat last_response.data.environments + data.total_count += last_response.data.total_count + end + end + alias list_environments environments + + # Create or update an environment with protection rules, such as required reviewers + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param environment_name [String] The name of the environment + # @option options [Integer] :wait_timer The amount of time to delay a job after the job is initially triggered. The time (in minutes) must be an integer between 0 and 43,200 (30 days). + # @option options [Array] :reviewers The people or teams that may review jobs that reference the environment. You can list up to six users or teams as reviewers. + # @option options [Object] :deployment_branch_policy The type of deployment branch policy for this environment. To allow all branches to deploy, set to null. + # @return [Sawyer::Resource] An environment + # @see https://docs.github.com/en/rest/deployments/environments#create-or-update-an-environment + def create_or_update_environment(repo, environment_name, options = {}) + put("#{Repository.path repo}/environments/#{environment_name}", options) + end + + # Delete an Environment + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param environment_name [String] The name of the environment + # @return [No Content] + # @see https://docs.github.com/en/rest/deployments/environments#delete-an-environment + def delete_environment(repo, environment_name, options = {}) + delete("#{Repository.path repo}/environments/#{environment_name}", options) + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/events.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/events.rb new file mode 100644 index 0000000..ceea04b --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/events.rb @@ -0,0 +1,151 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Method for the Events API + # + # @see https://developer.github.com/v3/activity/events/ + # @see https://developer.github.com/v3/issues/events/ + module Events + # List all public events for GitHub + # + # @return [Array] A list of all public events from GitHub + # @see https://developer.github.com/v3/activity/events/#list-public-events + # @example List all pubilc events + # Octokit.public_events + def public_events(options = {}) + paginate 'events', options + end + + # List all user events + # + # @param user [Integer, String] GitHub user login or id. + # @return [Array] A list of all user events + # @see https://developer.github.com/v3/activity/events/#list-events-performed-by-a-user + # @example List all user events + # Octokit.user_events("sferik") + def user_events(user, options = {}) + paginate "#{User.path user}/events", options + end + + # List public user events + # + # @param user [Integer, String] GitHub user login or id + # @return [Array] A list of public user events + # @see https://developer.github.com/v3/activity/events/#list-public-events-performed-by-a-user + # @example List public user events + # Octokit.user_events("sferik") + def user_public_events(user, options = {}) + paginate "#{User.path user}/events/public", options + end + + # List events that a user has received + # + # @param user [Integer, String] GitHub user login or id + # @return [Array] A list of all user received events + # @see https://developer.github.com/v3/activity/events/#list-events-that-a-user-has-received + # @example List all user received events + # Octokit.received_events("sferik") + def received_events(user, options = {}) + paginate "#{User.path user}/received_events", options + end + + # List public events a user has received + # + # @param user [Integer, String] GitHub user login or id + # @return [Array] A list of public user received events + # @see https://developer.github.com/v3/activity/events/#list-public-events-that-a-user-has-received + # @example List public user received events + # Octokit.received_public_events("sferik") + def received_public_events(user, options = {}) + paginate "#{User.path user}/received_events/public", options + end + + # List events for a repository + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @return [Array] A list of events for a repository + # @see https://developer.github.com/v3/activity/events/#list-repository-events + # @example List events for a repository + # Octokit.repository_events("sferik/rails_admin") + def repository_events(repo, options = {}) + paginate "#{Repository.path repo}/events", options + end + + # List public events for a repository's network + # + # @param repo [String, Repository, Hash] A GitHub repository + # @return [Array] A list of events for a repository's network + # @see https://developer.github.com/v3/activity/events/#list-public-events-for-a-network-of-repositories + # @example List events for a repository's network + # Octokit.repository_network_events("sferik/rails_admin") + def repository_network_events(repo, options = {}) + paginate "networks/#{Repository.new(repo)}/events", options + end + + # List all events for an organization + # + # Requires authenticated client. + # + # @param org [String] Organization GitHub handle + # @return [Array] List of all events from a GitHub organization + # @see https://developer.github.com/v3/activity/events/#list-events-for-an-organization + # @example List events for the lostisland organization + # @client.organization_events("lostisland") + def organization_events(org, options = {}) + paginate "users/#{login}/events/orgs/#{org}", options + end + + # List an organization's public events + # + # @param org [String, Integer] Organization GitHub login or id. + # @return [Array] List of public events from a GitHub organization + # @see https://developer.github.com/v3/activity/events/#list-public-events-for-an-organization + # @example List public events for GitHub + # Octokit.organization_public_events("GitHub") + def organization_public_events(org, options = {}) + paginate "#{Organization.path org}/events", options + end + + # Get all Issue Events for a given Repository + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # + # @return [Array] Array of all Issue Events for this Repository + # @see https://developer.github.com/v3/issues/events/#list-events-for-a-repository + # @see https://developer.github.com/v3/activity/events/#list-issue-events-for-a-repository + # @example Get all Issue Events for Octokit + # Octokit.repository_issue_events("octokit/octokit.rb") + def repository_issue_events(repo, options = {}) + paginate "#{Repository.path repo}/issues/events", options + end + alias repo_issue_events repository_issue_events + + # List events for an Issue + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param number [Integer] Issue number + # + # @return [Array] Array of events for that issue + # @see https://developer.github.com/v3/issues/events/#list-events-for-an-issue + # @example List all issues events for issue #38 on octokit/octokit.rb + # Octokit.issue_events("octokit/octokit.rb", 38) + def issue_events(repo, number, options = {}) + paginate "#{Repository.path repo}/issues/#{number}/events", options + end + + # Get information on a single Issue Event + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param number [Integer] Event number + # + # @return [Sawyer::Resource] A single Event for an Issue + # @see https://developer.github.com/v3/issues/events/#get-a-single-event + # @example Get Event information for ID 3094334 (a pull request was closed) + # Octokit.issue_event("octokit/octokit.rb", 3094334) + def issue_event(repo, number, options = {}) + paginate "#{Repository.path repo}/issues/events/#{number}", options + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/feeds.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/feeds.rb new file mode 100644 index 0000000..a689a63 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/feeds.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the Feeds API + # + # @see https://developer.github.com/v3/activity/feeds/ + module Feeds + # List Feeds + # + # The feeds returned depend on authentication, see the GitHub API docs + # for more information. + # + # @return [Array] list of feeds + # @see https://developer.github.com/v3/activity/feeds/#list-feeds + def feeds + get 'feeds' + end + + # Get a Feed by name + # + # @param name [Symbol, String] Name of feed to retrieve. + # @return [Feed] Parsed feed in the format returned by the configured + # parser. + def feed(name, options = {}) + if rel = feeds._links[name] + get rel.href, accept: rel.type, options: options + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/gists.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/gists.rb new file mode 100644 index 0000000..34b7921 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/gists.rb @@ -0,0 +1,234 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the Gists API + # + # @see https://developer.github.com/v3/gists/ + module Gists + # List gists for a user or all public gists + # + # @param user [String] An optional user to filter listing + # @return [Array] A list of gists + # @example Fetch all gists for defunkt + # Octokit.gists('defunkt') + # @example Fetch all public gists + # Octokit.gists + # @see https://developer.github.com/v3/gists/#list-gists + def gists(user = nil, options = {}) + if user.nil? + paginate 'gists', options + else + paginate "#{User.path user}/gists", options + end + end + alias list_gists gists + + # List public gists + # + # @return [Array] A list of gists + # @example Fetch all public gists + # Octokit.public_gists + # @see https://developer.github.com/v3/gists/#list-gists + def public_gists(options = {}) + paginate 'gists/public', options + end + + # List the authenticated user’s starred gists + # + # @return [Array] A list of gists + # @see https://developer.github.com/v3/gists/#list-gists + def starred_gists(options = {}) + paginate 'gists/starred', options + end + + # Get a single gist + # + # @param gist [String] ID of gist to fetch + # @option options [String] :sha Specific gist revision SHA + # @return [Sawyer::Resource] Gist information + # @see https://developer.github.com/v3/gists/#get-a-single-gist + # @see https://developer.github.com/v3/gists/#get-a-specific-revision-of-a-gist + def gist(gist, options = {}) + options = options.dup + if sha = options.delete(:sha) + get "gists/#{Gist.new(gist)}/#{sha}", options + else + get "gists/#{Gist.new(gist)}", options + end + end + + # Create a gist + # + # @param options [Hash] Gist information. + # @option options [String] :description + # @option options [Boolean] :public Sets gist visibility + # @option options [Array] :files Files that make up this gist. Keys + # should be the filename, the value a Hash with a :content key with text + # content of the Gist. + # @return [Sawyer::Resource] Newly created gist info + # @see https://developer.github.com/v3/gists/#create-a-gist + def create_gist(options = {}) + post 'gists', options + end + + # Edit a gist + # + # @param options [Hash] Gist information. + # @option options [String] :description + # @option options [Hash] :files Files that make up this gist. Keys + # should be the filename, the value a Hash with a :content key with text + # content of the Gist. + # + # NOTE: All files from the previous version of the + # gist are carried over by default if not included in the hash. Deletes + # can be performed by including the filename with a null hash. + # @return + # [Sawyer::Resource] Newly created gist info + # @see https://developer.github.com/v3/gists/#edit-a-gist + # @example Update a gist + # @client.edit_gist('some_id', { + # :files => {"boo.md" => {"content" => "updated stuff"}} + # }) + def edit_gist(gist, options = {}) + patch "gists/#{Gist.new(gist)}", options + end + + # List gist commits + # + # @param gist [String] Gist ID + # @return [Array] List of commits to the gist + # @see https://developer.github.com/v3/gists/#list-gist-commits + # @example List commits for a gist + # @client.gist_commits('some_id') + def gist_commits(gist, options = {}) + paginate "gists/#{Gist.new(gist)}/commits", options + end + + # + # Star a gist + # + # @param gist [String] Gist ID + # @return [Boolean] Indicates if gist is starred successfully + # @see https://developer.github.com/v3/gists/#star-a-gist + def star_gist(gist, options = {}) + boolean_from_response :put, "gists/#{Gist.new(gist)}/star", options + end + + # Unstar a gist + # + # @param gist [String] Gist ID + # @return [Boolean] Indicates if gist is unstarred successfully + # @see https://developer.github.com/v3/gists/#unstar-a-gist + def unstar_gist(gist, options = {}) + boolean_from_response :delete, "gists/#{Gist.new(gist)}/star", options + end + + # Check if a gist is starred + # + # @param gist [String] Gist ID + # @return [Boolean] Indicates if gist is starred + # @see https://developer.github.com/v3/gists/#check-if-a-gist-is-starred + def gist_starred?(gist, options = {}) + boolean_from_response :get, "gists/#{Gist.new(gist)}/star", options + end + + # Fork a gist + # + # @param gist [String] Gist ID + # @return [Sawyer::Resource] Data for the new gist + # @see https://developer.github.com/v3/gists/#fork-a-gist + def fork_gist(gist, options = {}) + post "gists/#{Gist.new(gist)}/forks", options + end + + # List gist forks + # + # @param gist [String] Gist ID + # @return [Array] List of gist forks + # @see https://developer.github.com/v3/gists/#list-gist-forks + # @example List gist forks + # @client.gist_forks('some-id') + def gist_forks(gist, options = {}) + paginate "gists/#{Gist.new(gist)}/forks", options + end + + # Delete a gist + # + # @param gist [String] Gist ID + # @return [Boolean] Indicating success of deletion + # @see https://developer.github.com/v3/gists/#delete-a-gist + def delete_gist(gist, options = {}) + boolean_from_response :delete, "gists/#{Gist.new(gist)}", options + end + + # List gist comments + # + # @param gist_id [String] Gist Id. + # @return [Array] Array of hashes representing comments. + # @see https://developer.github.com/v3/gists/comments/#list-comments-on-a-gist + # @example + # Octokit.gist_comments('3528ae645') + def gist_comments(gist_id, options = {}) + paginate "gists/#{gist_id}/comments", options + end + + # Get gist comment + # + # @param gist_id [String] Id of the gist. + # @param gist_comment_id [Integer] Id of the gist comment. + # @return [Sawyer::Resource] Hash representing gist comment. + # @see https://developer.github.com/v3/gists/comments/#get-a-single-comment + # @example + # Octokit.gist_comment('208sdaz3', 1451398) + def gist_comment(gist_id, gist_comment_id, options = {}) + get "gists/#{gist_id}/comments/#{gist_comment_id}", options + end + + # Create gist comment + # + # Requires authenticated client. + # + # @param gist_id [String] Id of the gist. + # @param comment [String] Comment contents. + # @return [Sawyer::Resource] Hash representing the new comment. + # @see https://developer.github.com/v3/gists/comments/#create-a-comment + # @example + # @client.create_gist_comment('3528645', 'This is very helpful.') + def create_gist_comment(gist_id, comment, options = {}) + options = options.merge({ body: comment }) + post "gists/#{gist_id}/comments", options + end + + # Update gist comment + # + # Requires authenticated client + # + # @param gist_id [String] Id of the gist. + # @param gist_comment_id [Integer] Id of the gist comment to update. + # @param comment [String] Updated comment contents. + # @return [Sawyer::Resource] Hash representing the updated comment. + # @see https://developer.github.com/v3/gists/comments/#edit-a-comment + # @example + # @client.update_gist_comment('208sdaz3', '3528645', ':heart:') + def update_gist_comment(gist_id, gist_comment_id, comment, options = {}) + options = options.merge({ body: comment }) + patch "gists/#{gist_id}/comments/#{gist_comment_id}", options + end + + # Delete gist comment + # + # Requires authenticated client. + # + # @param gist_id [String] Id of the gist. + # @param gist_comment_id [Integer] Id of the gist comment to delete. + # @return [Boolean] True if comment deleted, false otherwise. + # @see https://developer.github.com/v3/gists/comments/#delete-a-comment + # @example + # @client.delete_gist_comment('208sdaz3', '586399') + def delete_gist_comment(gist_id, gist_comment_id, options = {}) + boolean_from_response(:delete, "gists/#{gist_id}/comments/#{gist_comment_id}", options) + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/gitignore.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/gitignore.rb new file mode 100644 index 0000000..9436f9d --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/gitignore.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the Gitignore API + # + # @see https://developer.github.com/v3/gitignore/ + module Gitignore + # Listing available gitignore templates. + # + # These templates can be passed option when creating a repository. + # + # @see https://developer.github.com/v3/gitignore/#listing-available-templates + # + # @return [Array] List of templates. + # + # @example Git all the gitignore templates + # @client.gitignore_templates + def gitignore_templates(options = {}) + get 'gitignore/templates', options + end + + # Get a gitignore template. + # + # Use the raw {http://developer.github.com/v3/media/ media type} to get + # the raw contents. + # + # @param template_name [String] Name of the template. Template names are + # case sensitive, make sure to use a valid name from the + # .gitignore_templates list. + # + # @see https://developer.github.com/v3/gitignore/#get-a-single-template + # + # @return [Sawyer::Resource] Gitignore template + # + # @example Get the Ruby gitignore template + # @client.gitignore_template('Ruby') + def gitignore_template(template_name, options = {}) + get "gitignore/templates/#{template_name}", options + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/hooks.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/hooks.rb new file mode 100644 index 0000000..49f0bd8 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/hooks.rb @@ -0,0 +1,287 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the Hooks API + module Hooks + # List repo hooks + # + # Requires authenticated client. + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @return [Array] Array of hashes representing hooks. + # @see https://developer.github.com/v3/repos/hooks/#list-hooks + # @example + # @client.hooks('octokit/octokit.rb') + def hooks(repo, options = {}) + paginate "#{Repository.path repo}/hooks", options + end + + # Get single hook + # + # Requires authenticated client. + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @param id [Integer] Id of the hook to get. + # @return [Sawyer::Resource] Hash representing hook. + # @see https://developer.github.com/v3/repos/hooks/#get-single-hook + # @example + # @client.hook('octokit/octokit.rb', 100000) + def hook(repo, id, options = {}) + get "#{Repository.path repo}/hooks/#{id}", options + end + + # Create a hook + # + # Requires authenticated client. + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @param name [String] The name of the service that is being called. See + # {https://api.github.com/hooks Hooks} for the possible names. + # @param config [Hash] A Hash containing key/value pairs to provide + # settings for this hook. These settings vary between the services and + # are defined in the {https://github.com/github/github-services github-services} repo. + # @option options [Array] :events ('["push"]') Determines what + # events the hook is triggered for. + # @option options [Boolean] :active Determines whether the hook is + # actually triggered on pushes. + # @return [Sawyer::Resource] Hook info for the new hook + # @see https://api.github.com/hooks + # @see https://github.com/github/github-services + # @see https://developer.github.com/v3/repos/hooks/#create-a-hook + # @example + # @client.create_hook( + # 'octokit/octokit.rb', + # 'web', + # { + # :url => 'http://something.com/webhook', + # :content_type => 'json' + # }, + # { + # :events => ['push', 'pull_request'], + # :active => true + # } + # ) + def create_hook(repo, name, config, options = {}) + options = { name: name, config: config, events: ['push'], active: true }.merge(options) + post "#{Repository.path repo}/hooks", options + end + + # Edit a hook + # + # Requires authenticated client. + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @param id [Integer] Id of the hook being updated. + # @param name [String] The name of the service that is being called. See + # {https://api.github.com/hooks Hooks} for the possible names. + # @param config [Hash] A Hash containing key/value pairs to provide + # settings for this hook. These settings vary between the services and + # are defined in the {https://github.com/github/github-services github-services} repo. + # @option options [Array] :events ('["push"]') Determines what + # events the hook is triggered for. + # @option options [Array] :add_events Determines a list of events + # to be added to the list of events that the Hook triggers for. + # @option options [Array] :remove_events Determines a list of events + # to be removed from the list of events that the Hook triggers for. + # @option options [Boolean] :active Determines whether the hook is + # actually triggered on pushes. + # @return [Sawyer::Resource] Hook info for the updated hook + # @see https://api.github.com/hooks + # @see https://github.com/github/github-services + # @see https://developer.github.com/v3/repos/hooks/#edit-a-hook + # @example + # @client.edit_hook( + # 'octokit/octokit.rb', + # 100000, + # 'web', + # { + # :url => 'http://something.com/webhook', + # :content_type => 'json' + # }, + # { + # :add_events => ['status'], + # :remove_events => ['pull_request'], + # :active => true + # } + # ) + def edit_hook(repo, id, name, config, options = {}) + options = { name: name, config: config }.merge(options) + patch "#{Repository.path repo}/hooks/#{id}", options + end + + # Delete hook + # + # Requires authenticated client. + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @param id [Integer] Id of the hook to remove. + # @return [Boolean] True if hook removed, false otherwise. + # @see https://developer.github.com/v3/repos/hooks/#delete-a-hook + # @example + # @client.remove_hook('octokit/octokit.rb', 1000000) + def remove_hook(repo, id, options = {}) + boolean_from_response :delete, "#{Repository.path repo}/hooks/#{id}", options + end + + # Test hook + # + # Requires authenticated client. + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @param id [Integer] Id of the hook to test. + # @return [Boolean] Success + # @see https://developer.github.com/v3/repos/hooks/#test-a-push-hook + # @example + # @client.test_hook('octokit/octokit.rb', 1000000) + def test_hook(repo, id, options = {}) + boolean_from_response :post, "#{Repository.path repo}/hooks/#{id}/tests", options + end + + # Ping hook + # + # Requires authenticated client. + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @param id [Integer] Id of the hook to send a ping. + # @return [Boolean] Ping requested? + # @see https://developer.github.com/v3/repos/hooks/#ping-a-hook + # @example + # @client.ping_hook('octokit/octokit.rb', 1000000) + def ping_hook(repo, id, options = {}) + boolean_from_response :post, "#{Repository.path repo}/hooks/#{id}/pings", options + end + + # List org hooks + # + # Requires client authenticated as admin for the org. + # + # @param org [String, Integer] Organization GitHub login or id. + # @return [Array] Array of hashes representing hooks. + # @see https://developer.github.com/v3/orgs/hooks/#list-hooks + # @example + # @client.org_hooks('octokit') + def org_hooks(org, options = {}) + paginate "#{Organization.path org}/hooks", options + end + alias list_org_hooks org_hooks + + # Get an org hook + # + # Requires client authenticated as admin for the org. + # + # @param org [String, Integer] Organization GitHub login or id. + # @param id [Integer] Id of the hook to get. + # @return [Sawyer::Resource] Hash representing hook. + # @see https://developer.github.com/v3/orgs/hooks/#get-single-hook + # @example + # @client.org_hook('octokit', 123) + def org_hook(org, id, options = {}) + get "#{Organization.path org}/hooks/#{id}", options + end + + # Create an org hook + # + # Requires client authenticated as admin for the org. + # + # @param org [String, Integer] Organization GitHub login or id. + # @param config [Hash] A Hash containing key/value pairs to provide + # settings for this hook. + # @option options [Array] :events ('["push"]') Determines what + # events the hook is triggered for. + # @option options [Boolean] :active Determines whether the hook is + # actually triggered on pushes. + # @return [Sawyer::Resource] Hook info for the new hook + # @see https://api.github.com/hooks + # @see https://developer.github.com/v3/orgs/hooks/#create-a-hook + # @example + # @client.create_org_hook( + # 'octokit', + # { + # :url => 'http://something.com/webhook', + # :content_type => 'json' + # }, + # { + # :events => ['push', 'pull_request'], + # :active => true + # } + # ) + def create_org_hook(org, config, options = {}) + options = { name: 'web', config: config }.merge(options) + post "#{Organization.path org}/hooks", options + end + + # Update an org hook + # + # Requires client authenticated as admin for the org. + # + # @param org [String, Integer] Organization GitHub login or id. + # @param id [Integer] Id of the hook to update. + # @param config [Hash] A Hash containing key/value pairs to provide + # settings for this hook. + # @option options [Array] :events ('["push"]') Determines what + # events the hook is triggered for. + # @option options [Boolean] :active Determines whether the hook is + # actually triggered on pushes. + # @return [Sawyer::Resource] Hook info for the new hook + # @see https://api.github.com/hooks + # @see https://developer.github.com/v3/orgs/hooks/#edit-a-hook + # @example + # @client.edit_org_hook( + # 'octokit', + # 123, + # { + # :url => 'http://something.com/webhook', + # :content_type => 'json' + # }, + # { + # :events => ['push', 'pull_request'], + # :active => true + # } + # ) + def edit_org_hook(org, id, config, options = {}) + options = { config: config }.merge(options) + patch "#{Organization.path org}/hooks/#{id}", options + end + alias update_org_hook edit_org_hook + + # Ping org hook + # + # Requires client authenticated as admin for the org. + # + # @param org [String, Integer] Organization GitHub login or id. + # @param id [Integer] Id of the hook to update. + # @return [Boolean] Success + # @see https://developer.github.com/v3/orgs/hooks/#ping-a-hook + # @example + # @client.ping_org_hook('octokit', 1000000) + def ping_org_hook(org, id, options = {}) + boolean_from_response :post, "#{Organization.path org}/hooks/#{id}/pings", options + end + + # Remove org hook + # + # Requires client authenticated as admin for the org. + # + # @param org [String, Integer] Organization GitHub login or id. + # @param id [Integer] Id of the hook to update. + # @return [Boolean] True if hook removed, false otherwise. + # @see https://developer.github.com/v3/orgs/hooks/#delete-a-hook + # @example + # @client.remove_org_hook('octokit', 1000000) + def remove_org_hook(org, id, options = {}) + boolean_from_response :delete, "#{Organization.path org}/hooks/#{id}", options + end + + # Parse payload string + # + # @param payload_string [String] The payload + # @return [Sawyer::Resource] The payload object + # @see https://developer.github.com/v3/activity/events/types/ + def parse_payload(payload_string) + payload_hash = agent.class.decode payload_string + Sawyer::Resource.new agent, payload_hash + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/issues.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/issues.rb new file mode 100644 index 0000000..7e5ecc7 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/issues.rb @@ -0,0 +1,367 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the Issues API + # + # @see https://developer.github.com/v3/issues/ + module Issues + # List issues for the authenticated user or repository + # + # @param repository [Integer, String, Repository, Hash] A GitHub repository. + # @param options [Sawyer::Resource] A customizable set of options. + # @option options [Integer] :milestone Milestone number. + # @option options [String] :state (open) State: open, closed, or all. + # @option options [String] :assignee User login. + # @option options [String] :creator User login. + # @option options [String] :mentioned User login. + # @option options [String] :labels List of comma separated Label names. Example: bug,ui,@high. + # @option options [String] :sort (created) Sort: created, updated, or comments. + # @option options [String] :direction (desc) Direction: asc or desc. + # @option options [Integer] :page (1) Page number. + # @return [Array] A list of issues for a repository. + # @see https://developer.github.com/v3/issues/#list-issues-for-a-repository + # @see https://developer.github.com/v3/issues/#list-issues + # @example List issues for a repository + # Octokit.list_issues("sferik/rails_admin") + # @example List issues for the authenticated user across repositories + # @client = Octokit::Client.new(:login => 'foo', :password => 'bar') + # @client.list_issues + def list_issues(repository = nil, options = {}) + path = repository ? "#{Repository.new(repository).path}/issues" : 'issues' + paginate path, options + end + alias issues list_issues + + # List all issues across owned and member repositories for the authenticated user + # + # @param options [Sawyer::Resource] A customizable set of options. + # @option options [String] :filter (assigned) State: assigned, created, mentioned, subscribed or closed. + # @option options [String] :state (open) State: open, closed, or all. + # @option options [Array] :labels List of Label names. Example: ['bug', 'ui', '@high']. + # @option options [String] :sort (created) Sort: created, updated, or comments. + # @option options [String] :direction (desc) Direction: asc or desc. + # @option options [Integer] :page (1) Page number. + # @option options [String] :since Timestamp in ISO 8601 + # format: YYYY-MM-DDTHH:MM:SSZ + # @return [Array] A list of issues for a repository. + # @see https://developer.github.com/v3/issues/#list-issues + # @example List issues for the authenticated user across owned and member repositories + # @client = Octokit::Client.new(:login => 'foo', :password => 'bar') + # @client.user_issues + def user_issues(options = {}) + paginate 'user/issues', options + end + + # List all issues for a given organization for the authenticated user + # + # @param org [String, Integer] Organization GitHub login or id. + # @param options [Sawyer::Resource] A customizable set of options. + # @option options [String] :filter (assigned) State: assigned, created, mentioned, subscribed or closed. + # @option options [String] :state (open) State: open, closed, or all. + # @option options [Array] :labels List of Label names. Example: ['bug', 'ui', '@high']. + # @option options [String] :sort (created) Sort: created, updated, or comments. + # @option options [String] :direction (desc) Direction: asc or desc. + # @option options [Integer] :page (1) Page number. + # @option options [String] :since Timestamp in ISO 8601 + # format: YYYY-MM-DDTHH:MM:SSZ + # @return [Array] A list of issues. + # @see https://developer.github.com/v3/issues/#list-issues + # @example List all issues for a given organization for the authenticated user + # @client = Octokit::Client.new(:login => 'foo', :password => 'bar') + # @client.org_issues("octokit") + def org_issues(org, options = {}) + paginate "#{Organization.path org}/issues", options + end + + # Create an issue for a repository + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param title [String] A descriptive title + # @param body [String] An optional concise description + # @param options [Hash] A customizable set of options. + # @option options [String] :assignee User login. + # @option options [Array] :assignees User login. + # @option options [Integer] :milestone Milestone number. + # @option options [String] :labels List of comma separated Label names. Example: bug,ui,@high. + # @return [Sawyer::Resource] Your newly created issue + # @see https://developer.github.com/v3/issues/#create-an-issue + # @example Create a new Issues for a repository + # Octokit.create_issue("sferik/rails_admin", 'Updated Docs', 'Added some extra links') + def create_issue(repo, title, body = nil, options = {}) + options[:labels] = case options[:labels] + when String + options[:labels].split(',').map(&:strip) + when Array + options[:labels] + else + [] + end + parameters = { title: title } + parameters[:body] = body unless body.nil? + post "#{Repository.path repo}/issues", options.merge(parameters) + end + alias open_issue create_issue + + # Get a single issue from a repository + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param number [Integer] Number ID of the issue + # @return [Sawyer::Resource] The issue you requested, if it exists + # @see https://developer.github.com/v3/issues/#get-a-single-issue + # @example Get issue #25 from octokit/octokit.rb + # Octokit.issue("octokit/octokit.rb", "25") + def issue(repo, number, options = {}) + get "#{Repository.path repo}/issues/#{number}", options + end + + # Close an issue + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param number [Integer] Number ID of the issue + # @param options [Hash] A customizable set of options. + # @option options [String] :assignee User login. + # @option options [Array] :assignees User login. + # @option options [Integer] :milestone Milestone number. + # @option options [Array] :labels List of Label names. Example: ['bug', 'ui', '@high']. + # @return [Sawyer::Resource] The updated Issue + # @see https://developer.github.com/v3/issues/#edit-an-issue + # @example Close Issue #25 from octokit/octokit.rb + # Octokit.close_issue("octokit/octokit.rb", "25") + def close_issue(repo, number, options = {}) + patch "#{Repository.path repo}/issues/#{number}", options.merge({ state: 'closed' }) + end + + # Reopen an issue + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param number [Integer] Number ID of the issue + # @param options [Hash] A customizable set of options. + # @option options [String] :assignee User login. + # @option options [Array] :assignees User login. + # @option options [Integer] :milestone Milestone number. + # @option options [Array] :labels List of Label names. Example: ['bug', 'ui', '@high']. + # @return [Sawyer::Resource] The updated Issue + # @see https://developer.github.com/v3/issues/#edit-an-issue + # @example Reopen Issue #25 from octokit/octokit.rb + # Octokit.reopen_issue("octokit/octokit.rb", "25") + def reopen_issue(repo, number, options = {}) + patch "#{Repository.path repo}/issues/#{number}", options.merge({ state: 'open' }) + end + + # Lock an issue's conversation, limiting it to collaborators + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param number [Integer] Number ID of the issue + # @return [Boolean] Success + # @see https://developer.github.com/v3/issues/#lock-an-issue + # @example Lock Issue #25 from octokit/octokit.rb + # Octokit.lock_issue("octokit/octokit.rb", "25") + def lock_issue(repo, number, options = {}) + boolean_from_response :put, "#{Repository.path repo}/issues/#{number}/lock", options + end + + # Unlock an issue's conversation, opening it to all viewers + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param number [Integer] Number ID of the issue + # @return [Boolean] Success + # @see https://developer.github.com/v3/issues/#unlock-an-issue + # @example Unlock Issue #25 from octokit/octokit.rb + # Octokit.close_issue("octokit/octokit.rb", "25") + def unlock_issue(repo, number, options = {}) + boolean_from_response :delete, "#{Repository.path repo}/issues/#{number}/lock", options + end + + # Update an issue + # + # @overload update_issue(repo, number, title, body, options) + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param number [Integer] Number ID of the issue + # @param title [String] Updated title for the issue + # @param body [String] Updated body of the issue + # @param options [Hash] A customizable set of options. + # @option options [String] :assignee User login. + # @option options [Array] :assignees User login. + # @option options [Integer] :milestone Milestone number. + # @option options [String] :labels List of comma separated Label names. Example: bug,ui,@high. + # @option options [String] :state State of the issue. open or closed + # + # @overload update_issue(repo, number, options) + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param number [Integer] Number ID of the issue + # @param options [Hash] A customizable set of options. + # @option options [String] :title Updated title for the issue + # @option options [String] :body Updated body of the issue + # @option options [String] :assignee User login. + # @option options [Array] :assignees User login. + # @option options [Integer] :milestone Milestone number. + # @option options [Array] :labels List of Label names. Example: ['bug', 'ui', '@high']. + # @option options [String] :state State of the issue. open or closed + # @return [Sawyer::Resource] The updated Issue + # @see https://developer.github.com/v3/issues/#edit-an-issue + # + # @example Change the title of Issue #25 + # Octokit.update_issue("octokit/octokit.rb", "25", "A new title", "the same body") + # + # @example Change only the assignee of Issue #25 + # Octokit.update_issue("octokit/octokit.rb", "25", :assignee => "pengwynn") + def update_issue(repo, number, *args) + arguments = Arguments.new(args) + opts = arguments.options + + unless arguments.empty? + opts[:title] = arguments.shift + opts[:body] = arguments.shift + end + + patch "#{Repository.path repo}/issues/#{number}", opts + end + + # Get all comments attached to issues for the repository + # + # By default, Issue Comments are ordered by ascending ID. + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param options [Hash] Optional parameters + # @option options [String] :sort created or updated + # @option options [String] :direction asc or desc. Ignored without sort + # parameter. + # @option options [String] :since Timestamp in ISO 8601 + # format: YYYY-MM-DDTHH:MM:SSZ + # + # @return [Array] List of issues comments. + # + # @see https://developer.github.com/v3/issues/comments/#list-comments-in-a-repository + # + # @example Get the comments for issues in the octokit repository + # @client.issues_comments("octokit/octokit.rb") + # + # @example Get issues comments, sort by updated descending since a time + # @client.issues_comments("octokit/octokit.rb", { + # :sort => 'desc', + # :direction => 'asc', + # :since => '2010-05-04T23:45:02Z' + # }) + def issues_comments(repo, options = {}) + paginate "#{Repository.path repo}/issues/comments", options + end + + # Get all comments attached to an issue + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param number [Integer] Number ID of the issue + # @return [Array] Array of comments that belong to an issue + # @see https://developer.github.com/v3/issues/comments/#list-comments-on-an-issue + # @example Get comments for issue #25 from octokit/octokit.rb + # Octokit.issue_comments("octokit/octokit.rb", "25") + def issue_comments(repo, number, options = {}) + paginate "#{Repository.path repo}/issues/#{number}/comments", options + end + + # Get a single comment attached to an issue + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param number [Integer] Number ID of the comment + # @return [Sawyer::Resource] The specific comment in question + # @see https://developer.github.com/v3/issues/comments/#get-a-single-comment + # @example Get comment #1194549 from an issue on octokit/octokit.rb + # Octokit.issue_comment("octokit/octokit.rb", 1194549) + def issue_comment(repo, number, options = {}) + paginate "#{Repository.path repo}/issues/comments/#{number}", options + end + + # Add a comment to an issue + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param number [Integer] Issue number + # @param comment [String] Comment to be added + # @return [Sawyer::Resource] Comment + # @see https://developer.github.com/v3/issues/comments/#create-a-comment + # @example Add the comment "Almost to v1" to Issue #23 on octokit/octokit.rb + # Octokit.add_comment("octokit/octokit.rb", 23, "Almost to v1") + def add_comment(repo, number, comment, options = {}) + post "#{Repository.path repo}/issues/#{number}/comments", options.merge({ body: comment }) + end + + # Update a single comment on an issue + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param number [Integer] Comment number + # @param comment [String] Body of the comment which will replace the existing body. + # @return [Sawyer::Resource] Comment + # @see https://developer.github.com/v3/issues/comments/#edit-a-comment + # @example Update the comment #1194549 with body "I've started this on my 25-issue-comments-v3 fork" on an issue on octokit/octokit.rb + # Octokit.update_comment("octokit/octokit.rb", 1194549, "Almost to v1, added this on my fork") + def update_comment(repo, number, comment, options = {}) + patch "#{Repository.path repo}/issues/comments/#{number}", options.merge({ body: comment }) + end + + # Delete a single comment + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param number [Integer] Comment number + # @return [Boolean] Success + # @see https://developer.github.com/v3/issues/comments/#delete-a-comment + # @example Delete the comment #1194549 on an issue on octokit/octokit.rb + # Octokit.delete_comment("octokit/octokit.rb", 1194549) + def delete_comment(repo, number, options = {}) + boolean_from_response :delete, "#{Repository.path repo}/issues/comments/#{number}", options + end + + # Get the timeline for an issue + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param number [Integer] Number ID of the comment + # @return [Sawyer::Resource] The timeline for this issue + # @see https://developer.github.com/v3/issues/timeline/ + # @example Get timeline for issue #1435 on octokit/octokit.rb + # Octokit.issue_timeline("octokit/octokit.rb", 1435) + def issue_timeline(repo, number, options = {}) + paginate "#{Repository.path repo}/issues/#{number}/timeline", options + end + + # Lists the available assignees for issues in a repository. + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @return [Array] List of GitHub users. + # @see https://developer.github.com/v3/issues/assignees/#list-assignees + # @example Get available assignees on repository octokit/octokit.rb + # Octokit.list_assignees("octokit/octokit.rb") + def list_assignees(repo, options = {}) + paginate "#{Repository.path repo}/assignees", options + end + + # Add assignees to an issue + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param number [Integer] Issue number + # @param assignees [Array] Assignees to be added + # @return [Sawyer::Resource] Issue + # @see https://developer.github.com/v3/issues/assignees/#add-assignees-to-an-issue + # @example Add assignees "pengwynn" and "joeyw" to Issue #23 on octokit/octokit.rb + # Octokit.add_assignees("octokit/octokit.rb", 23, ["pengwynn", "joeyw"]) + def add_assignees(repo, number, assignees, options = {}) + post "#{Repository.path repo}/issues/#{number}/assignees", options.merge({ assignees: assignees }) + end + + # Remove assignees from an issue + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param number [Integer] Issue number + # @param assignees [Array] Assignees to be removed + # @param options [Hash] Header params for request + # @return [Sawyer::Resource] Issue + # @see https://developer.github.com/v3/issues/assignees/#remove-assignees-from-an-issue + # @example Remove assignees "pengwynn" and "joeyw" from Issue #23 on octokit/octokit.rb + # Octokit.remove_assignees("octokit/octokit.rb", 23, ["pengwynn", "joeyw"]) + # + # @example Remove assignees "pengwynn" from Issue #23 on octokit/octokit.rb + # Octokit.remove_assignees("octokit/octokit.rb", 23, ["pengwynn"], + # :accept => "application/vnd.github.v3+json") + def remove_assignees(repo, number, assignees, options = {}) + delete "#{Repository.path repo}/issues/#{number}/assignees", options.merge({ assignees: assignees }) + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/labels.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/labels.rb new file mode 100644 index 0000000..99a9bbd --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/labels.rb @@ -0,0 +1,156 @@ +# frozen_string_literal: true + +require 'erb' + +module Octokit + class Client + # Methods for the Issue Labels API + # + # @see https://developer.github.com/v3/issues/labels/ + module Labels + # List available labels for a repository + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @return [Array] A list of the labels across the repository + # @see https://developer.github.com/v3/issues/labels/#list-all-labels-for-this-repository + # @example List labels for octokit/octokit.rb + # Octokit.labels("octokit/octokit.rb") + def labels(repo, options = {}) + paginate "#{Repository.path repo}/labels", options + end + + # Get single label for a repository + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param name [String] Name of the label + # @return [Sawyer::Resource] A single label from the repository + # @see https://developer.github.com/v3/issues/labels/#get-a-single-label + # @example Get the "V3 Addition" label from octokit/octokit.rb + # Octokit.label("octokit/octokit.rb", "V3 Addition") + def label(repo, name, options = {}) + get "#{Repository.path repo}/labels/#{ERB::Util.url_encode(name)}", options + end + + # Add a label to a repository + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param label [String] A new label + # @param color [String] A color, in hex, without the leading # + # @return [Sawyer::Resource] The new label + # @see https://developer.github.com/v3/issues/labels/#create-a-label + # @example Add a new label "Version 1.0" with color "#cccccc" + # Octokit.add_label("octokit/octokit.rb", "Version 1.0", "cccccc") + def add_label(repo, label, color = 'ffffff', options = {}) + post "#{Repository.path repo}/labels", options.merge({ name: label, color: color }) + end + + # Update a label + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param label [String] The name of the label which will be updated + # @param options [Hash] A customizable set of options. + # @option options [String] :name An updated label name + # @option options [String] :color An updated color value, in hex, without leading # + # @return [Sawyer::Resource] The updated label + # @see https://developer.github.com/v3/issues/labels/#update-a-label + # @example Update the label "Version 1.0" with new color "#cceeaa" + # Octokit.update_label("octokit/octokit.rb", "Version 1.0", {:color => "cceeaa"}) + def update_label(repo, label, options = {}) + patch "#{Repository.path repo}/labels/#{ERB::Util.url_encode(label)}", options + end + + # Delete a label from a repository. + # + # This deletes the label from the repository, and removes it from all issues. + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param label [String] String name of the label + # @return [Boolean] Success + # @see https://developer.github.com/v3/issues/labels/#delete-a-label + # @example Delete the label "Version 1.0" from the repository. + # Octokit.delete_label!("octokit/octokit.rb", "Version 1.0") + def delete_label!(repo, label, options = {}) + boolean_from_response :delete, "#{Repository.path repo}/labels/#{ERB::Util.url_encode(label)}", options + end + + # Remove a label from an Issue + # + # This removes the label from the Issue + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param number [Integer] Number ID of the issue + # @param label [String] String name of the label + # @return [Array] A list of the labels currently on the issue + # @see https://developer.github.com/v3/issues/labels/#remove-a-label-from-an-issue + # @example Remove the label "Version 1.0" from the repository. + # Octokit.remove_label("octokit/octokit.rb", 23, "Version 1.0") + def remove_label(repo, number, label, options = {}) + delete "#{Repository.path repo}/issues/#{number}/labels/#{ERB::Util.url_encode(label)}", options + end + + # Remove all label from an Issue + # + # This removes the label from the Issue + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param number [Integer] Number ID of the issue + # @return [Boolean] Success of operation + # @see https://developer.github.com/v3/issues/labels/#remove-all-labels-from-an-issue + # @example Remove all labels from Issue #23 + # Octokit.remove_all_labels("octokit/octokit.rb", 23) + def remove_all_labels(repo, number, options = {}) + boolean_from_response :delete, "#{Repository.path repo}/issues/#{number}/labels", options + end + + # List labels for a given issue + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param number [Integer] Number ID of the issue + # @return [Array] A list of the labels currently on the issue + # @see https://developer.github.com/v3/issues/labels/#list-labels-on-an-issue + # @example List labels for octokit/octokit.rb, issue # 1 + # Octokit.labels_for_issue("octokit/octokit.rb", 1) + def labels_for_issue(repo, number, options = {}) + paginate "#{Repository.path repo}/issues/#{number}/labels", options + end + + # Add label(s) to an Issue + # + # @param repo [Integer, String, Repository, Hash] A Github repository + # @param number [Integer] Number ID of the issue + # @param labels [Array] An array of labels to apply to this Issue + # @return [Array] A list of the labels currently on the issue + # @see https://developer.github.com/v3/issues/labels/#add-labels-to-an-issue + # @example Add two labels for octokit/octokit.rb + # Octokit.add_labels_to_an_issue("octokit/octokit.rb", 10, ['V3 Transition', 'Improvement']) + def add_labels_to_an_issue(repo, number, labels) + post "#{Repository.path repo}/issues/#{number}/labels", labels + end + + # Replace all labels on an Issue + # + # @param repo [Integer, String, Repository, Hash] A Github repository + # @param number [Integer] Number ID of the issue + # @param labels [Array] An array of labels to use as replacement + # @return [Array] A list of the labels currently on the issue + # @see https://developer.github.com/v3/issues/labels/#replace-all-labels-for-an-issue + # @example Replace labels for octokit/octokit.rb Issue #10 + # Octokit.replace_all_labels("octokit/octokit.rb", 10, ['V3 Transition', 'Improvement']) + def replace_all_labels(repo, number, labels, _options = {}) + put "#{Repository.path repo}/issues/#{number}/labels", labels + end + + # Get labels for every issue in a milestone + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param number [Integer] Number ID of the milestone + # @return [Array] A list of the labels across the milestone + # @see http://developer.github.com/v3/issues/labels/#get-labels-for-every-issue-in-a-milestone + # @example List all labels for milestone #2 on octokit/octokit.rb + # Octokit.labels_for_milestone("octokit/octokit.rb", 2) + def labels_for_milestone(repo, number, options = {}) + paginate "#{Repository.path repo}/milestones/#{number}/labels", options + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/legacy_search.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/legacy_search.rb new file mode 100644 index 0000000..64caf9a --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/legacy_search.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the Legacy Search API + # + # @see https://developer.github.com/v3/search/ + module LegacySearch + # Legacy repository search + # + # @see https://developer.github.com/v3/search/#search-repositories + # @param q [String] Search keyword + # @return [Array] List of repositories found + def legacy_search_repositories(q, options = {}) + get("legacy/repos/search/#{q}", options)['repositories'] + end + + # Legacy search issues within a repository + # + # @param repo [String, Repository, Hash] A GitHub repository + # @param search_term [String] The term to search for + # @param state [String] :state (open) open or closed. + # @return [Array] A list of issues matching the search term and state + # @example Search for 'test' in the open issues for sferik/rails_admin + # Octokit.search_issues("sferik/rails_admin", 'test', 'open') + def legacy_search_issues(repo, search_term, state = 'open', options = {}) + get("legacy/issues/search/#{Repository.new(repo)}/#{state}/#{search_term}", options)['issues'] + end + + # Search for user. + # + # @param search [String] User to search for. + # @return [Array] Array of hashes representing users. + # @see https://developer.github.com/v3/search/#search-users + # @example + # Octokit.search_users('pengwynn') + def legacy_search_users(search, options = {}) + get("legacy/user/search/#{search}", options)['users'] + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/licenses.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/licenses.rb new file mode 100644 index 0000000..4365b43 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/licenses.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for licenses API + # + module Licenses + # List all licenses + # + # @see https://developer.github.com/v3/licenses/#list-all-licenses + # @return [Array] A list of licenses + # @example + # Octokit.licenses + def licenses(options = {}) + paginate 'licenses', options + end + + # List an individual license + # + # @see https://developer.github.com/v3/licenses/#get-an-individual-license + # @param license_name [String] The license name + # @return An individual license + # @example + # Octokit.license 'mit' + def license(license_name, options = {}) + get "licenses/#{license_name}", options + end + + # Returns the contents of the repository’s license file, if one is detected. + # + # @see https://developer.github.com/v3/licenses/#get-the-contents-of-a-repositorys-license + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @option options [String] :ref name of the Commit/Branch/Tag. Defaults to 'master'. + # @return [Sawyer::Resource] The detail of the license file + # @example + # Octokit.repository_license_contents 'benbalter/licensee' + def repository_license_contents(repo, options = {}) + get "#{Repository.path repo}/license", options + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/markdown.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/markdown.rb new file mode 100644 index 0000000..f1de799 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/markdown.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the Markdown API + # + # @see https://developer.github.com/v3/markdown/ + module Markdown + # Render an arbitrary Markdown document + # + # @param text [String] Markdown source + # @option options [String] (optional) :mode (`markdown` or `gfm`) + # @option options [String] (optional) :context Repo context + # @return [String] HTML renderization + # @see https://developer.github.com/v3/markdown/#render-an-arbitrary-markdown-document + # @example Render some GFM + # Octokit.markdown('Fixed in #111', :mode => "gfm", :context => "octokit/octokit.rb") + def markdown(text, options = {}) + options[:text] = text + options[:repo] = Repository.new(options[:repo]) if options[:repo] + options[:accept] = 'application/vnd.github.raw' + + post 'markdown', options + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/marketplace.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/marketplace.rb new file mode 100644 index 0000000..53d593e --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/marketplace.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the Marketplace Listing API + # + # @see https://developer.github.com/v3/apps/marketplace/ + module Marketplace + # List all plans for an app's marketplace listing + # + # @param options [Hash] A customizable set of options + # + # @see https://developer.github.com/v3/apps/marketplace/#list-all-plans-for-your-marketplace-listing + # + # @return [Array] A list of plans + def list_plans(options = {}) + paginate '/marketplace_listing/plans', options + end + + # List all GitHub accounts on a specific plan + # + # @param plan_id [Integer] The id of the GitHub plan + # @param options [Hash] A customizable set of options + # + # @see https://developer.github.com/v3/apps/marketplace/#list-all-github-accounts-user-or-organization-on-a-specific-plan + # + # @return [Array] A list of accounts + def list_accounts_for_plan(plan_id, options = {}) + paginate "/marketplace_listing/plans/#{plan_id}/accounts", options + end + + # Get the plan associated with a given GitHub account + # + # @param account_id [Integer] The id of the GitHub account + # @param options [Hash] A customizable set of options + # + # @see https://developer.github.com/v3/apps/marketplace/#check-if-a-github-account-is-associated-with-any-marketplace-listing + # + # @return Account with plan details, or nil + def plan_for_account(account_id, options = {}) + get "/marketplace_listing/accounts/#{account_id}", options + end + + # Get user's Marketplace purchases + # + # @param options [Hash] A customizable set of options + # + # @see https://developer.github.com/v3/apps/marketplace/#get-a-users-marketplace-purchases + # + # @return [Array] A list of Marketplace purchases + def marketplace_purchases(options = {}) + get '/user/marketplace_purchases', options + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/meta.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/meta.rb new file mode 100644 index 0000000..aa7d97c --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/meta.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the Meta API + # + # @see https://developer.github.com/v3/meta/ + module Meta + # Get meta information about GitHub.com, the service. + # @see https://developer.github.com/v3/meta/#meta + # @return [Sawyer::Resource] Hash with meta information. + # @example Get GitHub meta information + # @client.github_meta + def meta(options = {}) + get 'meta', options + end + alias github_meta meta + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/milestones.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/milestones.rb new file mode 100644 index 0000000..07a41b9 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/milestones.rb @@ -0,0 +1,87 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the Issues Milestones API + # + # @see https://developer.github.com/v3/issues/milestones/ + module Milestones + # List milestones for a repository + # + # @param repository [Integer, String, Repository, Hash] A GitHub repository + # @param options [Hash] A customizable set of options. + # @option options [Integer] :milestone Milestone number. + # @option options [String] :state (open) State: open, closed, or all. + # @option options [String] :sort (created) Sort: created, updated, or comments. + # @option options [String] :direction (desc) Direction: asc or desc. + # @return [Array] A list of milestones for a repository. + # @see https://developer.github.com/v3/issues/milestones/#list-milestones-for-a-repository + # @example List milestones for a repository + # Octokit.list_milestones("octokit/octokit.rb") + def list_milestones(repository, options = {}) + paginate "#{Repository.path repository}/milestones", options + end + alias milestones list_milestones + + # Get a single milestone for a repository + # + # @param repository [Integer, String, Repository, Hash] A GitHub repository + # @param options [Hash] A customizable set of options. + # @option options [Integer] :milestone Milestone number. + # @return [Sawyer::Resource] A single milestone from a repository. + # @see https://developer.github.com/v3/issues/milestones/#get-a-single-milestone + # @example Get a single milestone for a repository + # Octokit.milestone("octokit/octokit.rb", 1) + def milestone(repository, number, options = {}) + get "#{Repository.path repository}/milestones/#{number}", options + end + + # Create a milestone for a repository + # + # @param repository [Integer, String, Repository, Hash] A GitHub repository + # @param title [String] A unique title. + # @param options [Hash] A customizable set of options. + # @option options [String] :state (open) State: open or closed. + # @option options [String] :description A meaningful description + # @option options [Time] :due_on Set if the milestone has a due date + # @return [Sawyer::Resource] A single milestone object + # @see https://developer.github.com/v3/issues/milestones/#create-a-milestone + # @example Create a milestone for a repository + # Octokit.create_milestone("octokit/octokit.rb", "0.7.0", {:description => 'Add support for v3 of Github API'}) + def create_milestone(repository, title, options = {}) + post "#{Repository.path repository}/milestones", options.merge({ title: title }) + end + + # Update a milestone for a repository + # + # @param repository [Integer, String, Repository, Hash] A GitHub repository + # @param number [String, Integer] ID of the milestone + # @param options [Hash] A customizable set of options. + # @option options [String] :title A unique title. + # @option options [String] :state (open) State: open or closed. + # @option options [String] :description A meaningful description + # @option options [Time] :due_on Set if the milestone has a due date + # @return [Sawyer::Resource] A single milestone object + # @see https://developer.github.com/v3/issues/milestones/#update-a-milestone + # @example Update a milestone for a repository + # Octokit.update_milestone("octokit/octokit.rb", 1, {:description => 'Add support for v3 of Github API'}) + def update_milestone(repository, number, options = {}) + patch "#{Repository.path repository}/milestones/#{number}", options + end + alias edit_milestone update_milestone + + # Delete a single milestone for a repository + # + # @param repository [Integer, String, Repository, Hash] A GitHub repository + # @param options [Hash] A customizable set of options. + # @option options [Integer] :milestone Milestone number. + # @return [Boolean] Success + # @see https://developer.github.com/v3/issues/milestones/#delete-a-milestone + # @example Delete a single milestone from a repository + # Octokit.delete_milestone("octokit/octokit.rb", 1) + def delete_milestone(repository, number, options = {}) + boolean_from_response :delete, "#{Repository.path repository}/milestones/#{number}", options + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/notifications.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/notifications.rb new file mode 100644 index 0000000..5e8236b --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/notifications.rb @@ -0,0 +1,167 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the Notifications API + # + # @see https://developer.github.com/v3/activity/notifications/ + module Notifications + # List your notifications + # + # @param options [Hash] Optional parameters + # @option options [Boolean] :all 'true' to show notifications marked as + # read. + # @option options [Boolean] :participating 'true' to show only + # notifications in which the user is directly participating or + # mentioned. + # @option options [String] :since Time filters out any notifications + # updated before the given time. The time should be passed in as UTC in + # the ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ. Ex. '2012-10-09T23:39:01Z' + # @return [Array] Array of notifications. + # @see https://developer.github.com/v3/activity/notifications/#list-your-notifications + # @example Get users notifications + # @client.notifications + # @example Get all notifications since a certain time. + # @client.notifications({all: true, since: '2012-10-09T23:39:01Z'}) + def notifications(options = {}) + paginate 'notifications', options + end + + # List your notifications in a repository + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param options [Hash] Optional parameters + # @option options [Boolean] :all 'true' to show notifications marked as + # read. + # @option options [Boolean] :participating 'true' to show only + # notifications in which the user is directly participating or + # mentioned. + # @option options [String] :since Time filters out any notifications + # updated before the given time. The time should be passed in as UTC in + # the ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ. Ex. '2012-10-09T23:39:01Z' + # @return [Array] Array of notifications. + # @see https://developer.github.com/v3/activity/notifications/#list-your-notifications-in-a-repository + # @example Get your notifications for octokit/octokit.rb + # @client.repository_notifications('octokit/octokit.rb') + # @example Get your notifications for octokit/octokit.rb since a time. + # @client.repository_notifications({since: '2012-10-09T23:39:01Z'}) + def repository_notifications(repo, options = {}) + paginate "#{Repository.path repo}/notifications", options + end + alias repo_notifications repository_notifications + + # Mark notifications as read + # + # @param options [Hash] Optional parameters + # @option options [Boolean] :unread Changes the unread status of the + # threads. + # @option options [Boolean] :read Inverse of 'unread'. + # @option options [String] :last_read_at ('Now') Describes the last point + # that notifications were checked. Anything updated since this time + # will not be updated. The time should be passed in as UTC in the + # ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ. Ex. '2012-10-09T23:39:01Z' + # @return [Boolean] True if marked as read, false otherwise + # @see https://developer.github.com/v3/activity/notifications/#mark-as-read + # + # @example + # @client.mark_notifications_as_read + def mark_notifications_as_read(options = {}) + request :put, 'notifications', options + + last_response.status == 205 + end + + # Mark notifications from a specific repository as read + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param options [Hash] Optional parameters + # @option options [Boolean] :unread Changes the unread status of the + # threads. + # @option options [Boolean] :read Inverse of 'unread'. + # @option options [String] :last_read_at ('Now') Describes the last point + # that notifications were checked. Anything updated since this time + # will not be updated. The time should be passed in as UTC in the + # ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ. Ex. '2012-10-09T23:39:01Z' + # @return [Boolean] True if marked as read, false otherwise + # @see https://developer.github.com/v3/activity/notifications/#mark-notifications-as-read-in-a-repository + # @example + # @client.mark_notifications_as_read("octokit/octokit.rb") + def mark_repository_notifications_as_read(repo, options = {}) + request :put, "#{Repository.path repo}/notifications", options + + last_response.status == 205 + end + alias mark_repo_notifications_as_read mark_repository_notifications_as_read + + # List notifications for a specific thread + # + # @param thread_id [Integer] Id of the thread. + # @return [Array] Array of notifications. + # @see https://developer.github.com/v3/activity/notifications/#view-a-single-thread + # + # @example + # @client.notification_thread(1000) + def thread_notifications(thread_id, options = {}) + get "notifications/threads/#{thread_id}", options + end + + # Mark thread as read + # + # @param thread_id [Integer] Id of the thread to update. + # @return [Boolean] True if updated, false otherwise. + # @see https://developer.github.com/v3/activity/notifications/#mark-a-thread-as-read + # @example + # @client.mark_thread_as_read(1, :read => false) + def mark_thread_as_read(thread_id, options = {}) + request :patch, "notifications/threads/#{thread_id}", options + + last_response.status == 205 + end + + # Get thread subscription + # + # @param thread_id [Integer] Id of the thread. + # @return [Sawyer::Resource] Subscription. + # @see https://developer.github.com/v3/activity/notifications/#get-a-thread-subscription + # @example + # @client.thread_subscription(1) + def thread_subscription(thread_id, options = {}) + get "notifications/threads/#{thread_id}/subscription", options + end + + # Update thread subscription + # + # This lets you subscribe to a thread, or ignore it. Subscribing to a + # thread is unnecessary if the user is already subscribed to the + # repository. Ignoring a thread will mute all future notifications (until + # you comment or get @mentioned). + # + # @param thread_id [Integer] Id of the thread. + # @param options + # @option options [Boolean] :subscribed Determines if notifications + # should be received from this repository. + # @option options [Boolean] :ignored Deterimines if all notifications + # should be blocked from this repository. + # @return [Sawyer::Resource] Updated subscription. + # @see https://developer.github.com/v3/activity/notifications/#set-a-thread-subscription + # @example Subscribe to notifications + # @client.update_thread_subscription(1, :subscribed => true) + # @example Ignore notifications from a repo + # @client.update_thread_subscription(1, :ignored => true) + def update_thread_subscription(thread_id, options = {}) + put "notifications/threads/#{thread_id}/subscription", options + end + + # Delete a thread subscription + # + # @param thread_id [Integer] Id of the thread. + # @return [Boolean] True if delete successful, false otherwise. + # @see https://developer.github.com/v3/activity/notifications/#delete-a-thread-subscription + # @example + # @client.delete_thread_subscription(1) + def delete_thread_subscription(thread_id, options = {}) + boolean_from_response :delete, "notifications/threads/#{thread_id}/subscription", options + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/oauth_applications.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/oauth_applications.rb new file mode 100644 index 0000000..6db7197 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/oauth_applications.rb @@ -0,0 +1,116 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the OauthApplications API + # + # @see https://developer.github.com/v3/apps/oauth_applications + module OauthApplications + # Check if a token is valid. + # + # Applications can check if a token is valid without rate limits. + # + # @param access_token [String] 40 character GitHub OAuth access token + # + # @return [Sawyer::Resource] A single authorization for the authenticated user + # @see https://developer.github.com/v3/apps/oauth_applications/#check-a-token + # + # @example + # client = Octokit::Client.new(:client_id => 'abcdefg12345', :client_secret => 'secret') + # client.check_token('deadbeef1234567890deadbeef987654321') + def check_token(access_token, options = {}) + options[:access_token] = access_token + + key = options.delete(:client_id) || client_id + secret = options.delete(:client_secret) || client_secret + + as_app(key, secret) do |app_client| + app_client.post "applications/#{client_id}/token", options + end + end + alias check_application_authorization check_token + + # Reset a token + # + # Applications can reset a token without requiring a user to re-authorize. + # + # @param access_token [String] 40 character GitHub OAuth access token + # + # @return [Sawyer::Resource] A single authorization for the authenticated user + # @see https://developer.github.com/v3/apps/oauth_applications/#reset-a-token + # + # @example + # client = Octokit::Client.new(:client_id => 'abcdefg12345', :client_secret => 'secret') + # client.reset_token('deadbeef1234567890deadbeef987654321') + def reset_token(access_token, options = {}) + options[:access_token] = access_token + + key = options.delete(:client_id) || client_id + secret = options.delete(:client_secret) || client_secret + + as_app(key, secret) do |app_client| + app_client.patch "applications/#{client_id}/token", options + end + end + alias reset_application_authorization reset_token + + # Delete an app token + # + # Applications can revoke (delete) a token + # + # @param access_token [String] 40 character GitHub OAuth access token + # + # @return [Boolean] Result + # @see https://developer.github.com/v3/apps/oauth_applications/#delete-an-app-token + # + # @example + # client = Octokit::Client.new(:client_id => 'abcdefg12345', :client_secret => 'secret') + # client.delete_app_token('deadbeef1234567890deadbeef987654321') + def delete_app_token(access_token, options = {}) + options[:access_token] = access_token + + key = options.delete(:client_id) || client_id + secret = options.delete(:client_secret) || client_secret + + begin + as_app(key, secret) do |app_client| + app_client.delete "applications/#{client_id}/token", options + app_client.last_response.status == 204 + end + rescue Octokit::NotFound + false + end + end + alias delete_application_authorization delete_app_token + alias revoke_application_authorization delete_app_token + + # Delete an app authorization + # + # OAuth application owners can revoke a grant for their OAuth application and a specific user. + # + # @param access_token [String] 40 character GitHub OAuth access token + # + # @return [Boolean] Result + # @see https://developer.github.com/v3/apps/oauth_applications/#delete-an-app-token + # + # @example + # client = Octokit::Client.new(:client_id => 'abcdefg12345', :client_secret => 'secret') + # client.delete_app_authorization('deadbeef1234567890deadbeef987654321') + def delete_app_authorization(access_token, options = {}) + options[:access_token] = access_token + + key = options.delete(:client_id) || client_id + secret = options.delete(:client_secret) || client_secret + + begin + as_app(key, secret) do |app_client| + app_client.delete "applications/#{client_id}/grant", options + app_client.last_response.status == 204 + end + rescue Octokit::NotFound + false + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/objects.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/objects.rb new file mode 100644 index 0000000..0b3f5d7 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/objects.rb @@ -0,0 +1,141 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the Git Data API + # + # @see https://developer.github.com/v3/git/ + module Objects + # Get a single tree, fetching information about its root-level objects + # + # Pass :recursive => true in options to fetch information about all of the tree's objects, including those in subdirectories. + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param tree_sha [String] The SHA of the tree to fetch + # @return [Sawyer::Resource] A hash representing the fetched tree + # @see https://developer.github.com/v3/git/trees/#get-a-tree + # @see https://developer.github.com/v3/git/trees/#get-a-tree-recursively + # @example Fetch a tree and inspect the path of one of its files + # tree = Octokit.tree("octocat/Hello-World", "9fb037999f264ba9a7fc6274d15fa3ae2ab98312") + # tree.tree.first.path # => "file.rb" + # @example Fetch a tree recursively + # tree = Octokit.tree("octocat/Hello-World", "fc6274d15fa3ae2ab983129fb037999f264ba9a7", :recursive => true) + # tree.tree.first.path # => "subdir/file.txt" + def tree(repo, tree_sha, options = {}) + get "#{Repository.path repo}/git/trees/#{tree_sha}", options + end + + # Create a tree + # + # Pass :base_tree => "827efc6..." in options to update an existing tree with new data. + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param tree [Array] An array of hashes representing a tree structure + # @return [Sawyer::Resource] A hash representing the new tree + # @see https://developer.github.com/v3/git/trees/#create-a-tree + # @example Create a tree containing one file + # tree = Octokit.create_tree("octocat/Hello-World", [ { :path => "file.rb", :mode => "100644", :type => "blob", :sha => "44b4fc6d56897b048c772eb4087f854f46256132" } ]) + # tree.sha # => "cd8274d15fa3ae2ab983129fb037999f264ba9a7" + # tree.tree.first.path # => "file.rb" + def create_tree(repo, tree, options = {}) + parameters = { tree: tree } + post "#{Repository.path repo}/git/trees", options.merge(parameters) + end + + # Get a single blob, fetching its content and encoding + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param blob_sha [String] The SHA of the blob to fetch + # @return [Sawyer::Resource] A hash representing the fetched blob + # @see https://developer.github.com/v3/git/blobs/#get-a-blob + # @example Fetch a blob and inspect its contents + # blob = Octokit.blob("octocat/Hello-World", "827efc6d56897b048c772eb4087f854f46256132") + # blob.encoding # => "utf-8" + # blob.content # => "Foo bar baz" + # @example Fetch a base64-encoded blob and inspect its contents + # require "base64" + # blob = Octokit.blob("octocat/Hello-World", "827efc6d56897b048c772eb4087f854f46256132") + # blob.encoding # => "base64" + # blob.content # => "Rm9vIGJhciBiYXo=" + # Base64.decode64(blob.content) # => "Foo bar baz" + def blob(repo, blob_sha, options = {}) + get "#{Repository.path repo}/git/blobs/#{blob_sha}", options + end + + # Create a blob + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param content [String] Content of the blob + # @param encoding [String] The content's encoding. utf-8 and base64 are accepted. If your data cannot be losslessly sent as a UTF-8 string, you can base64 encode it + # @return [String] The new blob's SHA, e.g. 827efc6d56897b048c772eb4087f854f46256132 + # @see https://developer.github.com/v3/git/blobs/#create-a-blob + # @example Create a blob containing foo bar baz + # Octokit.create_blob("octocat/Hello-World", "foo bar baz") + # @example Create a blob containing foo bar baz, encoded using base64 + # require "base64" + # Octokit.create_blob("octocat/Hello-World", Base64.encode64("foo bar baz"), "base64") + def create_blob(repo, content, encoding = 'utf-8', options = {}) + parameters = { + content: content, + encoding: encoding + } + blob = post "#{Repository.path repo}/git/blobs", options.merge(parameters) + + blob.sha + end + + # Get a tag + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @param tag_sha [String] The SHA of the tag to fetch. + # @return [Sawyer::Resource] Hash representing the tag. + # @see https://developer.github.com/v3/git/tags/#get-a-tag + # @example Fetch a tag + # Octokit.tag('octokit/octokit.rb', '23aad20633f4d2981b1c7209a800db3014774e96') + def tag(repo, tag_sha, options = {}) + get "#{Repository.path repo}/git/tags/#{tag_sha}", options + end + + # Create a tag + # + # Requires authenticated client. + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @param tag [String] Tag string. + # @param message [String] Tag message. + # @param object_sha [String] SHA of the git object this is tagging. + # @param type [String] Type of the object we're tagging. Normally this is + # a `commit` but it can also be a `tree` or a `blob`. + # @param tagger_name [String] Name of the author of the tag. + # @param tagger_email [String] Email of the author of the tag. + # @param tagger_date [string] Timestamp of when this object was tagged. + # @return [Sawyer::Resource] Hash representing new tag. + # @see https://developer.github.com/v3/git/tags/#create-a-tag-object + # @example + # @client.create_tag( + # "octokit/octokit.rb", + # "v9000.0.0", + # "Version 9000\n", + # "f4cdf6eb734f32343ce3f27670c17b35f54fd82e", + # "commit", + # "Wynn Netherland", + # "wynn.netherland@gmail.com", + # "2012-06-03T17:03:11-07:00" + # ) + def create_tag(repo, tag, message, object_sha, type, tagger_name, tagger_email, tagger_date, options = {}) + options.merge!( + tag: tag, + message: message, + object: object_sha, + type: type, + tagger: { + name: tagger_name, + email: tagger_email, + date: tagger_date + } + ) + post "#{Repository.path repo}/git/tags", options + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/organizations.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/organizations.rb new file mode 100644 index 0000000..b4b0e7a --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/organizations.rb @@ -0,0 +1,864 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the Organizations API + # + # @see https://developer.github.com/v3/orgs/ + module Organizations + # Get an organization + # + # @param org [String, Integer] Organization GitHub login or id. + # @return [Sawyer::Resource] Hash representing GitHub organization. + # @see https://developer.github.com/v3/orgs/#get-an-organization + # @example + # Octokit.organization('github') + # @example + # Octokit.org('github') + def organization(org, options = {}) + get Organization.path(org), options + end + alias org organization + + # Update an organization. + # + # Requires authenticated client with proper organization permissions. + # + # @param org [String, Integer] Organization GitHub login or id. + # @param values [Hash] The updated organization attributes. + # @option values [String] :billing_email Billing email address. This address is not publicized. + # @option values [String] :company Company name. + # @option values [String] :email Publicly visible email address. + # @option values [String] :location Location of organization. + # @option values [String] :name GitHub username for organization. + # @option values [String] :default_repository_permission The default permission members have on organization repositories. + # @option values [Boolean] :members_can_create_repositories Set true to allow members to create repositories on the organization. + # @return [Sawyer::Resource] Hash representing GitHub organization. + # @see https://developer.github.com/v3/orgs/#edit-an-organization + # @example + # @client.update_organization('github', { + # :billing_email => 'support@github.com', + # :company => 'GitHub', + # :email => 'support@github.com', + # :location => 'San Francisco', + # :name => 'github' + # }) + # @example + # @client.update_org('github', {:company => 'Unicorns, Inc.'}) + def update_organization(org, values, options = {}) + patch Organization.path(org), options.merge(values) + end + alias update_org update_organization + + # Delete an organization. + # + # Requires authenticated organization owner. + # + # @param org [String, Integer] Organization login or ID. + # @return [Boolean] True if deletion successful, otherwise false. + # @see https://docs.github.com/rest/orgs/orgs#delete-an-organization + # @example + # @client.delete_organization("my-org") + # @example + # @client.delete_org("my-org") + def delete_organization(org) + boolean_from_response :delete, Organization.path(org) + end + alias delete_org delete_organization + + # Get organizations for a user. + # + # Nonauthenticated calls to this method will return organizations that + # the user is a public member. + # + # Use an authenticated client to get both public and private organizations + # for a user. + # + # Calling this method on a `@client` will return that users organizations. + # Private organizations are included only if the `@client` is authenticated. + # + # @param user [Integer, String] GitHub user login or id of the user to get + # list of organizations. + # @return [Array] Array of hashes representing organizations. + # @see https://developer.github.com/v3/orgs/#list-your-organizations + # @see https://developer.github.com/v3/orgs/#list-user-organizations + # @example + # Octokit.organizations('pengwynn') + # @example + # @client.organizations('pengwynn') + # @example + # Octokit.orgs('pengwynn') + # @example + # Octokit.list_organizations('pengwynn') + # @example + # Octokit.list_orgs('pengwynn') + # @example + # @client.organizations + def organizations(user = nil, options = {}) + paginate "#{User.path user}/orgs", options + end + alias list_organizations organizations + alias list_orgs organizations + alias orgs organizations + + # List all GitHub organizations + # + # This provides a list of every organization, in the order that they + # were created. + # + # @param options [Hash] Optional options. + # @option options [Integer] :since The integer ID of the last + # Organization that you’ve seen. + # + # @see https://developer.github.com/v3/orgs/#list-all-organizations + # + # @return [Array] List of GitHub organizations. + def all_organizations(options = {}) + paginate 'organizations', options + end + alias all_orgs all_organizations + + # List organization repositories + # + # Public repositories are available without authentication. Private repos + # require authenticated organization member. + # + # @param org [String, Integer] Organization GitHub login or id for which + # to list repos. + # @option options [String] :type ('all') Filter by repository type. + # `all`, `public`, `member`, `sources`, `forks`, or `private`. + # + # @return [Array] List of repositories + # @see https://developer.github.com/v3/repos/#list-organization-repositories + # @example + # Octokit.organization_repositories('github') + # @example + # Octokit.org_repositories('github') + # @example + # Octokit.org_repos('github') + # @example + # @client.org_repos('github', {:type => 'private'}) + def organization_repositories(org, options = {}) + paginate "#{Organization.path org}/repos", options + end + alias org_repositories organization_repositories + alias org_repos organization_repositories + + # Get organization members + # + # Public members of the organization are returned by default. An + # authenticated client that is a member of the GitHub organization + # is required to get private members. + # + # @param org [String, Integer] Organization GitHub login or id. + # @return [Array] Array of hashes representing users. + # @see https://developer.github.com/v3/orgs/members/#members-list + # @example + # Octokit.organization_members('github') + # @example + # Octokit.org_members('github') + def organization_members(org, options = {}) + options = options.dup + path = 'public_' if options.delete(:public) + paginate "#{Organization.path org}/#{path}members", options + end + alias org_members organization_members + + # Get organization public members + # + # Lists the public members of an organization + # + # @param org [String] Organization GitHub username. + # @return [Array] Array of hashes representing users. + # @see https://developer.github.com/v3/orgs/members/#public-members-list + # @example + # Octokit.organization_public_members('github') + # @example + # Octokit.org_public_members('github') + def organization_public_members(org, options = {}) + organization_members org, options.merge(public: true) + end + alias org_public_members organization_public_members + + # Check if a user is a member of an organization. + # + # Use this to check if another user is a member of an organization that + # you are a member. If you are not in the organization you are checking, + # use .organization_public_member? instead. + # + # @param org [String, Integer] Organization GitHub login or id. + # @param user [String] GitHub username of the user to check. + # + # @return [Boolean] Is a member? + # + # @see https://developer.github.com/v3/orgs/members/#check-membership + # + # @example Check if a user is in your organization + # @client.organization_member?('your_organization', 'pengwynn') + # => false + def organization_member?(org, user, options = {}) + result = boolean_from_response(:get, "#{Organization.path org}/members/#{user}", options) + if !result && last_response && last_response.status == 302 + boolean_from_response :get, last_response.headers['Location'] + else + result + end + end + alias org_member? organization_member? + + # Check if a user is a public member of an organization. + # + # If you are checking for membership of a user of an organization that + # you are in, use .organization_member? instead. + # + # @param org [String, Integer] Organization GitHub login or id. + # @param user [String] GitHub username of the user to check. + # + # @return [Boolean] Is a public member? + # + # @see https://developer.github.com/v3/orgs/members/#check-public-membership + # + # @example Check if a user is a hubbernaut + # @client.organization_public_member?('github', 'pengwynn') + # => true + def organization_public_member?(org, user, options = {}) + boolean_from_response :get, "#{Organization.path org}/public_members/#{user}", options + end + alias org_public_member? organization_public_member? + + # List pending organization invitations + # + # Requires authenticated organization member. + # + # @param org [String, Integer] Organization GitHub login or id. + # @return [Array] Array of hashes representing invitations. + # @see https://developer.github.com/v3/orgs/members/#list-pending-organization-invitations + # + # @example + # @client.organization_invitations('github') + def organization_invitations(org, options = {}) + get "#{Organization.path org}/invitations", options + end + alias org_invitations organization_invitations + + # List outside collaborators for an organization + # + # Requires authenticated organization members. + # + # @param org [String, Integer] Organization GitHub login or id. + # @return [Array] Array of hashes representing users. + # @see https://developer.github.com/v3/orgs/outside_collaborators/#list-outside-collaborators + # + # @example + # @client.outside_collaborators('github') + def outside_collaborators(org, options = {}) + paginate "#{Organization.path org}/outside_collaborators", options + end + + # Remove outside collaborator from an organization + # + # Requires authenticated organization members. + # + # @param org [String, Integer] Organization GitHub login or id. + # @param user [String] GitHub username to be removed as outside collaborator + # @return [Boolean] Return true if outside collaborator removed from organization, false otherwise. + # @see https://developer.github.com/v3/orgs/outside-collaborators/#remove-outside-collaborator + # + # @example + # @client.remove_outside_collaborator('github', 'lizzhale') + def remove_outside_collaborator(org, user, options = {}) + boolean_from_response :delete, "#{Organization.path org}/outside_collaborators/#{user}", options + end + + # Converts an organization member to an outside collaborator + # + # Requires authenticated organization members. + # + # @param org [String, Integer] Organization GitHub login or id. + # @param user [String] GitHub username to be removed as outside collaborator + # @return [Boolean] Return true if outside collaborator removed from organization, false otherwise. + # @see https://developer.github.com/v3/orgs/outside-collaborators/#convert-member-to-outside-collaborator + # + # @example + # @client.convert_to_outside_collaborator('github', 'lizzhale') + def convert_to_outside_collaborator(org, user, options = {}) + boolean_from_response :put, "#{Organization.path org}/outside_collaborators/#{user}", options + end + + # List teams + # + # Requires authenticated organization member. + # + # @param org [String, Integer] Organization GitHub login or id. + # @return [Array] Array of hashes representing teams. + # @see https://developer.github.com/v3/orgs/teams/#list-teams + # @example + # @client.organization_teams('github') + # @example + # @client.org_teams('github') + def organization_teams(org, options = {}) + paginate "#{Organization.path org}/teams", options + end + alias org_teams organization_teams + + # Create team + # + # Requires authenticated organization owner. + # + # @param org [String, Integer] Organization GitHub login or id. + # @option options [String] :name Team name. + # @option options [Array] :repo_names Repositories for the team. + # @option options [Array] :maintainers Maintainers for the team. + # @option options [Integer] :parent_team_id ID of a team to set as the parent team. + # @return [Sawyer::Resource] Hash representing new team. + # @see https://developer.github.com/v3/orgs/teams/#create-team + # @example + # @client.create_team('github', { + # :name => 'Designers', + # :repo_names => ['github/dotfiles'] + # }) + def create_team(org, options = {}) + if options.key?(:permission) + octokit_warn 'Deprecated: Passing :permission option to #create_team. Assign team repository permission by passing :permission to #add_team_repository instead.' + end + post "#{Organization.path org}/teams", options + end + + # Get team + # + # Requires authenticated organization member. + # + # @param team_id [Integer] Team id. + # @return [Sawyer::Resource] Hash representing team. + # @see https://developer.github.com/v3/orgs/teams/#get-team + # @example + # @client.team(100000) + def team(team_id, options = {}) + get "teams/#{team_id}", options + end + + # Get team by name and org + # + # Requires authenticated organization member. + # + # @param org [String, Integer] Organization GitHub login or id. + # @param team_slug [String] Team slug. + # @return [Sawyer::Resource] Hash representing team. + # @see https://developer.github.com/v3/teams/#get-team-by-name + # @example + # @client.team_by_name("github", "justice-league") + def team_by_name(org, team_slug, options = {}) + get "#{Organization.path(org)}/teams/#{team_slug}", options + end + + # Check team permissions for a repository + # + # Requires authenticated organization member. + # + # @param org [String, Integer] Organization GitHub login or id. + # @param team_slug_or_id [String, Integer] Team slug or Team ID. + # @param owner [String] Owner name for the repository. + # @param repo [String] Name of the repo to check permissions against. + # @return [String, Sawyer::Resource] Depending on options it may be an empty string or a resource. + # @example + # # Check whether the team has any permissions with the repository + # @client.team_permissions_for_repo("github", "justice-league", "octocat", "hello-world") + # + # @example + # # Get the full repository object including the permissions level and role for the team + # @client.team_permissions_for_repo("github", "justice-league", "octocat", "hello-world", :accept => 'application/vnd.github.v3.repository+json') + # @see https://docs.github.com/en/rest/teams/teams#check-team-permissions-for-a-repository + def team_permissions_for_repo(org, team_slug_or_id, owner, repo, options = {}) + get "#{Organization.path(org)}/teams/#{team_slug_or_id}/repos/#{owner}/#{repo}", options + end + + # List child teams + # + # Requires authenticated organization member. + # + # @param team_id [Integer] Team id. + # @return [Sawyer::Resource] Hash representing team. + # @see https://developer.github.com/v3/orgs/teams/#list-child-teams + # @example + # @client.child_teams(100000, :accept => "application/vnd.github.hellcat-preview+json") + def child_teams(team_id, options = {}) + paginate "teams/#{team_id}/teams", options + end + + # Update team + # + # Requires authenticated organization owner. + # + # @param team_id [Integer] Team id. + # @option options [String] :name Team name. + # @option options [String] :permission Permissions the team has for team repositories. + # + # `pull` - team members can pull, but not push to or administer these repositories. + # `push` - team members can pull and push, but not administer these repositories. + # `admin` - team members can pull, push and administer these repositories. + # @option options [Integer] :parent_team_id ID of a team to set as the parent team. + # @return [Sawyer::Resource] Hash representing updated team. + # @see https://developer.github.com/v3/orgs/teams/#edit-team + # @example + # @client.update_team(100000, { + # :name => 'Front-end Designers', + # :permission => 'push' + # }) + def update_team(team_id, options = {}) + patch "teams/#{team_id}", options + end + + # Delete team + # + # Requires authenticated organization owner. + # + # @param team_id [Integer] Team id. + # @return [Boolean] True if deletion successful, false otherwise. + # @see https://developer.github.com/v3/orgs/teams/#delete-team + # @example + # @client.delete_team(100000) + def delete_team(team_id, options = {}) + boolean_from_response :delete, "teams/#{team_id}", options + end + + # List team members + # + # Requires authenticated organization member. + # + # @param team_id [Integer] Team id. + # @return [Array] Array of hashes representing users. + # @see https://developer.github.com/v3/orgs/teams/#list-team-members + # @example + # @client.team_members(100000) + def team_members(team_id, options = {}) + paginate "teams/#{team_id}/members", options + end + + # Add team member + # + # Requires authenticated organization owner or member with team + # `admin` permission. + # + # @param team_id [Integer] Team id. + # @param user [String] GitHub username of new team member. + # @return [Boolean] True on successful addition, false otherwise. + # @see https://developer.github.com/v3/orgs/teams/#add-team-member + # @example + # @client.add_team_member(100000, 'pengwynn') + # + # @example + # # Opt-in to future behavior for this endpoint. Adds the member to the + # # team if they're already an org member. If not, the method will return + # # 422 and indicate the user should call the new Team Membership endpoint. + # @client.add_team_member \ + # 100000, + # 'pengwynn', + # :accept => "application/vnd.github.the-wasp-preview+json" + # @see https://developer.github.com/changes/2014-08-05-team-memberships-api/ + def add_team_member(team_id, user, options = {}) + # There's a bug in this API call. The docs say to leave the body blank, + # but it fails if the body is both blank and the content-length header + # is not 0. + boolean_from_response :put, "teams/#{team_id}/members/#{user}", options.merge({ name: user }) + end + + # Remove team member + # + # Requires authenticated organization owner or member with team + # `admin` permission. + # + # @param team_id [Integer] Team id. + # @param user [String] GitHub username of the user to boot. + # @return [Boolean] True if user removed, false otherwise. + # @see https://developer.github.com/v3/orgs/teams/#remove-team-member + # @example + # @client.remove_team_member(100000, 'pengwynn') + def remove_team_member(team_id, user, options = {}) + boolean_from_response :delete, "teams/#{team_id}/members/#{user}", options + end + + # Check if a user is a member of a team. + # + # Use this to check if another user is a member of a team that + # you are a member. + # + # @param team_id [Integer] Team id. + # @param user [String] GitHub username of the user to check. + # + # @return [Boolean] Is a member? + # + # @see https://developer.github.com/v3/orgs/teams/#get-team-member + # + # @example Check if a user is in your team + # @client.team_member?(100000, 'pengwynn') + # => false + def team_member?(team_id, user, options = {}) + boolean_from_response :get, "teams/#{team_id}/members/#{user}", options + end + + # List pending team invitations + # + # Requires authenticated organization member. + # + # @param team_id [Integer] Team id. + # @return [Array] Array of hashes representing invitations. + # @see https://developer.github.com/v3/orgs/teams/#list-pending-team-invitations + # + # @example + # @client.team_invitations('github') + def team_invitations(team_id, options = {}) + get "teams/#{team_id}/invitations", options + end + + # List team repositories + # + # Requires authenticated organization member. + # + # @param team_id [Integer] Team id. + # @return [Array] Array of hashes representing repositories. + # @see https://developer.github.com/v3/orgs/teams/#list-team-repos + # @example + # @client.team_repositories(100000) + # @example + # @client.team_repos(100000) + def team_repositories(team_id, options = {}) + paginate "teams/#{team_id}/repos", options + end + alias team_repos team_repositories + + # Check if a repo is managed by a specific team + # + # @param team_id [Integer] Team ID. + # @param repo [String, Hash, Repository] A GitHub repository. + # @return [Boolean] True if managed by a team. False if not managed by + # the team OR the requesting user does not have authorization to access + # the team information. + # @see https://developer.github.com/v3/orgs/teams/#check-if-a-team-manages-a-repository + # @example + # @client.team_repository?(8675309, 'octokit/octokit.rb') + # @example + # @client.team_repo?(8675309, 'octokit/octokit.rb') + def team_repository?(team_id, repo, _options = {}) + boolean_from_response :get, "teams/#{team_id}/repos/#{Repository.new(repo)}" + end + alias team_repo? team_repository? + + # Add team repository + # + # This can also be used to update the permission of an existing team + # + # Requires authenticated user to be an owner of the organization that the + # team is associated with. Also, the repo must be owned by the + # organization, or a direct form of a repo owned by the organization. + # + # @param team_id [Integer] Team id. + # @param repo [String, Hash, Repository] A GitHub repository. + # @option options [String] :permission The permission to grant the team. + # Only valid on organization-owned repositories. + # Can be one of: pull, push, or admin. + # If not specified, the team's permission attribute will be + # used to determine what permission to grant the team on this repository. + # @return [Boolean] True if successful, false otherwise. + # @see Octokit::Repository + # @see https://developer.github.com/v3/orgs/teams/#add-or-update-team-repository + # @example + # @client.add_team_repository(100000, 'github/developer.github.com') + # @example + # @client.add_team_repo(100000, 'github/developer.github.com') + # @example Add a team with admin permissions + # @client.add_team_repository(100000, 'github/developer.github.com', permission: 'admin') + def add_team_repository(team_id, repo, options = {}) + boolean_from_response :put, "teams/#{team_id}/repos/#{Repository.new(repo)}", options + end + alias add_team_repo add_team_repository + + # Remove team repository + # + # Removes repository from team. Does not delete the repository. + # + # Requires authenticated organization owner. + # + # @param team_id [Integer] Team id. + # @param repo [String, Hash, Repository] A GitHub repository. + # @return [Boolean] Return true if repo removed from team, false otherwise. + # @see Octokit::Repository + # @see https://developer.github.com/v3/orgs/teams/#remove-team-repository + # @example + # @client.remove_team_repository(100000, 'github/developer.github.com') + # @example + # @client.remove_team_repo(100000, 'github/developer.github.com') + def remove_team_repository(team_id, repo, _options = {}) + boolean_from_response :delete, "teams/#{team_id}/repos/#{Repository.new(repo)}" + end + alias remove_team_repo remove_team_repository + + # Remove organization member + # + # Requires authenticated organization owner or member with team `admin` access. + # + # @param org [String, Integer] Organization GitHub login or id. + # @param user [String] GitHub username of user to remove. + # @return [Boolean] True if removal is successful, false otherwise. + # @see https://developer.github.com/v3/orgs/members/#remove-a-member + # @example + # @client.remove_organization_member('github', 'pengwynn') + # @example + # @client.remove_org_member('github', 'pengwynn') + def remove_organization_member(org, user, options = {}) + # this is a synonym for: for team in org.teams: remove_team_member(team.id, user) + # provided in the GH API v3 + boolean_from_response :delete, "#{Organization.path org}/members/#{user}", options + end + alias remove_org_member remove_organization_member + + # Publicize a user's membership of an organization + # + # Requires authenticated organization owner. + # + # @param org [String, Integer] Organization GitHub login or id. + # @param user [String] GitHub username of user to publicize. + # @return [Boolean] True if publicization successful, false otherwise. + # @see https://developer.github.com/v3/orgs/members/#publicize-a-users-membership + # @example + # @client.publicize_membership('github', 'pengwynn') + def publicize_membership(org, user, options = {}) + boolean_from_response :put, "#{Organization.path org}/public_members/#{user}", options + end + + # Conceal a user's membership of an organization. + # + # Requires authenticated organization owner. + # + # @param org [String, Integer] Organization GitHub login or id. + # @param user [String] GitHub username of user to unpublicize. + # @return [Boolean] True of unpublicization successful, false otherwise. + # @see https://developer.github.com/v3/orgs/members/#conceal-a-users-membership + # @example + # @client.unpublicize_membership('github', 'pengwynn') + # @example + # @client.conceal_membership('github', 'pengwynn') + def unpublicize_membership(org, user, options = {}) + boolean_from_response :delete, "#{Organization.path org}/public_members/#{user}", options + end + alias conceal_membership unpublicize_membership + + # List all teams for the authenticated user across all their orgs + # + # @return [Array] Array of team resources. + # @see https://developer.github.com/v3/orgs/teams/#list-user-teams + def user_teams(options = {}) + paginate 'user/teams', options + end + + # Check if a user has a team membership. + # + # @param team_id [Integer] Team id. + # @param user [String] GitHub username of the user to check. + # + # @return [Sawyer::Resource] Hash of team membership info + # + # @see https://developer.github.com/v3/orgs/teams/#get-team-membership + # + # @example Check if a user has a membership for a team + # @client.team_membership(1234, 'pengwynn') + def team_membership(team_id, user, options = {}) + get "teams/#{team_id}/memberships/#{user}", options + end + + # Add or invite a user to a team + # + # @param team_id [Integer] Team id. + # @param user [String] GitHub username of the user to invite. + # + # @return [Sawyer::Resource] Hash of team membership info + # + # @see https://developer.github.com/v3/orgs/teams/#add-or-update-team-membership + # + # @example Check if a user has a membership for a team + # @client.add_team_membership(1234, 'pengwynn') + def add_team_membership(team_id, user, options = {}) + put "teams/#{team_id}/memberships/#{user}", options + end + + # Remove team membership + # + # @param team_id [Integer] Team id. + # @param user [String] GitHub username of the user to boot. + # @return [Boolean] True if user removed, false otherwise. + # @see https://developer.github.com/v3/orgs/teams/#remove-team-membership + # @example + # @client.remove_team_membership(100000, 'pengwynn') + def remove_team_membership(team_id, user, options = {}) + boolean_from_response :delete, "teams/#{team_id}/memberships/#{user}", options + end + + # List all organizations memberships for the authenticated user + # + # @return [Array] Array of organizations memberships. + # @see https://developer.github.com/v3/orgs/members/#list-your-organization-memberships + def organization_memberships(options = {}) + paginate 'user/memberships/orgs', options + end + alias org_memberships organization_memberships + + # Get an organization membership + # + # @param org [Integer, String] The GitHub Organization. + # @option options [String] :user The login of the user, otherwise authenticated user. + # @return [Sawyer::Resource] Hash representing the organization membership. + # @see https://developer.github.com/v3/orgs/members/#get-your-organization-membership + # @see https://developer.github.com/v3/orgs/members/#get-organization-membership + def organization_membership(org, options = {}) + options = options.dup + if user = options.delete(:user) + get "#{Organization.path(org)}/memberships/#{user}", options + else + get "user/memberships/orgs/#{org}", options + end + end + alias org_membership organization_membership + + # Edit an organization membership + # + # @param org [String, Integer] Organization GitHub login or id. + # @option options [String] :role The role of the user in the organization. + # @option options [String] :state The state that the membership should be in. + # @option options [String] :user The login of the user, otherwise authenticated user. + # @return [Sawyer::Resource] Hash representing the updated organization membership. + # @see https://developer.github.com/v3/orgs/members/#edit-your-organization-membership + # @see https://developer.github.com/v3/orgs/members/#add-or-update-organization-membership + def update_organization_membership(org, options = {}) + options = options.dup + if user = options.delete(:user) + options.delete(:state) + put "#{Organization.path(org)}/memberships/#{user}", options + else + options.delete(:role) + patch "user/memberships/orgs/#{org}", options + end + end + alias update_org_membership update_organization_membership + + # Remove an organization membership + # + # @param org [String, Integer] Organization GitHub login or id. + # @return [Boolean] Success + # @see https://developer.github.com/v3/orgs/members/#remove-organization-membership + def remove_organization_membership(org, options = {}) + options = options.dup + user = options.delete(:user) + user && boolean_from_response(:delete, "#{Organization.path(org)}/memberships/#{user}", options) + end + alias remove_org_membership remove_organization_membership + + # Initiates the generation of a migration archive. + # + # Requires authenticated organization owner. + # + # @param org [String, Integer] Organization GitHub login or id. + # @param repositories [Array] :repositories Repositories for the organization. + # @option options [Boolean, optional] :lock_repositories Indicates whether repositories should be locked during migration + # @return [Sawyer::Resource] Hash representing the new migration. + # @example + # @client.start_migration('github', ['github/dotfiles']) + # @see https://docs.github.com/en/rest/reference/migrations#start-an-organization-migration + def start_migration(org, repositories, options = {}) + options[:repositories] = repositories + post "#{Organization.path(org)}/migrations", options + end + + # Lists the most recent migrations. + # + # Requires authenticated organization owner. + # + # @param org [String, Integer] Organization GitHub login or id. + # @return [Array] Array of migration resources. + # @see https://docs.github.com/en/rest/reference/migrations#list-organization-migrations + def migrations(org, options = {}) + paginate "#{Organization.path(org)}/migrations", options + end + + # Fetches the status of a migration. + # + # Requires authenticated organization owner. + # + # @param org [String, Integer] Organization GitHub login or id. + # @param id [Integer] ID number of the migration. + # @see https://docs.github.com/en/rest/reference/migrations#get-an-organization-migration-status + def migration_status(org, id, options = {}) + get "#{Organization.path(org)}/migrations/#{id}", options + end + + # Fetches the URL to a migration archive. + # + # Requires authenticated organization owner. + # + # @param org [String, Integer] Organization GitHub login or id. + # @param id [Integer] ID number of the migration. + # @see https://docs.github.com/en/rest/reference/migrations#download-an-organization-migration-archive + def migration_archive_url(org, id, options = {}) + url = "#{Organization.path(org)}/migrations/#{id}/archive" + + response = client_without_redirects(options).get(url) + response.headers['location'] + end + + # Deletes a previous migration archive. + # + # Requires authenticated organization owner. + # + # @param org [String, Integer] Organization GitHub login or id. + # @param id [Integer] ID number of the migration. + # @see https://docs.github.com/en/rest/reference/migrations#delete-an-organization-migration-archive + def delete_migration_archive(org, id, options = {}) + delete "#{Organization.path(org)}/migrations/#{id}/archive", options + end + + # Unlock a previous migration archive. + # + # Requires authenticated organization owner. + # + # @param org [String, Integer] Organization GitHub login or id. + # @param id [Integer] ID number of the migration. + # @param repo [String] Name of the repository. + # @see https://docs.github.com/en/rest/reference/migrations#unlock-an-organization-repository + def unlock_repository(org, id, repo, options = {}) + delete "#{Organization.path(org)}/migrations/#{id}/repos/#{repo}/lock", options + end + + # Get GitHub Actions billing for an organization + # + # Requires authenticated organization owner. + # + # @param org [String, Integer] Organization GitHub login or id. + # @return [Sawyer::Resource] Hash representing GitHub Actions billing for an organization. + # @see https://docs.github.com/en/rest/reference/billing#get-github-actions-billing-for-an-organization + # + # @example + # @client.billing_actions('github') + def billing_actions(org) + get "#{Organization.path(org)}/settings/billing/actions" + end + + # Get organization audit log. + # + # Gets the audit log for an organization. + # + # @param org [String, Integer] Organization GitHub login or id for which + # to retrieve the audit log. + # @option options [String] :include ('all') Filter by event type. + # `all`, `git` or `web`. + # @option options [String] :phrase A search phrase. + # @option options [String] :order ('desc') The order of audit log events. To list newest events first, specify desc. + # To list oldest events first, specify asc. + # + # @return [Array] List of events + # @see https://docs.github.com/en/enterprise-cloud@latest/rest/orgs/orgs#get-the-audit-log-for-an-organization + # @example + # Octokit.organization_audit_log('github', {include: 'all', phrase: 'action:org.add_member created:>2022-08-29 user:octocat'}) + def organization_audit_log(org, options = {}) + paginate "#{Organization.path org}/audit-log", options + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/pages.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/pages.rb new file mode 100644 index 0000000..e1e7b08 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/pages.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the Pages API + # + # @see https://developer.github.com/v3/repos/pages/ + module Pages + # List Pages information for a repository + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @return Sawyer::Resource A GitHub Pages resource + # @see https://developer.github.com/v3/repos/pages/#get-information-about-a-pages-site + def pages(repo, options = {}) + get "#{Repository.path repo}/pages", options + end + + # Get a specific Pages build by ID + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param id [Integer, String] Build ID + # @return [Sawyer::Resource] Pages build information + # @see https://developer.github.com/v3/repos/pages/#list-a-specific-pages-build + # @example + # Octokit.pages_build("github/developer.github.com", 5472601) + def pages_build(repo, id, options = {}) + get "#{Repository.path repo}/pages/builds/#{id}", options + end + + # List Pages builds for a repository + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @return [Array] A list of build history for a repository. + # @see https://developer.github.com/v3/repos/pages/#list-pages-builds + def pages_builds(repo, options = {}) + get "#{Repository.path repo}/pages/builds", options + end + alias list_pages_builds pages_builds + + # List the latest Pages build information for a repository + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @return Sawyer::Resource A GitHub Pages resource about a build + # @see https://developer.github.com/v3/repos/pages/#list-latest-pages-build + def latest_pages_build(repo, options = {}) + get "#{Repository.path repo}/pages/builds/latest", options + end + + # Request a page build for the latest revision of the default branch + # + # You can only request builds for your repositories + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @return [Sawyer::Resource] Request result + # @see https://developer.github.com/v3/repos/pages/#request-a-page-build + def request_page_build(repo, options = {}) + post "#{Repository.path repo}/pages/builds", options + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/projects.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/projects.rb new file mode 100644 index 0000000..619f3a8 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/projects.rb @@ -0,0 +1,294 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for Projects API + # + # @see https://docs.github.com/en/rest/projects + module Projects + # List projects for a repository + # + # Requires authenticated client + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @return [Array] Repository projects + # @see https://developer.github.com/v3/projects/#list-repository-projects + # @example + # @client.projects('octokit/octokit.rb') + def projects(repo, options = {}) + paginate "#{Repository.path repo}/projects", options + end + + # Create a project + # + # Requires authenticated client + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param name [String] Project name + # @option options [String] :body Body of the project + # @return [Sawyer::Resource] Fresh new project + # @see https://developer.github.com/v3/projects/#create-a-repository-project + # @example Create project with only a name + # @client.create_project('octokit/octokit.rb', 'implement new APIs') + # + # @example Create project with name and body + # @client.create_project('octokit/octokit.rb', 'bugs be gone', body: 'Fix all the bugs @joeyw creates') + def create_project(repo, name, options = {}) + options[:name] = name + post "#{Repository.path repo}/projects", options + end + + # List organization projects + # + # Requires authenticated client + # + # @param org [String] A GitHub organization + # @return [Array] Organization projects + # @see https://developer.github.com/v3/projects/#list-organization-projects + # @example + # @client.org_projects("octokit") + def org_projects(org, options = {}) + paginate "orgs/#{org}/projects", options + end + alias organization_projects org_projects + + # Create organization project + # + # Requires authenticated client + # + # @param org [String] A GitHub organization + # @param name [String] Project name + # @option options [String] :body Project body + # @return [Sawyer::Resource] Organization project + # @see https://developer.github.com/v3/projects/#create-an-organization-project + # @example Create with only a name + # @client.create_org_project("octocat", "make more octocats") + # @example Create a project with name and body + # @client.create_org_project("octokit", "octocan", body: 'Improve clients') + def create_org_project(org, name, options = {}) + options[:name] = name + post "orgs/#{org}/projects", options + end + alias create_organization_project create_org_project + + # Get a project by id + # + # @param id [Integer] Project id + # @return [Sawyer::Resource] Project + # @see https://developer.github.com/v3/projects/#get-a-project + # @example + # Octokit.project(123942) + def project(id, options = {}) + get "projects/#{id}", options + end + + # Update a project + # + # Requires authenticated client + # + # @param id [Integer] Project id + # @option options [String] :name Project name + # @option options [String] :body Project body + # @return [Sawyer::Resource] Project + # @see https://developer.github.com/v3/projects/#update-a-project + # @example Update project name + # @client.update_project(123942, name: 'New name') + def update_project(id, options = {}) + patch "projects/#{id}", options + end + + # Delete a project + # + # Requires authenticated client + # + # @param id [Integer] Project id + # @return [Boolean] Result of deletion + # @see https://developer.github.com/v3/projects/#delete-a-project + # @example + # @client.delete_project(123942) + def delete_project(id, options = {}) + boolean_from_response :delete, "projects/#{id}", options + end + + # List project columns + # + # @param id [Integer] Project id + # @return [Array] List of project columns + # @see https://developer.github.com/v3/projects/columns/#list-project-columns + # @example + # @client.project_columns(123942) + def project_columns(id, options = {}) + paginate "projects/#{id}/columns", options + end + + # Create a project column + # + # Requires authenticated client + # + # @param id [Integer] Project column id + # @param name [String] New column name + # @return [Sawyer::Resource] Newly created column + # @see https://developer.github.com/v3/projects/columns/#create-a-project-column + # @example + # @client.create_project_column(123942, "To Dones") + def create_project_column(id, name, options = {}) + options[:name] = name + post "projects/#{id}/columns", options + end + + # Get a project column by ID + # + # @param id [Integer] Project column id + # @return [Sawyer::Resource] Project column + # @see https://developer.github.com/v3/projects/columns/#get-a-project-column + # @example + # Octokit.project_column(30294) + def project_column(id, options = {}) + get "projects/columns/#{id}", options + end + + # Update a project column + # + # Requires authenticated client + # + # @param id [Integer] Project column id + # @param name [String] New column name + # @return [Sawyer::Resource] Updated column + # @see https://developer.github.com/v3/projects/columns/#update-a-project-column + # @example + # @client.update_project_column(30294, "new column name") + def update_project_column(id, name, options = {}) + options[:name] = name + patch "projects/columns/#{id}", options + end + + # Delete a project column + # + # Requires authenticated client + # + # @param id [Integer] Project column id + # @return [Boolean] Result of deletion request, true when deleted + # @see https://developer.github.com/v3/projects/columns/#delete-a-project-column + # @example + # @client.delete_project_column(30294) + def delete_project_column(id, options = {}) + boolean_from_response :delete, "projects/columns/#{id}", options + end + + # Move a project column + # + # Requires authenticated client + # + # @param id [Integer] Project column id + # @param position [String] New position for the column. Can be one of + # first, last, or after:, where + # is the id value of a column in the same project. + # @return [Sawyer::Resource] Result + # @see https://developer.github.com/v3/projects/columns/#move-a-project-column + # @example + # @client.move_project_column(30294, "last") + def move_project_column(id, position, options = {}) + options[:position] = position + post "projects/columns/#{id}/moves", options + end + + # List columns cards + # + # Requires authenticated client + # + # @param id [Integer] Project column id + # @return [Array] Cards in the column + # @see https://developer.github.com/v3/projects/cards/#list-project-cards + # @example + # @client.column_cards(30294) + def column_cards(id, options = {}) + paginate "projects/columns/#{id}/cards", options + end + + # Create project card + # + # Requires authenticated client + # + # @param id [Integer] Project column id + # @option options [String] :note Card contents for a note type + # @option options [Integer] :content_id Issue ID for the card contents + # @option options [String] :content_type Type of content to associate + # with the card. Issue is presently the only avaiable value + # @note If :note is supplied, :content_id and :content_type must be + # excluded. Similarly, if :content_id is supplied, :content_type must + # be set and :note must not be included. + # @return [Sawyer::Resource] Newly created card + # @see https://developer.github.com/v3/projects/cards/#create-a-project-card + # @example Create a project card with a note + # @client.create_project_card(123495, note: 'New note card') + # @example Create a project card for an repository issue + # @client.create_project_card(123495, content_id: 1, content_type: 'Issue') + def create_project_card(id, options = {}) + post "projects/columns/#{id}/cards", options + end + + # Get a project card + # + # Requires authenticated client + # + # @param id [Integer] Project card id + # @return [Sawyer::Resource] Project card + # @see https://developer.github.com/v3/projects/cards/#get-a-project-card + # @example + # @client.project_card(123495) + def project_card(id, options = {}) + get "projects/columns/cards/#{id}", options + end + + # Update a project card + # + # Requires authenticated client + # + # @param id [Integer] Project card id + # @option options [String] :note The card's note content. Only valid for + # cards without another type of content, so this cannot be specified if + # the card already has a content_id and content_type. + # @return [Sawyer::Resource] Updated project card + # @see https://developer.github.com/v3/projects/cards/#update-a-project-card + # @example + # @client.update_project_card(12345, note: 'new note') + def update_project_card(id, options = {}) + patch "projects/columns/cards/#{id}", options + end + + # Move a project card + # + # Requires authenticated client + # + # @param id [Integer] Project card id + # @param position [String] Can be one of top, bottom, + # or after:, where is the id value of a + # card in the same column, or in the new column specified by column_id. + # @option options [Integer] :column_id The column id to move the card to, + # must be column in same project + # @return [Sawyer::Resource] Empty sawyer resource + # @see https://developer.github.com/v3/projects/cards/#move-a-project-card + # @example Move a card to the bottom of the same column + # @client.move_project_card(123495, 'bottom') + # @example Move a card to the top of another column + # @client.move_project_card(123495, 'top', column_id: 59402) + def move_project_card(id, position, options = {}) + options[:position] = position + post "projects/columns/cards/#{id}/moves", options + end + + # Delete a project card + # + # Requires authenticated client + # + # @param id [Integer] Project card id + # @return [Boolean] True of deleted, false otherwise + # @see https://developer.github.com/v3/projects/cards/#delete-a-project-card + # @example + # @client.delete_project_card(123495) + def delete_project_card(id, options = {}) + boolean_from_response :delete, "projects/columns/cards/#{id}", options + end + end # Projects + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/pull_requests.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/pull_requests.rb new file mode 100644 index 0000000..086f573 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/pull_requests.rb @@ -0,0 +1,324 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the Pull Requests API + # + # @see https://developer.github.com/v3/pulls/ + module PullRequests + # List pull requests for a repository + # + # @overload pull_requests(repo, options) + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param options [Hash] Method options + # @option options [String] :state `open` or `closed` or `all`. + # @return [Array] Array of pulls + # @see https://developer.github.com/v3/pulls/#list-pull-requests + # @example + # Octokit.pull_requests('rails/rails', :state => 'closed') + def pull_requests(repo, options = {}) + paginate "#{Repository.path repo}/pulls", options + end + alias pulls pull_requests + + # Get a pull request + # + # @see https://developer.github.com/v3/pulls/#get-a-single-pull-request + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param number [Integer] Number of the pull request to fetch + # @return [Sawyer::Resource] Pull request info + # @example + # Octokit.pull_request('rails/rails', 42, :state => 'closed') + def pull_request(repo, number, options = {}) + get "#{Repository.path repo}/pulls/#{number}", options + end + alias pull pull_request + + # Create a pull request + # + # @see https://developer.github.com/v3/pulls/#create-a-pull-request + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param base [String] The branch (or git ref) you want your changes + # pulled into. This should be an existing branch on the current + # repository. You cannot submit a pull request to one repo that requests + # a merge to a base of another repo. + # @param head [String] The branch (or git ref) where your changes are implemented. + # @param title [String] Title for the pull request + # @param body [String] The body for the pull request (optional). Supports GFM. + # @return [Sawyer::Resource] The newly created pull request + # @example + # @client.create_pull_request("octokit/octokit.rb", "master", "feature-branch", + # "Pull Request title", "Pull Request body") + def create_pull_request(repo, base, head, title, body = nil, options = {}) + pull = { + base: base, + head: head, + title: title + } + pull[:body] = body unless body.nil? + post "#{Repository.path repo}/pulls", options.merge(pull) + end + + # Create a pull request from existing issue + # + # @see https://developer.github.com/v3/pulls/#alternative-input + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param base [String] The branch (or git ref) you want your changes + # pulled into. This should be an existing branch on the current + # repository. You cannot submit a pull request to one repo that requests + # a merge to a base of another repo. + # @param head [String] The branch (or git ref) where your changes are implemented. + # @param issue [Integer] Number of Issue on which to base this pull request + # @return [Sawyer::Resource] The newly created pull request + def create_pull_request_for_issue(repo, base, head, issue, options = {}) + pull = { + base: base, + head: head, + issue: issue + } + post "#{Repository.path repo}/pulls", options.merge(pull) + end + + # Update a pull request + # @overload update_pull_request(repo, number, title=nil, body=nil, state=nil, options = {}) + # @deprecated + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @param number [Integer] Number of pull request to update. + # @param title [String] Title for the pull request. + # @param body [String] Body content for pull request. Supports GFM. + # @param state [String] State of the pull request. `open` or `closed`. + # @overload update_pull_request(repo, number, options = {}) + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @param number [Integer] Number of pull request to update. + # @option options [String] :title Title for the pull request. + # @option options [String] :body Body for the pull request. + # @option options [String] :state State for the pull request. + # @return [Sawyer::Resource] Hash representing updated pull request. + # @see https://developer.github.com/v3/pulls/#update-a-pull-request + # @example + # @client.update_pull_request('octokit/octokit.rb', 67, 'new title', 'updated body', 'closed') + # @example Passing nil for optional attributes to update specific attributes. + # @client.update_pull_request('octokit/octokit.rb', 67, nil, nil, 'open') + # @example Empty body by passing empty string + # @client.update_pull_request('octokit/octokit.rb', 67, nil, '') + def update_pull_request(*args) + arguments = Octokit::Arguments.new(args) + repo = arguments.shift + number = arguments.shift + patch "#{Repository.path repo}/pulls/#{number}", arguments.options + end + + # Close a pull request + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @param number [Integer] Number of pull request to update. + # @return [Sawyer::Resource] Hash representing updated pull request. + # @see https://developer.github.com/v3/pulls/#update-a-pull-request + # @example + # @client.close_pull_request('octokit/octokit.rb', 67) + def close_pull_request(repo, number, options = {}) + options.merge! state: 'closed' + update_pull_request(repo, number, options) + end + + # List commits on a pull request + # + # @see https://developer.github.com/v3/pulls/#list-commits-on-a-pull-request + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param number [Integer] Number of pull request + # @return [Array] List of commits + def pull_request_commits(repo, number, options = {}) + paginate "#{Repository.path repo}/pulls/#{number}/commits", options + end + alias pull_commits pull_request_commits + + # List pull request comments for a repository + # + # By default, Review Comments are ordered by ascending ID. + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param options [Hash] Optional parameters + # @option options [String] :sort created or updated + # @option options [String] :direction asc or desc. Ignored without sort + # parameter. + # @option options [String] :since Timestamp in ISO 8601 + # format: YYYY-MM-DDTHH:MM:SSZ + # + # @return [Array] List of pull request review comments. + # + # @see https://developer.github.com/v3/pulls/comments/#list-comments-in-a-repository + # + # @example Get the pull request review comments in the octokit repository + # @client.issues_comments("octokit/octokit.rb") + # + # @example Get review comments, sort by updated asc since a time + # @client.pull_requests_comments("octokit/octokit.rb", { + # :sort => 'updated', + # :direction => 'asc', + # :since => '2010-05-04T23:45:02Z' + # }) + def pull_requests_comments(repo, options = {}) + paginate("#{Repository.path repo}/pulls/comments", options) + end + alias pulls_comments pull_requests_comments + alias reviews_comments pull_requests_comments + + # List comments on a pull request + # + # @see https://developer.github.com/v3/pulls/comments/#list-comments-on-a-pull-request + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param number [Integer] Number of pull request + # @return [Array] List of comments + def pull_request_comments(repo, number, options = {}) + # return the comments for a pull request + paginate("#{Repository.path repo}/pulls/#{number}/comments", options) + end + alias pull_comments pull_request_comments + alias review_comments pull_request_comments + + # Get a pull request comment + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param comment_id [Integer] Id of comment to get + # @return [Sawyer::Resource] Hash representing the comment + # @see https://developer.github.com/v3/pulls/comments/#get-a-single-comment + # @example + # @client.pull_request_comment("pengwynn/octkit", 1903950) + def pull_request_comment(repo, comment_id, options = {}) + get "#{Repository.path repo}/pulls/comments/#{comment_id}", options + end + alias pull_comment pull_request_comment + alias review_comment pull_request_comment + + # Create a pull request comment + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param pull_id [Integer] Pull request id + # @param body [String] Comment content + # @param commit_id [String] Sha of the commit to comment on. + # @param path [String] Relative path of the file to comment on. + # @param line [Integer] Optional line index in the diff to comment on. + # For a multi-line comment, the last line of the range + # and specify 'start_line' in the 'options'. + # If not specified, the comment will be on the whole file. + # @return [Sawyer::Resource] Hash representing the new comment + # @deprecated The position will be deprecated in the next major version. Please refer to the details below. + # @see https://developer.github.com/v3/pulls/comments/#create-a-comment + # @example + # @client.create_pull_request_comment("octokit/octokit.rb", 163, ":shipit:", + # "2d3201e4440903d8b04a5487842053ca4883e5f0", "lib/octokit/request.rb", 47) + def create_pull_request_comment(repo, pull_id, body, commit_id, path, line = nil, options = {}) + comment = { + body: body, + commit_id: commit_id, + path: path + } + + if line.nil? + comment[:subject_type] = 'file' + else + comment[:line] = line + end + + options.merge! comment + post "#{Repository.path repo}/pulls/#{pull_id}/comments", options + end + alias create_pull_comment create_pull_request_comment + alias create_view_comment create_pull_request_comment + + # Create reply to a pull request comment + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param pull_id [Integer] Pull request id + # @param body [String] Comment contents + # @param comment_id [Integer] Comment id to reply to + # @return [Sawyer::Resource] Hash representing new comment + # @see https://developer.github.com/v3/pulls/comments/#create-a-comment + # @example + # @client.create_pull_request_comment_reply("octokit/octokit.rb", 163, "done.", 1903950) + def create_pull_request_comment_reply(repo, pull_id, body, comment_id, options = {}) + options.merge!({ + body: body, + in_reply_to: comment_id + }) + post "#{Repository.path repo}/pulls/#{pull_id}/comments", options + end + alias create_pull_reply create_pull_request_comment_reply + alias create_review_reply create_pull_request_comment_reply + + # Update pull request comment + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param comment_id [Integer] Id of the comment to update + # @param body [String] Updated comment content + # @return [Sawyer::Resource] Hash representing the updated comment + # @see https://developer.github.com/v3/pulls/comments/#edit-a-comment + # @example + # @client.update_pull_request_comment("octokit/octokit.rb", 1903950, ":shipit:") + def update_pull_request_comment(repo, comment_id, body, options = {}) + options.merge! body: body + patch("#{Repository.path repo}/pulls/comments/#{comment_id}", options) + end + alias update_pull_comment update_pull_request_comment + alias update_review_comment update_pull_request_comment + + # Delete pull request comment + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param comment_id [Integer] Id of the comment to delete + # @return [Boolean] True if deleted, false otherwise + # @see https://developer.github.com/v3/pulls/comments/#delete-a-comment + # @example + # @client.delete_pull_request_comment("octokit/octokit.rb", 1902707) + def delete_pull_request_comment(repo, comment_id, options = {}) + boolean_from_response(:delete, "#{Repository.path repo}/pulls/comments/#{comment_id}", options) + end + alias delete_pull_comment delete_pull_request_comment + alias delete_review_comment delete_pull_request_comment + + # List files on a pull request + # + # @see https://developer.github.com/v3/pulls/#list-pull-requests-files + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param number [Integer] Number of pull request + # @return [Array] List of files + def pull_request_files(repo, number, options = {}) + paginate "#{Repository.path repo}/pulls/#{number}/files", options + end + alias pull_files pull_request_files + + # Update a pull request branch + # + # @see https://developer.github.com/v3/pulls/#update-a-pull-request-branch + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param number [Integer] Number of pull request + # @param options [Hash] Optional parameters (e.g. expected_head_sha) + # @return [Boolean] True if the pull request branch has been updated + def update_pull_request_branch(repo, number, options = {}) + boolean_from_response(:put, "#{Repository.path repo}/pulls/#{number}/update-branch", options) + end + + # Merge a pull request + # + # @see https://developer.github.com/v3/pulls/#merge-a-pull-request-merge-button + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param number [Integer] Number of pull request + # @param commit_message [String] Optional commit message for the merge commit + # @return [Array] Merge commit info if successful + def merge_pull_request(repo, number, commit_message = '', options = {}) + put "#{Repository.path repo}/pulls/#{number}/merge", options.merge({ commit_message: commit_message }) + end + + # Check pull request merge status + # + # @see https://developer.github.com/v3/pulls/#get-if-a-pull-request-has-been-merged + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param number [Integer] Number of pull request + # @return [Boolean] True if the pull request has been merged + def pull_merged?(repo, number, options = {}) + boolean_from_response :get, "#{Repository.path repo}/pulls/#{number}/merge", options + end + alias pull_request_merged? pull_merged? + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/rate_limit.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/rate_limit.rb new file mode 100644 index 0000000..7387a02 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/rate_limit.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for API rate limiting info + # + # @see https://developer.github.com/v3/#rate-limiting + module RateLimit + # Get rate limit info from last response if available + # or make a new request to fetch rate limit + # + # @see https://developer.github.com/v3/rate_limit/#rate-limit + # @return [Octokit::RateLimit] Rate limit info + def rate_limit(_options = {}) + return rate_limit! if last_response.nil? + + Octokit::RateLimit.from_response(last_response) + end + alias ratelimit rate_limit + + # Get number of rate limted requests remaining + # + # @see https://developer.github.com/v3/rate_limit/#rate-limit + # @return [Integer] Number of requests remaining in this period + def rate_limit_remaining(_options = {}) + octokit_warn 'Deprecated: Please use .rate_limit.remaining' + rate_limit.remaining + end + alias ratelimit_remaining rate_limit_remaining + + # Refresh rate limit info by making a new request + # + # @see https://developer.github.com/v3/rate_limit/#rate-limit + # @return [Octokit::RateLimit] Rate limit info + def rate_limit!(_options = {}) + get 'rate_limit' + Octokit::RateLimit.from_response(last_response) + end + alias ratelimit! rate_limit! + + # Refresh rate limit info and get number of rate limted requests remaining + # + # @see https://developer.github.com/v3/rate_limit/#rate-limit + # @return [Integer] Number of requests remaining in this period + def rate_limit_remaining!(_options = {}) + octokit_warn 'Deprecated: Please use .rate_limit!.remaining' + rate_limit!.remaining + end + alias ratelimit_remaining! rate_limit_remaining! + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/reactions.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/reactions.rb new file mode 100644 index 0000000..e959055 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/reactions.rb @@ -0,0 +1,220 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the Reacions API + # + # @see https://developer.github.com/v3/reactions/ + module Reactions + # List reactions for a commit comment + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param id [Integer] The id of the commit comment + # @see https://developer.github.com/v3/reactions/#list-reactions-for-a-commit-comment + # + # @example + # @client.commit_comment_reactions("octokit/octokit.rb", 1) + # + # @return [Array] Array of Hashes representing the reactions. + def commit_comment_reactions(repo, id, options = {}) + get "#{Repository.path repo}/comments/#{id}/reactions", options + end + + # Create a reaction for a commit comment + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param id [Integer] The id of the commit comment + # @param reaction [String] The Reaction + # @see https://developer.github.com/v3/reactions/#create-reaction-for-a-commit-comment + # @see https://developer.github.com/v3/reactions/#reaction-types + # + # @example + # @client.create_commit_comment_reactions("octokit/octokit.rb", 1) + # + # @return [] Hash representing the reaction + def create_commit_comment_reaction(repo, id, reaction, options = {}) + options = options.merge(content: reaction) + post "#{Repository.path repo}/comments/#{id}/reactions", options + end + + # List reactions for an issue + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param number [Integer] The Issue number + # @see https://developer.github.com/v3/reactions/#list-reactions-for-an-issue + # + # @example + # @client.issue_reactions("octokit/octokit.rb", 1) + # + # @return [Array] Array of Hashes representing the reactions. + def issue_reactions(repo, number, options = {}) + get "#{Repository.path repo}/issues/#{number}/reactions", options + end + + # Create reaction for an issue + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param number [Integer] The Issue number + # @param reaction [String] The Reaction + # + # @see https://developer.github.com/v3/reactions/#create-reaction-for-an-issue + # @see https://developer.github.com/v3/reactions/#reaction-types + # + # @example + # @client.create_issue_reaction("octokit/octokit.rb", 1) + # + # @return [] Hash representing the reaction. + def create_issue_reaction(repo, number, reaction, options = {}) + options = options.merge(content: reaction) + post "#{Repository.path repo}/issues/#{number}/reactions", options + end + + # List reactions for an issue comment + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param id [Integer] The Issue comment id + # + # @see https://developer.github.com/v3/reactions/#list-reactions-for-an-issue-comment + # + # @example + # @client.issue_comment_reactions("octokit/octokit.rb", 1) + # + # @return [Array] Array of Hashes representing the reactions. + def issue_comment_reactions(repo, id, options = {}) + get "#{Repository.path repo}/issues/comments/#{id}/reactions", options + end + + # Create reaction for an issue comment + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param id [Integer] The Issue comment id + # @param reaction [String] The Reaction + # + # @see https://developer.github.com/v3/reactions/#create-reaction-for-an-issue-comment + # @see https://developer.github.com/v3/reactions/#reaction-types + # + # @example + # @client.create_issue_comment_reaction("octokit/octokit.rb", 1) + # + # @return [] Hashes representing the reaction. + def create_issue_comment_reaction(repo, id, reaction, options = {}) + options = options.merge(content: reaction) + post "#{Repository.path repo}/issues/comments/#{id}/reactions", options + end + + # Delete a reaction from an issue comment + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param comment_id [Integer] The Issue comment id + # @param reaction_id [Integer] The Reaction id + # + # @see https://docs.github.com/en/rest/reactions/reactions#delete-an-issue-comment-reaction + # + # @example + # @client.delete_issue_comment_reaction("octokit/octokit.rb", 1, 2) + # + # @return [Boolean] Return true if reaction was deleted, false otherwise. + def delete_issue_comment_reaction(repo, comment_id, reaction_id, options = {}) + boolean_from_response :delete, "#{Repository.path repo}/issues/comments/#{comment_id}/reactions/#{reaction_id}", options + end + + # List reactions for a pull request review comment + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param id [Integer] The Issue comment id + # + # @see https://developer.github.com/v3/reactions/#list-reactions-for-a-pull-request-review-comment + # + # @example + # @client.pull_request_review_comment_reactions("octokit/octokit.rb", 1) + # + # @return [Array] Array of Hashes representing the reactions. + def pull_request_review_comment_reactions(repo, id, options = {}) + get "#{Repository.path repo}/pulls/comments/#{id}/reactions", options + end + + # Create reaction for a pull request review comment + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param id [Integer] The Issue comment id + # @param reaction [String] The Reaction + # + # @see https://developer.github.com/v3/reactions/#create-reaction-for-a-pull-request-review-comment + # @see https://developer.github.com/v3/reactions/#reaction-types + # + # @example + # @client.create_pull_request_reiew_comment_reaction("octokit/octokit.rb", 1) + # + # @return [] Hash representing the reaction. + def create_pull_request_review_comment_reaction(repo, id, reaction, options = {}) + options = options.merge(content: reaction) + post "#{Repository.path repo}/pulls/comments/#{id}/reactions", options + end + + # Delete a reaction + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param issue_id [Integer] The Issue comment id + # @param reaction_id [Integer] The Reaction id + # + # @see https://docs.github.com/en/rest/reactions/reactions#delete-an-issue-reaction + # + # @example + # @client.delete_issue_reaction("octokit/octokit.rb", 1, 2) + # + # @return [Boolean] Return true if reaction was deleted, false otherwise. + def delete_issue_reaction(repo, issue_id, reaction_id, options = {}) + boolean_from_response :delete, "#{Repository.path repo}/issues/#{issue_id}/reactions/#{reaction_id}", options + end + + # List reactions for a release + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param id [Integer] The Release id + # + # @see https://docs.github.com/en/free-pro-team@latest/rest/reactions/reactions?apiVersion=2022-11-28#list-reactions-for-a-release + # + # @example + # @client.release_reactions("octokit/octokit.rb", 1) + # + # @return [Array] Array of Hashes representing the reactions. + def release_reactions(repo, release_id, options = {}) + get "#{Repository.path repo}/releases/#{release_id}/reactions", options + end + + # Create reaction for a release + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param id [Integer] The Release id + # @param reaction [String] The Reaction + # + # @see https://docs.github.com/en/free-pro-team@latest/rest/reactions/reactions?apiVersion=2022-11-28#create-reaction-for-a-release + # @see https://developer.github.com/v3/reactions/#reaction-types + # + # @example + # @client.create_release_reaction("octokit/octokit.rb", 1) + # + # @return [] Hash representing the reaction. + def create_release_reaction(repo, release_id, reaction, options = {}) + options = options.merge(content: reaction) + post "#{Repository.path repo}/releases/#{release_id}/reactions", options + end + + # Delete a reaction for a release + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param issue_id [Integer] The Release id + # @param reaction_id [Integer] The Reaction id + # + # @see https://docs.github.com/en/free-pro-team@latest/rest/reactions/reactions?apiVersion=2022-11-28#delete-a-release-reaction + # + # @example + # @client.delete_release_reaction("octokit/octokit.rb", 1, 2) + # + # @return [Boolean] Return true if reaction was deleted, false otherwise. + def delete_release_reaction(repo, release_id, reaction_id, options = {}) + boolean_from_response :delete, "#{Repository.path repo}/releases/#{release_id}/reactions/#{reaction_id}", options + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/refs.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/refs.rb new file mode 100644 index 0000000..6fa52f4 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/refs.rb @@ -0,0 +1,131 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for References for Git Data API + # + # @see https://developer.github.com/v3/git/refs/ + module Refs + # List all refs for a given user and repo + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param namespace [String] The ref namespace, e.g. tag or heads + # @return [Array] A list of references matching the repo and the namespace + # @see https://developer.github.com/v3/git/refs/#get-all-references + # @example Fetch all refs for sferik/rails_admin + # Octokit.refs("sferik/rails_admin") + def refs(repo, namespace = nil, options = {}) + path = "#{Repository.path repo}/git/refs" + path += "/#{namespace}" unless namespace.nil? + paginate path, options + end + alias list_refs refs + alias references refs + alias list_references refs + + # Fetch matching refs + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param ref [String] The ref, e.g. tags/v0.0.3 or heads/rails-3 + # @return [Array] The reference matching the given repo and the ref id + # @see https://developer.github.com/v3/git/refs/#list-matching-references + # @example Fetch refs matching tags/v2 for sferik/rails_admin + # Octokit.ref("sferik/rails_admin","tags/v2") + def matching_refs(repo, ref, options = {}) + paginate "#{Repository.path repo}/git/matching-refs/#{ref}", options + end + + # Fetch a given reference + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param ref [String] The ref, e.g. tags/v0.0.3 + # @return [Sawyer::Resource] The reference matching the given repo and the ref id + # @see https://developer.github.com/v3/git/refs/#get-a-reference + # @example Fetch tags/v0.0.3 for sferik/rails_admin + # Octokit.ref("sferik/rails_admin","tags/v0.0.3") + def ref(repo, ref, options = {}) + get "#{Repository.path repo}/git/refs/#{ref}", options + end + alias reference ref + + # Create a reference + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param ref [String] The ref, e.g. tags/v0.0.3 + # @param sha [String] A SHA, e.g. 827efc6d56897b048c772eb4087f854f46256132 + # @return [Array] The list of references, already containing the new one + # @see https://developer.github.com/v3/git/refs/#create-a-reference + # @example Create refs/heads/master for octocat/Hello-World with sha 827efc6d56897b048c772eb4087f854f46256132 + # Octokit.create_ref("octocat/Hello-World", "heads/master", "827efc6d56897b048c772eb4087f854f46256132") + def create_ref(repo, ref, sha, options = {}) + ref = "refs/#{ref}" unless ref =~ %r{\Arefs/} + parameters = { + ref: ref, + sha: sha + } + post "#{Repository.path repo}/git/refs", options.merge(parameters) + end + alias create_reference create_ref + + # Update a reference + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param ref [String] The ref, e.g. tags/v0.0.3 + # @param sha [String] A SHA, e.g. 827efc6d56897b048c772eb4087f854f46256132 + # @param force [Boolean] A flag indicating whether to force the update or to make sure the update is a fast-forward update. + # @return [Array] The list of references updated + # @see https://developer.github.com/v3/git/refs/#update-a-reference + # @example Force update heads/sc/featureA for octocat/Hello-World with sha aa218f56b14c9653891f9e74264a383fa43fefbd + # Octokit.update_ref("octocat/Hello-World", "heads/sc/featureA", "aa218f56b14c9653891f9e74264a383fa43fefbd") + def update_ref(repo, ref, sha, force = false, options = {}) + parameters = { + sha: sha, + force: force + } + patch "#{Repository.path repo}/git/refs/#{ref}", options.merge(parameters) + end + alias update_reference update_ref + + # Update a branch + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param branch [String] The ref, e.g. feature/new-shiny + # @param sha [String] A SHA, e.g. 827efc6d56897b048c772eb4087f854f46256132 + # @param force [Boolean] A flag indicating whether to force the update or to make sure the update is a fast-forward update. + # @return [Array] The list of references updated + # @see https://developer.github.com/v3/git/refs/#update-a-reference + # @example Force update heads/sc/featureA for octocat/Hello-World with sha aa218f56b14c9653891f9e74264a383fa43fefbd + # Octokit.update_branch("octocat/Hello-World", "sc/featureA", "aa218f56b14c9653891f9e74264a383fa43fefbd") + # @example Fast-forward update heads/sc/featureA for octocat/Hello-World with sha aa218f56b14c9653891f9e74264a383fa43fefbd + # Octokit.update_branch("octocat/Hello-World", "sc/featureA", "aa218f56b14c9653891f9e74264a383fa43fefbd", false) + def update_branch(repo, branch, sha, force = true, options = {}) + update_ref repo, "heads/#{branch}", sha, force, options + end + + # Delete a single branch + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param branch [String] The branch, e.g. fix-refs + # @return [Boolean] Success + # @see https://developer.github.com/v3/git/refs/#delete-a-reference + # @example Delete uritemplate for sigmavirus24/github3.py + # Octokit.delete_branch("sigmavirus24/github3.py", "uritemplate") + def delete_branch(repo, branch, options = {}) + delete_ref repo, "heads/#{branch}", options + end + + # Delete a single reference + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param ref [String] The ref, e.g. tags/v0.0.3 + # @return [Boolean] Success + # @see https://developer.github.com/v3/git/refs/#delete-a-reference + # @example Delete tags/v0.0.3 for sferik/rails_admin + # Octokit.delete_ref("sferik/rails_admin","tags/v0.0.3") + def delete_ref(repo, ref, options = {}) + boolean_from_response :delete, "#{Repository.path repo}/git/refs/#{ref}", options + end + alias delete_reference delete_ref + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/releases.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/releases.rb new file mode 100644 index 0000000..5a6efdd --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/releases.rb @@ -0,0 +1,164 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the Releases API + # + # @see https://developer.github.com/v3/repos/releases/ + module Releases + # List releases for a repository + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @return [Array] A list of releases + # @see https://developer.github.com/v3/repos/releases/#list-releases-for-a-repository + def releases(repo, options = {}) + paginate "#{Repository.path repo}/releases", options + end + alias list_releases releases + + # Create a release + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param tag_name [String] Git tag from which to create release + # @option options [String] :target_commitish Specifies the commitish value that determines where the Git tag is created from. + # @option options [String] :name Name for the release + # @option options [String] :body Content for release notes + # @option options [Boolean] :draft Mark this release as a draft + # @option options [Boolean] :prerelease Mark this release as a pre-release + # @return [Sawyer::Resource] The release + # @see https://developer.github.com/v3/repos/releases/#create-a-release + def create_release(repo, tag_name, options = {}) + opts = options.merge(tag_name: tag_name) + post "#{Repository.path repo}/releases", opts + end + + # Get a release + # + # @param url [String] URL for the release as returned from .releases + # @return [Sawyer::Resource] The release + # @see https://developer.github.com/v3/repos/releases/#get-a-single-release + def release(url, options = {}) + get url, options + end + + # Update a release + # + # @param url [String] URL for the release as returned from .releases + # @option options [String] :tag_name Git tag from which to create release + # @option options [String] :target_commitish Specifies the commitish value that determines where the Git tag is created from. + # @option options [String] :name Name for the release + # @option options [String] :body Content for release notes + # @option options [Boolean] :draft Mark this release as a draft + # @option options [Boolean] :prerelease Mark this release as a pre-release + # @return [Sawyer::Resource] The release + # @see https://developer.github.com/v3/repos/releases/#edit-a-release + def update_release(url, options = {}) + patch url, options + end + alias edit_release update_release + + # Delete a release + # + # @param url [String] URL for the release as returned from .releases + # @return [Boolean] Success or failure + # @see https://developer.github.com/v3/repos/releases/#delete-a-release + def delete_release(url, options = {}) + boolean_from_response(:delete, url, options) + end + + # List release assets + # + # @param release_url [String] URL for the release as returned from .releases + # @return [Array] A list of release assets + # @see https://developer.github.com/v3/repos/releases/#list-assets-for-a-release + def release_assets(release_url, options = {}) + paginate release(release_url).rels[:assets].href, options + end + + # Upload a release asset + # + # @param release_url [String] URL for the release as returned from .releases + # @param path_or_file [String] Path to file to upload + # @option options [String] :content_type The MIME type for the file to upload + # @option options [String] :name The name for the file + # @return [Sawyer::Resource] The release asset + # @see https://developer.github.com/v3/repos/releases/#upload-a-release-asset + def upload_asset(release_url, path_or_file, options = {}) + file = path_or_file.respond_to?(:read) ? path_or_file : File.new(path_or_file, 'rb') + options[:content_type] ||= content_type_from_file(file) + raise Octokit::MissingContentType if options[:content_type].nil? + + unless name = options[:name] + name = File.basename(file.path) + end + upload_url = release(release_url).rels[:upload].href_template.expand(name: name) + + request :post, upload_url, file.read, parse_query_and_convenience_headers(options) + ensure + file&.close + end + + # Get a single release asset + # + # + # @param asset_url [String] URL for the asset as returned from .release_assets + # @return [Sawyer::Resource] The release asset + # @see https://developer.github.com/v3/repos/releases/#get-a-single-release-asset + def release_asset(asset_url, options = {}) + get(asset_url, options) + end + + # Update a release asset + # + # @param asset_url [String] URL for the asset as returned from .release_assets + # @option options [String] :name The name for the file + # @option options [String] :label The download text for the file + # @return [Sawyer::Resource] The release asset + # @see https://developer.github.com/v3/repos/releases/#edit-a-release-asset + def update_release_asset(asset_url, options = {}) + patch(asset_url, options) + end + alias edit_release_asset update_release_asset + + # Delete a release asset + # + # @param asset_url [String] URL for the asset as returned from .release_assets + # @return [Boolean] Success or failure + # @see https://developer.github.com/v3/repos/releases/#delete-a-release-asset + def delete_release_asset(asset_url, options = {}) + boolean_from_response(:delete, asset_url, options) + end + + # Get the release for a given tag + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param tag_name [String] the name for a tag + # @return [Sawyer::Resource] The release + # @see https://developer.github.com/v3/repos/releases/#get-a-release-by-tag-name + def release_for_tag(repo, tag_name, options = {}) + get "#{Repository.path repo}/releases/tags/#{tag_name}", options + end + + # Get the latest release + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @return [Sawyer::Resource] The release + # @see https://developer.github.com/v3/repos/releases/#get-the-latest-release + def latest_release(repo, options = {}) + get "#{Repository.path repo}/releases/latest", options + end + + private + + def content_type_from_file(file) + require 'mime/types' + if mime_type = MIME::Types.type_for(file.path).first + mime_type.content_type + end + rescue LoadError + msg = 'Please pass content_type or install mime-types gem to guess content type from file' + raise Octokit::MissingContentType, msg + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/repositories.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/repositories.rb new file mode 100644 index 0000000..ae59837 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/repositories.rb @@ -0,0 +1,822 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the Repositories API + # + # @see https://developer.github.com/v3/repos/ + module Repositories + # Check if a repository exists + # + # @see https://developer.github.com/v3/repos/#get + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @return [Boolean] + def repository?(repo, options = {}) + !!repository(repo, options) + rescue Octokit::InvalidRepository, Octokit::NotFound + false + end + + # Get a single repository + # + # @see https://developer.github.com/v3/repos/#get + # @see https://developer.github.com/v3/licenses/#get-a-repositorys-license + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @return [Sawyer::Resource] Repository information + def repository(repo, options = {}) + get Repository.path(repo), options + end + alias repo repository + + # Edit a repository + # + # @see https://developer.github.com/v3/repos/#update-a-repository + # @param repo [String, Hash, Repository] A GitHub repository + # @param options [Hash] Repository information to update + # @option options [String] :name Name of the repo + # @option options [String] :description Description of the repo + # @option options [String] :homepage Home page of the repo + # @option options [String] :private `true` makes the repository private, and `false` makes it public. + # @option options [String] :has_issues `true` enables issues for this repo, `false` disables issues. + # @option options [String] :has_wiki `true` enables wiki for this repo, `false` disables wiki. + # @option options [Boolean] :is_template `true` makes the repository a template, `false` makes it not a template. + # @option options [String] :has_downloads `true` enables downloads for this repo, `false` disables downloads. + # @option options [String] :default_branch Update the default branch for this repository. + # @return [Sawyer::Resource] Repository information + def edit_repository(repo, options = {}) + repo = Repository.new(repo) + options[:name] ||= repo.name + patch "repos/#{repo}", options + end + alias edit edit_repository + alias update_repository edit_repository + alias update edit_repository + + # List user repositories + # + # If user is not supplied, repositories for the current + # authenticated user are returned. + # + # @note If the user provided is a GitHub organization, only the + # organization's public repositories will be listed. For retrieving + # organization repositories the {Organizations#organization_repositories} + # method should be used instead. + # @see https://developer.github.com/v3/repos/#list-your-repositories + # @see https://developer.github.com/v3/repos/#list-user-repositories + # @param user [Integer, String] Optional GitHub user login or id for which + # to list repos. + # @return [Array] List of repositories + def repositories(user = nil, options = {}) + paginate "#{User.path user}/repos", options + end + alias list_repositories repositories + alias list_repos repositories + alias repos repositories + + # List all repositories + # + # This provides a dump of every repository, in the order that they were + # created. + # + # @see https://developer.github.com/v3/repos/#list-all-public-repositories + # + # @param options [Hash] Optional options + # @option options [Integer] :since The integer ID of the last Repository + # that you’ve seen. + # @return [Array] List of repositories. + def all_repositories(options = {}) + paginate 'repositories', options + end + + # Star a repository + # + # @param repo [String, Hash, Repository] A GitHub repository + # @return [Boolean] `true` if successfully starred + # @see https://developer.github.com/v3/activity/starring/#star-a-repository + def star(repo, options = {}) + boolean_from_response :put, "user/starred/#{Repository.new(repo)}", options + end + + # Unstar a repository + # + # @param repo [String, Hash, Repository] A GitHub repository + # @return [Boolean] `true` if successfully unstarred + # @see https://developer.github.com/v3/activity/starring/#unstar-a-repository + def unstar(repo, options = {}) + boolean_from_response :delete, "user/starred/#{Repository.new(repo)}", options + end + + # Watch a repository + # + # @param repo [String, Hash, Repository] A GitHub repository + # @return [Boolean] `true` if successfully watched + # @deprecated Use #star instead + # @see https://developer.github.com/v3/activity/watching/#watch-a-repository-legacy + def watch(repo, options = {}) + boolean_from_response :put, "user/watched/#{Repository.new(repo)}", options + end + + # Unwatch a repository + # + # @param repo [String, Hash, Repository] A GitHub repository + # @return [Boolean] `true` if successfully unwatched + # @deprecated Use #unstar instead + # @see https://developer.github.com/v3/activity/watching/#stop-watching-a-repository-legacy + def unwatch(repo, options = {}) + boolean_from_response :delete, "user/watched/#{Repository.new(repo)}", options + end + + # Fork a repository + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @return [Sawyer::Resource] Repository info for the new fork + # @see https://developer.github.com/v3/repos/forks/#create-a-fork + def fork(repo, options = {}) + post "#{Repository.path repo}/forks", options + end + + # Create a repository for a user or organization + # + # @param name [String] Name of the new repo + # @option options [String] :description Description of the repo + # @option options [String] :homepage Home page of the repo + # @option options [String] :private `true` makes the repository private, and `false` makes it public. + # @option options [String] :has_issues `true` enables issues for this repo, `false` disables issues. + # @option options [String] :has_wiki `true` enables wiki for this repo, `false` disables wiki. + # @option options [Boolean] :is_template `true` makes this repo available as a template repository, `false` to prevent it. + # @option options [String] :has_downloads `true` enables downloads for this repo, `false` disables downloads. + # @option options [String] :organization Short name for the org under which to create the repo. + # @option options [Integer] :team_id The id of the team that will be granted access to this repository. This is only valid when creating a repo in an organization. + # @option options [Boolean] :auto_init `true` to create an initial commit with empty README. Default is `false`. + # @option options [String] :gitignore_template Desired language or platform .gitignore template to apply. Ignored if auto_init parameter is not provided. + # @return [Sawyer::Resource] Repository info for the new repository + # @see https://developer.github.com/v3/repos/#create + def create_repository(name, options = {}) + opts = options.dup + organization = opts.delete :organization + opts.merge! name: name + + if organization.nil? + post 'user/repos', opts + else + post "#{Organization.path organization}/repos", opts + end + end + alias create_repo create_repository + alias create create_repository + + # Delete repository + # + # Note: If OAuth is used, 'delete_repo' scope is required + # + # @see https://developer.github.com/v3/repos/#delete-a-repository + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @return [Boolean] `true` if repository was deleted + def delete_repository(repo, options = {}) + boolean_from_response :delete, Repository.path(repo), options + end + alias delete_repo delete_repository + + # Transfer repository + # + # Transfer a repository owned by your organization + # + # @see https://developer.github.com/v3/repos/#transfer-a-repository + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param new_owner [String] The username or organization name the repository will be transferred to. + # @param options [Array] :team_ids ID of the team or teams to add to the repository. Teams can only be added to organization-owned repositories. + # @return [Sawyer::Resource] Repository info for the transferred repository + def transfer_repository(repo, new_owner, options = {}) + post "#{Repository.path repo}/transfer", options.merge({ new_owner: new_owner }) + end + alias transfer_repo transfer_repository + + # Create a repository for a user or organization generated from a template repository + # + # @param repo [Integer, String, Hash, Repository] A GitHub template repository + # @param name [String] Name of the new repo + # @option options [String] :owner Organization or user who the new repository will belong to. + # @option options [String] :description Description of the repo + # @option options [String] :private `true` makes the repository private, and `false` makes it public. + # @option options [Boolean] :include_all_branches `true` copies all branches from the template repository, `false` (default) makes it only copy the master branch. + # @return [Sawyer::Resource] Repository info for the new repository + def create_repository_from_template(repo, name, options = {}) + options.merge! name: name + post "#{Repository.path repo}/generate", options + end + alias create_repo_from_template create_repository_from_template + + # Hide a public repository + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @return [Sawyer::Resource] Updated repository info + def set_private(repo, options = {}) + # GitHub Api for setting private updated to use private attr, rather than public + update_repository repo, options.merge({ private: true }) + end + + # Unhide a private repository + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @return [Sawyer::Resource] Updated repository info + def set_public(repo, options = {}) + # GitHub Api for setting private updated to use private attr, rather than public + update_repository repo, options.merge({ private: false }) + end + + # Get deploy keys on a repo + # + # Requires authenticated client. + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @return [Array] Array of hashes representing deploy keys. + # @see https://developer.github.com/v3/repos/keys/#list-deploy-keys + # @example + # @client.deploy_keys('octokit/octokit.rb') + # @example + # @client.list_deploy_keys('octokit/octokit.rb') + def deploy_keys(repo, options = {}) + paginate "#{Repository.path repo}/keys", options + end + alias list_deploy_keys deploy_keys + + # Get a single deploy key for a repo + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @param id [Integer] Deploy key ID. + # @return [Sawyer::Resource] Deploy key. + # @see https://developer.github.com/v3/repos/keys/#get-a-deploy-key + # @example + # @client.deploy_key('octokit/octokit.rb', 8675309) + def deploy_key(repo, id, options = {}) + get "#{Repository.path repo}/keys/#{id}", options + end + + # Add deploy key to a repo + # + # Requires authenticated client. + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @param title [String] Title reference for the deploy key. + # @param key [String] Public key. + # @return [Sawyer::Resource] Hash representing newly added key. + # @see https://developer.github.com/v3/repos/keys/#add-a-new-deploy-key + # @example + # @client.add_deploy_key('octokit/octokit.rb', 'Staging server', 'ssh-rsa AAA...') + def add_deploy_key(repo, title, key, options = {}) + post "#{Repository.path repo}/keys", options.merge(title: title, key: key) + end + + # Edit a deploy key + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @param id [Integer] Deploy key ID. + # @param options [Hash] Attributes to edit. + # @option title [String] Key title. + # @option key [String] Public key. + # @return [Sawyer::Resource] Updated deploy key. + # @deprecated This method is no longer supported in the API + # @see https://developer.github.com/changes/2014-02-24-finer-grained-scopes-for-ssh-keys/ + # @see https://developer.github.com/v3/repos/keys/#edit-a-deploy-key + # @example Update the key for a deploy key. + # @client.edit_deploy_key('octokit/octokit.rb', 8675309, :key => 'ssh-rsa BBB...') + # @example + # @client.update_deploy_key('octokit/octokit.rb', 8675309, :title => 'Uber', :key => 'ssh-rsa BBB...')) + def edit_deploy_key(repo, id, options) + patch "#{Repository.path repo}/keys/#{id}", options + end + alias update_deploy_key edit_deploy_key + + # Remove deploy key from a repo + # + # Requires authenticated client. + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @param id [Integer] Id of the deploy key to remove. + # @return [Boolean] True if key removed, false otherwise. + # @see https://developer.github.com/v3/repos/keys/#remove-a-deploy-key + # @example + # @client.remove_deploy_key('octokit/octokit.rb', 100000) + def remove_deploy_key(repo, id, options = {}) + boolean_from_response :delete, "#{Repository.path repo}/keys/#{id}", options + end + + # List collaborators + # + # Requires authenticated client for private repos. + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @option options [String] :affiliation Filters the return array by affiliation. + # Can be one of: outside, direct, or all. + # If not specified, defaults to all + # @return [Array] Array of hashes representing collaborating users. + # @see https://developer.github.com/v3/repos/collaborators/#list-collaborators + # @example + # Octokit.collaborators('octokit/octokit.rb') + # @example + # Octokit.collabs('octokit/octokit.rb') + # @example + # @client.collabs('octokit/octokit.rb') + def collaborators(repo, options = {}) + paginate "#{Repository.path repo}/collaborators", options + end + alias collabs collaborators + + # Add collaborator to repo + # + # This can also be used to update the permission of an existing collaborator + # + # Requires authenticated client. + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @param collaborator [String] Collaborator GitHub username to add. + # @option options [String] :permission The permission to grant the collaborator. + # Only valid on organization-owned repositories. + # Can be one of: pull, push, or admin. + # If not specified, defaults to push + # @return [Boolean] True if collaborator added, false otherwise. + # @see https://developer.github.com/v3/repos/collaborators/#add-user-as-a-collaborator + # @example + # @client.add_collaborator('octokit/octokit.rb', 'holman') + # @example + # @client.add_collab('octokit/octokit.rb', 'holman') + # @example Add a collaborator with admin permissions + # @client.add_collaborator('octokit/octokit.rb', 'holman', permission: 'admin') + def add_collaborator(repo, collaborator, options = {}) + boolean_from_response :put, "#{Repository.path repo}/collaborators/#{collaborator}", options + end + alias add_collab add_collaborator + + # Remove collaborator from repo. + # + # Requires authenticated client. + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @param collaborator [String] Collaborator GitHub username to remove. + # @return [Boolean] True if collaborator removed, false otherwise. + # @see https://developer.github.com/v3/repos/collaborators/#remove-user-as-a-collaborator + # @example + # @client.remove_collaborator('octokit/octokit.rb', 'holman') + # @example + # @client.remove_collab('octokit/octokit.rb', 'holman') + def remove_collaborator(repo, collaborator, options = {}) + boolean_from_response :delete, "#{Repository.path repo}/collaborators/#{collaborator}", options + end + alias remove_collab remove_collaborator + + # Checks if a user is a collaborator for a repo. + # + # Requires authenticated client. + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @param collaborator [String] Collaborator GitHub username to check. + # @return [Boolean] True if user is a collaborator, false otherwise. + # @see https://developer.github.com/v3/repos/collaborators/#check-if-a-user-is-a-collaborator + # @example + # @client.collaborator?('octokit/octokit.rb', 'holman') + def collaborator?(repo, collaborator, options = {}) + boolean_from_response :get, "#{Repository.path repo}/collaborators/#{collaborator}", options + end + + # Get a user's permission level for a repo. + # + # Requires authenticated client + # + # @return [Sawyer::Resource] Hash representing the user's permission level for the given repository + # @see https://developer.github.com/v3/repos/collaborators/#review-a-users-permission-level + # @example + # @client.permission_level('octokit/octokit.rb', 'lizzhale') + def permission_level(repo, collaborator, options = {}) + get "#{Repository.path repo}/collaborators/#{collaborator}/permission", options + end + + # List teams for a repo + # + # Requires authenticated client that is an owner or collaborator of the repo. + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @return [Array] Array of hashes representing teams. + # @see https://developer.github.com/v3/repos/#list-teams + # @example + # @client.repository_teams('octokit/pengwynn') + # @example + # @client.repo_teams('octokit/pengwynn') + # @example + # @client.teams('octokit/pengwynn') + def repository_teams(repo, options = {}) + paginate "#{Repository.path repo}/teams", options + end + alias repo_teams repository_teams + alias teams repository_teams + + # List all topics for a repository + # + # Requires authenticated client for private repos. + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @return [Sawyer::Resource] representing the topics for given repo + # @see https://developer.github.com/v3/repos/#list-all-topics-for-a-repository + # @example List topics for octokit/octokit.rb + # Octokit.topics('octokit/octokit.rb') + # @example List topics for octokit/octokit.rb + # client.topics('octokit/octokit.rb') + def topics(repo, options = {}) + paginate "#{Repository.path repo}/topics", options + end + + # Replace all topics for a repository + # + # Requires authenticated client. + # + # @param repo [Integer, String, Repository, Hash] A Github repository + # @param names [Array] An array of topics to add to the repository. + # @return [Sawyer::Resource] representing the replaced topics for given repo + # @see https://developer.github.com/v3/repos/#replace-all-topics-for-a-repository + # @example Replace topics for octokit/octokit.rb + # client.replace_all_topics('octokit/octokit.rb', ['octocat', 'atom', 'electron', 'API']) + # @example Clear all topics for octokit/octokit.rb + # client.replace_all_topics('octokit/octokit.rb', []) + def replace_all_topics(repo, names, options = {}) + put "#{Repository.path repo}/topics", options.merge(names: names) + end + + # List contributors to a repo + # + # Requires authenticated client for private repos. + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @param anon [Boolean] Set true to include anonymous contributors. + # @return [Array] Array of hashes representing users. + # @see https://developer.github.com/v3/repos/#list-contributors + # @example + # Octokit.contributors('octokit/octokit.rb', true) + # @example + # Octokit.contribs('octokit/octokit.rb') + # @example + # @client.contribs('octokit/octokit.rb') + def contributors(repo, anon = nil, options = {}) + options[:anon] = 1 if anon.to_s[/1|true/] + paginate "#{Repository.path repo}/contributors", options + end + alias contribs contributors + + # List stargazers of a repo + # + # Requires authenticated client for private repos. + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @return [Array] Array of hashes representing users. + # @see https://developer.github.com/v3/activity/starring/#list-stargazers + # @example + # Octokit.stargazers('octokit/octokit.rb') + # @example + # @client.stargazers('octokit/octokit.rb') + def stargazers(repo, options = {}) + paginate "#{Repository.path repo}/stargazers", options + end + + # @deprecated Use {#stargazers} instead + # + # List watchers of repo. + # + # Requires authenticated client for private repos. + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @return [Array] Array of hashes representing users. + # @see https://developer.github.com/v3/repos/watching/#list-watchers + # @example + # Octokit.watchers('octokit/octokit.rb') + # @example + # @client.watchers('octokit/octokit.rb') + def watchers(repo, options = {}) + paginate "#{Repository.path repo}/watchers", options + end + + # List forks + # + # Requires authenticated client for private repos. + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @return [Array] Array of hashes representing repos. + # @see https://developer.github.com/v3/repos/forks/#list-forks + # @example + # Octokit.forks('octokit/octokit.rb') + # @example + # Octokit.network('octokit/octokit.rb') + # @example + # @client.forks('octokit/octokit.rb') + def forks(repo, options = {}) + paginate "#{Repository.path repo}/forks", options + end + alias network forks + + # List languages of code in the repo. + # + # Requires authenticated client for private repos. + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @return [Array] Array of Hashes representing languages. + # @see https://developer.github.com/v3/repos/#list-languages + # @example + # Octokit.languages('octokit/octokit.rb') + # @example + # @client.languages('octokit/octokit.rb') + def languages(repo, options = {}) + paginate "#{Repository.path repo}/languages", options + end + + # List tags + # + # Requires authenticated client for private repos. + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @return [Array] Array of hashes representing tags. + # @see https://developer.github.com/v3/repos/#list-tags + # @example + # Octokit.tags('octokit/octokit.rb') + # @example + # @client.tags('octokit/octokit.rb') + def tags(repo, options = {}) + paginate "#{Repository.path repo}/tags", options + end + + # List branches + # + # Requires authenticated client for private repos. + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @return [Array] Array of hashes representing branches. + # @see https://developer.github.com/v3/repos/#list-branches + # @example + # Octokit.branches('octokit/octokit.rb') + # @example + # @client.branches('octokit/octokit.rb') + def branches(repo, options = {}) + paginate "#{Repository.path repo}/branches", options + end + + # Get a single branch from a repository + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @param branch [String] Branch name + # @return [Sawyer::Resource] The branch requested, if it exists + # @see https://developer.github.com/v3/repos/#get-branch + # @example Get branch 'master` from octokit/octokit.rb + # Octokit.branch("octokit/octokit.rb", "master") + def branch(repo, branch, options = {}) + get "#{Repository.path repo}/branches/#{CGI.escape(branch)}", options + end + alias get_branch branch + + # Lock a single branch from a repository + # + # Requires authenticated client + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @param branch [String] Branch name + # @option options [Hash] :required_status_checks If not null, the following keys are required: + # :enforce_admins [boolean] Enforce required status checks for repository administrators. + # :strict [boolean] Require branches to be up to date before merging. + # :contexts [Array] The list of status checks to require in order to merge into this branch + # + # @option options [Hash] :restrictions If not null, the following keys are required: + # :users [Array] The list of user logins with push access + # :teams [Array] The list of team slugs with push access. + # + # Teams and users restrictions are only available for organization-owned repositories. + # @return [Sawyer::Resource] The protected branch + # @see https://developer.github.com/v3/repos/#enabling-and-disabling-branch-protection + # @example + # @client.protect_branch('octokit/octokit.rb', 'master', foo) + def protect_branch(repo, branch, options = {}) + options[:restrictions] ||= nil + options[:required_status_checks] ||= nil + put "#{Repository.path repo}/branches/#{branch}/protection", options + end + + # Get branch protection summary + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @param branch [String] Branch name + # @return [Sawyer::Resource, nil] Branch protection summary or nil if the branch + # is not protected + # @see https://developer.github.com/v3/repos/branches/#get-branch-protection + # @example + # @client.branch_protection('octokit/octokit.rb', 'master') + def branch_protection(repo, branch, options = {}) + get "#{Repository.path repo}/branches/#{branch}/protection", options + rescue Octokit::BranchNotProtected + nil + end + + # Unlock a single branch from a repository + # + # Requires authenticated client + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @param branch [String] Branch name + # @return [Sawyer::Resource] The unprotected branch + # @see https://developer.github.com/v3/repos/#enabling-and-disabling-branch-protection + # @example + # @client.unprotect_branch('octokit/octokit.rb', 'master') + def unprotect_branch(repo, branch, options = {}) + boolean_from_response :delete, "#{Repository.path repo}/branches/#{branch}/protection", options + end + + # Rename a single branch from a repository + # + # Requires authenticated client + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @param branch [String] Current branch name + # @param new_name [String] New branch name + # @return [Sawyer::Resource] The renamed branch + # @see https://developer.github.com/v3/repos/#rename-a-branch + # @example + # @client.rename_branch('octokit/octokit.rb', 'master', 'main') + def rename_branch(repo, branch, new_name, options = {}) + params = { + new_name: new_name + } + post "#{Repository.path repo}/branches/#{branch}/rename", params.merge(options) + end + + # List users available for assigning to issues. + # + # Requires authenticated client for private repos. + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @return [Array] Array of hashes representing users. + # @see https://developer.github.com/v3/issues/assignees/#list-assignees + # @example + # Octokit.repository_assignees('octokit/octokit.rb') + # @example + # Octokit.repo_assignees('octokit/octokit.rb') + # @example + # @client.repository_assignees('octokit/octokit.rb') + def repository_assignees(repo, options = {}) + paginate "#{Repository.path repo}/assignees", options + end + alias repo_assignees repository_assignees + + # Check to see if a particular user is an assignee for a repository. + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @param assignee [String] User login to check + # @return [Boolean] True if assignable on project, false otherwise. + # @see https://developer.github.com/v3/issues/assignees/#check-assignee + # @example + # Octokit.check_assignee('octokit/octokit.rb', 'andrew') + def check_assignee(repo, assignee, options = {}) + boolean_from_response :get, "#{Repository.path repo}/assignees/#{assignee}", options + end + + # List watchers subscribing to notifications for a repo + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @return [Array] Array of users watching. + # @see https://developer.github.com/v3/activity/watching/#list-watchers + # @example + # @client.subscribers("octokit/octokit.rb") + def subscribers(repo, options = {}) + paginate "#{Repository.path repo}/subscribers", options + end + + # Get a repository subscription + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @return [Sawyer::Resource] Repository subscription. + # @see https://developer.github.com/v3/activity/watching/#get-a-repository-subscription + # @example + # @client.subscription("octokit/octokit.rb") + def subscription(repo, options = {}) + get "#{Repository.path repo}/subscription", options + end + + # Update repository subscription + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @param options [Hash] + # + # @option options [Boolean] :subscribed Determines if notifications + # should be received from this repository. + # @option options [Boolean] :ignored Deterimines if all notifications + # should be blocked from this repository. + # @return [Sawyer::Resource] Updated repository subscription. + # @see https://developer.github.com/v3/activity/watching/#set-a-repository-subscription + # @example Subscribe to notifications for a repository + # @client.update_subscription("octokit/octokit.rb", {subscribed: true}) + def update_subscription(repo, options = {}) + put "#{Repository.path repo}/subscription", options + end + + # Delete a repository subscription + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @return [Boolean] True if subscription deleted, false otherwise. + # @see https://developer.github.com/v3/activity/watching/#delete-a-repository-subscription + # + # @example + # @client.delete_subscription("octokit/octokit.rb") + def delete_subscription(repo, options = {}) + boolean_from_response :delete, "#{Repository.path repo}/subscription", options + end + + # Create a repository dispatch event + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @param event_type [String] A custom webhook event name. + # @option options [Hash] :client_payload payload with extra information + # about the webhook event that your action or worklow may use. + # + # @return [Boolean] True if event was dispatched, false otherwise. + # @see https://developer.github.com/v3/repos/#create-a-repository-dispatch-event + def dispatch_event(repo, event_type, options = {}) + boolean_from_response :post, "#{Repository.path repo}/dispatches", options.merge({ event_type: event_type }) + end + + # Check to see if vulnerability alerts are enabled for a repository + # + # The authenticated user must have admin access to the repository. + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @return [Boolean] True if vulnerability alerts are enabled, false otherwise. + # @see https://docs.github.com/en/rest/reference/repos#check-if-vulnerability-alerts-are-enabled-for-a-repository + # + # @example + # @client.vulnerability_alerts_enabled?("octokit/octokit.rb") + def vulnerability_alerts_enabled?(repo, options = {}) + boolean_from_response(:get, "#{Repository.path repo}/vulnerability-alerts", options) + end + + # Enable vulnerability alerts for a repository + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @param options [Hash] + # + # @return [Boolean] True if vulnerability alerts enabled, false otherwise. + # @see https://docs.github.com/en/rest/reference/repos#enable-vulnerability-alerts + # @example Enable vulnerability alerts for a repository + # @client.enable_vulnerability_alerts("octokit/octokit.rb") + def enable_vulnerability_alerts(repo, options = {}) + boolean_from_response(:put, "#{Repository.path repo}/vulnerability-alerts", options) + end + + # Disable vulnerability alerts for a repository + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @param options [Hash] + # + # @return [Boolean] True if vulnerability alerts disabled, false otherwise. + # @see https://docs.github.com/en/rest/reference/repos#disable-vulnerability-alerts + # @example Disable vulnerability alerts for a repository + # @client.disable_vulnerability_alerts("octokit/octokit.rb") + def disable_vulnerability_alerts(repo, options = {}) + boolean_from_response(:delete, "#{Repository.path repo}/vulnerability-alerts", options) + end + + # Check to see if automated security fixes are enabled for a repository + # + # The authenticated user must have admin access to the repository. + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @return [Boolean] True if automated security fixes are enabled, false otherwise. + # @see https://docs.github.com/en/rest/reference/repos#check-if-automated-security-fixes-are-enabled-for-a-repository + # + # @example + # @client.automated_security_fixes_enabled?("octokit/octokit.rb") + def automated_security_fixes_enabled?(repo, options = {}) + response = get "#{Repository.path repo}/automated-security-fixes", options + return response[:enabled] if @last_response.status == 200 + + false + end + + # Enable automated security fixes for a repository + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @param options [Hash] + # + # @return [Boolean] True if vulnerability alerts enabled, false otherwise. + # @see https://docs.github.com/en/rest/reference/repos#automated-security-fixes + # @example Enable automated security fixes for a repository + # @client.enable_automated_security_fixes("octokit/octokit.rb") + def enable_automated_security_fixes(repo, options = {}) + boolean_from_response(:put, "#{Repository.path repo}/automated-security-fixes", options) + end + + # Disable automated security fixes for a repository + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @param options [Hash] + # + # @return [Boolean] True if vulnerability alerts disabled, false otherwise. + # @see https://docs.github.com/en/rest/reference/repos#automated-security-fixes + # @example Disable automated security fixes for a repository + # @client.disable_automated_security_fixes("octokit/octokit.rb") + def disable_automated_security_fixes(repo, options = {}) + boolean_from_response(:delete, "#{Repository.path repo}/automated-security-fixes", options) + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/repository_invitations.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/repository_invitations.rb new file mode 100644 index 0000000..e3e2c80 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/repository_invitations.rb @@ -0,0 +1,96 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the Repository Invitations API + # + # @see https://developer.github.com/v3/repos/invitations/ + module RepositoryInvitations + # Invite a user to a repository + # + # Requires authenticated client + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param user [String] User GitHub username to add + # @return [Sawyer::Resource] The repository invitation + # @see https://developer.github.com/v3/repos/collaborators/#add-user-as-a-collaborator + def invite_user_to_repository(repo, user, options = {}) + put "#{Repository.path repo}/collaborators/#{user}", options + end + alias invite_user_to_repo invite_user_to_repository + + # List all invitations for a repository + # + # Requires authenticated client + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @return [Array] A list of invitations + # @see https://developer.github.com/v3/repos/invitations/#list-invitations-for-a-repository + def repository_invitations(repo, options = {}) + paginate "#{Repository.path repo}/invitations", options + end + alias repo_invitations repository_invitations + + # Delete an invitation for a repository + # + # Requires authenticated client + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param invitation_id [Integer] The id of the invitation + # @return [Boolean] True if the invitation was successfully deleted + # @see https://developer.github.com/v3/repos/invitations/#delete-a-repository-invitation + def delete_repository_invitation(repo, invitation_id, options = {}) + boolean_from_response :delete, "#{Repository.path repo}/invitations/#{invitation_id}", options + end + alias delete_repo_invitation delete_repository_invitation + + # Update an invitation for a repository + # + # Requires authenticated client + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param invitation_id [Integer] The id of the invitation + # @return [Sawyer::Resource] The updated repository invitation + # @see https://developer.github.com/v3/repos/invitations/#update-a-repository-invitation + def update_repository_invitation(repo, invitation_id, options = {}) + patch "#{Repository.path repo}/invitations/#{invitation_id}", options + end + alias update_repo_invitation update_repository_invitation + + # List all repository invitations for the user + # + # Requires authenticated client + # + # @return [Array] The users repository invitations + # @see https://developer.github.com/v3/repos/invitations/#list-a-users-repository-invitations + def user_repository_invitations(options = {}) + paginate '/user/repository_invitations', options + end + alias user_repo_invitations user_repository_invitations + + # Accept a repository invitation + # + # Requires authenticated client + # + # @param invitation_id [Integer] The id of the invitation + # @return [Boolean] True if the acceptance of the invitation was successful + # @see https://developer.github.com/v3/repos/invitations/#accept-a-repository-invitation + def accept_repository_invitation(invitation_id, options = {}) + patch "/user/repository_invitations/#{invitation_id}", options + end + alias accept_repo_invitation accept_repository_invitation + + # Decline a repository invitation + # + # Requires authenticated client + # + # @param invitation_id [Integer] The id of the invitation + # @return [Boolean] True if the acceptance of the invitation was successful + # @see https://developer.github.com/v3/repos/invitations/#decline-a-repository-invitation + def decline_repository_invitation(invitation_id, options = {}) + boolean_from_response :delete, "/user/repository_invitations/#{invitation_id}", options + end + alias decline_invitation decline_repository_invitation + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/reviews.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/reviews.rb new file mode 100644 index 0000000..e1d410e --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/reviews.rb @@ -0,0 +1,227 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the Reviews API + # + # @see https://developer.github.com/v3/pulls/reviews/ + module Reviews + # List reviews on a pull request + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param number [Integer] Number ID of the pull request + # @see https://developer.github.com/v3/pulls/reviews/#list-reviews-on-a-pull-request + # + # @example + # @client.pull_request_reviews('octokit/octokit.rb', 2) + # + # @return [Array] Array of Hashes representing the reviews + def pull_request_reviews(repo, number, options = {}) + paginate "#{Repository.path repo}/pulls/#{number}/reviews", options + end + + # Get a single review + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param number [Integer] Number ID of the pull request + # @param review [Integer] The id of the review + # @see https://developer.github.com/v3/pulls/reviews/#get-a-single-review + # + # @example + # @client.pull_request_review('octokit/octokit.rb', 825, 6505518) + # + # @return [Sawyer::Resource] Hash representing the review + def pull_request_review(repo, number, review, options = {}) + get "#{Repository.path repo}/pulls/#{number}/reviews/#{review}", options + end + + # Delete a pending review + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param number [Integer] Number ID of the pull request + # @param review [Integer] The id of the review + # @see https://developer.github.com/v3/pulls/reviews/#delete-a-pending-review + # + # @example + # @client.delete_pull_request_review('octokit/octokit.rb', 825, 6505518) + # + # @return [Sawyer::Resource] Hash representing the deleted review + def delete_pull_request_review(repo, number, review, options = {}) + delete "#{Repository.path repo}/pulls/#{number}/reviews/#{review}", options + end + + # Get comments for a single review + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param number [Integer] Number ID of the pull request + # @param review [Integer] The id of the review + # @see https://developer.github.com/v3/pulls/reviews/#get-comments-for-a-single-review + # + # @example + # @client.pull_request_review_comments('octokit/octokit.rb', 825, 6505518) + # + # @return [Array] Array of Hashes representing the review comments + def pull_request_review_comments(repo, number, review, options = {}) + paginate "#{Repository.path repo}/pulls/#{number}/reviews/#{review}/comments", options + end + + # Create a pull request review + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param number [Integer] Number ID of the pull request + # @param options [Hash] Method options + # @option options [String] :event The review action (event) to perform; + # can be one of APPROVE, REQUEST_CHANGES, or COMMENT. + # If left blank, the review is left PENDING. + # @option options [String] :body The body text of the pull request review + # @option options [Array] :comments Comments part of the review + # @option comments [String] :path The path to the file being commented on + # @option comments [Integer] :position The position in the file to be commented on + # @option comments [String] :body Body of the comment + # @see https://developer.github.com/v3/pulls/reviews/#create-a-pull-request-review + # + # @example + # comments = [ + # { path: '.travis.yml', position: 10, body: 'ruby-head is under development that is not stable.' }, + # { path: '.travis.yml', position: 32, body: 'ruby-head is also required in thervm section.' }, + # ] + # options = { event: 'REQUEST_CHANGES', comments: comments } + # @client.create_pull_request_review('octokit/octokit.rb', 844, options) + # + # @return [Sawyer::Resource>] Hash respresenting the review + def create_pull_request_review(repo, number, options = {}) + post "#{Repository.path repo}/pulls/#{number}/reviews", options + end + + # Submit a pull request review + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param number [Integer] Number ID of the pull request + # @param review [Integer] The id of the review + # @param event [String] The review action (event) to perform; can be one of + # APPROVE, REQUEST_CHANGES, or COMMENT. + # @param options [Hash] Method options + # @option options [String] :body The body text of the pull request review + # @see https://developer.github.com/v3/pulls/reviews/#submit-a-pull-request-review + # + # @example + # @client.submit_pull_request_review('octokit/octokit.rb', 825, 6505518, + # 'APPROVE', body: 'LGTM!') + # + # @return [Sawyer::Resource] Hash respresenting the review + def submit_pull_request_review(repo, number, review, event, options = {}) + options = options.merge(event: event) + post "#{Repository.path repo}/pulls/#{number}/reviews/#{review}/events", options + end + + # Dismiss a pull request review + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param number [Integer] Number ID of the pull request + # @param review [Integer] The id of the review + # @param message [String] The message for the pull request review dismissal + # @see https://developer.github.com/v3/pulls/reviews/#dismiss-a-pull-request-review + # + # @example + # @client.dismiss_pull_request_review('octokit/octokit.rb', 825, 6505518, 'The message.') + # + # @return [Sawyer::Resource] Hash representing the dismissed review + def dismiss_pull_request_review(repo, number, review, message, options = {}) + options = options.merge(message: message) + put "#{Repository.path repo}/pulls/#{number}/reviews/#{review}/dismissals", options + end + + # List review requests + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param number [Integer] Number ID of the pull request + # @see https://developer.github.com/v3/pulls/review_requests/#list-review-requests + # + # @example + # @client.pull_request_review_requests('octokit/octokit.rb', 2) + # + # @return [Array] Array of Hashes representing the review requests + def pull_request_review_requests(repo, number, options = {}) + paginate "#{Repository.path repo}/pulls/#{number}/requested_reviewers", options + end + + # Create a review request + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param number [Integer] Number ID of the pull request + # @param reviewers [Hash] :reviewers [Array] An array of user logins + # @param options [Hash] :team_reviewers [Array] An array of team slugs + # @see https://developer.github.com/v3/pulls/review_requests/#request-reviewers-for-a-pull-request + # + # @example + # @client.request_pull_request_review('octokit/octokit.rb', 2, reviewers: ['soudy']) + # + # @return [Sawyer::Resource>] Hash respresenting the pull request + def request_pull_request_review(repo, number, reviewers = {}, options = {}) + # TODO(5.0): remove deprecated behavior + if reviewers.is_a?(Array) + octokit_warn( + 'Deprecated: Octokit::Client#request_pull_request_review ' \ + "no longer takes a separate :reviewers argument.\n" \ + 'Please update your call to pass :reviewers and :team_reviewers as part of the options hash.' + ) + options = options.merge(reviewers: reviewers) + else + options = options.merge(reviewers) + end + + post "#{Repository.path repo}/pulls/#{number}/requested_reviewers", options + end + + # Delete a review request + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param id [Integer] The id of the pull request + # @param reviewers [Hash] :reviewers [Array] An array of user logins + # @param options [Hash] :team_reviewers [Array] An array of team slugs + # + # @see https://developer.github.com/v3/pulls/review_requests/#delete-a-review-request + # + # @example + # options = { + # "reviewers" => [ "octocat", "hubot", "other_user" ], + # "team_reviewers" => [ "justice-league" ] + # } + # @client.delete_pull_request_review_request('octokit/octokit.rb', 2, options) + # + # @return [Sawyer::Resource>] Hash representing the pull request + def delete_pull_request_review_request(repo, id, reviewers = {}, options = {}) + # TODO(5.0): remove deprecated behavior + if !reviewers.empty? && !options.empty? + octokit_warn( + 'Deprecated: Octokit::Client#delete_pull_request_review_request ' \ + "no longer takes a separate :reviewers argument.\n" \ + 'Please update your call to pass :reviewers and :team_reviewers as part of the options hash.' + ) + end + # For backwards compatibility, this endpoint can be called with a separate reviewers hash. + # If not called with a separate hash, then 'reviewers' is, in fact, 'options'. + options = options.merge(reviewers) + delete "#{Repository.path repo}/pulls/#{id}/requested_reviewers", options + end + + # Update a review request comment + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param number [Integer] Number ID of the pull request + # @param review [Integer] The id of the review + # @param body [String] body text of the pull request review. + # @param options [Hash] Method options + # @see https://developer.github.com/v3/pulls/reviews/#update-a-pull-request-review + # + # @example + # @client.update_pull_request_review('octokit/octokit.rb', 825, 6505518, 'This is close to perfect! Please address the suggested inline change. And add more about this.') + # + # @return [Sawyer::Resource] Hash representing the review comment + def update_pull_request_review(repo, number, review, body, options = {}) + options[:body] = body + put "#{Repository.path repo}/pulls/#{number}/reviews/#{review}", options + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/say.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/say.rb new file mode 100644 index 0000000..6245adf --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/say.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the unpublished Octocat API + module Say + # Return a nifty ASCII Octocat with GitHub wisdom + # or your own + # + # @return [String] + def say(text = nil, options = {}) + options[:s] = text if text + get 'octocat', options + end + alias octocat say + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/search.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/search.rb new file mode 100644 index 0000000..6d030d5 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/search.rb @@ -0,0 +1,105 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the Search API + # + # @see https://developer.github.com/v3/search/ + module Search + # Search code + # + # @param query [String] Search term and qualifiers + # @param options [Hash] Sort and pagination options + # @option options [String] :sort Sort field + # @option options [String] :order Sort order (asc or desc) + # @option options [Integer] :page Page of paginated results + # @option options [Integer] :per_page Number of items per page + # @return [Sawyer::Resource] Search results object + # @see https://developer.github.com/v3/search/#search-code + def search_code(query, options = {}) + search 'search/code', query, options + end + + # Search commits + # + # @param query [String] Search terms and qualifiers + # @param options [Hash] Sort and pagination options + # @option options [String] :sort Sort field + # @option options [String] :order Sort order (asc or desc) + # @option options [Integer] :page Page of paginated results + # @option options [Integer] :per_page Number of items per page + # @return [Sawyer::Resource] Search results object + # @see https://developer.github.com/v3/search/#search-commits + def search_commits(query, options = {}) + search 'search/commits', query, options + end + + # Search issues + # + # @param query [String] Search term and qualifiers + # @param options [Hash] Sort and pagination options + # @option options [String] :sort Sort field + # @option options [String] :order Sort order (asc or desc) + # @option options [Integer] :page Page of paginated results + # @option options [Integer] :per_page Number of items per page + # @return [Sawyer::Resource] Search results object + # @see https://developer.github.com/v3/search/#search-issues-and-pull-requests + # @see https://docs.github.com/en/rest/search#limitations-on-query-length + def search_issues(query, options = {}) + search 'search/issues', query, options + end + + # Search repositories + # + # @param query [String] Search term and qualifiers + # @param options [Hash] Sort and pagination options + # @option options [String] :sort Sort field + # @option options [String] :order Sort order (asc or desc) + # @option options [Integer] :page Page of paginated results + # @option options [Integer] :per_page Number of items per page + # @return [Sawyer::Resource] Search results object + # @see https://developer.github.com/v3/search/#search-repositories + def search_repositories(query, options = {}) + search 'search/repositories', query, options + end + alias search_repos search_repositories + + # Search topics + # + # @param query [String] Search term and qualifiers + # @param options [Hash] Sort and pagination options + # @option options [String] :sort Sort field + # @option options [String] :order Sort order (asc or desc) + # @option options [Integer] :page Page of paginated results + # @option options [Integer] :per_page Number of items per page + # @return [Sawyer::Resource] Search results object + # @see https://developer.github.com/v3/search/#search-topics + def search_topics(query, options = {}) + search 'search/topics', query, options + end + + # Search users + # + # @param query [String] Search term and qualifiers + # @param options [Hash] Sort and pagination options + # @option options [String] :sort Sort field + # @option options [String] :order Sort order (asc or desc) + # @option options [Integer] :page Page of paginated results + # @option options [Integer] :per_page Number of items per page + # @return [Sawyer::Resource] Search results object + # @see https://developer.github.com/v3/search/#search-users + def search_users(query, options = {}) + search 'search/users', query, options + end + + private + + def search(path, query, options = {}) + opts = options.merge(q: query) + paginate(path, opts) do |data, last_response| + data.items.concat last_response.data.items + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/service_status.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/service_status.rb new file mode 100644 index 0000000..2e8a182 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/service_status.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the GitHub Status API + # + # @see https://status.github.com/api + module ServiceStatus + # Root for status API + # @private + SUMMARY_ROOT = 'https://www.githubstatus.com/api/v2/summary.json' + STATUS_ROOT = 'https://www.githubstatus.com/api/v2/status.json' + COMPONENTS_ROOT = 'https://www.githubstatus.com/api/v2/components.json' + + # Returns a summary with the current status and the last status messages. + # + # @return [] GitHub status summary + # @see https://www.githubstatus.com/api#summory + def github_status_summary + get(SUMMARY_ROOT) + end + + # Returns the current system status + # + # @return [Sawyer::Resource] GitHub status + # @see https://www.githubstatus.com/api#status + def github_status + get(STATUS_ROOT) + end + + # Returns the last human communication, status, and timestamp. + # + # @return [Sawyer::Resource] GitHub status last message + # @see https://www.githubstatus.com/api/#components + def github_status_last_message + get(COMPONENTS_ROOT).components.first + end + + # Returns the most recent human communications with status and timestamp. + # + # @return [Array] GitHub status messages + # @see https://www.githubstatus.com/api#components + def github_status_messages + get(COMPONENTS_ROOT).components + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/source_import.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/source_import.rb new file mode 100644 index 0000000..7cc13a9 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/source_import.rb @@ -0,0 +1,156 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the Source Import API + # + # @see https://developer.github.com/v3/migration/source_imports + module SourceImport + # Start a source import to a GitHub repository using GitHub Importer. + # + # @overload start_source_import(repo, vcs, vcs_url, options = {}) + # @deprecated + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @param vcs [String] The originating VCS type. Can be one of "subversion", "git", "mercurial", or "tfvc". + # @param vcs_url [String] The URL of the originating repository. + # @param options [Hash] + # @option options [String] :vcs_username If authentication is required, the username to provide to vcs_url. + # @option options [String] :vcs_password If authentication is required, the password to provide to vcs_url. + # @option options [String] :tfvc_project For a tfvc import, the name of the project that is being imported. + # @overload start_source_import(repo, vcs_url, options = {}) + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @param vcs_url [String] The URL of the originating repository. + # @param options [Hash] + # @param options [String] :vcs The originating VCS type. Can be one of "subversion", "git", "mercurial", or "tfvc". + # @option options [String] :vcs_username If authentication is required, the username to provide to vcs_url. + # @option options [String] :vcs_password If authentication is required, the password to provide to vcs_url. + # @option options [String] :tfvc_project For a tfvc import, the name of the project that is being imported. + # @return [Sawyer::Resource] Hash representing the repository import + # @see https://developer.github.com/v3/migration/source_imports/#start-an-import + # + # @example + # @client.start_source_import("octokit/octokit.rb", "http://svn.mycompany.com/svn/myproject", { + # :vcs => "subversion", + # :vcs_username" => "octocat", + # :vcs_password => "secret" + # }) + def start_source_import(*args) + arguments = Octokit::RepoArguments.new(args) + vcs_url = arguments.pop + vcs = arguments.pop + if vcs + octokit_warn 'Octokit#start_source_import vcs parameter is now an option, please update your call before the next major Octokit version update.' + arguments.options.merge!(vcs: vcs) + end + options = arguments.options.merge(vcs_url: vcs_url) + put "#{Repository.path arguments.repo}/import", options + end + + # View the progress of an import. + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @return [Sawyer::Resource] Hash representing the progress of the import + # @see https://developer.github.com/v3/migration/source_imports/#get-import-progress + # + # @example + # @client.source_import_progress("octokit/octokit.rb") + def source_import_progress(repo, options = {}) + get "#{Repository.path repo}/import", options + end + + # Update source import with authentication or project choice + # Restart source import if no options are passed + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @return [Sawyer::Resource] Hash representing the repository import + # @see https://developer.github.com/v3/migration/source_imports/#update-existing-import + # @option options [String] :vcs_username If authentication is required, the username to provide to vcs_url. + # @option options [String] :vcs_password If authentication is required, the password to provide to vcs_url. + # @option options [String] To update project choice, please refer to the project_choice array from the progress return hash for the exact attributes. + # https://developer.github.com/v3/migration/source_imports/#update-existing-import + # + # @example + # @client.update_source_import("octokit/octokit.rb", { + # :vcs_username" => "octocat", + # :vcs_password => "secret" + # }) + def update_source_import(repo, options = {}) + patch "#{Repository.path repo}/import", options + end + + # List source import commit authors + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @param options [Hash] + # @option options [String] :since Only authors found after this id are returned. + # @return [Array] Array of hashes representing commit_authors. + # @see https://developer.github.com/v3/migration/source_imports/#get-commit-authors + # + # @example + # @client.source_import_commit_authors("octokit/octokit.rb") + def source_import_commit_authors(repo, options = {}) + get "#{Repository.path repo}/import/authors", options + end + + # Update an author's identity for the import. + # + # @param author_url [String] The source import API url for the commit author + # @param values [Hash] The updated author attributes + # @option values [String] :email The new Git author email. + # @option values [String] :name The new Git author name. + # @return [Sawyer::Resource] Hash representing the updated commit author + # @see https://developer.github.com/v3/migration/source_imports/#map-a-commit-author + # + # @example + # author_url = "https://api.github.com/repos/octokit/octokit.rb/import/authors/1" + # @client.map_source_import_commit_author(author_url, { + # :email => "hubot@github.com", + # :name => "Hubot the Robot" + # }) + def map_source_import_commit_author(author_url, values, options = {}) + options = options.merge(values) + patch author_url, options + end + + # Stop an import for a repository. + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @return [Boolean] True if the import has been cancelled, false otherwise. + # @see https://developer.github.com/v3/migration/source_imports/#cancel-an-import + # + # @example + # @client.cancel_source_import("octokit/octokit.rb") + def cancel_source_import(repo, options = {}) + boolean_from_response :delete, "#{Repository.path repo}/import", options + end + + # List source import large files + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @param options [Hash] + # @option options [Integer] :page Page of paginated results + # @return [Array] Array of hashes representing files over 100MB. + # @see https://developer.github.com/v3/migration/source_imports/#get-large-files + # + # @example + # @client.source_import_large_files("octokit/octokit.rb") + def source_import_large_files(repo, options = {}) + get "#{Repository.path repo}/import/large_files", options + end + + # Set preference for using Git LFS to import files over 100MB + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @param use_lfs [String] Preference for using Git LFS to import large files. Can be one of "opt_in" or "opt_out" + # @return [Sawyer::Resource] Hash representing the repository import + # @see https://developer.github.com/v3/migration/source_imports/#set-git-lfs-preference + # + # @example + # @client.opt_in_source_import_lfs("octokit/octokit.rb", "opt_in") + def set_source_import_lfs_preference(repo, use_lfs, options = {}) + options = options.merge(use_lfs: use_lfs) + patch "#{Repository.path repo}/import/lfs", options + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/stats.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/stats.rb new file mode 100644 index 0000000..637efa6 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/stats.rb @@ -0,0 +1,108 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the Repository Statistics API + # + # @see https://developer.github.com/v3/repos/statistics/ + module Stats + # Get contributors list with additions, deletions, and commit counts + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @option retry_timeout [Number] How long Octokit should keep trying to get stats (in seconds) + # @option retry_wait [Number] How long Octokit should wait between retries. + # @return [Array] Array of contributor stats + # @see https://developer.github.com/v3/repos/statistics/#get-contributors-list-with-additions-deletions-and-commit-counts + # @example Get contributor stats for octokit + # @client.contributors_stats('octokit/octokit.rb') + def contributors_stats(repo, options = {}) + get_stats(repo, 'contributors', options) + end + alias contributor_stats contributors_stats + + # Get the last year of commit activity data + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @option retry_timeout [Number] How long Octokit should keep trying to get stats (in seconds) + # @option retry_wait [Number] How long Octokit should wait between retries. + # @return [Array] The last year of commit activity grouped by + # week. The days array is a group of commits per day, starting on Sunday. + # @see https://developer.github.com/v3/repos/statistics/#get-the-last-year-of-commit-activity-data + # @example Get commit activity for octokit + # @client.commit_activity_stats('octokit/octokit.rb') + def commit_activity_stats(repo, options = {}) + get_stats(repo, 'commit_activity', options) + end + + # Get the number of additions and deletions per week + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @option retry_timeout [Number] How long Octokit should keep trying to get stats (in seconds) + # @option retry_wait [Number] How long Octokit should wait between retries. + # @return [Array] Weekly aggregate of the number of additions + # and deletions pushed to a repository. + # @see https://developer.github.com/v3/repos/statistics/#get-the-number-of-additions-and-deletions-per-week + # @example Get code frequency stats for octokit + # @client.code_frequency_stats('octokit/octokit.rb') + def code_frequency_stats(repo, options = {}) + get_stats(repo, 'code_frequency', options) + end + + # Get the weekly commit count for the repo owner and everyone else + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @option retry_timeout [Number] How long Octokit should keep trying to get stats (in seconds) + # @option retry_wait [Number] How long Octokit should wait between retries. + # @return [Sawyer::Resource] Total commit counts for the owner and total commit + # counts in all. all is everyone combined, including the owner in the last + # 52 weeks. If you’d like to get the commit counts for non-owners, you can + # subtract all from owner. + # @see https://developer.github.com/v3/repos/statistics/#get-the-weekly-commit-count-for-the-repository-owner-and-everyone-else + # @example Get weekly commit counts for octokit + # @client.participation_stats("octokit/octokit.rb") + def participation_stats(repo, options = {}) + get_stats(repo, 'participation', options) + end + + # Get the number of commits per hour in each day + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @option retry_timeout [Number] How long Octokit should keep trying to get stats (in seconds) + # @option retry_wait [Number] How long Octokit should wait between retries. + # @return [Array] Arrays containing the day number, hour number, and + # number of commits + # @see https://developer.github.com/v3/repos/statistics/#get-the-number-of-commits-per-hour-in-each-day + # @example Get octokit punch card + # @octokit.punch_card_stats + def punch_card_stats(repo, options = {}) + get_stats(repo, 'punch_card', options) + end + alias punch_card punch_card_stats + + private + + # @private Get stats for a repository + # + # @param repo [Integer, String, Hash, Repository] A GitHub repository + # @param metric [String] The metrics you are looking for + # @return [Array or nil] Stats in metric-specific format, or nil if not yet calculated. + # @see https://developer.github.com/v3/repos/statistics/ + def get_stats(repo, metric, options = {}) + options = options.dup + if retry_timeout = options.delete(:retry_timeout) + retry_wait = options.delete(:retry_wait) || 0.5 + timeout = Time.now + retry_timeout + end + loop do + data = get("#{Repository.path repo}/stats/#{metric}", options) + return data if last_response.status == 200 + return [] if last_response.status == 204 + return nil unless retry_timeout + return nil if Time.now >= timeout + + sleep retry_wait if retry_wait + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/statuses.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/statuses.rb new file mode 100644 index 0000000..6696424 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/statuses.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the Commit Statuses API + # + # @see https://developer.github.com/v3/repos/statuses/ + module Statuses + # List all statuses for a given commit + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param sha [String] The SHA1 for the commit + # @return [Array] A list of statuses + # @see https://developer.github.com/v3/repos/statuses/#list-statuses-for-a-specific-ref + def statuses(repo, sha, options = {}) + paginate "#{Repository.path repo}/statuses/#{sha}", options + end + alias list_statuses statuses + + # Get the combined status for a ref + # + # @param repo [Integer, String, Repository, Hash] a GitHub repository + # @param ref [String] A Sha or Ref to fetch the status of + # @return [Sawyer::Resource] The combined status for the commit + # @see https://developer.github.com/v3/repos/statuses/#get-the-combined-status-for-a-specific-ref + def combined_status(repo, ref, options = {}) + get "#{Repository.path repo}/commits/#{ref}/status", options + end + alias status combined_status + + # Create status for a commit + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @param sha [String] The SHA1 for the commit + # @param state [String] The state: pending, success, failure, error + # @option options [String] :context A context to differentiate this status from others + # @option options [String] :target_url A link to more details about this status + # @option options [String] :description A short human-readable description of this status + # @return [Sawyer::Resource] A status + # @see https://developer.github.com/v3/repos/statuses/#create-a-status + def create_status(repo, sha, state, options = {}) + options = options.merge(state: state) + post "#{Repository.path repo}/statuses/#{sha}", options + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/tokens.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/tokens.rb new file mode 100644 index 0000000..05771db --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/tokens.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Method to check scopes + # + # @see https://developer.github.com/v3/oauth_authorizations/#oauth-authorizations-api + module Tokens + # Check scopes for a token + # + # @param token [String] GitHub OAuth token + # @param options [Hash] Header params for request + # @return [Array] OAuth scopes + # @see https://developer.github.com/v3/oauth/#scopes + def scopes(token = @access_token, options = {}) + options = options.dup + raise ArgumentError, 'Access token required' if token.nil? + + auth = { 'Authorization' => "token #{token}" } + headers = (options.delete(:headers) || {}).merge(auth) + + agent.call(:get, 'user', headers: headers) + .headers['X-OAuth-Scopes'] + .to_s + .split(',') + .map(&:strip) + .sort + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/traffic.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/traffic.rb new file mode 100644 index 0000000..eccb52d --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/traffic.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the Traffic API + # + # @see https://developer.github.com/v3/repos/traffic/ + module Traffic + # Get the top 10 referrers over the last 14 days + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @return [Array] List of referrers and stats + # @see https://developer.github.com/v3/repos/traffic/#list-referrers + # @example + # @client.top_referrers('octokit/octokit.rb') + def top_referrers(repo, options = {}) + get "#{Repository.path repo}/traffic/popular/referrers", options + end + + # Get the top 10 popular contents over the last 14 days + # + # @param repo [Integer, String, Repository, Hash] A GitHub repository + # @return [Array] List of popular contents + # @see https://developer.github.com/v3/repos/traffic/#list-paths + # @example + # @client.top_paths('octokit/octokit.rb') + def top_paths(repo, options = {}) + get "#{Repository.path repo}/traffic/popular/paths", options + end + + # Get the total number of views and breakdown per day or week for the + # last 14 days + # + # @param repo [Integer, String, Repository, Hash] A GitHub Repository + # @option options [String] :per ('day') Views per. day or + # week + # @return [Sawyer::Resource] Breakdown of view stats + # @see https://developer.github.com/v3/repos/traffic/#views + # @example Views per day + # @client.views('octokit/octokit.rb') + # @example Views per week + # @client.views('octokit/octokit.rb', per: 'week') + def views(repo, options = {}) + get "#{Repository.path repo}/traffic/views", options + end + + # Get the total number of clones and breakdown per day or week for the + # last 14 days + # + # @param repo [Integer, String, Repository, Hash] A GitHub Repository + # @option options [String] :per ('day') Views per. day or + # week + # @return [Sawyer::Resource] Breakdown of clone stats + # @see https://developer.github.com/v3/repos/traffic/#clones + # @example Clones per day + # @client.clones('octokit/octokit.rb') + # @example Clones per week + # @client.clones('octokit/octokit.rb', per: 'week') + def clones(repo, options = {}) + get "#{Repository.path repo}/traffic/clones", options + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/users.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/users.rb new file mode 100644 index 0000000..34e5579 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/client/users.rb @@ -0,0 +1,462 @@ +# frozen_string_literal: true + +module Octokit + class Client + # Methods for the Users API + # + # @see https://developer.github.com/v3/users/ + module Users + # List all GitHub users + # + # This provides a list of every user, in the order that they signed up + # for GitHub. + # + # @param options [Hash] Optional options. + # @option options [Integer] :since The integer ID of the last User that + # you’ve seen. + # + # @see https://developer.github.com/v3/users/#get-all-users + # + # @return [Array] List of GitHub users. + def all_users(options = {}) + paginate 'users', options + end + + # Get a single user + # + # @param user [Integer, String] GitHub user login or id. + # @return [Sawyer::Resource] + # @see https://developer.github.com/v3/users/#get-a-single-user + # @see https://developer.github.com/v3/users/#get-the-authenticated-user + # @example + # Octokit.user("sferik") + def user(user = nil, options = {}) + get User.path(user), options + end + + # Retrieve the access_token. + # + # @param code [String] Authorization code generated by GitHub. + # @param app_id [String] Client Id we received when our application was registered with GitHub. Defaults to client_id. + # @param app_secret [String] Client Secret we received when our application was registered with GitHub. Defaults to client_secret. + # @return [Sawyer::Resource] Hash holding the access token. + # @see https://developer.github.com/v3/oauth/#web-application-flow + # @example + # Octokit.exchange_code_for_token('aaaa', 'xxxx', 'yyyy', {:accept => 'application/json'}) + def exchange_code_for_token(code, app_id = client_id, app_secret = client_secret, options = {}) + options = options.merge({ + code: code, + client_id: app_id, + client_secret: app_secret, + headers: { + content_type: 'application/json', + accept: 'application/json' + } + }) + + post "#{web_endpoint}login/oauth/access_token", options + end + + # Refresh a user's access token with a refresh token. + # + # Applications can refresh an access token without requiring a user to re-authorize using refresh access token. + # + # @param code [String] 40 character GitHub OAuth refresh access token + # + # @return [Sawyer::Resource] + # @see https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/refreshing-user-access-tokens#refreshing-a-user-access-token-with-a-refresh-token + # + # @example + # client = Octokit::Client.new(:client_id => 'abcdefg12345', :client_secret => 'secret') + # client.refresh_access_token('40-character-refresh-token') + def refresh_access_token(code, app_id = client_id, app_secret = client_secret, options = {}) + options = options.merge({ + refresh_token: code, + client_id: app_id, + client_secret: app_secret, + grant_type: 'refresh_token', + headers: { + content_type: 'application/json', + accept: 'application/json' + } + }) + + post "#{web_endpoint}login/oauth/access_token", options + end + + # Validate user username and password + # + # @param options [Hash] User credentials + # @option options [String] :login GitHub login + # @option options [String] :password GitHub password + # @return [Boolean] True if credentials are valid + def validate_credentials(options = {}) + !self.class.new(options).user.nil? + rescue Octokit::Unauthorized + false + end + + # Update the authenticated user + # + # @param options [Hash] A customizable set of options. + # @option options [String] :name + # @option options [String] :email Publically visible email address. + # @option options [String] :blog + # @option options [String] :company + # @option options [String] :location + # @option options [Boolean] :hireable + # @option options [String] :bio + # @return [Sawyer::Resource] + # @see https://developer.github.com/v3/users/#update-the-authenticated-user + # @example + # Octokit.update_user(:name => "Erik Michaels-Ober", :email => "sferik@gmail.com", :company => "Code for America", :location => "San Francisco", :hireable => false) + def update_user(options) + patch 'user', options + end + + # Get a user's followers. + # + # @param user [Integer, String] GitHub user login or id of the user whose + # list of followers you are getting. + # @return [Array] Array of hashes representing users + # followers. + # @see https://developer.github.com/v3/users/followers/#list-followers-of-a-user + # @example + # Octokit.followers('pengwynn') + def followers(user = login, options = {}) + paginate "#{User.path user}/followers", options + end + + # Get list of users a user is following. + # + # @param user [Intger, String] GitHub user login or id of the user who you + # are getting the list of the people they follow. + # @return [Array] Array of hashes representing users a + # user is following. + # @see https://developer.github.com/v3/users/followers/#list-users-followed-by-another-user + # @example + # Octokit.following('pengwynn') + def following(user = login, options = {}) + paginate "#{User.path user}/following", options + end + + # Check if you are following a user. Alternatively, check if a given user + # is following a target user. + # + # Requries an authenticated client. + # + # @overload follows?(target) + # @param target [String] GitHub login of the user that you want to + # check if you are following. + # @overload follows?(user, target) + # @param user [Integer, String] GitHub user login or id of first user + # @param target [String] GitHub login of the target user + # @return [Boolean] True following target user, false otherwise. + # @see https://developer.github.com/v3/users/followers/#check-if-you-are-following-a-user + # @see https://developer.github.com/v3/users/followers/#check-if-one-user-follows-another + # @example + # @client.follows?('pengwynn') + # @example + # @client.follows?('catsby', 'pengwynn') + def follows?(*args) + target = args.pop + user = args.first + boolean_from_response :get, "#{User.path user}/following/#{target}" + end + + # Follow a user. + # + # Requires authenticatied client. + # + # @param user [String] Username of the user to follow. + # @return [Boolean] True if follow was successful, false otherwise. + # @see https://developer.github.com/v3/users/followers/#follow-a-user + # @example + # @client.follow('holman') + def follow(user, options = {}) + boolean_from_response :put, "user/following/#{user}", options + end + + # Unfollow a user. + # + # Requires authenticated client. + # + # @param user [String] Username of the user to unfollow. + # @return [Boolean] True if unfollow was successful, false otherwise. + # @see https://developer.github.com/v3/users/followers/#unfollow-a-user + # @example + # @client.unfollow('holman') + def unfollow(user, options = {}) + boolean_from_response :delete, "user/following/#{user}", options + end + + # Get list of repos starred by a user. + # + # @param user [Integer, String] GitHub user login of the user to get the + # list of their starred repositories. + # @param options [Hash] Optional options + # @option options [String] :sort (created) Sort: created or updated. + # @option options [String] :direction (desc) Direction: asc or desc. + # @return [Array] Array of hashes representing repositories starred by user. + # @see https://developer.github.com/v3/activity/starring/#list-repositories-being-starred + # @example + # Octokit.starred('pengwynn') + def starred(user = login, options = {}) + paginate user_path(user, 'starred'), options + end + + # Check if you are starring a repo. + # + # Requires authenticated client. + # + # @param repo [String, Hash, Repository] A GitHub repository + # @return [Boolean] True if you are following the repo, false otherwise. + # @see https://developer.github.com/v3/activity/starring/#check-if-you-are-starring-a-repository + # @example + # @client.starred?('pengwynn/octokit') + def starred?(repo, options = {}) + boolean_from_response :get, "user/starred/#{Repository.new(repo)}", options + end + + # Get a public key. + # + # Note, when using dot notation to retrieve the values, ruby will return + # the hash key for the public keys value instead of the actual value, use + # symbol or key string to retrieve the value. See example. + # + # Requires authenticated client. + # + # @param key_id [Integer] Key to retreive. + # @return [Sawyer::Resource] Hash representing the key. + # @see https://developer.github.com/v3/users/keys/#get-a-single-public-key + # @example + # @client.key(1) + # @example Retrieve public key contents + # public_key = @client.key(1) + # public_key.key + # # => Error + # + # public_key[:key] + # # => "ssh-rsa AAA..." + # + # public_key['key'] + # # => "ssh-rsa AAA..." + def key(key_id, options = {}) + get "user/keys/#{key_id}", options + end + + # Get list of public keys for user. + # + # Requires authenticated client. + # + # @return [Array] Array of hashes representing public keys. + # @see https://developer.github.com/v3/users/keys/#list-your-public-keys + # @example + # @client.keys + def keys(options = {}) + paginate 'user/keys', options + end + + # Get list of public keys for user. + # + # @param user [Integer, String] GitHub user login or id. + # @return [Array] Array of hashes representing public keys. + # @see https://developer.github.com/v3/users/keys/#list-public-keys-for-a-user + # @example + # @client.user_keys('pengwynn') + def user_keys(user, options = {}) + # TODO: Roll this into .keys + paginate "#{User.path user}/keys", options + end + + # Add public key to user account. + # + # Requires authenticated client. + # + # @param title [String] Title to give reference to the public key. + # @param key [String] Public key. + # @return [Sawyer::Resource] Hash representing the newly added public key. + # @see https://developer.github.com/v3/users/keys/#create-a-public-key + # @example + # @client.add_key('Personal projects key', 'ssh-rsa AAA...') + def add_key(title, key, options = {}) + post 'user/keys', options.merge({ title: title, key: key }) + end + + # Update a public key + # + # Requires authenticated client + # + # @param key_id [Integer] Id of key to update. + # @param options [Hash] Hash containing attributes to update. + # @option options [String] :title + # @option options [String] :key + # @return [Sawyer::Resource] Hash representing the updated public key. + # + # @deprecated This method is no longer supported in the API + # @see https://developer.github.com/v3/users/keys/#update-a-public-key + # @see https://developer.github.com/changes/2014-02-24-finer-grained-scopes-for-ssh-keys/ + # @example + # @client.update_key(1, :title => 'new title', :key => "ssh-rsa BBB") + def update_key(key_id, options = {}) + patch "user/keys/#{key_id}", options + end + + # Remove a public key from user account. + # + # Requires authenticated client. + # + # @param id [String] Id of the public key to remove. + # @return [Boolean] True if removal was successful, false otherwise. + # @see https://developer.github.com/v3/users/keys/#delete-a-public-key + # @example + # @client.remove_key(1) + def remove_key(id, options = {}) + boolean_from_response :delete, "user/keys/#{id}", options + end + + # List email addresses for a user. + # + # Requires authenticated client. + # + # @return [Array] Array of email addresses. + # @see https://developer.github.com/v3/users/emails/#list-email-addresses-for-a-user + # @example + # @client.emails + def emails(options = {}) + paginate 'user/emails', options + end + + # Add email address to user. + # + # Requires authenticated client. + # + # @param email [String] Email address to add to the user. + # @return [Array] Array of all email addresses of the user. + # @see https://developer.github.com/v3/users/emails/#add-email-addresses + # @example + # @client.add_email('new_email@user.com') + def add_email(email, _options = {}) + email = Array(email) + post 'user/emails', email + end + + # Remove email from user. + # + # Requires authenticated client. + # + # @param email [String] Email address to remove. + # @return [Array] Array of all email addresses of the user. + # @see https://developer.github.com/v3/users/emails/#delete-email-addresses + # @example + # @client.remove_email('old_email@user.com') + def remove_email(email) + email = Array(email) + boolean_from_response :delete, 'user/emails', email + end + + # List repositories being watched by a user. + # + # @param user [Integer, String] GitHub user login or id. + # @return [Array] Array of repositories. + # @see https://developer.github.com/v3/activity/watching/#list-repositories-being-watched + # @example + # @client.subscriptions("pengwynn") + def subscriptions(user = login, options = {}) + paginate user_path(user, 'subscriptions'), options + end + alias watched subscriptions + + # Initiates the generation of a migration archive. + # + # Requires authenticated user. + # + # @param repositories [Array] :repositories Repositories for the organization. + # @option options [Boolean, optional] :lock_repositories Indicates whether repositories should be locked during migration + # @option options [Boolean, optional] :exclude_attachments Exclude attachments fro the migration data + # @return [Sawyer::Resource] Hash representing the new migration. + # @example + # @client.start_migration(['octocat/hello-world']) + # @see https://docs.github.com/en/rest/reference/migrations#start-a-user-migration + def start_user_migration(repositories, options = {}) + options[:repositories] = repositories + post 'user/migrations', options + end + + # Lists the most recent migrations. + # + # Requires authenticated user. + # + # @return [Array] Array of migration resources. + # @see https://docs.github.com/en/rest/reference/migrations#list-user-migrations + def user_migrations(options = {}) + paginate 'user/migrations', options + end + + # Fetches the status of a migration. + # + # Requires authenticated user. + # + # @param id [Integer] ID number of the migration. + # @see https://docs.github.com/en/rest/reference/migrations#get-a-user-migration-status + def user_migration_status(id, options = {}) + get "user/migrations/#{id}", options + end + + # Fetches the URL to a migration archive. + # + # Requires authenticated user. + # + # @param id [Integer] ID number of the migration. + # @see https://docs.github.com/en/rest/reference/migrations#download-a-user-migration-archive + def user_migration_archive_url(id, options = {}) + url = "user/migrations/#{id}/archive" + + response = client_without_redirects(options).get(url) + response.headers['location'] + end + + # Deletes a previous migration archive. + # + # Requires authenticated user. + # + # @param id [Integer] ID number of the migration. + # @see https://docs.github.com/en/rest/reference/migrations#delete-a-user-migration-archive + def delete_user_migration_archive(id, options = {}) + delete "user/migrations/#{id}/archive", options + end + + # List repositories for a user migration. + # + # Requires authenticated user. + # + # @param id [Integer] ID number of the migration. + # @see https://docs.github.com/en/rest/reference/migrations#list-repositories-for-a-user-migration + def user_migration_repositories(id, options = {}) + get "user/migrations/#{id}/repositories", options + end + + # Unlock a user repository which has been locked by a migration. + # + # Requires authenticated user. + # + # @param id [Integer] ID number of the migration. + # @param repo [String] Name of the repository. + # @see https://docs.github.com/en/rest/reference/migrations#unlock-a-user-repository + def unlock_user_repository(id, repo, options = {}) + delete "user/migrations/#{id}/repos/#{repo}/lock", options + end + end + + private + + # convenience method for constructing a user specific path, if the user is logged in + def user_path(user, path) + if user == login && user_authenticated? + "user/#{path}" + else + "#{User.path user}/#{path}" + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/configurable.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/configurable.rb new file mode 100644 index 0000000..af75019 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/configurable.rb @@ -0,0 +1,171 @@ +# frozen_string_literal: true + +module Octokit + # Configuration options for {Client}, defaulting to values + # in {Default} + module Configurable + # @!attribute [w] access_token + # @see https://developer.github.com/v3/oauth/ + # @return [String] OAuth2 access token for authentication + # @!attribute api_endpoint + # @return [String] Base URL for API requests. default: https://api.github.com/ + # @!attribute auto_paginate + # @return [Boolean] Auto fetch next page of results until rate limit reached + # @!attribute [w] bearer_token + # @see https://developer.github.com/early-access/integrations/authentication/#as-an-integration + # @return [String] JWT bearer token for authentication + # @!attribute client_id + # @see https://developer.github.com/v3/oauth/ + # @return [String] Configure OAuth app key + # @!attribute [w] client_secret + # @see https://developer.github.com/v3/oauth/ + # @return [String] Configure OAuth app secret + # @!attribute default_media_type + # @see https://developer.github.com/v3/media/ + # @return [String] Configure preferred media type (for API versioning, for example) + # @!attribute connection_options + # @see https://github.com/lostisland/faraday + # @return [Hash] Configure connection options for Faraday + # @!attribute login + # @return [String] GitHub username for Basic Authentication + # @!attribute management_console_password + # @return [String] An admin password set up for your GitHub Enterprise management console + # @!attribute management_console_endpoint + # @return [String] Base URL for API requests to the GitHub Enterprise management console + # @!attribute manage_ghes_endpoint + # @return [String] Base URL for API requests to the GitHub Enterprise Server Manage API + # @!attribute manage_ghes_username + # @return [String] API username for requests to the GitHub Enterprise Server Manage API + # @!attribute manage_ghes_password + # @return [String] API user password for requests to the GitHub Enterprise Server Manage API + # @!attribute middleware + # @see https://github.com/lostisland/faraday + # @return [Faraday::Builder or Faraday::RackBuilder] Configure middleware for Faraday + # @!attribute netrc + # @return [Boolean] Instruct Octokit to get credentials from .netrc file + # @!attribute netrc_file + # @return [String] Path to .netrc file. default: ~/.netrc + # @!attribute [w] password + # @return [String] GitHub password for Basic Authentication + # @!attribute per_page + # @return [String] Configure page size for paginated results. API default: 30 + # @!attribute proxy + # @see https://github.com/lostisland/faraday + # @return [String] URI for proxy server + # @!attribute ssl_verify_mode + # @see https://github.com/lostisland/faraday + # @return [String] SSL verify mode for ssl connections + # @!attribute user_agent + # @return [String] Configure User-Agent header for requests. + # @!attribute web_endpoint + # @return [String] Base URL for web URLs. default: https://github.com/ + + attr_accessor :access_token, :auto_paginate, :bearer_token, :client_id, + :client_secret, :default_media_type, :connection_options, + :middleware, :netrc, :netrc_file, + :per_page, :proxy, :ssl_verify_mode, :user_agent + attr_writer :password, :web_endpoint, :api_endpoint, :login, + :management_console_endpoint, :management_console_password, + :manage_ghes_endpoint, + :manage_ghes_username, + :manage_ghes_password + + class << self + # List of configurable keys for {Octokit::Client} + # @return [Array] of option keys + def keys + @keys ||= %i[ + access_token + api_endpoint + auto_paginate + bearer_token + client_id + client_secret + connection_options + default_media_type + login + management_console_endpoint + management_console_password + manage_ghes_endpoint + manage_ghes_username + manage_ghes_password + middleware + netrc + netrc_file + per_page + password + proxy + ssl_verify_mode + user_agent + web_endpoint + ] + end + end + + # Set configuration options using a block + def configure + yield self + end + + # Reset configuration options to default values + def reset! + # rubocop:disable Style/HashEachMethods + # + # This may look like a `.keys.each` which should be replaced with `#each_key`, but + # this doesn't actually work, since `#keys` is just a method we've defined ourselves. + # The class doesn't fulfill the whole `Enumerable` contract. + Octokit::Configurable.keys.each do |key| + # rubocop:enable Style/HashEachMethods + instance_variable_set(:"@#{key}", Octokit::Default.options[key]) + end + self + end + alias setup reset! + + # Compares client options to a Hash of requested options + # + # @param opts [Hash] Options to compare with current client options + # @return [Boolean] + def same_options?(opts) + opts.hash == options.hash + end + + def api_endpoint + File.join(@api_endpoint, '') + end + + def management_console_endpoint + File.join(@management_console_endpoint, '') + end + + def manage_ghes_endpoint + File.join(@manage_ghes_endpoint, '') + end + + # Base URL for generated web URLs + # + # @return [String] Default: https://github.com/ + def web_endpoint + File.join(@web_endpoint, '') + end + + def login + @login ||= (user.login if token_authenticated?) + end + + def netrc? + !!@netrc + end + + private + + def options + Octokit::Configurable.keys.to_h { |key| [key, instance_variable_get(:"@#{key}")] } + end + + def fetch_client_id_and_secret(overrides = {}) + opts = options.merge(overrides) + opts.values_at :client_id, :client_secret + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/connection.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/connection.rb new file mode 100644 index 0000000..089b604 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/connection.rb @@ -0,0 +1,220 @@ +# frozen_string_literal: true + +require 'sawyer' +require 'octokit/authentication' +module Octokit + # Network layer for API clients. + module Connection + include Octokit::Authentication + + # Header keys that can be passed in options hash to {#get},{#head} + CONVENIENCE_HEADERS = Set.new(%i[accept content_type]) + + # Make a HTTP GET request + # + # @param url [String] The path, relative to {#api_endpoint} + # @param options [Hash] Query and header params for request + # @return [Sawyer::Resource] + def get(url, options = {}) + request :get, url, parse_query_and_convenience_headers(options) + end + + # Make a HTTP POST request + # + # @param url [String] The path, relative to {#api_endpoint} + # @param options [Hash] Body and header params for request + # @return [Sawyer::Resource] + def post(url, options = {}) + request :post, url, options + end + + # Make a HTTP PUT request + # + # @param url [String] The path, relative to {#api_endpoint} + # @param options [Hash] Body and header params for request + # @return [Sawyer::Resource] + def put(url, options = {}) + request :put, url, options + end + + # Make a HTTP PATCH request + # + # @param url [String] The path, relative to {#api_endpoint} + # @param options [Hash] Body and header params for request + # @return [Sawyer::Resource] + def patch(url, options = {}) + request :patch, url, options + end + + # Make a HTTP DELETE request + # + # @param url [String] The path, relative to {#api_endpoint} + # @param options [Hash] Query and header params for request + # @return [Sawyer::Resource] + def delete(url, options = {}) + request :delete, url, options + end + + # Make a HTTP HEAD request + # + # @param url [String] The path, relative to {#api_endpoint} + # @param options [Hash] Query and header params for request + # @return [Sawyer::Resource] + def head(url, options = {}) + request :head, url, parse_query_and_convenience_headers(options) + end + + # Make one or more HTTP GET requests, optionally fetching + # the next page of results from URL in Link response header based + # on value in {#auto_paginate}. + # + # @param url [String] The path, relative to {#api_endpoint} + # @param options [Hash] Query and header params for request + # @param block [Block] Block to perform the data concatination of the + # multiple requests. The block is called with two parameters, the first + # contains the contents of the requests so far and the second parameter + # contains the latest response. + # @return [Sawyer::Resource] + def paginate(url, options = {}) + opts = parse_query_and_convenience_headers(options) + if @auto_paginate || @per_page + opts[:query][:per_page] ||= @per_page || (@auto_paginate ? 100 : nil) + end + + data = request(:get, url, opts.dup) + + if @auto_paginate + while @last_response.rels[:next] && rate_limit.remaining > 0 + @last_response = @last_response.rels[:next].get(headers: opts[:headers]) + if block_given? + yield(data, @last_response) + else + data.concat(@last_response.data) if @last_response.data.is_a?(Array) + end + end + + end + + data + end + + # Hypermedia agent for the GitHub API + # + # @return [Sawyer::Agent] + def agent + @agent ||= Sawyer::Agent.new(endpoint, sawyer_options) do |http| + http.headers[:accept] = default_media_type + http.headers[:content_type] = 'application/json' + http.headers[:user_agent] = user_agent + http_cache_middleware = http.builder.handlers.delete(Faraday::HttpCache) if Faraday.const_defined?(:HttpCache) + if basic_authenticated? + http.request(*FARADAY_BASIC_AUTH_KEYS, @login, @password) + elsif token_authenticated? + http.request :authorization, 'token', @access_token + elsif bearer_authenticated? + http.request :authorization, 'Bearer', @bearer_token + elsif application_authenticated? + http.request(*FARADAY_BASIC_AUTH_KEYS, @client_id, @client_secret) + end + http.builder.handlers.push(http_cache_middleware) unless http_cache_middleware.nil? + end + end + + # Fetch the root resource for the API + # + # @return [Sawyer::Resource] + def root + get '/' + end + + # Response for last HTTP request + # + # @return [Sawyer::Response] + def last_response + @last_response if defined? @last_response + end + + protected + + def endpoint + api_endpoint + end + + private + + def reset_agent + @agent = nil + end + + def request(method, path, data, options = {}) + if data.is_a?(Hash) + options[:query] = data.delete(:query) || {} + options[:headers] = data.delete(:headers) || {} + if accept = data.delete(:accept) + options[:headers][:accept] = accept + end + end + + @last_response = response = agent.call(method, Addressable::URI.parse(path.to_s).normalize.to_s, data, options) + response_data_correctly_encoded(response) + rescue Octokit::Error => e + @last_response = nil + raise e + end + + # Executes the request, checking if it was successful + # + # @return [Boolean] True on success, false otherwise + def boolean_from_response(method, path, options = {}) + request(method, path, options) + [201, 202, 204].include? @last_response.status + rescue Octokit::NotFound + false + end + + def sawyer_options + opts = { + links_parser: Sawyer::LinkParsers::Simple.new + } + conn_opts = @connection_options + conn_opts[:builder] = @middleware.dup if @middleware + conn_opts[:proxy] = @proxy if @proxy + if conn_opts[:ssl].nil? + conn_opts[:ssl] = { verify_mode: @ssl_verify_mode } if @ssl_verify_mode + else + verify = @connection_options[:ssl][:verify] + conn_opts[:ssl] = { + verify: verify, + verify_mode: verify == false ? 0 : @ssl_verify_mode + } + end + opts[:faraday] = Faraday.new(conn_opts) + + opts + end + + def parse_query_and_convenience_headers(options) + options = options.dup + headers = options.delete(:headers) { {} } + CONVENIENCE_HEADERS.each do |h| + if header = options.delete(h) + headers[h] = header + end + end + query = options.delete(:query) + opts = { query: options } + opts[:query].merge!(query) if query.is_a?(Hash) + opts[:headers] = headers unless headers.empty? + + opts + end + + def response_data_correctly_encoded(response) + content_type = response.headers.fetch('content-type', '') + return response.data unless content_type.include?('charset') && response.data.is_a?(String) + + reported_encoding = content_type.match(/charset=([^ ]+)/)[1] + response.data.force_encoding(reported_encoding) + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/default.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/default.rb new file mode 100644 index 0000000..ca2e2fb --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/default.rb @@ -0,0 +1,207 @@ +# frozen_string_literal: true + +require 'octokit/middleware/follow_redirects' +require 'octokit/response/raise_error' +require 'octokit/response/feed_parser' +require 'octokit/version' +require 'octokit/warnable' + +if Gem::Version.new(Faraday::VERSION) >= Gem::Version.new('2.0') + begin + require 'faraday/retry' + rescue LoadError + Octokit::Warnable.octokit_warn 'To use retry middleware with Faraday v2.0+, install `faraday-retry` gem' + end +end + +module Octokit + # Default configuration options for {Client} + module Default + # Default API endpoint + API_ENDPOINT = 'https://api.github.com' + + # Default User Agent header string + USER_AGENT = "Octokit Ruby Gem #{Octokit::VERSION}" + + # Default media type + MEDIA_TYPE = 'application/vnd.github.v3+json' + + # Default WEB endpoint + WEB_ENDPOINT = 'https://github.com' + + # Default Faraday middleware stack + MIDDLEWARE = Faraday::RackBuilder.new do |builder| + # In Faraday 2.x, Faraday::Request::Retry was moved to a separate gem + # so we use it only when it's available. + if defined?(Faraday::Request::Retry) + retry_exceptions = Faraday::Request::Retry::DEFAULT_EXCEPTIONS + [Octokit::ServerError] + builder.use Faraday::Request::Retry, exceptions: retry_exceptions + elsif defined?(Faraday::Retry::Middleware) + retry_exceptions = Faraday::Retry::Middleware::DEFAULT_EXCEPTIONS + [Octokit::ServerError] + builder.use Faraday::Retry::Middleware, exceptions: retry_exceptions + end + + builder.use Octokit::Middleware::FollowRedirects + builder.use Octokit::Response::RaiseError + builder.use Octokit::Response::FeedParser + builder.adapter Faraday.default_adapter + end + + class << self + # Configuration options + # @return [Hash] + def options + Octokit::Configurable.keys.to_h { |key| [key, send(key)] } + end + + # Default access token from ENV + # @return [String] + def access_token + ENV.fetch('OCTOKIT_ACCESS_TOKEN', nil) + end + + # Default API endpoint from ENV or {API_ENDPOINT} + # @return [String] + def api_endpoint + ENV.fetch('OCTOKIT_API_ENDPOINT') { API_ENDPOINT } + end + + # Default pagination preference from ENV + # @return [String] + def auto_paginate + ENV.fetch('OCTOKIT_AUTO_PAGINATE', nil) + end + + # Default bearer token from ENV + # @return [String] + def bearer_token + ENV.fetch('OCTOKIT_BEARER_TOKEN', nil) + end + + # Default OAuth app key from ENV + # @return [String] + def client_id + ENV.fetch('OCTOKIT_CLIENT_ID', nil) + end + + # Default OAuth app secret from ENV + # @return [String] + def client_secret + ENV.fetch('OCTOKIT_SECRET', nil) + end + + # Default management console password from ENV + # @return [String] + def management_console_password + ENV.fetch('OCTOKIT_ENTERPRISE_MANAGEMENT_CONSOLE_PASSWORD', nil) + end + + # Default management console endpoint from ENV + # @return [String] + def management_console_endpoint + ENV.fetch('OCTOKIT_ENTERPRISE_MANAGEMENT_CONSOLE_ENDPOINT', nil) + end + + # Default GHES Manage API endpoint from ENV + # @return [String] + def manage_ghes_endpoint + ENV.fetch('OCTOKIT_MANAGE_GHES_ENDPOINT', nil) + end + + # Default GHES Manage API username from ENV + # @return [String] + def manage_ghes_username + ENV.fetch('OCTOKIT_MANAGE_GHES_USERNAME', nil) + end + + # Default GHES Manage API password from ENV + # @return [String] + def manage_ghes_password + ENV.fetch('OCTOKIT_MANAGE_GHES_PASSWORD', nil) + end + + # Default options for Faraday::Connection + # @return [Hash] + def connection_options + { + headers: { + accept: default_media_type, + user_agent: user_agent + } + } + end + + # Default media type from ENV or {MEDIA_TYPE} + # @return [String] + def default_media_type + ENV.fetch('OCTOKIT_DEFAULT_MEDIA_TYPE') { MEDIA_TYPE } + end + + # Default GitHub username for Basic Auth from ENV + # @return [String] + def login + ENV.fetch('OCTOKIT_LOGIN', nil) + end + + # Default middleware stack for Faraday::Connection + # from {MIDDLEWARE} + # @return [Faraday::RackBuilder or Faraday::Builder] + def middleware + MIDDLEWARE + end + + # Default GitHub password for Basic Auth from ENV + # @return [String] + def password + ENV.fetch('OCTOKIT_PASSWORD', nil) + end + + # Default pagination page size from ENV + # @return [Integer] Page size + def per_page + page_size = ENV.fetch('OCTOKIT_PER_PAGE', nil) + + page_size&.to_i + end + + # Default proxy server URI for Faraday connection from ENV + # @return [String] + def proxy + ENV.fetch('OCTOKIT_PROXY', nil) + end + + # Default SSL verify mode from ENV + # @return [Integer] + def ssl_verify_mode + # 0 is OpenSSL::SSL::VERIFY_NONE + # 1 is OpenSSL::SSL::SSL_VERIFY_PEER + # the standard default for SSL is SSL_VERIFY_PEER which requires a server certificate check on the client + ENV.fetch('OCTOKIT_SSL_VERIFY_MODE', 1).to_i + end + + # Default User-Agent header string from ENV or {USER_AGENT} + # @return [String] + def user_agent + ENV.fetch('OCTOKIT_USER_AGENT') { USER_AGENT } + end + + # Default web endpoint from ENV or {WEB_ENDPOINT} + # @return [String] + def web_endpoint + ENV.fetch('OCTOKIT_WEB_ENDPOINT') { WEB_ENDPOINT } + end + + # Default behavior for reading .netrc file + # @return [Boolean] + def netrc + ENV.fetch('OCTOKIT_NETRC', false) + end + + # Default path for .netrc file + # @return [String] + def netrc_file + ENV.fetch('OCTOKIT_NETRC_FILE') { File.join(Dir.home.to_s, '.netrc') } + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/enterprise_admin_client.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/enterprise_admin_client.rb new file mode 100644 index 0000000..8b334ee --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/enterprise_admin_client.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +require 'octokit/connection' +require 'octokit/configurable' +require 'octokit/warnable' +require 'octokit/enterprise_admin_client/admin_stats' +require 'octokit/enterprise_admin_client/license' +require 'octokit/enterprise_admin_client/orgs' +require 'octokit/enterprise_admin_client/search_indexing' +require 'octokit/enterprise_admin_client/users' + +module Octokit + # EnterpriseAdminClient is only meant to be used by GitHub Enterprise Admins + # and provides access the Admin only API endpoints including Admin Stats, + # Management Console, and the Search Indexing API. + # + # @see Octokit::Client Use Octokit::Client for regular API use for GitHub + # and GitHub Enterprise. + # @see https://developer.github.com/v3/enterprise/ + class EnterpriseAdminClient + include Octokit::Configurable + include Octokit::Connection + include Octokit::Warnable + include Octokit::EnterpriseAdminClient::AdminStats + include Octokit::EnterpriseAdminClient::License + include Octokit::EnterpriseAdminClient::Orgs + include Octokit::EnterpriseAdminClient::SearchIndexing + include Octokit::EnterpriseAdminClient::Users + + def initialize(options = {}) + # Use options passed in, but fall back to module defaults + # + # rubocop:disable Style/HashEachMethods + # + # This may look like a `.keys.each` which should be replaced with `#each_key`, but + # this doesn't actually work, since `#keys` is just a method we've defined ourselves. + # The class doesn't fulfill the whole `Enumerable` contract. + Octokit::Configurable.keys.each do |key| + # rubocop:enable Style/HashEachMethods + instance_variable_set(:"@#{key}", options[key] || Octokit.instance_variable_get(:"@#{key}")) + end + + login_from_netrc unless user_authenticated? || application_authenticated? + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/enterprise_admin_client/admin_stats.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/enterprise_admin_client/admin_stats.rb new file mode 100644 index 0000000..80d3d81 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/enterprise_admin_client/admin_stats.rb @@ -0,0 +1,119 @@ +# frozen_string_literal: true + +module Octokit + class EnterpriseAdminClient + # Methods for the Enterprise Admin Stats API + # + # @see https://developer.github.com/v3/enterprise-admin/admin_stats/ + module AdminStats + # Get all available stats + # + # @return [Sawyer::Resource] All available stats + # @example Get all available stats + # @client.admin_stats + def admin_stats + get_admin_stats 'all' + end + + # Get only repository-related stats + # + # @return [Sawyer::Resource] Only repository-related stats + # @example Get only repository-related stats + # @client.admin_repository_stats + def admin_repository_stats + get_admin_stats 'repos' + end + + # Get only hooks-related stats + # + # @return [Sawyer::Resource] Only hooks-related stats + # @example Get only hooks-related stats + # @client.admin_hooks_stats + def admin_hooks_stats + get_admin_stats 'hooks' + end + + # Get only pages-related stats + # + # @return [Sawyer::Resource] Only pages-related stats + # @example Get only pages-related stats + # @client.admin_pages_stats + def admin_pages_stats + get_admin_stats 'pages' + end + + # Get only organization-related stats + # + # @return [Sawyer::Resource] Only organization-related stats + # @example Get only organization-related stats + # @client.admin_organization_stats + def admin_organization_stats + get_admin_stats 'orgs' + end + + # Get only user-related stats + # + # @return [Sawyer::Resource] Only user-related stats + # @example Get only user-related stats + # @client.admin_users_stats + def admin_users_stats + get_admin_stats 'users' + end + + # Get only pull request-related stats + # + # @return [Sawyer::Resource] Only pull request-related stats + # @example Get only pull request-related stats + # @client.admin_pull_requests_stats + def admin_pull_requests_stats + get_admin_stats 'pulls' + end + + # Get only issue-related stats + # + # @return [Sawyer::Resource] Only issue-related stats + # @example Get only issue-related stats + # @client.admin_issues_stats + def admin_issues_stats + get_admin_stats 'issues' + end + + # Get only milestone-related stats + # + # @return [Sawyer::Resource] Only milestone-related stats + # @example Get only milestone-related stats + # @client.admin_milestones_stats + def admin_milestones_stats + get_admin_stats 'milestones' + end + + # Get only gist-related stats + # + # @return [Sawyer::Resource] Only only gist-related stats + # @example Get only gist-related stats + # @client.admin_gits_stats + def admin_gists_stats + get_admin_stats 'gists' + end + + # Get only comment-related stats + # + # @return [Sawyer::Resource] Only comment-related stats + # @example Get only comment-related stats + # @client.admin_comments_stats + def admin_comments_stats + get_admin_stats 'comments' + end + + private + + # @private Get enterprise stats + # + # @param metric [String] The metrics you are looking for + # @return [Sawyer::Resource] Magical unicorn stats + def get_admin_stats(metric) + get "enterprise/stats/#{metric}" + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/enterprise_admin_client/license.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/enterprise_admin_client/license.rb new file mode 100644 index 0000000..4150cb5 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/enterprise_admin_client/license.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module Octokit + class EnterpriseAdminClient + # Methods for the Enterprise License API + # + # @see https://developer.github.com/v3/enterprise-admin/license/ + module License + # Get information about the Enterprise license + # + # @return [Sawyer::Resource] The license information + def license_info + get 'enterprise/settings/license' + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/enterprise_admin_client/orgs.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/enterprise_admin_client/orgs.rb new file mode 100644 index 0000000..2dd2af2 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/enterprise_admin_client/orgs.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module Octokit + class EnterpriseAdminClient + # Methods for the Enterprise Orgs API + # + # @see https://developer.github.com/v3/enterprise-admin/orgs/ + module Orgs + # Create a new organization on the instance. + # + # @param login [String] The organization's username. + # @param admin [String] The login of the user who will manage this organization. + # @param options [Hash] A set of options. + # @option options [String] :profile_name The organization's display name. + # @return [nil] + # @see https://developer.github.com/v3/enterprise-admin/orgs/#create-an-organization + # @example + # @admin_client.create_organization('SuchAGreatOrg', 'gjtorikian') + def create_organization(login, admin, options = {}) + options[:login] = login + options[:admin] = admin + post 'admin/organizations', options + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/enterprise_admin_client/search_indexing.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/enterprise_admin_client/search_indexing.rb new file mode 100644 index 0000000..d725af9 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/enterprise_admin_client/search_indexing.rb @@ -0,0 +1,82 @@ +# frozen_string_literal: true + +module Octokit + class EnterpriseAdminClient + # Methods for the Enterprise Search Indexing API + # + # @see https://developer.github.com/v3/enterprise-admin/search_indexing/ + module SearchIndexing + # Queue a User or Organization to be indexed + # + # @param user [String] A GitHub Enterprise user or organization + # @return [Sawyer:Resource] Result of the queuing containing `:message` + def index_user(user) + queue_index user + end + alias index_organization index_user + + # Queue a Repository to be indexed + # + # @param repo [String, Hash, Repository] A GitHub repository + # @return [Sawyer:Resource] Result of the queuing containing `:message` + def index_repository(repo) + queue_index Repository.new repo + end + + # Queue a repository's Issues to be indexed + # + # @param repo [String, Hash, Repository] A GitHub repository + # @return [Sawyer:Resource] Result of the queuing containing `:message` + def index_repository_issues(repo) + queue_index "#{Repository.new repo}/issues" + end + + # Queue a repository's code to be indexed + # + # @param repo [String, Hash, Repository] A GitHub repository + # @return [Sawyer:Resource] Result of the queuing containing `:message` + def index_repository_code(repo) + queue_index "#{Repository.new repo}/code" + end + + # Queue a user's or organization's repositories to be indexed + # + # @param user [String] A GitHub Enterprise user or organization + # @return [Sawyer:Resource] Result of the queuing containing `:message` + def index_users_repositories(user) + queue_index "#{user}/*" + end + alias index_organizations_repositories index_users_repositories + + # Queue an index of all the issues across all of a user's or + # organization's repositories + # + # @param user [String] A GitHub Enterprise user or organization + # @return [Sawyer:Resource] Result of the queuing containing `:message` + def index_users_repositories_issues(user) + queue_index "#{user}/*/issues" + end + alias index_organizations_repositories_issues index_users_repositories_issues + + # Queue an index of all the code contained in all of a user's or + # organization's repositories + # + # @param user [String] A GitHub Enterprise user or organization + # @return [Sawyer:Resource] Result of the queuing containing `:message` + def index_users_repositories_code(user) + queue_index "#{user}/*/code" + end + alias index_organizations_repositories_code index_users_repositories_code + + private + + # @private Queue a target for indexing + # + # @param target [String] Target to index + # @return [Sawyer:Resource] Result of the queuing containing `:message` + def queue_index(target) + post 'staff/indexing_jobs', target: target + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/enterprise_admin_client/users.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/enterprise_admin_client/users.rb new file mode 100644 index 0000000..182d851 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/enterprise_admin_client/users.rb @@ -0,0 +1,129 @@ +# frozen_string_literal: true + +module Octokit + class EnterpriseAdminClient + # Methods for the Enterprise User Administration API + # + # @see https://developer.github.com/enterprise/v3/enterprise-admin/users/ + module Users + # Create a new user. + # + # @param login [String] The user's username. + # @param email [String] The user's email address. + # @see https://developer.github.com/enterprise/v3/enterprise-admin/users#create-a-new-user + # @example + # @admin_client.create_user('foobar', 'notreal@foo.bar') + def create_user(login, email, options = {}) + options[:login] = login + options[:email] = email + post 'admin/users', options + end + + # Promote an ordinary user to a site administrator + # + # @param user [String] Username of the user to promote. + # @return [Boolean] True if promote was successful, false otherwise. + # @see https://developer.github.com/enterprise/v3/enterprise-admin/users/#promote-an-ordinary-user-to-a-site-administrator + # @example + # @admin_client.promote('holman') + def promote(user, options = {}) + boolean_from_response :put, "users/#{user}/site_admin", options + end + + # Demote a site administrator to an ordinary user + # + # @param user [String] Username of the user to demote. + # @return [Boolean] True if demote was successful, false otherwise. + # @see https://developer.github.com/enterprise/v3/enterprise-admin/users/#demote-a-site-administrator-to-an-ordinary-user + # @example + # @admin_client.demote('holman') + def demote(user, options = {}) + boolean_from_response :delete, "users/#{user}/site_admin", options + end + + # Rename a user. + # + # @param old_login [String] The user's old username. + # @param new_login [String] The user's new username. + # @see https://developer.github.com/enterprise/v3/enterprise-admin/users/#rename-an-existing-user + # @example + # @admin_client.rename_user('foobar', 'foofoobar') + def rename_user(old_login, new_login, options = {}) + options[:login] = new_login + patch "admin/users/#{old_login}", options + end + + # Deletes a user. + # + # @param username [String] The username to delete. + # @see https://developer.github.com/enterprise/v3/enterprise-admin/users/#delete-a-user + # @example + # @admin_client.delete_key(1) + def delete_user(username, options = {}) + boolean_from_response :delete, "admin/users/#{username}", options + end + + # Suspend a user. + # + # @param user [String] Username of the user to suspend. + # @return [Boolean] True if suspend was successful, false otherwise. + # @see https://developer.github.com/enterprise/v3/enterprise-admin/users/#suspend-a-user + # @example + # @admin_client.suspend('holman') + def suspend(user, options = {}) + boolean_from_response :put, "users/#{user}/suspended", options + end + + # Unsuspend a user. + # + # @param user [String] Username of the user to unsuspend. + # @return [Boolean] True if unsuspend was successful, false otherwise. + # @see https://developer.github.com/enterprise/v3/enterprise-admin/users/#unsuspend-a-user + # @example + # @admin_client.unsuspend('holman') + def unsuspend(user, options = {}) + boolean_from_response :delete, "users/#{user}/suspended", options + end + + # Creates an impersonation OAuth token. + # + # @param login [String] The user to create a token for. + # @param options [Array] :scopes The scopes to apply. + # @see https://developer.github.com/enterprise/v3/enterprise-admin/users/#create-an-impersonation-oauth-token + # @example + # @admin_client.create_impersonation_token('foobar', {:scopes => ['repo:write']}) + def create_impersonation_token(login, options = {}) + post "admin/users/#{login}/authorizations", options + end + + # Deletes an impersonation OAuth token. + # + # @param login [String] The user whose token should be deleted. + # @see https://developer.github.com/enterprise/v3/enterprise-admin/users/#delete-an-impersonation-oauth-token + # @example + # @admin_client.delete_impersonation_token('foobar') + def delete_impersonation_token(login, options = {}) + boolean_from_response :delete, "admin/users/#{login}/authorizations", options + end + + # Lists all the public SSH keys. + # + # @see https://developer.github.com/enterprise/v3/enterprise-admin/users/#list-all-public-keys + # @example + # @admin_client.list_all_keys + def list_all_keys(options = {}) + get 'admin/keys', options + end + + # Deletes a public SSH keys. + # + # @param id [Number] The ID of the key to delete. + # @see https://developer.github.com/enterprise/v3/enterprise-admin/users/#delete-a-public-key + # @example + # @admin_client.delete_key(1) + def delete_key(id, options = {}) + boolean_from_response :delete, "admin/keys/#{id}", options + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/enterprise_management_console_client.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/enterprise_management_console_client.rb new file mode 100644 index 0000000..244e16e --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/enterprise_management_console_client.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +require 'octokit/configurable' +require 'octokit/connection' +require 'octokit/warnable' +require 'octokit/enterprise_management_console_client/management_console' + +module Octokit + # EnterpriseManagementConsoleClient is only meant to be used by GitHub Enterprise Admins + # and provides access to the management console API endpoints. + # + # @see Octokit::Client Use Octokit::Client for regular API use for GitHub + # and GitHub Enterprise. + # @see https://developer.github.com/v3/enterprise-admin/management_console/ + class EnterpriseManagementConsoleClient + include Octokit::Configurable + include Octokit::Connection + include Octokit::Warnable + include Octokit::EnterpriseManagementConsoleClient::ManagementConsole + + def initialize(options = {}) + # Use options passed in, but fall back to module defaults + # rubocop:disable Style/HashEachMethods + # + # This may look like a `.keys.each` which should be replaced with `#each_key`, but + # this doesn't actually work, since `#keys` is just a method we've defined ourselves. + # The class doesn't fulfill the whole `Enumerable` contract. + Octokit::Configurable.keys.each do |key| + # rubocop:enable Style/HashEachMethods + instance_variable_set(:"@#{key}", options[key] || Octokit.instance_variable_get(:"@#{key}")) + end + end + + protected + + def endpoint + management_console_endpoint + end + + # Set Enterprise Management Console password + # + # @param value [String] Management console admin password + def management_console_password=(value) + reset_agent + @management_console_password = value + end + + # Set Enterprise Management Console endpoint + # + # @param value [String] Management console endpoint + def management_console_endpoint=(value) + reset_agent + @management_console_endpoint = value + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/enterprise_management_console_client/management_console.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/enterprise_management_console_client/management_console.rb new file mode 100644 index 0000000..5871870 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/enterprise_management_console_client/management_console.rb @@ -0,0 +1,194 @@ +# frozen_string_literal: true + +module Octokit + class EnterpriseManagementConsoleClient + # Methods for the Enterprise Management Console API + # + # @see https://developer.github.com/v3/enterprise-admin/management_console/ + module ManagementConsole + # Uploads a license for the first time + # + # @param license [String] The path to your .ghl license file. + # @param settings [Hash] A hash configuration of the initial settings. + # + # @see https://docs.github.com/en/enterprise-server@3.4/rest/enterprise-admin/management-console#create-a-github-license + # @return nil + def upload_license(license, settings = nil) + octokit_warn('The Management Console API will be deprecated in GitHub Enterprise Server 3.15.0, please use the ManageGHES client instead.') + conn = faraday_configuration + + params = {} + params[:license] = Faraday::UploadIO.new(license, 'binary') + params[:password] = @management_console_password + params[:settings] = settings.to_json.to_s unless settings.nil? + + @last_response = conn.post('/setup/api/start', params) + end + + # Start a configuration process. + # + # @return nil + def start_configuration + octokit_warn('The Management Console API will be deprecated in GitHub Enterprise Server 3.15.0, please use the ManageGHES client instead.') + post '/setup/api/configure', password_hash + end + + # Upgrade an Enterprise installation + # + # @param license [String] The path to your .ghl license file. + # + # @return nil + def upgrade(license) + octokit_warn('The Management Console API will be deprecated in GitHub Enterprise Server 3.15.0, please use the ManageGHES client instead.') + conn = faraday_configuration + + params = {} + params[:license] = Faraday::UploadIO.new(license, 'binary') + params[:api_key] = @management_console_password + @last_response = conn.post('/setup/api/upgrade', params) + end + + # Get information about the Enterprise installation + # + # @return [Sawyer::Resource] The installation information + def config_status + octokit_warn('The Management Console API will be deprecated in GitHub Enterprise Server 3.15.0, please use the ManageGHES client instead.') + get '/setup/api/configcheck', password_hash + end + alias config_check config_status + + # Get information about the Enterprise installation + # + # @return [Sawyer::Resource] The settings + def settings + octokit_warn('The Management Console API will be deprecated in GitHub Enterprise Server 3.15.0, please use the ManageGHES client instead.') + get '/setup/api/settings', password_hash + end + alias get_settings settings + + # Modify the Enterprise settings + # + # @param settings [Hash] A hash configuration of the new settings + # + # @return [nil] + def edit_settings(settings) + octokit_warn('The Management Console API will be deprecated in GitHub Enterprise Server 3.15.0, please use the ManageGHES client instead.') + queries = password_hash + queries[:query][:settings] = settings.to_json.to_s + put '/setup/api/settings', queries + end + + # Get information about the Enterprise maintenance status + # + # @return [Sawyer::Resource] The maintenance status + def maintenance_status + octokit_warn('The Management Console API will be deprecated in GitHub Enterprise Server 3.15.0, please use the ManageGHES client instead.') + get '/setup/api/maintenance', password_hash + end + alias get_maintenance_status maintenance_status + + # Start (or turn off) the Enterprise maintenance mode + # + # @param maintenance [Hash] A hash configuration of the maintenance settings + # @return [nil] + def set_maintenance_status(maintenance) + octokit_warn('The Management Console API will be deprecated in GitHub Enterprise Server 3.15.0, please use the ManageGHES client instead.') + queries = password_hash + queries[:query][:maintenance] = maintenance.to_json.to_s + post '/setup/api/maintenance', queries + end + alias edit_maintenance_status set_maintenance_status + + # Fetch the authorized SSH keys on the Enterprise install + # + # @return [Sawyer::Resource] An array of authorized SSH keys + def authorized_keys + octokit_warn('The Management Console API will be deprecated in GitHub Enterprise Server 3.15.0, please use the ManageGHES client instead.') + get '/setup/api/settings/authorized-keys', password_hash + end + alias get_authorized_keys authorized_keys + + # Add an authorized SSH keys on the Enterprise install + # + # @param key Either the file path to a key, a File handler to the key, or the contents of the key itself + # @return [Sawyer::Resource] An array of authorized SSH keys + def add_authorized_key(key) + octokit_warn('The Management Console API will be deprecated in GitHub Enterprise Server 3.15.0, please use the ManageGHES client instead.') + queries = password_hash + case key + when String + if File.exist?(key) + key = File.open(key, 'r') + content = key.read.strip + key.close + else + content = key + end + when File + content = key.read.strip + key.close + end + + queries[:query][:authorized_key] = content + post '/setup/api/settings/authorized-keys', queries + end + + # Removes an authorized SSH keys from the Enterprise install + # + # @param key Either the file path to a key, a File handler to the key, or the contents of the key itself + # @return [Sawyer::Resource] An array of authorized SSH keys + def remove_authorized_key(key) + octokit_warn('The Management Console API will be deprecated in GitHub Enterprise Server 3.15.0, please use the ManageGHES client instead.') + queries = password_hash + case key + when String + if File.exist?(key) + key = File.open(key, 'r') + content = key.read.strip + key.close + else + content = key + end + when File + content = key.read.strip + key.close + end + + queries[:query][:authorized_key] = content + delete '/setup/api/settings/authorized-keys', queries + end + alias delete_authorized_key remove_authorized_key + end + + private + + def password_hash + { query: { api_key: @management_console_password } } + end + + # We fall back to raw Faraday for handling the licenses because I'm suspicious + # that Sawyer isn't handling binary POSTs correctly: https://github.com/lostisland/sawyer/blob/03fca4c020f465ec42856d0486ec3991859b0aed/lib/sawyer/agent.rb#L85 + def faraday_configuration + @faraday_configuration ||= Faraday.new(url: @management_console_endpoint) do |http| + http.headers[:user_agent] = user_agent + begin + http.request :multipart + rescue Faraday::Error + raise Faraday::Error, <<~ERROR + The `faraday-multipart` gem is required to upload a license. + Please add `gem "faraday-multipart"` to your Gemfile. + ERROR + end + http.request :url_encoded + + # Disabling SSL is essential for certain self-hosted Enterprise instances + if connection_options[:ssl] && !connection_options[:ssl][:verify] + http.ssl[:verify] = false + end + + http.use Octokit::Response::RaiseError + http.adapter Faraday.default_adapter + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/error.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/error.rb new file mode 100644 index 0000000..9f114b8 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/error.rb @@ -0,0 +1,371 @@ +# frozen_string_literal: true + +module Octokit + # Custom error class for rescuing from all GitHub errors + class Error < StandardError + attr_reader :context + + # Returns the appropriate Octokit::Error subclass based + # on status and response message + # + # @param [Hash] response HTTP response + # @return [Octokit::Error] + # rubocop:disable Metrics/CyclomaticComplexity + def self.from_response(response) + status = response[:status].to_i + body = response[:body].to_s + headers = response[:response_headers] + + if klass = case status + when 400 then Octokit::BadRequest + when 401 then error_for_401(headers) + when 403 then error_for_403(body) + when 404 then error_for_404(body) + when 405 then Octokit::MethodNotAllowed + when 406 then Octokit::NotAcceptable + when 409 then Octokit::Conflict + when 410 then Octokit::Deprecated + when 415 then Octokit::UnsupportedMediaType + when 422 then error_for_422(body) + when 451 then Octokit::UnavailableForLegalReasons + when 400..499 then Octokit::ClientError + when 500 then Octokit::InternalServerError + when 501 then Octokit::NotImplemented + when 502 then Octokit::BadGateway + when 503 then Octokit::ServiceUnavailable + when 500..599 then Octokit::ServerError + end + klass.new(response) + end + end + # rubocop:enable Metrics/CyclomaticComplexity + + def build_error_context + if RATE_LIMITED_ERRORS.include?(self.class) + @context = Octokit::RateLimit.from_response(@response) + end + end + + def initialize(response = nil) + @response = response + super(build_error_message) + build_error_context + end + + # Documentation URL returned by the API for some errors + # + # @return [String] + def documentation_url + data[:documentation_url] if data.is_a? Hash + end + + # Returns most appropriate error for 401 HTTP status code + # @private + # rubocop:disable Naming/VariableNumber + def self.error_for_401(headers) + # rubocop:enbale Naming/VariableNumber + if Octokit::OneTimePasswordRequired.required_header(headers) + Octokit::OneTimePasswordRequired + else + Octokit::Unauthorized + end + end + + # Returns most appropriate error for 403 HTTP status code + # @private + def self.error_for_403(body) + # rubocop:enable Naming/VariableNumber + case body + when /rate limit exceeded/i, /exceeded a secondary rate limit/i + Octokit::TooManyRequests + when /login attempts exceeded/i + Octokit::TooManyLoginAttempts + when /(returns|for) blobs (up to|between) [0-9-]+ MB/i + Octokit::TooLargeContent + when /abuse/i + Octokit::AbuseDetected + when /repository access blocked/i + Octokit::RepositoryUnavailable + when /email address must be verified/i + Octokit::UnverifiedEmail + when /account was suspended/i + Octokit::AccountSuspended + when /billing issue/i + Octokit::BillingIssue + when /Resource protected by organization SAML enforcement/i + Octokit::SAMLProtected + when /suspended your access|This installation has been suspended/i + Octokit::InstallationSuspended + else + Octokit::Forbidden + end + end + + # Return most appropriate error for 404 HTTP status code + # @private + # rubocop:disable Naming/VariableNumber + def self.error_for_404(body) + # rubocop:enable Naming/VariableNumber + if body =~ /Branch not protected/i + Octokit::BranchNotProtected + else + Octokit::NotFound + end + end + + # Return most appropriate error for 422 HTTP status code + # @private + # rubocop:disable Naming/VariableNumber + def self.error_for_422(body) + # rubocop:enable Naming/VariableNumber + if body =~ /PullRequestReviewComment/i && body =~ /(commit_id|end_commit_oid) is not part of the pull request/i + Octokit::CommitIsNotPartOfPullRequest + elsif body =~ /Path diff too large/i + Octokit::PathDiffTooLarge + else + Octokit::UnprocessableEntity + end + end + + # Array of validation errors + # @return [Array] Error info + def errors + if data.is_a?(Hash) + data[:errors] || [] + else + [] + end + end + + # Status code returned by the GitHub server. + # + # @return [Integer] + def response_status + @response[:status] + end + + # Headers returned by the GitHub server. + # + # @return [Hash] + def response_headers + @response[:response_headers] + end + + # Body returned by the GitHub server. + # + # @return [String] + def response_body + @response[:body] + end + + private + + def data + @data ||= + if (body = @response[:body]) && !body.empty? + if body.is_a?(String) && + @response[:response_headers] && + @response[:response_headers][:content_type] =~ /json/ + + Sawyer::Agent.serializer.decode(body) + else + body + end + end + end + + def response_message + case data + when Hash + data[:message] + when String + data + end + end + + def response_error + "Error: #{data[:error]}" if data.is_a?(Hash) && data[:error] + end + + def response_error_summary + return nil unless data.is_a?(Hash) && !Array(data[:errors]).empty? + + summary = +"\nError summary:\n" + return summary << data[:errors] if data[:errors].is_a?(String) + + summary << data[:errors].map do |error| + if error.is_a? Hash + error.map { |k, v| " #{k}: #{v}" } + else + " #{error}" + end + end.join("\n") + + summary + end + + def build_error_message + return nil if @response.nil? + + message = +"#{@response[:method].to_s.upcase} " + message << "#{redact_url(@response[:url].to_s.dup)}: " + message << "#{@response[:status]} - " + message << response_message.to_s unless response_message.nil? + message << response_error.to_s unless response_error.nil? + message << response_error_summary.to_s unless response_error_summary.nil? + message << " // See: #{documentation_url}" unless documentation_url.nil? + message + end + + def redact_url(url_string) + %w[client_secret access_token api_key].each do |token| + if url_string.include? token + url_string.gsub!(/#{token}=\S+/, "#{token}=(redacted)") + end + end + url_string + end + end + + # Raised on errors in the 400-499 range + class ClientError < Error; end + + # Raised when GitHub returns a 400 HTTP status code + class BadRequest < ClientError; end + + # Raised when GitHub returns a 401 HTTP status code + class Unauthorized < ClientError; end + + # Raised when GitHub returns a 401 HTTP status code + # and headers include "X-GitHub-OTP" + class OneTimePasswordRequired < ClientError + # @private + OTP_DELIVERY_PATTERN = /required; (\w+)/i.freeze + + # @private + def self.required_header(headers) + OTP_DELIVERY_PATTERN.match headers['X-GitHub-OTP'].to_s + end + + # Delivery method for the user's OTP + # + # @return [String] + def password_delivery + @password_delivery ||= delivery_method_from_header + end + + private + + def delivery_method_from_header + if match = self.class.required_header(@response[:response_headers]) + match[1] + end + end + end + + # Raised when GitHub returns a 403 HTTP status code + class Forbidden < ClientError; end + + # Raised when GitHub returns a 403 HTTP status code + # and body matches 'rate limit exceeded' + class TooManyRequests < Forbidden; end + + # Raised when GitHub returns a 403 HTTP status code + # and body matches 'login attempts exceeded' + class TooManyLoginAttempts < Forbidden; end + + # Raised when GitHub returns a 403 HTTP status code + # and body matches 'returns blobs up to [0-9]+ MB' + class TooLargeContent < Forbidden; end + + # Raised when GitHub returns a 403 HTTP status code + # and body matches 'abuse' + class AbuseDetected < Forbidden; end + + # Raised when GitHub returns a 403 HTTP status code + # and body matches 'repository access blocked' + class RepositoryUnavailable < Forbidden; end + + # Raised when GitHub returns a 403 HTTP status code + # and body matches 'email address must be verified' + class UnverifiedEmail < Forbidden; end + + # Raised when GitHub returns a 403 HTTP status code + # and body matches 'account was suspended' + class AccountSuspended < Forbidden; end + + # Raised when GitHub returns a 403 HTTP status code + # and body matches 'billing issue' + class BillingIssue < Forbidden; end + + # Raised when GitHub returns a 403 HTTP status code + # and body matches 'Resource protected by organization SAML enforcement' + class SAMLProtected < Forbidden; end + + # Raised when GitHub returns a 403 HTTP status code + # and body matches 'suspended your access' + class InstallationSuspended < Forbidden; end + + # Raised when GitHub returns a 404 HTTP status code + class NotFound < ClientError; end + + # Raised when GitHub returns a 404 HTTP status code + # and body matches 'Branch not protected' + class BranchNotProtected < ClientError; end + + # Raised when GitHub returns a 405 HTTP status code + class MethodNotAllowed < ClientError; end + + # Raised when GitHub returns a 406 HTTP status code + class NotAcceptable < ClientError; end + + # Raised when GitHub returns a 409 HTTP status code + class Conflict < ClientError; end + + # Raised when GHES Manage return a 410 HTTP status code + class Deprecated < ClientError; end + + # Raised when GitHub returns a 414 HTTP status code + class UnsupportedMediaType < ClientError; end + + # Raised when GitHub returns a 422 HTTP status code + class UnprocessableEntity < ClientError; end + + # Raised when GitHub returns a 422 HTTP status code + # and body matches 'PullRequestReviewComment' and 'commit_id (or end_commit_oid) is not part of the pull request' + class CommitIsNotPartOfPullRequest < UnprocessableEntity; end + + # Raised when GitHub returns a 422 HTTP status code and body matches 'Path diff too large'. + # It could occur when attempting to post review comments on a "too large" file. + class PathDiffTooLarge < UnprocessableEntity; end + + # Raised when GitHub returns a 451 HTTP status code + class UnavailableForLegalReasons < ClientError; end + + # Raised on errors in the 500-599 range + class ServerError < Error; end + + # Raised when GitHub returns a 500 HTTP status code + class InternalServerError < ServerError; end + + # Raised when GitHub returns a 501 HTTP status code + class NotImplemented < ServerError; end + + # Raised when GitHub returns a 502 HTTP status code + class BadGateway < ServerError; end + + # Raised when GitHub returns a 503 HTTP status code + class ServiceUnavailable < ServerError; end + + # Raised when client fails to provide valid Content-Type + class MissingContentType < ArgumentError; end + + # Raised when a method requires an application client_id + # and secret but none is provided + class ApplicationCredentialsRequired < StandardError; end + + # Raised when a repository is created with an invalid format + class InvalidRepository < ArgumentError; end + + RATE_LIMITED_ERRORS = [Octokit::TooManyRequests, Octokit::AbuseDetected].freeze +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/gist.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/gist.rb new file mode 100644 index 0000000..cb6dab9 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/gist.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +module Octokit + # Class to parse and create Gist URLs + class Gist + # !@attribute id + # @return [String] Gist ID + attr_accessor :id + + # Instantiate {Gist} object from Gist URL + # @ return [Gist] + def self.from_url(url) + Gist.new(URI.parse(url).path[1..]) + end + + def initialize(gist) + case gist + when Integer, String + @id = gist.to_s + end + end + + # Gist ID + # @return [String] + def to_s + @id + end + + # Gist URL + # @return [String] + def url + "https://gist.github.com/#{@id}" + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/manage_ghes_client.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/manage_ghes_client.rb new file mode 100644 index 0000000..f8ecaf6 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/manage_ghes_client.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +require 'octokit/configurable' +require 'octokit/connection' +require 'octokit/warnable' +require 'octokit/manage_ghes_client/manage_ghes' + +module Octokit + # ManageGHESClient is only meant to be used by GitHub Enterprise Server (GHES) operators + # and provides access to the Manage GHES API endpoints. + # + # @see Octokit::Client Use Octokit::Client for regular API use for GitHub + # and GitHub Enterprise. + # @see https://developer.github.com/v3/enterprise-admin/manage-ghes/ + class ManageGHESClient + include Octokit::Configurable + include Octokit::Connection + include Octokit::Warnable + include Octokit::ManageGHESClient::ManageAPI + + def initialize(options = {}) + # Use options passed in, but fall back to module defaults + # rubocop:disable Style/HashEachMethods + # + # This may look like a `.keys.each` which should be replaced with `#each_key`, but + # this doesn't actually work, since `#keys` is just a method we've defined ourselves. + # The class doesn't fulfill the whole `Enumerable` contract. + Octokit::Configurable.keys.each do |key| + # rubocop:enable Style/HashEachMethods + instance_variable_set(:"@#{key}", options[key] || Octokit.instance_variable_get(:"@#{key}")) + end + end + + protected + + def endpoint + manage_ghes_endpoint + end + + # Set Manage GHES API endpoint + # + # @param value [String] Manage GHES API endpoint + def manage_ghes_endpoint=(value) + reset_agent + @manage_ghes_endpoint = value + end + + # Set Manage GHES API username + # + # @param value [String] Manage GHES API username + def manage_ghes_username=(value) + reset_agent + @manage_ghes_username = value + end + + # Set Manage GHES API password + # + # @param value [String] Manage GHES API password + def manage_ghes_password=(value) + reset_agent + @manage_ghes_password = value + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/manage_ghes_client/manage_ghes.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/manage_ghes_client/manage_ghes.rb new file mode 100644 index 0000000..53ca796 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/manage_ghes_client/manage_ghes.rb @@ -0,0 +1,178 @@ +# frozen_string_literal: true + +module Octokit + # Client for the Manage GitHub Enterprise Server API + class ManageGHESClient + # Methods for the Manage GitHub Enterprise Server API + # + # @see https://developer.github.com/v3/enterprise-admin/manage-ghes + module ManageAPI + # Get information about the maintenance status of the GHES instance + # + # @return [nil] + def maintenance_mode + conn = authenticated_client + + @last_response = conn.get('/manage/v1/maintenance') + end + + # Configure the maintenance mode of the GHES instance + # + # @param maintenance [Hash] A hash configuration of the maintenance mode status + # @return [nil] + def set_maintenance_mode(enabled, options = {}) + conn = authenticated_client + + options[:enabled] = enabled + @last_response = conn.post('/manage/v1/maintenance', options) + end + alias configure_maintenance_mode set_maintenance_mode + end + + # Uploads a license for the first time + # + # @param license [String] The path to your .ghl license file. + # + # @return [nil] + def upload_license(license) + conn = authenticated_client + begin + conn.request :multipart + rescue Faraday::Error + raise Faraday::Error, <<~ERROR + The `faraday-multipart` gem is required to upload a license. + Please add `gem "faraday-multipart"` to your Gemfile. + ERROR + end + params = {} + params[:license] = Faraday::FilePart.new(license, 'binary') + params[:password] = @manage_ghes_password + @last_response = conn.post('/manage/v1/config/init', params, { 'Content-Type' => 'multipart/form-data' }) + end + + # Start a configuration process. + # + # @return [nil] + def start_configuration + conn = authenticated_client + @last_response = conn.post('/manage/v1/config/apply') + end + + # Get information about the Enterprise installation + # + # @return [nil] + def config_status + conn = authenticated_client + @last_response = conn.get('/manage/v1/config/apply') + end + alias config_check config_status + + # Get information about the Enterprise installation + # + # @return [nil] + def settings + conn = authenticated_client + @last_response = conn.get('/manage/v1/config/settings') + end + alias get_settings settings + + # Modify the Enterprise settings + # + # @param settings [Hash] A hash configuration of the new settings + # + # @return [nil] + def edit_settings(settings) + conn = authenticated_client + @last_response = conn.put('/manage/v1/config/settings', settings.to_json.to_s) + end + + def authorized_keys + conn = authenticated_client + @last_response = conn.get('/manage/v1/access/ssh') + end + alias get_authorized_keys authorized_keys + + # Add an authorized SSH keys on the Enterprise install + # + # @param key Either the file path to a key, a File handler to the key, or the contents of the key itself + # @return [nil] + def add_authorized_key(key) + conn = authenticated_client + case key + when String + if File.exist?(key) + key = File.open(key, 'r') + content = key.read.strip + key.close + else + content = key + end + when File + content = key.read.strip + key.close + end + + queries = {} + queries[:key] = content + @last_response = conn.post('/manage/v1/access/ssh', queries) + end + + # Removes an authorized SSH keys from the Enterprise install + # + # @param key Either the file path to a key, a File handler to the key, or the contents of the key itself + # @return [nil] + def remove_authorized_key(key) + conn = authenticated_client + case key + when String + if File.exist?(key) + key = File.open(key, 'r') + content = key.read.strip + key.close + else + content = key + end + when File + content = key.read.strip + key.close + end + + queries = {} + queries[:key] = content + @last_response = conn.run_request(:delete, '/manage/v1/access/ssh', queries, nil) + end + alias delete_authorized_key remove_authorized_key + + private + + def basic_authenticated? + !!(@manage_ghes_username && @manage_ghes_password) + end + + # If no username is provided, we assume root site admin should be used + def root_site_admin_assumed? + !@manage_ghes_username + end + + def authenticated_client + @authenticated_client ||= Faraday.new(url: @manage_ghes_endpoint) do |c| + c.headers[:user_agent] = user_agent + c.request :json + c.response :json + c.adapter Faraday.default_adapter + + if root_site_admin_assumed? + username = 'api_key' + elsif basic_authenticated? + username = @manage_ghes_username + end + c.request(*FARADAY_BASIC_AUTH_KEYS, username, @manage_ghes_password) + + # Disabling SSL is essential for certain self-hosted Enterprise instances + c.ssl[:verify] = false if connection_options[:ssl] && !connection_options[:ssl][:verify] + + c.use Octokit::Response::RaiseError + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/middleware/follow_redirects.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/middleware/follow_redirects.rb new file mode 100644 index 0000000..fe291cf --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/middleware/follow_redirects.rb @@ -0,0 +1,135 @@ +# frozen_string_literal: true + +require 'faraday' +require 'set' + +# Adapted from lostisland/faraday_middleware. Trimmed down to just the logic +# that we need for octokit.rb. +# +# https://github.com/lostisland/faraday_middleware/blob/138766e/lib/faraday_middleware/response/follow_redirects.rb + +module Octokit + module Middleware + # Public: Exception thrown when the maximum amount of requests is exceeded. + class RedirectLimitReached < Faraday::ClientError + attr_reader :response + + def initialize(response) + super("too many redirects; last one to: #{response['location']}") + @response = response + end + end + + # Public: Follow HTTP 301, 302, 303, and 307 redirects. + # + # For HTTP 303, the original GET, POST, PUT, DELETE, or PATCH request gets + # converted into a GET. For HTTP 301, 302, and 307, the HTTP method remains + # unchanged. + # + # This middleware currently only works with synchronous requests; i.e. it + # doesn't support parallelism. + class FollowRedirects < Faraday::Middleware + # HTTP methods for which 30x redirects can be followed + ALLOWED_METHODS = Set.new %i[head options get post put patch delete] + + # HTTP redirect status codes that this middleware implements + REDIRECT_CODES = Set.new [301, 302, 303, 307] + + # Keys in env hash which will get cleared between requests + ENV_TO_CLEAR = Set.new %i[status response response_headers] + + # Default value for max redirects followed + FOLLOW_LIMIT = 3 + + # Regex that matches characters that need to be escaped in URLs, sans + # the "%" character which we assume already represents an escaped + # sequence. + URI_UNSAFE = %r{[^\-_.!~*'()a-zA-Z\d;/?:@&=+$,\[\]%]}.freeze + + # Public: Initialize the middleware. + # + # options - An options Hash (default: {}): + # :limit - A Integer redirect limit (default: 3). + def initialize(app, options = {}) + super(app) + @options = options + + @convert_to_get = Set.new [303] + end + + def call(env) + perform_with_redirection(env, follow_limit) + end + + private + + def convert_to_get?(response) + !%i[head options].include?(response.env[:method]) && + @convert_to_get.include?(response.status) + end + + def perform_with_redirection(env, follows) + request_body = env[:body] + response = @app.call(env) + + response.on_complete do |response_env| + if follow_redirect?(response_env, response) + raise(RedirectLimitReached, response) if follows.zero? + + new_request_env = update_env(response_env, request_body, response) + response = perform_with_redirection(new_request_env, follows - 1) + end + end + response + end + + def update_env(env, request_body, response) + original_url = env[:url] + env[:url] += safe_escape(response['location']) + unless same_host?(original_url, env[:url]) + # HACK: Faraday’s Authorization middlewares don’t touch the request if the `Authorization` header is set. + # This is a workaround to drop authentication info. + # See https://github.com/octokit/octokit.rb/pull/1359#issuecomment-925609697 + env[:request_headers]['Authorization'] = 'dummy' + end + + if convert_to_get?(response) + env[:method] = :get + env[:body] = nil + else + env[:body] = request_body + end + + ENV_TO_CLEAR.each { |key| env.delete(key) } + + env + end + + def follow_redirect?(env, response) + ALLOWED_METHODS.include?(env[:method]) && + REDIRECT_CODES.include?(response.status) + end + + def follow_limit + @options.fetch(:limit, FOLLOW_LIMIT) + end + + def same_host?(original_url, redirect_url) + original_uri = Addressable::URI.parse(original_url) + redirect_uri = Addressable::URI.parse(redirect_url) + + redirect_uri.host.nil? || original_uri.host == redirect_uri.host + end + + # Internal: Escapes unsafe characters from a URL which might be a path + # component only or a fully-qualified URI so that it can be joined onto a + # URI:HTTP using the `+` operator. Doesn't escape "%" characters so to not + # risk double-escaping. + def safe_escape(uri) + uri.to_s.gsub(URI_UNSAFE) do |match| + "%#{match.unpack('H2' * match.bytesize).join('%').upcase}" + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/organization.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/organization.rb new file mode 100644 index 0000000..a3cb0de --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/organization.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Octokit + # GitHub organization class to generate API path urls + class Organization + # Get the api path for an organization + # + # @param org [String, Integer] GitHub organization login or id + # @return [String] Organization Api path + def self.path(org) + case org + when String + "orgs/#{org}" + when Integer + "organizations/#{org}" + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/rate_limit.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/rate_limit.rb new file mode 100644 index 0000000..fd6020d --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/rate_limit.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +module Octokit + # Class for API Rate Limit info + # + # @!attribute [w] limit + # @return [Integer] Max tries per rate limit period + # @!attribute [w] remaining + # @return [Integer] Remaining tries per rate limit period + # @!attribute [w] resets_at + # @return [Time] Indicates when rate limit resets + # @!attribute [w] resets_in + # @return [Integer] Number of seconds when rate limit resets + # + # @see https://developer.github.com/v3/#rate-limiting + class RateLimit < Struct.new(:limit, :remaining, :resets_at, :resets_in) + # Get rate limit info from HTTP response + # + # @param response [#headers] HTTP response + # @return [RateLimit] + def self.from_response(response) + info = new + headers = response.headers if response.respond_to?(:headers) && !response.headers.nil? + headers ||= response.response_headers if response.respond_to?(:response_headers) && !response.response_headers.nil? + if headers + info.limit = (headers['X-RateLimit-Limit'] || 1).to_i + info.remaining = (headers['X-RateLimit-Remaining'] || 1).to_i + info.resets_at = Time.at((headers['X-RateLimit-Reset'] || Time.now).to_i) + info.resets_in = [(info.resets_at - Time.now).to_i, 0].max + end + + info + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/repo_arguments.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/repo_arguments.rb new file mode 100644 index 0000000..73eaa22 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/repo_arguments.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module Octokit + # Class to extract options from Ruby arguments for + # Repository-related methods + class RepoArguments < Arguments + # !@attribute [r] repo + # @return [Repository] + attr_reader :repo + + def initialize(args) + arguments = super + @repo = arguments.shift + + arguments + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/repository.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/repository.rb new file mode 100644 index 0000000..47f9a26 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/repository.rb @@ -0,0 +1,102 @@ +# frozen_string_literal: true + +module Octokit + # Class to parse GitHub repository owner and name from + # URLs and to generate URLs + class Repository + attr_accessor :owner, :name, :id + + NAME_WITH_OWNER_PATTERN = %r{\A[\w.-]+/[\w.-]+\z}i.freeze + + # Instantiate from a GitHub repository URL + # + # @return [Repository] + def self.from_url(url) + new URI.parse(url).path[1..] + .gsub(%r{^repos/}, '') + .split('/', 3)[0..1] + .join('/') + end + + # @raise [Octokit::InvalidRepository] if the repository + # has an invalid format + def initialize(repo) + case repo + when Integer + @id = repo + when NAME_WITH_OWNER_PATTERN + @owner, @name = repo.split('/') + when Repository + @owner = repo.owner + @name = repo.name + when Hash + @name = repo[:repo] || repo[:name] + @owner = repo[:owner] || repo[:user] || repo[:username] + else + raise_invalid_repository!(repo) + end + validate_owner_and_name!(repo) if @owner && @name + end + + # Repository owner/name + # @return [String] + def slug + "#{@owner}/#{@name}" + end + alias to_s slug + + # @return [String] Repository API path + def path + return named_api_path if @owner && @name + + id_api_path if @id + end + + # Get the api path for a repo + # @param repo [Integer, String, Hash, Repository] A GitHub repository. + # @return [String] Api path. + def self.path(repo) + new(repo).path + end + + # @return [String] Api path for owner/name identified repos + def named_api_path + "repos/#{slug}" + end + + # @return [String] Api path for id identified repos + def id_api_path + "repositories/#{@id}" + end + + # Repository URL based on {Octokit::Client#web_endpoint} + # @return [String] + def url + "#{Octokit.web_endpoint}#{slug}" + end + + alias user owner + alias username owner + alias repo name + + private + + ABS_URI_REGEXP = if URI.const_defined?(:RFC2396_PARSER) # Ruby 3.4+ + URI::RFC2396_PARSER.regexp.fetch(:ABS_URI) + else + URI::RFC2396_Parser.new.regexp.fetch(:ABS_URI) + end + + def validate_owner_and_name!(repo) + if @owner.include?('/') || @name.include?('/') || !url.match?(ABS_URI_REGEXP) + raise_invalid_repository!(repo) + end + end + + def raise_invalid_repository!(repo) + msg = "#{repo.inspect} is invalid as a repository identifier. " \ + 'Use the user/repo (String) format, or the repository ID (Integer), or a hash containing :repo and :user keys.' + raise Octokit::InvalidRepository, msg + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/response/base_middleware.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/response/base_middleware.rb new file mode 100644 index 0000000..ed971a2 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/response/base_middleware.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +require 'faraday' + +module Octokit + module Response + # In Faraday 2.x, Faraday::Response::Middleware was removed + BaseMiddleware = defined?(Faraday::Response::Middleware) ? Faraday::Response::Middleware : Faraday::Middleware + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/response/feed_parser.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/response/feed_parser.rb new file mode 100644 index 0000000..3a73b06 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/response/feed_parser.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +require 'octokit/response/base_middleware' + +module Octokit + module Response + # Parses RSS and Atom feed responses. + class FeedParser < BaseMiddleware + def on_complete(env) + if env[:response_headers]['content-type'] =~ /(\batom|\brss)/ + require 'rss' + env[:body] = RSS::Parser.parse env[:body] + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/response/raise_error.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/response/raise_error.rb new file mode 100644 index 0000000..4b0fab0 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/response/raise_error.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require 'octokit/response/base_middleware' +require 'octokit/error' + +module Octokit + # Faraday response middleware + module Response + # This class raises an Octokit-flavored exception based + # HTTP status codes returned by the API + class RaiseError < BaseMiddleware + def on_complete(response) + if error = Octokit::Error.from_response(response) + raise error + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/user.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/user.rb new file mode 100644 index 0000000..1adf3d0 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/user.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module Octokit + # GitHub user class to generate API path urls + class User + # Get the api path for a user + # + # @param user [String, Integer] GitHub user login or id + # @return [String] User Api path + def self.path(user) + case user + when String + "users/#{user}" + when Integer + "user/#{user}" + else + 'user' + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/version.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/version.rb new file mode 100644 index 0000000..8375448 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/version.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Octokit + # Current major release. + # @return [Integer] + MAJOR = 10 + + # Current minor release. + # @return [Integer] + MINOR = 0 + + # Current patch level. + # @return [Integer] + PATCH = 0 + + # Full release version. + # @return [String] + VERSION = [MAJOR, MINOR, PATCH].join('.').freeze +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/warnable.rb b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/warnable.rb new file mode 100644 index 0000000..63551f6 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/lib/octokit/warnable.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Octokit + # Allows warnings to be suppressed via environment variable. + module Warnable + module_function + + # Wrapper around Kernel#warn to print warnings unless + # OCTOKIT_SILENT is set to true. + # + # @return [nil] + def octokit_warn(*message) + warn message unless ENV['OCTOKIT_SILENT'] + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/octokit.gemspec b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/octokit.gemspec new file mode 100644 index 0000000..ba79e9e --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/octokit-10.0.0/octokit.gemspec @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +lib = File.expand_path('lib', __dir__) +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) +require 'octokit/version' + +Gem::Specification.new do |spec| + spec.add_dependency 'faraday', '>= 1', '< 3' + spec.add_dependency 'sawyer', '~> 0.9' + spec.authors = ['Wynn Netherland', 'Erik Michaels-Ober', 'Clint Shryock'] + spec.description = 'Simple wrapper for the GitHub API' + spec.email = ['wynn.netherland@gmail.com', 'sferik@gmail.com', 'clint@ctshryock.com'] + spec.files = %w[.document CONTRIBUTING.md LICENSE.md README.md Rakefile octokit.gemspec] + spec.files += Dir.glob('lib/**/*.rb') + spec.homepage = 'https://github.com/octokit/octokit.rb' + spec.licenses = ['MIT'] + spec.name = 'octokit' + spec.require_paths = ['lib'] + spec.required_ruby_version = '>= 2.7.0' + spec.required_rubygems_version = '>= 1.3.5' + spec.summary = 'Ruby toolkit for working with the GitHub API' + spec.version = Octokit::VERSION.dup + spec.metadata = { 'rubygems_mfa_required' => 'true' } +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/BSDmakefile b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/BSDmakefile new file mode 100644 index 0000000..13174f8 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/BSDmakefile @@ -0,0 +1,58 @@ +# GNU makefile proxy script for BSD make +# +# Written and maintained by Mahmoud Al-Qudsi +# Copyright NeoSmart Technologies 2014-2019 +# Obtain updates from +# +# 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. + +JARG = +GMAKE = "gmake" +# When gmake is called from another make instance, -w is automatically added +# which causes extraneous messages about directory changes to be emitted. +# Running with --no-print-directory silences these messages. +GARGS = "--no-print-directory" + +.if "$(.MAKE.JOBS)" != "" + JARG = -j$(.MAKE.JOBS) +.endif + +# bmake prefers out-of-source builds and tries to cd into ./obj (among others) +# where possible. GNU Make doesn't, so override that value. +.OBJDIR: ./ + +# The GNU convention is to use the lowercased `prefix` variable/macro to +# specify the installation directory. Humor them. +GPREFIX = "" +.if defined(PREFIX) && ! defined(prefix) + GPREFIX = 'prefix = "$(PREFIX)"' +.endif + +.BEGIN: .SILENT + which $(GMAKE) || printf "Error: GNU Make is required!\n\n" 1>&2 && false + +.PHONY: FRC +$(.TARGETS): FRC + $(GMAKE) $(GPREFIX) $(GARGS) $(.TARGETS:S,.DONE,,) $(JARG) + +.DONE .DEFAULT: .SILENT + $(GMAKE) $(GPREFIX) $(GARGS) $(.TARGETS:S,.DONE,,) $(JARG) diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/CHANGELOG.md b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/CHANGELOG.md new file mode 100644 index 0000000..1f384df --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/CHANGELOG.md @@ -0,0 +1,786 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [1.9.0] - 2026-01-27 + +### Added + +- Lots of work on the Ripper translation layer to make it more compatible and efficient. +- Alias `Prism::Node#breadth_first_search` to `Prism::Node#find`. +- Add `Prism::Node#breadth_first_search_all`/`Prism::Node#find_all` for finding all nodes matching a condition. + +### Changed + +- Fixed location of opening tokens when invalid syntax is parsed. +- Fix RBI for parsing options. + +## [1.8.0] - 2026-01-12 + +### Added + +- Optimize ruby visitor. +- Report unterminated construct errors at opening token. + +### Changed + +- Correctly expose ripper state. +- Use one file for versioned parser classes. +- Fix denominator of rational float literal. +- Decouple ripper translator from ripper library. +- Sync Prism::Translation::ParserCurrent with Ruby 4.0. + +## [1.7.0] - 2025-12-18 + +### Added + +- Support `4.1` as a version option. +- Add `equal_loc` to `CallNode`. +- Add `len()`/`is_empty()` to `ConstantList` and `NodeList` in the Rust API. + +### Changed + +- Rename version `3.5` to version `4.0`. +- Fix compiling the gem from source on Windows. +- Fix parsing of unary method calls like `42.~@`. +- Reject `def f a, (b) = 1`. +- Reject endless method as a block parameter default. +- Reject variable capture in alternative pattern. +- Many fixes in regards to memory safety, found through fuzzing. +- Many fixes to better handle invalid syntax, also found through fuzzing. +- Fix the ruby version used by the `ripper` translator. +- Fix `ruby_parser` translation comment processing. + +## [1.6.0] - 2025-10-16 + +### Added + +- Add support for passing `"current"` as the version option to `Prism.*` APIs. + +### Changed + +- Remove a compiler warning for a missing unsigned cast for a shift value. + +## [1.5.2] - 2025-10-09 + +### Changed + +- Fix character literal forced encoding when a unicode escape sequence is used. +- Reject `1 if foo = bar baz`. +- Clear static literal flag on interpolated strings. +- Reject optional argument/endless method definition ambiguity. + +## [1.5.1] - 2025-09-13 + +### Changed + +- Revert of a bug introduced with static literal flags on interpolated strings. + +## [1.5.0] - 2025-09-12 + +### Added + +- Add `Prism::Translation::ParserCurrent`. +- Add `Integer::to_u32_digits` for the Rust API. +- Add `pm_comment_type_t` field for the Rust API. +- Support leading logical operators for CRuby 3.5+. + +### Changed + +- Mark Prism as ractor-safe. +- Enforce a minimum version for the parser translation layer. +- Many fixes to the parser translation layer. +- Accept a newline after the `defined?` keyword. +- Reject `true && not true`. +- Make `it = it` assign nil to match parse.y behavior [Bug #21139]. +- Some fixes to the ruby parser translation layer. +- Ensure call nodes have the correct ending location. +- Reject `foo && return bar`. + +## [1.4.0] - 2025-03-18 + +### Added + +- Support `3.5` as a version option. +- Many, many compatibility fixes for the parser translation layer. +- Handle escapes in named capture names. +- The `freeze` option is added to the various `Prism::` APIs to deeply freeze the AST. +- Properly support `it` for the parser and ruby_parser translation layers. +- Track the `then` keyword on `rescue` nodes. +- Add a `multiple_statements?` flag to parentheses nodes to support desired `defined?` behavior. + +### Changed + +- The strings used in the AST are now frozen. +- Fixed handling escaped characters after control sequences in character literals. +- Fix reading off the end of an unterminated global variable. +- Raise a syntax error for defining `[]=` with endless method syntax. +- Increase value of `PRISM_DEPTH_MAXIMUM` to `10000`. +- Freeze `Prism::VERSION`. +- Fix up rescue modifier precedence. + +## [1.3.0] - 2024-12-21 + +### Added + +- Introduce `Prism::StringQuery`. +- Introduce `Prism::Relocation`. +- Track `do` keyword for `WhileNode` and `UntilNode`. +- Change the way the gem is built to rely on `mkmf` instead of `make`. +- Lots more documentation on node fields. + +### Changed + +- Properly add an error for `def @foo; end`. +- Properly add an error for `foo(**, *)`. +- Fix up regression in string parsing in `RubyParser` translation. +- Reject invalid dot method call after match expression. +- Reject invalid operator after match expression. +- Fix up %-literals delimited by newlines. +- Properly add an error for `-> { _1; -> { _1 } }`. +- Reject blocks and keywords in index writes. + +## [1.2.0] - 2024-10-10 + +### Added + +- Introduce `Prism::CodeUnitsCache`. + +### Changed + +- Properly handle lexing global variables that begin with `$-`. +- Properly reject invalid multi writes within parentheses. +- Fix unary `*` binding power. +- Set `contains_keywords` flag for implicit `gets` calls when `-p` is used. +- Properly reject invalid non-associative operator patterns. +- Do not warn about unused variables declared on negative lines. + +## [1.1.0] - 2024-10-02 + +### Added + +- Explicitly type each child node field in the Ruby API. +- Add the `main_script` option to the parse APIs, which controls whether or not shebangs are considered. +- Add the `partial_script` options to the parse APIs, which controls whether or not jumps that would otherwise be considered invalid are allowed. This is useful for parsing things like ERB sources, where you know it will be evaluated in a different context. Note that this functionality is replacing the previous idiom of passing in a list of scopes to indicate an `eval` context, because that behavior has changed upstream in `ruby/ruby`. +- Add `ArgumentsNode#contains_multiple_splats?`. +- Add `ArgumentsNode#contains_forwarding?`. +- Accept all valid Ruby versions for the `version` option on parse APIs. +- Accept version shorthands like `"3.3"` and `"3.4"` for the `version` option on parse APIs. +- Support a max depth to protect against malicious payloads without hitting the stack limit. + +### Changed + +- Fix some token incompatibilities in the `parser` translation. +- Fix up parsing tempfiles on Windows. +- Fix up handling UTF-8 characters in file paths on Windows. +- Do not warn for a `\r` at the end of a shebang on Windows. +- Properly handle erroring for parsing a directory on Windows. +- When a numbered reference is out of range, warn instead of raise. +- Allow returns in default parameter values. +- Reject many more invalid syntax patterns. + +## [1.0.0] - 2024-08-28 + +### Added + +- Add `Node#breadth_first_search`. +- Add `Node#node_id`. +- Add `ArgumentsNode#contains_splat?`. +- Passing the special value `false` for the `encoding` option tells Prism to ignore magic encoding comments. +- Expose flags on every node type (allows checking static literal and newline). +- Implement mismatched indentation warning. +- Add C API for receiving a callback when parsing shebangs with additional flags. + +### Changed + +- **BREAKING**: Some fields are renamed that had illogical names. The previous names all now emit deprecation warnings. + - `CaseMatchNode#consequent` was renamed to `CaseMatchNode#else_clause` + - `CaseNode#consequent` was renamed to `CaseNode#else_clause` + - `IfNode#consequent` was renamed to `IfNode#subsequent` + - `RescueNode#consequent` was renamed to `RescueNode#subsequent` + - `UnlessNode#consequent` was renamed to `UnlessNode#else_clause` +- Block exits are now allowed in loop predicates (e.g., `while _ && break do end`). +- Multi-writes are now disallowed when not at the statement level. +- Ensure that range operators are non-associative. +- (JavaScript) Correctly deserialize encoded strings. +- Properly support parsing regular expressions in extended mode. +- Use gmake on FreeBSD. +- Parsing streams now handles NUL bytes in the middle of the stream. +- Properly detect invalid returns. + +## [0.30.0] - 2024-06-07 + +### Added + +- More correctly raise mixed encoding errors. +- Implement ambiguous binary operator warning. +- Fix up regexp escapes with control and meta characters. +- Fix up support for the `it` implicit local variable. +- Heredoc identifiers now properly disallow CLRF. +- Errors added for void value expressions in begin clauses. +- Many updates to more closely match the `parser` gem in parser translation. +- Many errors added for invalid regular expressions. + +### Changed + +- Handle parser translation missing the `parser` gem. +- Handle ruby_parser translation missing the `ruby_parser` gem. +- Various error messages have been updated to more closely match CRuby. +- `RationalNode` now has a `numerator` and `denominator` field instead of a `numeric` field. For the Ruby API we provide a `RationalNode#numeric` method for backwards-compatibility. + +## [0.29.0] - 2024-05-10 + +### Added + +- Added `Prism::CallNode#full_message_loc`, which gives the location including the `=` if there is one. +- A warning for when `# shareable_constant_value` is not used on its own line. +- An error for invalid implicit local variable writes. +- Implicit hash patterns in array patterns are disallowed. +- We now validate that Unicode escape sequences are not surrogates. + +### Changed + +- All fields named `operator` have been renamed to `binary_operator` for `*OperatorWriteNode` nodes. This is to make it easier to provide C++ support. In the Ruby API, the old fields are aliased to the new fields with a deprecation warning. +- Many updated error messages to more closely match CRuby. +- We ensure keyword parameters do not end in `!` or `?`. +- Fixed some escaping in string literals with control sequences and hex escapes. +- Fix a bug with RBS types when used outside the `ruby/prism` codebase. + +## [0.28.0] - 2024-05-03 + +### Added + +- Nested hashes will now warn for duplicated keys, as in: `{ foo: 1, **{ foo: 2 } }`. +- `Prism::ReturnNode` now has a flag on it to indicate if it is redundant. +- `Prism::Location#slice_lines` and `Prism::Node#slice_lines` are now provided to slice the source code of a node including the content before the node on the same line that it starts on and the content after the node on the same line that it ends on. +- Symbols with invalid byte sequences now give errors. +- You can now pass `"3.3.1"` to the `version:` parameter on all `Prism.*` APIs. +- `Prism::Source#lines`, `Prism::Location#source_lines`, `Prism::Node#source_lines`, and `Prism::Node#script_lines` are now provided, which will all return the source code of the source as an array of strings. +- `Prism::ASCIISource` is now provided, which is a subclass of `Prism::Source` but specialized to increase performance when the source is entirely ASCII. +- Prism now provides errors when parsing Ruby 3.4+ syntax for index expressions with keywords or blocks. +- Prism now provides an error when `**nil` is used after other keyword parameters. +- Prism now provides errors when safe navigation is used in call target expressions, e.g., `foo&.bar, = 1`. +- `Prism::Node#tunnel` is now provided, which returns an array of nodes starting at the current node that contain a given line and column. + +### Changed + +- All translation layers now assume an eval context, which means they will not return errors for invalid jumps like `yield`. +- `Prism::Node#inspect` now uses a queue instead of recursion to avoid stack overflows. +- Prism now more closely mirrors CRuby interpolation semantics, which means you could potentially have a static literal string that directly interpolates another static literal string. +- The shipped RBI sorbet types no longer use generics. +- `Prism::ConstantPathNode#child` and `Prism::ConstantTargetNode#child` are now deprecated, replaced by two new fields on these nodes: `name` and `name_loc`. + +## [0.27.0] - 2024-04-23 + +### Added + +- Implemented `===` for each of the nodes, which will check if equality but ignore the specific ranges of locations. + +### Changed + +- Fix translation of `ItParametersNode` for parser translation. +- Fix translation of `dstr` for ruby_parser translation. +- Do not allow omitted hash values whose keys end with `!` or `?`. +- Split up `Prism::ParseResult` into `Prism::Result` with subclasses `Prism::ParseResult`, `Prism::LexResult`, `Prism::ParseLexResult`, and `Prism::LexCompat::Result`. +- Change reflection classes to have only a single `IntegerField` class and rename `DoubleField` to `FloatField`. +- Fall back to default `AR` and `CC` in `Makefile`. +- Use GC-able symbols for the syntax tree to avoid adding to the global symbol table. +- Fix a bug with karatsuba_multiply that would result in a stack overflow. +- Fix parser translation when looking for tokens with `srange_find`. + +## [0.26.0] - 2024-04-18 + +### Added + +- Add `Prism::Node::fields`, which returns a list of `Prism::Reflection::Field` objects representing the fields of the node class. This is useful in metaprogramming contexts. +- `Prism::Location#chop`, for removing the last byte from a location. +- The void statement warning is now implemented. +- The unreachable statement warning is now implemented. +- A syntax error has been added for block arguments on yields, e.g., `yield(&foo)`. + +### Changed + +- Better fidelity to `parser` when translating heredocs with interpolation. +- Fixed `RBI` and `RBS` types for `Prism::parse_*` signatures. +- Remove some incorrect warnings about unused local variables. +- More closely match CRuby error messages for global variables. +- Fix an issue with `parser` translation when line continuations are found in string literals. + +## [0.25.0] - 2024-04-05 + +### Added + +- `Prism::Translation::Ripper` is now able to mirror all of the Ripper APIs. +- `Prism::Location#leading_comments` and `Prism::Location#trailing_comments` is added. +- `Prism::Comment#slice` is added. +- Warn for writing literal values in conditional predicates. +- Check for `_POSIX_MAPPED_FILES` before using `mmap`. +- `Prism::ItParametersNode` is added, to support `-> { it }`. +- Parse integer and float literal values onto the tree. +- Warn on duplicated hash keys and duplicated when clauses. +- Ship much improved `RBI` and `RBS` types. +- Support for the `-p`, `-n`, `-a`, and `-l` command line switches. +- Warn on integer literals in flip-flops. +- Support BSD make. +- Add `Prism::WhenNode#then_keyword_loc`. +- Support custom allocation functions through the `PRISM_XALLOCATOR` define. +- Warn for certain keywrods at the end of the line. +- Provide `pm_visit_node`, a C visitor API. +- `Prism::parse_stream` is added, which works for any Ruby `IO` object. +- Provide flags for regular expression literals for their derived encoding. +- Provide flags for whether or not an interpolated string literal is frozen. +- Add `Prism::StringNode.mutable?` for when a string is explicitly mutable, to support delineating chilled strings. +- Warn for incorrect character literal syntax. +- Warn for chained comparison operators. +- Warn for `**` interpreted as an argument prefix. +- Warn for `&` interpreted as an argument prefix. +- `Prism::ShareableConstantNode` added to support ractors. +- Warn for frozen string literals found after tokens. +- Support `PRISM_BUILD_MINIMAL` to provide only the minimal necessary functionality to reduce the binary size. +- Handle CLRF inside heredocs, strings, and regular expressions. +- Mark inner strings in interpolated strings as frozen. +- Support the `-x` command line switch. +- Error messages now much more closely mirror CRuby. +- Provide syntax errors for invalid block exits (`break`, `next`, `retry`, and `yield`). +- Warn on unused local variables. +- Do not syntax error on default parameter values that only write to the parameter. + +### Changed + +- Many improvements to the compatibility with the `whitequark/parser` translation. +- Accept newlines before pattern terminators `)` or `]`. +- `Prism::Node#start_offset` and `Prism::Node#end_offset` are now much more efficient. +- Read files using `fread` instead of `mmap` when we're going to keep around the source through the Ruby API. +- Fix `Sexp#line_max` setting in the `seattlerb/ruby_parser` translation layer. +- Allow spaces before the encoding comment. + +## [0.24.0] - 2024-02-15 + +### Added + +- More support for `Prism::Translation::Ripper` is added. +- Support multiple versions for `Prism::Translation::Parser`. +- Improved memory usage in the FFI backend. +- Very large speed and memory improvements for creating the Ruby AST. + +### Changed + +- Fix location for empty symbol in hash key. +- Fix parsing a rescue modifier on the value of an assignment when the LHS is a method call with arguments and no parentheses. + +## [0.23.0] - 2024-02-14 + +### Added + +- More support for `Prism::RipperCompat` is added. +- A significantly faster offset cache for `Prism::Translation::Parser` is added for files with multibyte characters. +- `Prism::Translation::RubyParser` is added. +- `Prism::ConstantPathTarget#full_name` is added. +- `version: "3.4.0"` is added as an option that is an alias for `version: "latest"`. +- Four new APIs are added to `Prism::Location`: + - `Prism::Location#start_code_units_offset` + - `Prism::Location#end_code_units_offset` + - `Prism::Location#start_code_units_column` + - `Prism::Location#end_code_units_column` +- Invalid multibyte characters are now validated within strings, lists, and heredocs. + +### Changed + +- When defining `def !@`, the `name_loc` was previously only pointing to `!`, but now includes the `@`. The `name` is the same. +- `Prism::RipperCompat` has been moved to `Prism::Translation::Ripper`. +- Many of the error messages that prism produces have been changed to match the error messages that CRuby produces. + +## [0.22.0] - 2024-02-07 + +### Added + +- More support for `Prism::RipperCompat` is added. +- Support for Ruby 2.7 has been added, and the minimum Ruby requirement has been lowered to 2.7. + +### Changed + +- The error for an invalid source encoding has a new `:argument` level to indicate it raises an argument error. +- `BeginNode` nodes that are used when a class, singleton class, module, method definition, or block have an inline `rescue`/`ensure`/`else` now have their opening locations set to the beginning of the respective keyword. +- Improved error messages for invalid characters. +- `Prism.parse_file` and similar APIs will raise more appropriate errors when the file does not exist or cannot be mapped. +- Correctly handle the `recover` parameter for `Prism::Translation::Parser`. + +## [0.21.0] - 2024-02-05 + +### Added + +- Add the `pm_constant_pool_find` API for finding a constant. + +### Changed + +- Fixes for `Prism::Translation::Parser`. + - Ensure all errors flow through `parser.diagnostics.process`. + - Fix the find pattern node. + - Fix block forwarding with `NumberedParametersNode`. + - Ensure we can parse strings with invalid bytes for the encoding. + - Fix hash pairs in pattern matching. +- Properly reject operator writes on operator calls, e.g., `a.+ -= b`. +- Fix multi-byte escapes. +- Handle missing body in `begin` within the receiver of a method call. + +## [0.20.0] - 2024-02-01 + +### Added + +- String literal hash keys are now marked as frozen as static literal. +- `IndexTargetNode` now always has the `ATTRIBUTE_WRITE` flag. +- `Call*Node` nodes now have an `IGNORE_VISIBILITY` flag. +- We now support the `it` default parameter. +- Errors and warnings now have levels associated with them. +- Symbols now have correct encoding flags. +- We have now merged `parser-prism` in, which provides translation to the `whitequark/parser` AST. +- We now emit errors for invalid method definition receivers. + +### Changed + +- We now emit errors on invalid pinned local variables. +- When passed scopes, it is now assumed that the innermost scope is the current binding. +- We now provide better error recovery for non terminated heredocs. +- Fix for `RationalNode#value` for non-decimal integers. +- Unary symbols `!@` and `~@` now unescape to `!` and `~`, respectively. +- `frozen_string_literal: false` now works properly. + +### Removed + +- We've removed the `locals_body_index` field. +- We've removed the `verbose` option on the various parse APIs. Warnings are now always emitted with their associated level so that consumers can decide how to handle them. + +## [0.19.0] - 2023-12-14 + +### Added + +- `ArrayNode` now has a `contains_splat?` flag if it has a splatted element in it. +- All of the remaining encodings have been implemented. +- Allow forwarding `&` in a method that has a `...` parameter. +- Many statements that are found in non-statement positions are being properly rejected now. +- Void values are now properly checked. +- Referencing a parameter in its own default value is now properly rejected. +- `DATA`/`__END__` is now parsed as its own field on parse result (`data_loc`) as opposed to as a comment. +- Blank `*` now properly forwards into arrays. +- `ImplicitRestNode` is introduced to represent the implicit rest of a destructure. +- We now support negative start lines. +- `StringNode#heredoc?`, `InterpolatedStringNode#heredoc?`, `XStringNode#heredoc?`, and `InterpolatedXStringNode#heredoc?` are introduced. +- `NumberedParametersNode` is introduced to represent the implicit set of parameters when numbered parameters are used. +- `Prism::parse_success?` and `Prism::parse_failure?` are introduced to bypass reifying the AST. +- We now emit a warning for constant assignments in method definitions. +- We now provide flags on strings and xstrings to indicate the correct encoding. +- The hash pattern `rest` field now more accurately parses `**` and `**nil`. +- The equality operators are now properly parsed as non-associative. + +### Changed + +- **BREAKING**: Many fields have changed positions within their nodes. This impacts the C API and the Ruby API if you are manually creating nodes through the initializer. +- **BREAKING**: Almost all of the error messages have been updated to begin with lowercase characters to match ruby/spec. +- Unterminated strings with only plain content are now always `StringNode` as opposed to `InterpolatedStringNode` +- **BREAKING**: Call node has been split up when it is in the target position into `CallTargetNode` and `IndexTargetNode`. + +## [0.18.0] - 2023-11-21 + +### Added + +- The `ParametersNode#signature` method is added, which returns the same thing as `Method#parameters`. +- Visitor functionality has been added to the JavaScript API. +- The `Node#to_dot` API has been added to convert syntax trees to Graphviz digraphs. +- `IfNode` and `UnlessNode` now have a `then_keyword_loc` field. +- Many more encodings are now supported. +- Some new `Location` APIs have been added for dealing with characters instead of bytes, which are: `start_character_offset`, `end_character_offset`, `start_character_column`, and `end_character_column`. +- A warning has been added for when `END {}` is used within a method. +- `ConstantPathNode#full_name{,_parts}` will now raise an error if the receiver of the constant path is not itself a constant. +- The `in` keyword and the `=>` operator now respect non-associativity. +- The `..` and `...` operators now properly respect non-associativity. + +### Changed + +- Previously `...` in blocks was accepted, but it is now properly rejected. +- **BREAKING**: `librubyparser.*` has been renamed to `libprism.*` in the C API. +- We now properly reject floats with exponent and rational suffixes. +- We now properly reject void value expressions. +- **BREAKING**: The `--disable-static` option has been removed from the C extension. +- The rescue modifier keyword is now properly parsed in terms of precedence. +- We now properly reject defining a numbered parameter method. +- **BREAKING**: `MatchWriteNode` now has a list of `targets`, which are local variable target nodes. This is instead of `locals` which was a constant list. This is to support writing to local variables outside the current scope. It has the added benefit of providing location information for the local variable targets. +- **BREAKING**: `CaseNode` has been split into `CaseNode` and `CaseMatchNode`, the latter is used for `case ... in` expressions. +- **BREAKING**: `StringConcatNode` has been removed in favor of using `InterpolatedStringNode` as a list. + +## [0.17.1] - 2023-11-03 + +### Changed + +- Do not use constant nesting in RBI files. + +## [0.17.0] - 2023-11-03 + +### Added + +- We now properly support forwarding arguments into arrays, like `def foo(*) = [*]`. +- We now have much better documentation for the C and Ruby APIs. +- We now properly provide an error message when attempting to assign to numbered parameters from within regular expression named capture groups, as in `/(?<_1>)/ =~ ""`. + +### Changed + +- **BREAKING**: `KeywordParameterNode` is split into `OptionalKeywordParameterNode` and `RequiredKeywordParameterNode`. `RequiredKeywordParameterNode` has no `value` field. +- **BREAKING**: Most of the `Prism::` APIs now accept a bunch of keyword options. The options we now support are: `filepath`, `encoding`, `line`, `frozen_string_literal`, `verbose`, and `scopes`. See [the pull request](https://github.com/ruby/prism/pull/1763) for more details. +- **BREAKING**: Comments are now split into three different classes instead of a single class, and the `type` field has been removed. They are: `InlineComment`, `EmbDocComment`, and `DATAComment`. + +## [0.16.0] - 2023-10-30 + +### Added + +- `InterpolatedMatchLastLineNode#options` and `MatchLastLineNode#options` are added, which are the same methods as are exposed on `InterpolatedRegularExpressionNode` and `RegularExpressionNode`. +- The project can now be compiled with `wasi-sdk` to expose a WebAssembly interface. +- `ArgumentsNode#keyword_splat?` is added to indicate if the arguments node has a keyword splat. +- The C API `pm_prettyprint` has a much improved output which lines up closely with `Node#inspect`. +- Prism now ships with `RBS` and `RBI` type signatures (in the `/sig` and `/rbi` directories, respectively). +- `Prism::parse_comments` and `Prism::parse_file_comments` APIs are added to extract only the comments from the source code. + +### Changed + +- **BREAKING**: `Multi{Target,Write}Node#targets` is split up now into `lefts`, `rest`, and `rights`. This is to avoid having to scan the list in the case that there are splat nodes. +- Some bugs are fixed on `Multi{Target,Write}Node` accidentally creating additional nesting when not necessary. +- **BREAKING**: `RequiredDestructuredParameterNode` has been removed in favor of using `MultiTargetNode` in those places. +- **BREAKING**: `HashPatternNode#assocs` has been renamed to `HashPatternNode#elements`. `HashPatternNode#kwrest` has been renamed to `HashPatternNode#rest`. + +## [0.15.1] - 2023-10-18 + +### Changed + +- Fix compilation warning on assigning to bitfield. + +## [0.15.0] - 2023-10-18 + +### Added + +- `BackReferenceReadNode#name` is now provided. +- `Index{Operator,And,Or}WriteNode` are introduced, split out from `Call{Operator,And,Or}WriteNode` when the method is `[]`. + +### Changed + +- Ensure `PM_NODE_FLAG_COMMON_MASK` into a constant expression to fix compile errors. +- `super(&arg)` is now fixed. +- Ensure the last encoding flag on regular expressions wins. +- Fix the common whitespace calculation when embedded expressions begin on a line. +- Capture groups in regular expressions now scan the unescaped version to get the correct local variables. +- `*` and `&` are added to the local table when `...` is found in the parameters of a method definition. + +## [0.14.0] - 2023-10-13 + +### Added + +- Syntax errors are added for invalid lambda local semicolon placement. +- Lambda locals are now checked for duplicate names. +- Destructured parameters are now checked for duplicate names. +- `Constant{Read,Path,PathTarget}Node#full_name` and `Constant{Read,Path,PathTarget}Node#full_name_parts` are added to walk constant paths for you to find the full name of the constant. +- Syntax errors are added when assigning to a numbered parameter. +- `Node::type` is added, which matches the `Node#type` API. +- Magic comments are now parsed as part of the parsing process and a new field is added in the form of `ParseResult#magic_comments` to access them. + +### Changed + +- **BREAKING**: `Call*Node#name` methods now return symbols instead of strings. +- **BREAKING**: For loops now have their index value considered as part of the body, so depths of local variable assignments will be increased by 1. +- Tilde heredocs now split up their lines into multiple string nodes to make them easier to dedent. + +## [0.13.0] - 2023-09-29 + +### Added + +- `BEGIN {}` blocks are only allowed at the top-level, and will now provide a syntax error if they are not. +- Numbered parameters are not allowed in block parameters, and will now provide a syntax error if they are. +- Many more Ruby modules and classes are now documented. Also, many have been moved into their own files and autoloaded so that initial boot time of the gem is much faster. +- `PM_TOKEN_METHOD_NAME` is introduced, used to indicate an identifier that if definitely a method name because it has an `!` or `?` at the end. +- In the C API, arrays, assocs, and hashes now can have the `PM_NODE_FLAG_STATIC_LITERAL` flag attached if they can be compiled statically. This is used in CRuby, for example, to determine if a `duphash`/`duparray` instruction can be used as opposed to a `newhash`/`newarray`. +- `Node#type` is introduced, which returns a symbol representing the type of the node. This is useful for case comparisons when you have to compare against multiple types. + +### Changed + +- **BREAKING**: Everything has been renamed to `prism` instead of `yarp`. The `yp_`/`YP_` prefix in the C API has been changed to `pm_`/`PM_`. For the most part, everything should be find/replaceable. +- **BREAKING**: `BlockArgumentNode` nodes now go into the `block` field on `CallNode` nodes, in addition to the `BlockNode` nodes that used to be there. Hopefully this makes it more consistent to compile/deal with in general, but it does mean it can be a surprising breaking change. +- Escaped whitespace in `%w` lists is now properly unescaped. +- `Node#pretty_print` now respects pretty print indentation. +- `Dispatcher` was previously firing `_leave` events in the incorrect order. This has now been fixed. +- **BREAKING**: `Visitor` has now been split into `Visitor` and `Compiler`. The visitor visits nodes but doesn't return anything from the visit methods. It is suitable for taking action based on the tree, but not manipulating the tree itself. The `Compiler` visits nodes and returns the computed value up the tree. It is suitable for compiling the tree into another format. As such, `MutationVisitor` has been renamed to `MutationCompiler`. + +## [0.12.0] - 2023-09-15 + +### Added + +- `RegularExpressionNode#options` and `InterpolatedRegularExpressionNode#options` are now provided. These return integers that match up to the `Regexp#options` API. +- Greatly improved `Node#inspect` and `Node#pretty_print` APIs. +- `MatchLastLineNode` and `InterpolatedMatchLastLineNode` are introduced to represent using a regular expression as the predicate of an `if` or `unless` statement. +- `IntegerNode` now has a base flag on it. +- Heredocs that were previously `InterpolatedStringNode` and `InterpolatedXStringNode` nodes without any actual interpolation are now `StringNode` and `XStringNode`, respectively. +- `StringNode` now has a `frozen?` flag on it, which respects the `frozen_string_literal` magic comment. +- Numbered parameters are now supported, and are properly represented using `LocalVariableReadNode` nodes. +- `ImplicitNode` is introduced, which wraps implicit calls, local variable reads, or constant reads in omitted hash values. +- `YARP::Dispatcher` is introduced, which provides a way for multiple objects to listen for certain events on the AST while it is being walked. This is effectively a way to implement a more efficient visitor pattern when you have many different uses for the AST. + +### Changed + +- **BREAKING**: Flags fields are now marked as private, to ensure we can change their implementation under the hood. Actually querying should be through the accessor methods. +- **BREAKING**: `AliasNode` is now split into `AliasMethodNode` and `AliasGlobalVariableNode`. +- Method definitions on local variables is now correctly handled. +- Unary minus precedence has been fixed. +- Concatenating character literals with string literals is now fixed. +- Many more invalid syntaxes are now properly rejected. +- **BREAKING**: Comments now no longer include their trailing newline. + +## [0.11.0] - 2023-09-08 + +### Added + +- `Node#inspect` is much improved. +- `YARP::Pattern` is introduced, which can construct procs to match against nodes. +- `BlockLocalVariableNode` is introduced to take the place of the locations array on `BlockParametersNode`. +- `ParseResult#attach_comments!` is now provided to attach comments to locations in the tree. +- `MultiTargetNode` is introduced as the target of multi writes and for loops. +- `Node#comment_targets` is introduced to return the list of objects that can have attached comments. + +### Changed + +- **BREAKING**: `GlobalVariable*Node#name` now returns a symbol. +- **BREAKING**: `Constant*Node#name` now returns a symbol. +- **BREAKING**: `BlockParameterNode`, `KeywordParameterNode`, `KeywordRestParameterNode`, `RestParameterNode`, `DefNode` all have their `name` methods returning symbols now. +- **BREAKING**: `ClassNode#name` and `ModuleNode#name` now return symbols. +- **BREAKING**: `Location#end_column` is now exclusive instead of inclusive. +- `Location#slice` now returns a properly encoded string. +- `CallNode#operator_loc` is now `CallNode#call_operator_loc`. +- `CallOperatorAndWriteNode` is renamed to `CallAndWriteNode` and its structure has changed. +- `CallOperatorOrWriteNode` is renamed to `CallOrWriteNode` and its structure has changed. + +## [0.10.0] - 2023-09-01 + +### Added + +- `InstanceVariable*Node` and `ClassVariable*Node` objects now have their `name` returning a Symbol. This is because they are now part of the constant pool. +- `NumberedReferenceReadNode` now has a `number` field, which returns an Integer. + +### Changed + +- **BREAKING**: Various `operator_id` and `constant_id` fields have been renamed to `operator` and `name`, respectively. See [09d0a144](https://github.com/ruby/yarp/commit/09d0a144dfd519c5b5f96f0b6ee95d256e2cb1a6) for details. +- `%w`, `%W`, `%i`, `%I`, `%q`, and `%Q` literals can now span around the contents of a heredoc. +- **BREAKING**: All of the public C APIs that accept the source string now accept `const uint8_t *` as opposed to `const char *`. + +## [0.9.0] - 2023-08-25 + +### Added + +- Regular expressions can now be bound by `\n`, `\r`, and a combination of `\r\n`. +- Strings delimited by `%`, `%q`, and `%Q` can now be bound by `\n`, `\r`, and a combination of `\r\n`. +- `IntegerNode#value` now returns the value of the integer as a Ruby `Integer`. +- `FloatNode#value` now returns the value of the float as a Ruby `Float`. +- `RationalNode#value` now returns the value of the rational as a Ruby `Rational`. +- `ImaginaryNode#value` now returns the value of the imaginary as a Ruby `Complex`. +- `ClassNode#name` is now a string that returns the name of just the class, without the namespace. +- `ModuleNode#name` is now a string that returns the name of just the module, without the namespace. +- Regular expressions and strings found after a heredoc declaration but before the heredoc body are now parsed correctly. +- The serialization API now supports shared strings, which should help reduce the size of the serialized AST. +- `*Node#copy` is introduced, which returns a copy of the node with the given overrides. +- `Location#copy` is introduced, which returns a copy of the location with the given overrides. +- `DesugarVisitor` is introduced, which provides a simpler AST for use in tools that want to process fewer node types. +- `{ClassVariable,Constant,ConstantPath,GlobalVariable,InstanceVariable,LocalVariable}TargetNode` are introduced. These nodes represent the target of writes in locations where a value cannot be provided, like a multi write or a rescue reference. +- `UntilNode#closing_loc` and `WhileNode#closing_loc` are now provided. +- `Location#join` is now provided, which joins two locations together. +- `YARP::parse_lex` and `YARP::parse_lex_file` are introduced to parse and lex in one result. + +### Changed + +- When there is a magic encoding comment, the encoding of the first token's source string is now properly reencoded. +- Constants followed by unary `&` are now properly parsed as a call with a passed block argument. +- Escaping multi-byte characters in a string literal will now properly escape the entire character. +- `YARP.lex_compat` now has more accurate behavior when a byte-order mark is present in the file. +- **BREAKING**: `AndWriteNode`, `OrWriteNode`, and `OperatorWriteNode` have been split back up into their `0.7.0` versions. +- We now properly support spaces between the `encoding` and `=`/`:` in a magic encoding comment. +- We now properly parse `-> foo: bar do end`. + +## [0.8.0] - 2023-08-18 + +### Added + +- Some performance improvements when converting from the C AST to the Ruby AST. +- Two rust crates have been added: `yarp-sys` and `yarp`. They are as yet unpublished. + +### Changed + +- Escaped newlines in strings and heredocs are now handled more correctly. +- Dedenting heredocs that result in empty string nodes will now drop those string nodes from the list. +- Beginless and endless ranges in conditional expressions now properly form a flip flop node. +- `%` at the end of files no longer crashes. +- Location information has been corrected for `if/elsif` chains that have no `else`. +- `__END__` at the very end of the file was previously parsed as an identifier, but is now correct. +- **BREAKING**: Nodes that reference `&&=`, `||=`, and other writing operators have been consolidated. Previously, they were separate individual nodes. Now they are a tree with the target being the left-hand side and the value being the right-hand side with a joining `AndWriteNode`, `OrWriteNode`, or `OperatorWriteNode` in the middle. This impacts all of the nodes that match this pattern: `{ClassVariable,Constant,ConstantPath,GlobalVariable,InstanceVariable,LocalVariable}Operator{And,Or,}WriteNode`. +- **BREAKING**: `BlockParametersNode`, `ClassNode`, `DefNode`, `LambdaNode`, `ModuleNode`, `ParenthesesNode`, and `SingletonClassNode` have had their `statements` field renamed to `body` to give a hint that it might not be a `StatementsNode` (it could also be a `BeginNode`). + +## [0.7.0] - 2023-08-14 + +### Added + +- We now have an explicit `FlipFlopNode`. It has the same flags as `RangeNode`. +- We now have a syntax error when implicit and explicit blocks are passed to a method call. +- `Node#slice` is now implemented, for retrieving the slice of the source code corresponding to a node. +- We now support the `utf8-mac` encoding. +- Predicate methods have been added for nodes that have flags. For example `CallNode#safe_navigation?` and `RangeNode#exclude_end?`. +- The gem now functions on JRuby and TruffleRuby, thanks to a new FFI backend. +- Comments are now part of the serialization API. + +### Changed + +- Autotools has been removed from the build system, so when the gem is installed it will no longer need to go through a configure step. +- The AST for `foo = *bar` has changed to have an explicit array on the right hand side, rather than a splat node. This is more consistent with how other parsers handle this. +- **BREAKING**: `RangeNodeFlags` has been renamed to `RangeFlags`. +- Unary minus on number literals is now parsed as part of the literal, rather than a call to a unary operator. This is more consistent with how other parsers handle this. + +## [0.6.0] - 2023-08-09 + +### Added + +- 🎉 Initial release! 🎉 + +[unreleased]: https://github.com/ruby/prism/compare/v1.9.0...HEAD +[1.9.0]: https://github.com/ruby/prism/compare/v1.8.0...v1.9.0 +[1.8.0]: https://github.com/ruby/prism/compare/v1.7.0...v1.8.0 +[1.7.0]: https://github.com/ruby/prism/compare/v1.6.0...v1.7.0 +[1.6.0]: https://github.com/ruby/prism/compare/v1.5.2...v1.6.0 +[1.5.2]: https://github.com/ruby/prism/compare/v1.5.1...v1.5.2 +[1.5.1]: https://github.com/ruby/prism/compare/v1.5.0...v1.5.1 +[1.5.0]: https://github.com/ruby/prism/compare/v1.4.0...v1.5.0 +[1.4.0]: https://github.com/ruby/prism/compare/v1.3.0...v1.4.0 +[1.3.0]: https://github.com/ruby/prism/compare/v1.2.0...v1.3.0 +[1.2.0]: https://github.com/ruby/prism/compare/v1.1.0...v1.2.0 +[1.1.0]: https://github.com/ruby/prism/compare/v1.0.0...v1.1.0 +[1.0.0]: https://github.com/ruby/prism/compare/v0.30.0...v1.0.0 +[0.30.0]: https://github.com/ruby/prism/compare/v0.29.0...v0.30.0 +[0.29.0]: https://github.com/ruby/prism/compare/v0.28.0...v0.29.0 +[0.28.0]: https://github.com/ruby/prism/compare/v0.27.0...v0.28.0 +[0.27.0]: https://github.com/ruby/prism/compare/v0.26.0...v0.27.0 +[0.26.0]: https://github.com/ruby/prism/compare/v0.25.0...v0.26.0 +[0.25.0]: https://github.com/ruby/prism/compare/v0.24.0...v0.25.0 +[0.24.0]: https://github.com/ruby/prism/compare/v0.23.0...v0.24.0 +[0.23.0]: https://github.com/ruby/prism/compare/v0.22.0...v0.23.0 +[0.22.0]: https://github.com/ruby/prism/compare/v0.21.0...v0.22.0 +[0.21.0]: https://github.com/ruby/prism/compare/v0.20.0...v0.21.0 +[0.20.0]: https://github.com/ruby/prism/compare/v0.19.0...v0.20.0 +[0.19.0]: https://github.com/ruby/prism/compare/v0.18.0...v0.19.0 +[0.18.0]: https://github.com/ruby/prism/compare/v0.17.1...v0.18.0 +[0.17.1]: https://github.com/ruby/prism/compare/v0.17.0...v0.17.1 +[0.17.0]: https://github.com/ruby/prism/compare/v0.16.0...v0.17.0 +[0.16.0]: https://github.com/ruby/prism/compare/v0.15.1...v0.16.0 +[0.15.1]: https://github.com/ruby/prism/compare/v0.15.0...v0.15.1 +[0.15.0]: https://github.com/ruby/prism/compare/v0.14.0...v0.15.0 +[0.14.0]: https://github.com/ruby/prism/compare/v0.13.0...v0.14.0 +[0.13.0]: https://github.com/ruby/prism/compare/v0.12.0...v0.13.0 +[0.12.0]: https://github.com/ruby/prism/compare/v0.11.0...v0.12.0 +[0.11.0]: https://github.com/ruby/prism/compare/v0.10.0...v0.11.0 +[0.10.0]: https://github.com/ruby/prism/compare/v0.9.0...v0.10.0 +[0.9.0]: https://github.com/ruby/prism/compare/v0.8.0...v0.9.0 +[0.8.0]: https://github.com/ruby/prism/compare/v0.7.0...v0.8.0 +[0.7.0]: https://github.com/ruby/prism/compare/v0.6.0...v0.7.0 +[0.6.0]: https://github.com/ruby/prism/compare/d60531...v0.6.0 diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/CODE_OF_CONDUCT.md b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..f6ae7f6 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/CODE_OF_CONDUCT.md @@ -0,0 +1,76 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +- The use of sexualized language or imagery and unwelcome sexual attention or + advances +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or electronic + address, without explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at opensource@shopify.com. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/CONTRIBUTING.md b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/CONTRIBUTING.md new file mode 100644 index 0000000..8e6fa3b --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/CONTRIBUTING.md @@ -0,0 +1,58 @@ +# Contributing + +Thank you for your interest in contributing to prism! Below are a couple of ways that you can help out. + +## Discussions + +The discussions page on the GitHub repository are open. If you have a question or want to discuss the project, feel free to open a new discussion or comment on an existing one. This is the best place to ask questions about the project. + +## Code + +If you want to contribute code, please first open or contribute to a discussion. A lot of the project is in flux, and we want to make sure that you are contributing to the right place. Once you have a discussion going, you can open a pull request with your changes. We will review your code and get it merged in. + +## Tests + +We could always use more tests! One of the biggest challenges of this project is building up a big test suite. If you want to contribute tests, feel free to open a pull request. These will get merged in as soon as possible. + +The `test` Rake task will not compile libraries or the C extension, and this is intentional (to make testing against an installed version easier). If you want to test your changes, please make sure you're also running either the task: + +``` sh +bundle exec rake +``` + +or explicitly running the `compile` task: + +``` sh +bundle exec rake compile test +# or to just compile the C extension ... +bundle exec rake compile:prism test +``` + +To test the rust bindings (with caveats about setting up your Rust environment properly first): + +``` sh +bundle exec rake compile test:rust +``` + + +## Documentation + +We could always use more documentation! If you want to contribute documentation, feel free to open a pull request. These will get merged in as soon as possible. Documenting functions or methods is always useful, but we also need more guides and tutorials. If you have an idea for a guide or tutorial, feel free to open an issue and we can discuss it. + +## Developing + +To get `clangd` support in the editor for development, generate the compilation database. This command will +create an ignored `compile_commands.json` file at the project root, which is used by clangd to provide functionality. + +You will need `bear` which can be installed on macOS with `brew install bear`. + +```sh +bundle exec rake bear +``` + +## Debugging + +Some useful rake tasks: + +- `test:valgrind` runs the test suite under valgrind to look for illegal memory access or memory leaks +- `test:gdb` and `test:lldb` run the test suite under those debuggers diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/LICENSE.md b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/LICENSE.md new file mode 100644 index 0000000..2d449f9 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/LICENSE.md @@ -0,0 +1,7 @@ +Copyright 2022-present, Shopify 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. diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/Makefile b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/Makefile new file mode 100644 index 0000000..265d478 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/Makefile @@ -0,0 +1,116 @@ + +# V=0 quiet, V=1 verbose. other values don't work. +V = 0 +V0 = $(V:0=) +Q1 = $(V:1=) +Q = $(Q1:0=@) +ECHO1 = $(V:1=@ :) +ECHO = $(ECHO1:0=@ echo) +FUZZ_OUTPUT_DIR = $(CURDIR)/fuzz/output + +SOEXT ?= $(shell ruby -e 'puts RbConfig::CONFIG["SOEXT"]') + +CPPFLAGS := -Iinclude $(CPPFLAGS) +CFLAGS := -g -O2 -std=c99 -Wall -Werror -Wextra -Wpedantic -Wundef -Wconversion -Wno-missing-braces -fPIC -fvisibility=hidden -Wimplicit-fallthrough $(CFLAGS) +JAVA_WASM_CFLAGS := -g -Oz -std=c99 -Wall -Werror -Wextra -Wpedantic -Wundef -Wconversion -Wno-missing-braces -fPIC -fvisibility=hidden -Wimplicit-fallthrough $(JAVA_WASM_CFLAGS) +CC ?= cc +AR ?= ar +ARFLAGS ?= -r$(V0:1=v) +WASI_SDK_PATH := /opt/wasi-sdk + +MAKEDIRS ?= mkdir -p +RMALL ?= rm -f -r + +HEADERS := $(wildcard include/*.h include/*/*.h include/*/*/*.h') +SOURCES := $(wildcard src/*.c src/*/*.c) +SHARED_OBJECTS := $(subst src/,build/shared/,$(SOURCES:.c=.o)) +STATIC_OBJECTS := $(subst src/,build/static/,$(SOURCES:.c=.o)) + +all: shared static + +shared: build/libprism.$(SOEXT) +static: build/libprism.a +wasm: javascript/src/prism.wasm +java-wasm: java-wasm/src/test/resources/prism.wasm + +build/libprism.$(SOEXT): $(SHARED_OBJECTS) + $(ECHO) "linking $@ with $(CC)" + $(Q) $(CC) $(DEBUG_FLAGS) $(CFLAGS) -shared -o $@ $(SHARED_OBJECTS) + +build/libprism.a: $(STATIC_OBJECTS) + $(ECHO) "building $@ with $(AR)" + $(Q) $(AR) $(ARFLAGS) $@ $(STATIC_OBJECTS) + +javascript/src/prism.wasm: Makefile $(SOURCES) $(HEADERS) + $(ECHO) "building $@" + $(Q) $(WASI_SDK_PATH)/bin/clang --sysroot=$(WASI_SDK_PATH)/share/wasi-sysroot/ \ + $(DEBUG_FLAGS) \ + -DPRISM_EXPORT_SYMBOLS -DPRISM_EXCLUDE_PRETTYPRINT -DPRISM_EXCLUDE_JSON -DPRISM_EXCLUDE_PACK \ + -D_WASI_EMULATED_MMAN -lwasi-emulated-mman $(CPPFLAGS) $(CFLAGS) \ + -Wl,--export-all -Wl,--gc-sections -Wl,--strip-all -Wl,--lto-O3 -Wl,--no-entry -mexec-model=reactor \ + -Oz -g0 -flto -fdata-sections -ffunction-sections \ + -o $@ $(SOURCES) + +java-wasm/src/test/resources/prism.wasm: Makefile $(SOURCES) $(HEADERS) + $(ECHO) "building $@" + $(Q) $(WASI_SDK_PATH)/bin/clang $(DEBUG_FLAGS) -DPRISM_EXCLUDE_PRETTYPRINT -DPRISM_EXPORT_SYMBOLS -D_WASI_EMULATED_MMAN -lwasi-emulated-mman $(CPPFLAGS) $(JAVA_WASM_CFLAGS) -Wl,--export-all -Wl,--no-entry -mexec-model=reactor -lc++ -lc++abi -o $@ $(SOURCES) + +build/shared/%.o: src/%.c Makefile $(HEADERS) + $(ECHO) "compiling $@" + $(Q) $(MAKEDIRS) $(@D) + $(Q) $(CC) $(DEBUG_FLAGS) -DPRISM_EXPORT_SYMBOLS $(CPPFLAGS) $(CFLAGS) -c -o $@ $< + +build/static/%.o: src/%.c Makefile $(HEADERS) + $(ECHO) "compiling $@" + $(Q) $(MAKEDIRS) $(@D) + $(Q) $(CC) $(DEBUG_FLAGS) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< + +build/fuzz.%: $(SOURCES) fuzz/%.c fuzz/fuzz.c + $(ECHO) "building $* fuzzer" + $(Q) $(MAKEDIRS) $(@D) + $(ECHO) "building main fuzz binary" + $(Q) afl-clang-lto $(DEBUG_FLAGS) $(CPPFLAGS) $(CFLAGS) $(FUZZ_FLAGS) -O0 -fsanitize-ignorelist=fuzz/asan.ignore -fsanitize=fuzzer,address -ggdb3 -std=c99 -Iinclude -o $@ $^ + $(ECHO) "building cmplog binary" + $(Q) AFL_LLVM_CMPLOG=1 afl-clang-lto $(DEBUG_FLAGS) $(CPPFLAGS) $(CFLAGS) $(FUZZ_FLAGS) -O0 -fsanitize-ignorelist=fuzz/asan.ignore -fsanitize=fuzzer,address -ggdb3 -std=c99 -Iinclude -o $@.cmplog $^ + +build/fuzz.heisenbug.%: $(SOURCES) fuzz/%.c fuzz/heisenbug.c + $(Q) afl-clang-lto $(DEBUG_FLAGS) $(CPPFLAGS) $(CFLAGS) $(FUZZ_FLAGS) -O0 -fsanitize-ignorelist=fuzz/asan.ignore -fsanitize=fuzzer,address -ggdb3 -std=c99 -Iinclude -o $@ $^ + +fuzz-debug: + $(ECHO) "entering debug shell" + $(Q) docker run -it --rm -e HISTFILE=/prism/fuzz/output/.bash_history -v $(CURDIR):/prism -v $(FUZZ_OUTPUT_DIR):/fuzz_output prism/fuzz + +fuzz-docker-build: fuzz/docker/Dockerfile + $(ECHO) "building docker image" + $(Q) docker build -t prism/fuzz fuzz/docker/ + +fuzz-run-%: FORCE fuzz-docker-build + $(ECHO) "generating templates" + $(Q) bundle exec rake templates + $(ECHO) "running $* fuzzer" + $(Q) docker run --rm -v $(CURDIR):/prism prism/fuzz /bin/bash -c "FUZZ_FLAGS=\"$(FUZZ_FLAGS)\" make build/fuzz.$*" + $(ECHO) "starting AFL++ run" + $(Q) $(MAKEDIRS) $(FUZZ_OUTPUT_DIR)/$* + $(Q) docker run -it --rm -v $(CURDIR):/prism -v $(FUZZ_OUTPUT_DIR):/fuzz_output prism/fuzz /bin/bash -c "./fuzz/$*.sh /fuzz_output/$*" +FORCE: + +fuzz-clean: + $(Q) $(RMALL) fuzz/output + +clean: + $(Q) $(RMALL) build + +.PHONY: clean fuzz-clean + +all-no-debug: DEBUG_FLAGS := -DNDEBUG=1 +all-no-debug: OPTFLAGS := -O3 +all-no-debug: all + +minimal: CFLAGS := $(CFLAGS) -DPRISM_BUILD_MINIMAL +minimal: all + +run: Makefile $(STATIC_OBJECTS) $(HEADERS) test.c + $(ECHO) "compiling test.c" + $(Q) $(CC) $(CPPFLAGS) $(CFLAGS) $(STATIC_OBJECTS) test.c + $(ECHO) "running test.c" + $(Q) ./a.out diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/README.md b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/README.md new file mode 100644 index 0000000..e92ef7b --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/README.md @@ -0,0 +1,143 @@ +

Prism Ruby parser

+
+ Prism Ruby parser +
+ +This 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. + +## Overview + +The repository contains the infrastructure for both a shared library (libprism) and a native CRuby extension. The shared library has no bindings to CRuby itself, and so can be used by other projects. The native CRuby extension links against `ruby.h`, and so is suitable in the context of CRuby. + +``` +. +├── Makefile configuration to compile the shared library and native tests +├── Rakefile configuration to compile the native extension and run the Ruby tests +├── bin +│   ├── lex runs the lexer on a file or string, prints the tokens, and compares to ripper +│   ├── parse runs the parse on a file or string and prints the AST +│   └── prism a CLI for development and debugging +├── config.yml specification for tokens and nodes in the tree +├── doc documentation website +├── docs markdown documentation about the project +├── ext +│   └── prism +│   ├── extconf.rb configuration to generate the Makefile for the native extension +│   └── extension.c the native extension that interacts with libprism +├── fuzz files related to fuzz testing +├── gemfiles gemfiles used by different Ruby versions in CI +├── include +│   ├── prism header files for the shared library +│   └── prism.h main header file for the shared library +├── java Java bindings for the shared library +├── java-wasm Java WASM bindings for the shared library +├── javascript JavaScript WASM bindings for the shared library +├── lib +│   ├── prism Ruby library files +│   └── prism.rb main entrypoint for the Ruby library +├── rakelib various Rake tasks for the project +├── rbi RBI type signatures for the Ruby library +├── rust +│   ├── ruby-prism Rustified crate for the shared library +│   └── ruby-prism-sys FFI binding for Rust +├── sample +│ └── prism Sample code that uses the Ruby API for documentation purposes +├── sig RBS type signatures for the Ruby library +├── src +│   ├── util various utility files +│   └── prism.c main entrypoint for the shared library +├── templates contains ERB templates generated by templates/template.rb +│   └── template.rb generates code from the nodes and tokens configured by config.yml +└── test + └── prism + ├── fixtures Ruby code used for testing + └── snapshots snapshots of generated syntax trees corresponding to fixtures +``` + +## Getting started + +To compile the shared library, you will need: + +* C99 compiler +* GNU make +* Ruby 2.7.0 or later + +Once you have these dependencies, run: + +``` +bundle install +``` + +to fetch the Ruby dependencies. Finally, run: + +``` +bundle exec rake compile +``` + +to compile the shared library. It will be built in the `build` directory. To test that everything is working, run: + +``` +bin/parse -e "1 + 2" +``` + +to see the syntax tree for the expression `1 + 2`. + +## Contributing + +See the [CONTRIBUTING.md](CONTRIBUTING.md) file for more information. We additionally have documentation about the overall design of the project as well as various subtopics. + +* [Build system](docs/build_system.md) +* [Configuration](docs/configuration.md) +* [CRuby compilation](docs/cruby_compilation.md) +* [Design](docs/design.md) +* [Encoding](docs/encoding.md) +* [Fuzzing](docs/fuzzing.md) +* [Heredocs](docs/heredocs.md) +* [JavaScript](docs/javascript.md) +* [Local variable depth](docs/local_variable_depth.md) +* [Mapping](docs/mapping.md) +* [Parser translation](docs/parser_translation.md) +* [Parsing rules](docs/parsing_rules.md) +* [Releasing](docs/releasing.md) +* [Ripper translation](docs/ripper_translation.md) +* [Ruby API](docs/ruby_api.md) +* [RubyParser translation](docs/ruby_parser_translation.md) +* [Serialization](docs/serialization.md) +* [Testing](docs/testing.md) + +## Examples + +Prism has been integrated into the majority of Ruby runtimes, many libraries, and some applications. Below is a list of some of the projects that use Prism: + +### Runtimes + +* [CRuby](https://github.com/ruby/ruby/pull/7964) (via C) +* [Garnet](https://github.com/camertron/garnet-js) (via WASM) +* [JRuby](https://github.com/jruby/jruby/pull/8103) (via Java) +* [Natalie](https://github.com/natalie-lang/natalie/pull/1213) (via C++ and Ruby) +* [Opal](https://github.com/opal/opal/pull/2642) (via Ruby and WASM) +* [TruffleRuby](https://github.com/truffleruby/truffleruby/issues/3117) (via Java) + +### Libraries + +* [dispersion](https://github.com/joeldrapper/dispersion) +* [minifyrb](https://github.com/koic/minifyrb) +* [packwerk](https://github.com/Shopify/packwerk/pull/388) (via parser translator) +* [rbi](https://github.com/Shopify/rbi) +* [rails](https://github.com/rails/rails) + * [parsing renders](https://github.com/rails/rails/pull/49438) + * [parsing rdoc](https://github.com/rails/rails/pull/50870) + * [parsing tests](https://github.com/rails/rails/pull/51006) +* [repl_type_completor](https://github.com/ruby/repl_type_completor) +* [rubocop](https://docs.rubocop.org/rubocop/configuration.html#setting-the-parser-engine) (via parser translator) +* [ruby-lsp](https://github.com/Shopify/ruby-lsp) +* [smart_todo](https://github.com/Shopify/smart_todo/pull/69) +* [sorbet-eraser](https://github.com/kddnewton/sorbet-eraser/pull/25) +* [synvert](https://github.com/xinminlabs/synvert-core-ruby) +* [typeprof](https://github.com/ruby/typeprof) +* [unparser](https://github.com/mbj/unparser) (via parser translator) + +### Applications + +* [gem.sh](https://github.com/marcoroth/gem.sh/pull/96) +* [Sorbet](https://github.com/sorbet/sorbet) diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/config.yml b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/config.yml new file mode 100644 index 0000000..4e5b077 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/config.yml @@ -0,0 +1,4739 @@ +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_CONFLICT_AMPERSAND + - ARGUMENT_CONFLICT_STAR + - ARGUMENT_CONFLICT_STAR_STAR + - ARGUMENT_FORMAL_CLASS + - ARGUMENT_FORMAL_CONSTANT + - ARGUMENT_FORMAL_GLOBAL + - ARGUMENT_FORMAL_IVAR + - ARGUMENT_FORWARDING_UNBOUND + - ARGUMENT_NO_FORWARDING_AMPERSAND + - 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_PARAMETERS + - 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_LIST + - ESCAPE_INVALID_UNICODE_LITERAL + - ESCAPE_INVALID_UNICODE_LONG + - ESCAPE_INVALID_UNICODE_SHORT + - 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_FOR_DELIMITER + - EXPECT_IDENT_REQ_PARAMETER + - EXPECT_IN_DELIMITER + - EXPECT_LPAREN_AFTER_NOT_LPAREN + - EXPECT_LPAREN_AFTER_NOT_OTHER + - EXPECT_LPAREN_REQ_PARAMETER + - EXPECT_MESSAGE + - EXPECT_RBRACKET + - EXPECT_RPAREN + - EXPECT_RPAREN_AFTER_MULTI + - EXPECT_RPAREN_REQ_PARAMETER + - EXPECT_SINGLETON_CLASS_DELIMITER + - 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_NUMBERED + - 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_IDENTIFIER + - HEREDOC_TERM + - INCOMPLETE_QUESTION_MARK + - INCOMPLETE_VARIABLE_CLASS + - INCOMPLETE_VARIABLE_CLASS_3_3 + - INCOMPLETE_VARIABLE_INSTANCE + - INCOMPLETE_VARIABLE_INSTANCE_3_3 + - INSTANCE_VARIABLE_BARE + - INVALID_BLOCK_EXIT + - INVALID_CHARACTER + - INVALID_COMMA + - INVALID_ENCODING_MAGIC_COMMENT + - INVALID_ESCAPE_CHARACTER + - 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_FRACTION + - INVALID_NUMBER_HEXADECIMAL + - INVALID_NUMBER_OCTAL + - INVALID_NUMBER_UNDERSCORE_INNER + - INVALID_NUMBER_UNDERSCORE_TRAILING + - INVALID_PERCENT + - INVALID_PERCENT_EOF + - INVALID_PRINTABLE_CHARACTER + - INVALID_RETRY_AFTER_ELSE + - INVALID_RETRY_AFTER_ENSURE + - INVALID_RETRY_WITHOUT_RESCUE + - INVALID_SYMBOL + - INVALID_VARIABLE_GLOBAL + - INVALID_VARIABLE_GLOBAL_3_3 + - 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 + - NESTING_TOO_DEEP + - NO_LOCAL_VARIABLE + - NON_ASSOCIATIVE_OPERATOR + - NOT_EXPRESSION + - NUMBER_LITERAL_UNDERSCORE + - NUMBERED_PARAMETER_INNER_BLOCK + - NUMBERED_PARAMETER_IT + - NUMBERED_PARAMETER_ORDINARY + - NUMBERED_PARAMETER_OUTER_BLOCK + - OPERATOR_MULTI_ASSIGN + - OPERATOR_WRITE_ARGUMENTS + - OPERATOR_WRITE_BLOCK + - PARAMETER_ASSOC_SPLAT_MULTI + - PARAMETER_BLOCK_MULTI + - PARAMETER_CIRCULAR + - PARAMETER_FORWARDING_AFTER_REST + - 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_UNEXPECTED_NO_KW + - PARAMETER_WILD_LOOSE_COMMA + - PATTERN_ARRAY_MULTIPLE_RESTS + - PATTERN_CAPTURE_DUPLICATE + - PATTERN_CAPTURE_IN_ALTERNATIVE + - 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_FIND_MISSING_INNER + - PATTERN_HASH_IMPLICIT + - PATTERN_HASH_KEY + - PATTERN_HASH_KEY_DUPLICATE + - PATTERN_HASH_KEY_INTERPOLATED + - 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_PARSE_ERROR + - 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_DISALLOWED + - UNARY_RECEIVER + - UNDEF_ARGUMENT + - UNEXPECTED_BLOCK_ARGUMENT + - UNEXPECTED_INDEX_BLOCK + - UNEXPECTED_INDEX_KEYWORDS + - UNEXPECTED_LABEL + - UNEXPECTED_MULTI_WRITE + - UNEXPECTED_PARAMETER_DEFAULT_VALUE + - UNEXPECTED_RANGE_OPERATOR + - UNEXPECTED_SAFE_NAVIGATION + - 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_BINARY_OPERATOR + - 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 + - END_IN_METHOD + - DUPLICATED_HASH_KEY + - DUPLICATED_WHEN_CLAUSE + - FLOAT_OUT_OF_RANGE + - IGNORED_FROZEN_STRING_LITERAL + - INDENTATION_MISMATCH + - INTEGER_IN_FLIP_FLOP + - INVALID_CHARACTER + - INVALID_MAGIC_COMMENT_VALUE + - INVALID_NUMBERED_REFERENCE + - KEYWORD_EOL + - LITERAL_IN_CONDITION_DEFAULT + - LITERAL_IN_CONDITION_VERBOSE + - SHAREABLE_CONSTANT_VALUE_LINE + - SHEBANG_CARRIAGE_RETURN + - UNEXPECTED_CARRIAGE_RETURN + - UNREACHABLE_STATEMENT + - UNUSED_LOCAL_VARIABLE + - VOID_STATEMENT +tokens: + # The order of the tokens at the beginning is important, because we use them + # for a lookup table. + - name: EOF + value: 1 + comment: final token in the file + - name: BRACE_RIGHT + comment: "}" + - name: COMMA + comment: "," + - name: EMBEXPR_END + comment: "}" + - name: KEYWORD_DO + comment: "do" + - name: KEYWORD_ELSE + comment: "else" + - name: KEYWORD_ELSIF + comment: "elsif" + - name: KEYWORD_END + comment: "end" + - name: KEYWORD_ENSURE + comment: "ensure" + - name: KEYWORD_IN + comment: "in" + - name: KEYWORD_RESCUE + comment: "rescue" + - name: KEYWORD_THEN + comment: "then" + - name: KEYWORD_WHEN + comment: "when" + - name: NEWLINE + comment: "a newline character outside of other tokens" + - name: PARENTHESIS_RIGHT + comment: ")" + - name: PIPE + comment: "|" + - name: SEMICOLON + comment: ";" + # Tokens from here on are not used for lookup, and can be in any order. + - 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: 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: 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: 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_LOOP + comment: "do keyword for a predicate in a while, until, or for loop" + - name: KEYWORD_END_UPCASE + comment: "END" + - 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_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_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_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_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: 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: 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_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: 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" + - 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" +flags: + - name: ArgumentsNodeFlags + values: + - name: CONTAINS_FORWARDING + comment: "if the arguments contain forwarding" + - name: CONTAINS_KEYWORDS + comment: "if the arguments contain keywords" + - name: CONTAINS_KEYWORD_SPLAT + comment: "if the arguments contain a keyword splat" + - name: CONTAINS_SPLAT + comment: "if the arguments contain a splat" + - name: CONTAINS_MULTIPLE_SPLATS + comment: "if the arguments contain multiple splats" + 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: ParenthesesNodeFlags + values: + - name: MULTIPLE_STATEMENTS + comment: "parentheses that contain multiple potentially void statements" + comment: Flags for parentheses 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: 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 + kind: + - GlobalVariableReadNode + - BackReferenceReadNode + - NumberedReferenceReadNode + comment: | + Represents the new name of the global variable that can be used after aliasing. + + alias $foo $bar + ^^^^ + - name: old_name + type: node + kind: + - GlobalVariableReadNode + - BackReferenceReadNode + - NumberedReferenceReadNode + - on error: SymbolNode # alias $a b + - on error: MissingNode # alias $a 42 + comment: | + Represents the old name of the global variable that can be used before aliasing. + + 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 + kind: + - SymbolNode + - InterpolatedSymbolNode + comment: | + Represents the new name of the method that will be aliased. + + alias foo bar + ^^^ + + alias :foo :bar + ^^^^ + + alias :"#{foo}" :"#{bar}" + ^^^^^^^^^ + - name: old_name + type: node + kind: + - SymbolNode + - InterpolatedSymbolNode + - on error: GlobalVariableReadNode # alias a $b + - on error: MissingNode # alias a 42 + comment: | + Represents the old name of the method that will be aliased. + + alias foo bar + ^^^ + + alias :foo :bar + ^^^^ + + alias :"#{foo}" :"#{bar}" + ^^^^^^^^^ + - name: keyword_loc + type: location + comment: | + Represents the location of the `alias` keyword. + + alias foo bar + ^^^^^ + comment: | + Represents the use of the `alias` keyword to alias a method. + + alias foo bar + ^^^^^^^^^^^^^ + - name: AlternationPatternNode + fields: + - name: left + type: node + kind: pattern expression + comment: | + Represents the left side of the expression. + + foo => bar | baz + ^^^ + - name: right + type: node + kind: pattern expression + comment: | + Represents the right side of the expression. + + foo => bar | baz + ^^^ + - name: operator_loc + type: location + comment: | + Represents the alternation operator location. + + foo => bar | baz + ^ + comment: | + Represents an alternation pattern in pattern matching. + + foo => bar | baz + ^^^^^^^^^ + - name: AndNode + fields: + - name: left + type: node + kind: non-void expression + 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 + kind: Node + comment: | + Represents the right side of the 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 + flags: ArgumentsNodeFlags + fields: + - name: arguments + type: node[] + kind: non-void expression + comment: | + The list of arguments, if present. These can be any [non-void expressions](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + + foo(bar, baz) + ^^^^^^^^ + comment: | + Represents a set of arguments to a method or a keyword. + + return foo, bar, baz + ^^^^^^^^^^^^^ + - name: ArrayNode + flags: ArrayNodeFlags + fields: + - name: elements + type: node[] + kind: non-void expression + 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? + kind: + - ConstantPathNode + - ConstantReadNode + comment: | + Represents the optional constant preceding the Array + + foo in Bar[] + ^^^ + + foo in Bar[1, 2, 3] + ^^^ + + foo in Bar::Baz[1, 2, 3] + ^^^^^^^^ + - name: requireds + type: node[] + kind: pattern expression + comment: | + Represents the required elements of the array pattern. + + foo in [1, 2] + ^ ^ + - name: rest + type: node? + kind: pattern expression + comment: | + Represents the rest element of the array pattern. + + foo in *bar + ^^^^ + - name: posts + type: node[] + kind: pattern expression + comment: | + Represents the elements after the rest element of the array pattern. + + foo in *bar, baz + ^^^ + - name: opening_loc + type: location? + comment: | + Represents the opening location of the array pattern. + + foo in [1, 2] + ^ + - name: closing_loc + type: location? + comment: | + Represents the closing location of the array pattern. + + foo in [1, 2] + ^ + comment: | + Represents an array pattern in pattern matching. + + foo in 1, 2 + ^^^^^^^^^^^ + + foo in [1, 2] + ^^^^^^^^^^^^^ + + foo in *bar + ^^^^^^^^^^^ + + foo in Bar[] + ^^^^^^^^^^^^ + + foo in Bar[1, 2, 3] + ^^^^^^^^^^^^^^^^^^^ + - name: AssocNode + fields: + - name: key + type: node + kind: non-void expression + 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 + kind: non-void expression + 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? + kind: non-void expression + 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? + comment: | + Represents the location of the `begin` keyword. + + begin x end + ^^^^^ + - name: statements + type: node? + kind: StatementsNode + comment: | + Represents the statements within the begin block. + + begin x end + ^ + - name: rescue_clause + type: node? + kind: RescueNode + comment: | + Represents the rescue clause within the begin block. + + begin x; rescue y; end + ^^^^^^^^ + - name: else_clause + type: node? + kind: ElseNode + comment: | + Represents the else clause within the begin block. + + begin x; rescue y; else z; end + ^^^^^^ + - name: ensure_clause + type: node? + kind: EnsureNode + comment: | + Represents the ensure clause within the begin block. + + begin x; ensure y; end + ^^^^^^^^ + - name: end_keyword_loc + type: location? + comment: | + Represents the location of the `end` keyword. + + begin x end + ^^^ + newline: false + comment: | + Represents a begin statement. + + begin + foo + end + ^^^^^ + - name: BlockArgumentNode + fields: + - name: expression + type: node? + kind: non-void expression + comment: | + The expression that is being passed as a block argument. This can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + + foo(&args) + ^^^^^ + - name: operator_loc + type: location + comment: | + Represents the location of the `&` operator. + + foo(&args) + ^ + comment: | + Represents a block argument using `&`. + + bar(&args) + ^^^^^^^^^^ + - name: BlockLocalVariableNode + flags: ParameterFlags + fields: + - name: name + type: constant + comment: | + The name of the block local variable. + + a { |; b| } # name `:b` + ^ + comment: | + Represents a block local variable. + + a { |; b| } + ^ + - name: BlockNode + fields: + - name: locals + type: constant[] + comment: | + The local variables declared in the block. + + [1, 2, 3].each { |i| puts x } # locals: [:i] + ^ + - name: parameters + type: node? + kind: + - BlockParametersNode + - NumberedParametersNode + - ItParametersNode + comment: | + The parameters of the block. + + [1, 2, 3].each { |i| puts x } + ^^^ + [1, 2, 3].each { puts _1 } + ^^^^^^^^^^^ + [1, 2, 3].each { puts it } + ^^^^^^^^^^^ + - name: body + type: node? + kind: + - StatementsNode + - BeginNode + comment: | + The body of the block. + + [1, 2, 3].each { |i| puts x } + ^^^^^^ + - name: opening_loc + type: location + comment: | + Represents the location of the opening `{` or `do`. + + [1, 2, 3].each { |i| puts x } + ^ + - name: closing_loc + type: location + comment: | + Represents the location of the closing `}` or `end`. + + [1, 2, 3].each { |i| puts x } + ^ + comment: | + Represents a block of ruby code. + + [1, 2, 3].each { |i| puts x } + ^^^^^^^^^^^^^^ + - name: BlockParameterNode + flags: ParameterFlags + fields: + - name: name + type: constant? + comment: | + The name of the block parameter. + + def a(&b) # name `:b` + ^ + end + - name: name_loc + type: location? + comment: | + Represents the location of the block parameter name. + + def a(&b) + ^ + - name: operator_loc + type: location + comment: | + Represents the location of the `&` operator. + + def a(&b) + ^ + end + comment: | + Represents a block parameter of a method, block, or lambda definition. + + def a(&b) + ^^ + end + - name: BlockParametersNode + fields: + - name: parameters + type: node? + kind: ParametersNode + comment: | + Represents the parameters of the block. + + -> (a, b = 1; local) { } + ^^^^^^^^ + + foo do |a, b = 1; local| + ^^^^^^^^ + end + - name: locals + type: node[] + kind: BlockLocalVariableNode + comment: | + Represents the local variables of the block. + + -> (a, b = 1; local) { } + ^^^^^ + + foo do |a, b = 1; local| + ^^^^^ + end + - name: opening_loc + type: location? + comment: | + Represents the opening location of the block parameters. + + -> (a, b = 1; local) { } + ^ + + foo do |a, b = 1; local| + ^ + end + - name: closing_loc + type: location? + comment: | + Represents the closing location of the block parameters. + + -> (a, b = 1; local) { } + ^ + + foo do |a, b = 1; local| + ^ + end + 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 + flags: CallNodeFlags + fields: + - name: receiver + type: node? + kind: non-void expression + 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 &&= value + ^^^ + - name: call_operator_loc + type: location? + comment: | + Represents the location of the call operator. + + foo.bar &&= value + ^ + - name: message_loc + type: location? + comment: | + Represents the location of the message. + + foo.bar &&= value + ^^^ + - name: read_name + type: constant + comment: | + Represents the name of the method being called. + + foo.bar &&= value # read_name `:bar` + ^^^ + - name: write_name + type: constant + comment: | + Represents the name of the method being written to. + + foo.bar &&= value # write_name `:bar=` + ^^^ + - name: operator_loc + type: location + comment: | + Represents the location of the operator. + + foo.bar &&= value + ^^^ + - name: value + type: node + kind: non-void expression + comment: | + Represents the value being assigned. + + foo.bar &&= value + ^^^^^ + comment: | + Represents the use of the `&&=` operator on a call. + + foo.bar &&= value + ^^^^^^^^^^^^^^^^^ + - name: CallNode + flags: CallNodeFlags + fields: + - name: receiver + type: node? + kind: non-void expression + 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? + comment: | + Represents the location of the call operator. + + foo.bar + ^ + + foo&.bar + ^^ + - name: name + type: constant + comment: | + Represents the name of the method being called. + + foo.bar # name `:foo` + ^^^ + - name: message_loc + type: location? + comment: | + Represents the location of the message. + + foo.bar + ^^^ + - name: opening_loc + type: location? + comment: | + Represents the location of the left parenthesis. + foo(bar) + ^ + - name: arguments + type: node? + kind: ArgumentsNode + comment: | + Represents the arguments to the method call. These can be any [non-void expressions](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + + foo(bar) + ^^^ + - name: closing_loc + type: location? + comment: | + Represents the location of the right parenthesis. + + foo(bar) + ^ + - name: equal_loc + type: location? + comment: | + Represents the location of the equal sign, in the case that this is an attribute write. + + foo.bar = value + ^ + + foo[bar] = value + ^ + - name: block + type: node? + kind: + - BlockNode + - BlockArgumentNode + comment: | + Represents the block that is being passed to the method. + + foo { |a| a } + ^^^^^^^^^ + 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 + flags: CallNodeFlags + fields: + - name: receiver + type: node? + kind: non-void expression + comment: | + The object that the method is being called on. This can be either `nil` or any [non-void expressions](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + + foo.bar += value + ^^^ + - name: call_operator_loc + type: location? + comment: | + Represents the location of the call operator. + + foo.bar += value + ^ + - name: message_loc + type: location? + comment: | + Represents the location of the message. + + foo.bar += value + ^^^ + - name: read_name + type: constant + comment: | + Represents the name of the method being called. + + foo.bar += value # read_name `:bar` + ^^^ + - name: write_name + type: constant + comment: | + Represents the name of the method being written to. + + foo.bar += value # write_name `:bar=` + ^^^ + - name: binary_operator + type: constant + comment: | + Represents the binary operator being used. + + foo.bar += value # binary_operator `:+` + ^ + - name: binary_operator_loc + type: location + comment: | + Represents the location of the binary operator. + + foo.bar += value + ^^ + - name: value + type: node + kind: non-void expression + comment: | + Represents the value being assigned. + + foo.bar += value + ^^^^^ + comment: | + Represents the use of an assignment operator on a call. + + foo.bar += baz + ^^^^^^^^^^^^^^ + - name: CallOrWriteNode + flags: CallNodeFlags + fields: + - name: receiver + type: node? + kind: non-void expression + comment: | + The object that the method is being called on. This can be either `nil` or any [non-void expressions](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + + foo.bar ||= value + ^^^ + - name: call_operator_loc + type: location? + comment: | + Represents the location of the call operator. + + foo.bar ||= value + ^ + - name: message_loc + type: location? + comment: | + Represents the location of the message. + + foo.bar ||= value + ^^^ + - name: read_name + type: constant + comment: | + Represents the name of the method being called. + + foo.bar ||= value # read_name `:bar` + ^^^ + - name: write_name + type: constant + comment: | + Represents the name of the method being written to. + + foo.bar ||= value # write_name `:bar=` + ^^^ + - name: operator_loc + type: location + comment: | + Represents the location of the operator. + + foo.bar ||= value + ^^^ + - name: value + type: node + kind: non-void expression + comment: | + Represents the value being assigned. + + foo.bar ||= value + ^^^^^ + comment: | + Represents the use of the `||=` operator on a call. + + foo.bar ||= value + ^^^^^^^^^^^^^^^^^ + - name: CallTargetNode + flags: CallNodeFlags + fields: + - name: receiver + type: node + kind: non-void expression + comment: | + The object that the method is being called on. This can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + + foo.bar = 1 + ^^^ + - name: call_operator_loc + type: location + comment: | + Represents the location of the call operator. + + foo.bar = 1 + ^ + - name: name + type: constant + comment: | + Represents the name of the method being called. + + foo.bar = 1 # name `:foo` + ^^^ + - name: message_loc + type: location + comment: | + Represents the location of the message. + + foo.bar = 1 + ^^^ + 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 + kind: pattern expression + comment: | + Represents the value to capture. + + foo => bar + ^^^ + - name: target + type: node + kind: LocalVariableTargetNode + comment: | + Represents the target of the capture. + + foo => bar + ^^^ + - name: operator_loc + type: location + comment: | + Represents the location of the `=>` operator. + + foo => bar + ^^ + comment: | + Represents assigning to a local variable in pattern matching. + + foo => [bar => baz] + ^^^^^^^^^^^^ + - name: CaseMatchNode + fields: + - name: predicate + type: node? + kind: non-void expression + comment: | + Represents the predicate of the case match. This can be either `nil` or any [non-void expressions](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + + case true; in false; end + ^^^^ + - name: conditions + type: node[] + kind: InNode + comment: | + Represents the conditions of the case match. + + case true; in false; end + ^^^^^^^^ + - name: else_clause + type: node? + kind: ElseNode + comment: | + Represents the else clause of the case match. + + case true; in false; else; end + ^^^^ + - name: case_keyword_loc + type: location + comment: | + Represents the location of the `case` keyword. + + case true; in false; end + ^^^^ + - name: end_keyword_loc + type: location + comment: | + Represents the location of the `end` keyword. + + case true; in false; end + ^^^ + comment: | + Represents the use of a case statement for pattern matching. + + case true + in false + end + ^^^^^^^^^ + - name: CaseNode + fields: + - name: predicate + type: node? + kind: non-void expression + comment: | + Represents the predicate of the case statement. This can be either `nil` or any [non-void expressions](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + + case true; when false; end + ^^^^ + - name: conditions + type: node[] + kind: WhenNode + comment: | + Represents the conditions of the case statement. + + case true; when false; end + ^^^^^^^^^^ + - name: else_clause + type: node? + kind: ElseNode + comment: | + Represents the else clause of the case statement. + + case true; when false; else; end + ^^^^ + - name: case_keyword_loc + type: location + comment: | + Represents the location of the `case` keyword. + + case true; when false; end + ^^^^ + - name: end_keyword_loc + type: location + comment: | + Represents the location of the `end` keyword. + + case true; when false; end + ^^^ + 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 + comment: | + Represents the location of the `class` keyword. + + class Foo end + ^^^^^ + - name: constant_path + type: node + kind: + - ConstantReadNode + - ConstantPathNode + - on error: CallNode # class 0.X end + - name: inheritance_operator_loc + type: location? + comment: | + Represents the location of the `<` operator. + + class Foo < Bar + ^ + - name: superclass + type: node? + kind: non-void expression + comment: | + Represents the superclass of the class. + + class Foo < Bar + ^^^ + - name: body + type: node? + kind: + - StatementsNode + - BeginNode + comment: | + Represents the body of the class. + + class Foo + foo + ^^^ + - name: end_keyword_loc + type: location + comment: | + Represents the location of the `end` keyword. + + class Foo end + ^^^ + - name: name + type: constant + comment: | + The name of the class. + + class Foo end # name `:Foo` + comment: | + Represents a class declaration involving the `class` keyword. + + class Foo end + ^^^^^^^^^^^^^ + - name: ClassVariableAndWriteNode + 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). + + @@target &&= value # name `:@@target` + ^^^^^^^^ + - name: name_loc + type: location + comment: | + Represents the location of the variable name. + + @@target &&= value + ^^^^^^^^ + - name: operator_loc + type: location + comment: | + Represents the location of the `&&=` operator. + + @@target &&= value + ^^^ + - name: value + type: node + kind: non-void expression + comment: | + Represents the value being assigned. This can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + + @@target &&= value + ^^^^^ + 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: binary_operator_loc + type: location + - name: value + type: node + kind: non-void expression + - name: binary_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 + kind: non-void expression + 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 + kind: non-void expression + 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 + kind: non-void expression + 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: binary_operator_loc + type: location + - name: value + type: node + kind: non-void expression + - name: binary_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 + kind: non-void expression + 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 + kind: non-void expression + comment: | + Represents the use of the `&&=` operator for assignment to a constant path. + + Parent::Child &&= value + ^^^^^^^^^^^^^^^^^^^^^^^ + - name: ConstantPathNode + fields: + - name: parent + type: node? + kind: non-void expression + 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: name + type: constant? + comment: The name of the constant being accessed. This could be `nil` in the event of a syntax error. + - name: delimiter_loc + type: location + comment: | + The location of the `::` delimiter. + + ::Foo + ^^ + + One::Two + ^^ + - name: name_loc + type: location + comment: | + The location of the name of the constant. + + ::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: binary_operator_loc + type: location + - name: value + type: node + kind: non-void expression + - name: binary_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 + kind: non-void expression + comment: | + Represents the use of the `||=` operator for assignment to a constant path. + + Parent::Child ||= value + ^^^^^^^^^^^^^^^^^^^^^^^ + - name: ConstantPathTargetNode + fields: + - name: parent + type: node? + kind: non-void expression + - name: name + type: constant? + - name: delimiter_loc + type: location + - name: name_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 + kind: non-void expression + 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 + kind: non-void expression + 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? + kind: non-void expression + - name: parameters + type: node? + kind: ParametersNode + - name: body + type: node? + kind: + - StatementsNode + - BeginNode + - 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 + kind: Node # More than non-void expression as defined?(return) is allowed, yet defined?(BEGIN{}) is SyntaxError + - 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 + kind: + - InstanceVariableReadNode + - ClassVariableReadNode + - GlobalVariableReadNode + - BackReferenceReadNode + - NumberedReferenceReadNode + 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? + kind: + - ConstantPathNode + - ConstantReadNode + comment: | + Represents the optional constant preceding the pattern + + foo in Foo(*bar, baz, *qux) + ^^^ + - name: left + type: node + kind: SplatNode + comment: | + Represents the first wildcard node in the pattern. + + foo in *bar, baz, *qux + ^^^^ + + foo in Foo(*bar, baz, *qux) + ^^^^ + - name: requireds + type: node[] + kind: pattern expression + comment: | + Represents the nodes in between the wildcards. + + foo in *bar, baz, *qux + ^^^ + + foo in Foo(*bar, baz, 1, *qux) + ^^^^^^ + - name: right + type: node + kind: + - SplatNode + - on error: MissingNode + comment: | + Represents the second wildcard node in the pattern. + + foo in *bar, baz, *qux + ^^^^ + + foo in Foo(*bar, baz, *qux) + ^^^^ + - name: opening_loc + type: location? + comment: | + The location of the opening brace. + + foo in [*bar, baz, *qux] + ^ + + foo in Foo(*bar, baz, *qux) + ^ + - name: closing_loc + type: location? + comment: | + The location of the closing brace. + + foo in [*bar, baz, *qux] + ^ + + foo in Foo(*bar, baz, *qux) + ^ + comment: | + Represents a find pattern in pattern matching. + + foo in *bar, baz, *qux + ^^^^^^^^^^^^^^^ + + foo in [*bar, baz, *qux] + ^^^^^^^^^^^^^^^^^ + + foo in Foo(*bar, baz, *qux) + ^^^^^^^^^^^^^^^^^^^^ + + foo => *bar, baz, *qux + ^^^^^^^^^^^^^^^ + - name: FlipFlopNode + flags: RangeFlags + fields: + - name: left + type: node? + kind: non-void expression + - name: right + type: node? + kind: non-void expression + - 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 + kind: + - LocalVariableTargetNode + - InstanceVariableTargetNode + - ClassVariableTargetNode + - GlobalVariableTargetNode + - ConstantTargetNode + - ConstantPathTargetNode + - CallTargetNode + - IndexTargetNode + - MultiTargetNode + - on error: BackReferenceReadNode # for $& in a end + - on error: NumberedReferenceReadNode # for $1 in a end + - on error: MissingNode # for in 1..10; end + comment: | + The index expression for `for` loops. + + for i in a end + ^ + - name: collection + type: node + kind: non-void expression + comment: | + The collection to iterate over. + + for i in a end + ^ + - name: statements + type: node? + kind: StatementsNode + comment: | + Represents the body of statements to execute for each iteration of the loop. + + for i in a + foo(i) + ^^^^^^ + end + - name: for_keyword_loc + type: location + comment: | + The location of the `for` keyword. + + for i in a end + ^^^ + - name: in_keyword_loc + type: location + comment: | + The location of the `in` keyword. + + for i in a end + ^^ + - name: do_keyword_loc + type: location? + comment: | + The location of the `do` keyword, if present. + + for i in a do end + ^^ + - name: end_keyword_loc + type: location + comment: | + The location of the `end` keyword. + + for i in a end + ^^^ + 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: | + All other arguments are forwarded as normal, except the original block is replaced with the new block. + comment: | + Represents the use of the `super` keyword without parentheses or arguments, but which might have a block. + + super + ^^^^^ + + super { 123 } + ^^^^^^^^^^^^^ + + If it has any other arguments, it would be a `SuperNode` instead. + - name: GlobalVariableAndWriteNode + fields: + - name: name + type: constant + - name: name_loc + type: location + - name: operator_loc + type: location + - name: value + type: node + kind: non-void expression + 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: binary_operator_loc + type: location + - name: value + type: node + kind: non-void expression + - name: binary_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 + kind: non-void expression + 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 + kind: non-void expression + 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? + kind: + - ConstantPathNode + - ConstantReadNode + comment: | + Represents the optional constant preceding the Hash. + + foo => Bar[a: 1, b: 2] + ^^^ + + foo => Bar::Baz[a: 1, b: 2] + ^^^^^^^^ + - name: elements + type: node[] + kind: AssocNode + comment: | + Represents the explicit named hash keys and values. + + foo => { a: 1, b:, ** } + ^^^^^^^^ + - name: rest + type: node? + kind: + - AssocSplatNode + - NoKeywordsParameterNode + comment: | + Represents the rest of the Hash keys and values. This can be named, unnamed, or explicitly forbidden via `**nil`, this last one results in a `NoKeywordsParameterNode`. + + foo => { a: 1, b:, **c } + ^^^ + + foo => { a: 1, b:, ** } + ^^ + + foo => { a: 1, b:, **nil } + ^^^^^ + - name: opening_loc + type: location? + comment: | + The location of the opening brace. + + foo => { a: 1 } + ^ + + foo => Bar[a: 1] + ^ + - name: closing_loc + type: location? + comment: | + The location of the closing brace. + + foo => { a: 1 } + ^ + + foo => Bar[a: 1] + ^ + comment: | + Represents a hash pattern in pattern matching. + + foo => { a: 1, b: 2 } + ^^^^^^^^^^^^^^ + + foo => { a: 1, b: 2, **c } + ^^^^^^^^^^^^^^^^^^^ + + foo => Bar[a: 1, b: 2] + ^^^^^^^^^^^^^^^ + + foo in { a: 1, b: 2 } + ^^^^^^^^^^^^^^ + - 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 + kind: non-void expression + 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: subsequent + type: node? + kind: + - ElseNode + - IfNode + 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 + kind: + - LocalVariableReadNode + - CallNode + - ConstantReadNode + - LocalVariableTargetNode + 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 + kind: pattern expression + - 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 + flags: CallNodeFlags + fields: + - name: receiver + type: node? + kind: non-void expression + - 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? + kind: BlockArgumentNode # foo[&b] &&= value, only valid on Ruby < 3.4 + - name: operator_loc + type: location + - name: value + type: node + kind: non-void expression + comment: | + Represents the use of the `&&=` operator on a call to the `[]` method. + + foo.bar[baz] &&= value + ^^^^^^^^^^^^^^^^^^^^^^ + - name: IndexOperatorWriteNode + flags: CallNodeFlags + fields: + - name: receiver + type: node? + kind: non-void expression + - 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? + kind: BlockArgumentNode # foo[&b] += value, only valid on Ruby < 3.4 + - name: binary_operator + type: constant + - name: binary_operator_loc + type: location + - name: value + type: node + kind: non-void expression + comment: | + Represents the use of an assignment operator on a call to `[]`. + + foo.bar[baz] += value + ^^^^^^^^^^^^^^^^^^^^^ + - name: IndexOrWriteNode + flags: CallNodeFlags + fields: + - name: receiver + type: node? + kind: non-void expression + - 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? + kind: BlockArgumentNode # foo[&b] ||= value, only valid on Ruby < 3.4 + - name: operator_loc + type: location + - name: value + type: node + kind: non-void expression + comment: | + Represents the use of the `||=` operator on a call to `[]`. + + foo.bar[baz] ||= value + ^^^^^^^^^^^^^^^^^^^^^^ + - name: IndexTargetNode + flags: CallNodeFlags + fields: + - name: receiver + type: node + kind: non-void expression + - name: opening_loc + type: location + - name: arguments + type: node? + kind: ArgumentsNode + - name: closing_loc + type: location + - name: block + type: node? + kind: BlockArgumentNode # foo[&b], = 1, only valid on Ruby < 3.4 + 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 + kind: non-void expression + 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: binary_operator_loc + type: location + - name: value + type: node + kind: non-void expression + - name: binary_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 + kind: non-void expression + 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 + kind: non-void expression + 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 + flags: IntegerBaseFlags + fields: + - name: value + type: integer + comment: The value of the integer literal as a number. + comment: | + Represents an integer number literal. + + 1 + ^ + - name: InterpolatedMatchLastLineNode + flags: RegularExpressionFlags + fields: + - 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 + flags: RegularExpressionFlags + fields: + - 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 + flags: InterpolatedStringNodeFlags + fields: + - name: opening_loc + type: location? + - name: parts + type: node[] + kind: + - StringNode + - EmbeddedStatementsNode + - EmbeddedVariableNode + - InterpolatedStringNode # `"a" "#{b}"` + - on error: XStringNode # `<<`FOO` "bar" + - on error: InterpolatedXStringNode + - on error: SymbolNode + - on error: InterpolatedSymbolNode + - 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: ItLocalVariableReadNode + comment: | + Represents reading from the implicit `it` local variable. + + -> { it } + ^^ + - 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 + flags: KeywordHashNodeFlags + fields: + - name: elements + type: node[] + kind: + - AssocNode + - AssocSplatNode + comment: | + Represents a hash literal without opening and closing braces. + + foo(a: b) + ^^^^ + - name: KeywordRestParameterNode + flags: ParameterFlags + fields: + - 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? + kind: + - BlockParametersNode + - NumberedParametersNode + - ItParametersNode + - name: body + type: node? + kind: + - StatementsNode + - BeginNode + 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 + kind: non-void expression + - 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: binary_operator_loc + type: location + - name: value + type: node + kind: non-void expression + - name: name + type: constant + - name: binary_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 + kind: non-void expression + - 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` + + - 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 + ^^^ ^^^ + + foo => 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 + kind: non-void expression + 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 + flags: RegularExpressionFlags + fields: + - 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 + kind: non-void expression + - name: pattern + type: node + kind: pattern expression + - name: operator_loc + type: location + comment: | + Represents the use of the modifier `in` operator. + + foo in bar + ^^^^^^^^^^ + - name: MatchRequiredNode + fields: + - name: value + type: node + kind: non-void expression + comment: | + Represents the left-hand side of the operator. + + foo => bar + ^^^ + - name: pattern + type: node + kind: pattern expression + comment: | + Represents the right-hand side of the operator. The type of the node depends on the expression. + + Anything that looks like a local variable name (including `_`) will result in a `LocalVariableTargetNode`. + + foo => a # This is equivalent to writing `a = foo` + ^ + + Using an explicit `Array` or combining expressions with `,` will result in a `ArrayPatternNode`. This can be preceded by a constant. + + foo => [a] + ^^^ + + foo => a, b + ^^^^ + + foo => Bar[a, b] + ^^^^^^^^^ + + If the array pattern contains at least two wildcard matches, a `FindPatternNode` is created instead. + + foo => *, 1, *a + ^^^^^ + + Using an explicit `Hash` or a constant with square brackets and hash keys in the square brackets will result in a `HashPatternNode`. + + foo => { a: 1, b: } + + foo => Bar[a: 1, b:] + + foo => Bar[**] + + To use any variable that needs run time evaluation, pinning is required. This results in a `PinnedVariableNode` + + foo => ^a + ^^ + + Similar, any expression can be used with pinning. This results in a `PinnedExpressionNode`. + + foo => ^(a + 1) + + Anything else will result in the regular node for that expression, for example a `ConstantReadNode`. + + foo => CONST + - name: operator_loc + type: location + comment: | + The location of the operator. + + foo => bar + ^^ + 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. + + /(?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 + kind: + - ConstantReadNode + - ConstantPathNode + - on error: MissingNode # module Parent module end + - name: body + type: node? + kind: + - StatementsNode + - BeginNode + - 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 # def m((a,b)); end + - on error: BackReferenceReadNode # a, (b, $&) = z + - on error: NumberedReferenceReadNode # a, (b, $1) = z + comment: | + Represents the targets expressions before a splat node. + + a, (b, c, *) = 1, 2, 3, 4, 5 + ^^^^ + + The splat node can be absent, in that case all target expressions are in the left field. + + a, (b, c) = 1, 2, 3, 4, 5 + ^^^^ + - name: rest + type: node? + kind: + - ImplicitRestNode + - SplatNode + comment: | + Represents a splat node in the target expression. + + a, (b, *c) = 1, 2, 3, 4 + ^^ + + The variable can be empty, this results in a `SplatNode` with a `nil` expression field. + + a, (b, *) = 1, 2, 3, 4 + ^ + + If the `*` is omitted, this field will contain an `ImplicitRestNode` + + a, (b,) = 1, 2, 3, 4 + ^ + - name: rights + type: node[] + kind: + - LocalVariableTargetNode + - InstanceVariableTargetNode + - ClassVariableTargetNode + - GlobalVariableTargetNode + - ConstantTargetNode + - ConstantPathTargetNode + - CallTargetNode + - IndexTargetNode + - MultiTargetNode + - RequiredParameterNode # def m((*,b)); end + - on error: BackReferenceReadNode # a, (*, $&) = z + - on error: NumberedReferenceReadNode # a, (*, $1) = z + comment: | + Represents the targets expressions after a splat node. + + a, (*, b, c) = 1, 2, 3, 4, 5 + ^^^^ + - name: lparen_loc + type: location? + comment: | + The location of the opening parenthesis. + + a, (b, c) = 1, 2, 3 + ^ + - name: rparen_loc + type: location? + comment: | + The location of the closing parenthesis. + + a, (b, c) = 1, 2, 3 + ^ + comment: | + Represents a multi-target expression. + + a, (b, c) = 1, 2, 3 + ^^^^^^ + + This can be a part of `MultiWriteNode` as above, or the target of a `for` loop + + for a, b in [[1, 2], [3, 4]] + ^^^^ + - name: MultiWriteNode + fields: + - name: lefts + type: node[] + kind: + - LocalVariableTargetNode + - InstanceVariableTargetNode + - ClassVariableTargetNode + - GlobalVariableTargetNode + - ConstantTargetNode + - ConstantPathTargetNode + - CallTargetNode + - IndexTargetNode + - MultiTargetNode + - on error: BackReferenceReadNode # $&, = z + - on error: NumberedReferenceReadNode # $1, = z + comment: | + Represents the targets expressions before a splat node. + + a, b, * = 1, 2, 3, 4, 5 + ^^^^ + + The splat node can be absent, in that case all target expressions are in the left field. + + a, b, c = 1, 2, 3, 4, 5 + ^^^^^^^ + - name: rest + type: node? + kind: + - ImplicitRestNode + - SplatNode + comment: | + Represents a splat node in the target expression. + + a, b, *c = 1, 2, 3, 4 + ^^ + + The variable can be empty, this results in a `SplatNode` with a `nil` expression field. + + a, b, * = 1, 2, 3, 4 + ^ + + If the `*` is omitted, this field will contain an `ImplicitRestNode` + + a, b, = 1, 2, 3, 4 + ^ + - name: rights + type: node[] + kind: + - LocalVariableTargetNode + - InstanceVariableTargetNode + - ClassVariableTargetNode + - GlobalVariableTargetNode + - ConstantTargetNode + - ConstantPathTargetNode + - CallTargetNode + - IndexTargetNode + - MultiTargetNode + - on error: BackReferenceReadNode # *, $& = z + - on error: NumberedReferenceReadNode # *, $1 = z + comment: | + Represents the targets expressions after a splat node. + + a, *, b, c = 1, 2, 3, 4, 5 + ^^^^ + - name: lparen_loc + type: location? + comment: | + The location of the opening parenthesis. + + (a, b, c) = 1, 2, 3 + ^ + - name: rparen_loc + type: location? + comment: | + The location of the closing parenthesis. + + (a, b, c) = 1, 2, 3 + ^ + - name: operator_loc + type: location + comment: | + The location of the operator. + + a, b, c = 1, 2, 3 + ^ + - name: value + type: node + kind: non-void expression + comment: | + The value to write to the targets. It can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + + a, b, c = 1, 2, 3 + ^^^^^^^ + 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 + flags: ParameterFlags + fields: + - name: name + type: constant + - name: name_loc + type: location + - name: value + type: node + kind: non-void expression + comment: | + Represents an optional keyword parameter to a method, block, or lambda definition. + + def a(b: 1) + ^^^^ + end + - name: OptionalParameterNode + flags: ParameterFlags + fields: + - name: name + type: constant + - name: name_loc + type: location + - name: operator_loc + type: location + - name: value + type: node + kind: non-void expression + comment: | + Represents an optional parameter to a method, block, or lambda definition. + + def a(b = 1) + ^^^^^ + end + - name: OrNode + fields: + - name: left + type: node + kind: non-void expression + 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 + kind: Node + comment: | + Represents the right side of the 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: + - on error: KeywordRestParameterNode + - on error: NoKeywordsParameterNode + # On parsing error of `f(..., ...)`, the first forwarding parameter is moved here: + - on error: ForwardingParameterNode + - 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 + flags: ParenthesesNodeFlags + fields: + - name: body + type: node? + kind: non-void expression # Usually a StatementsNode but not always e.g. `1 in (..10)` + - 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 + kind: non-void expression + comment: | + The expression used in the pinned expression + + foo in ^(bar) + ^^^ + - name: operator_loc + type: location + comment: | + The location of the `^` operator + + foo in ^(bar) + ^ + - name: lparen_loc + type: location + comment: | + The location of the opening parenthesis. + + foo in ^(bar) + ^ + - name: rparen_loc + type: location + comment: | + The location of the closing parenthesis. + + foo in ^(bar) + ^ + 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 + kind: + - LocalVariableReadNode + - InstanceVariableReadNode + - ClassVariableReadNode + - GlobalVariableReadNode # foo in ^$a + - BackReferenceReadNode # foo in ^$& + - NumberedReferenceReadNode # foo in ^$1 + - ItLocalVariableReadNode # proc { 1 in ^it } + - on error: MissingNode # foo in ^Bar + comment: | + The variable used in the pinned expression + + foo in ^bar + ^^^ + - name: operator_loc + type: location + comment: | + The location of the `^` operator + + foo in ^bar + ^ + 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 + flags: RangeFlags + fields: + - name: left + type: node? + kind: non-void expression + 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? + kind: non-void expression + 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 + flags: IntegerBaseFlags + fields: + - name: numerator + type: integer + comment: | + The numerator of the rational number. + + 1.5r # numerator 3 + - name: denominator + type: integer + comment: | + The denominator of the rational number. + + 1.5r # denominator 2 + comment: | + Represents a rational number literal. + + 1.0r + ^^^^ + - name: RedoNode + comment: | + Represents the use of the `redo` keyword. + + redo + ^^^^ + - name: RegularExpressionNode + flags: RegularExpressionFlags + fields: + - 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 + flags: ParameterFlags + fields: + - 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 + flags: ParameterFlags + fields: + - 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 + kind: Node + - name: keyword_loc + type: location + - name: rescue_expression + type: node + kind: 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[] + kind: non-void expression + - name: operator_loc + type: location? + - name: reference + type: node? + kind: + - LocalVariableTargetNode + - InstanceVariableTargetNode + - ClassVariableTargetNode + - GlobalVariableTargetNode + - ConstantTargetNode + - ConstantPathTargetNode + - CallTargetNode + - IndexTargetNode + - on error: BackReferenceReadNode # => begin; rescue => $&; end + - on error: NumberedReferenceReadNode # => begin; rescue => $1; end + - on error: MissingNode # begin; rescue =>; end + - name: then_keyword_loc + type: location? + - name: statements + type: node? + kind: StatementsNode + - name: subsequent + 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 `reference` field. + - name: RestParameterNode + flags: ParameterFlags + fields: + - 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: 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 + flags: ShareableConstantNodeFlags + fields: + - 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 + kind: non-void expression + - name: body + type: node? + kind: + - StatementsNode + - BeginNode + - 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 + flags: StringFlags + fields: + - 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? + kind: non-void expression + comment: | + Represents the use of the splat operator. + + [*a] + ^^ + - name: StatementsNode + fields: + - name: body + type: node[] + kind: Node + comment: | + Represents a set of statements contained within some scope. + + foo; bar; baz + ^^^^^^^^^^^^^ + - name: StringNode + flags: StringFlags + fields: + - 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 + comment: "Can be only `nil` when there are empty parentheses, like `super()`." + - name: rparen_loc + type: location? + - name: block + type: node? + kind: + - BlockNode + - BlockArgumentNode + comment: | + Represents the use of the `super` keyword with parentheses or arguments. + + super() + ^^^^^^^ + + super foo, bar + ^^^^^^^^^^^^^^ + + If no arguments are provided (except for a block), it would be a `ForwardingSuperNode` instead. + - name: SymbolNode + flags: SymbolFlags + fields: + - 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 + kind: non-void expression + 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: else_clause + 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 + flags: LoopFlags + fields: + - name: keyword_loc + type: location + - name: do_keyword_loc + type: location? + - name: closing_loc + type: location? + - name: predicate + type: node + kind: non-void expression + - 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[] + kind: non-void expression + - 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 + flags: LoopFlags + fields: + - name: keyword_loc + type: location + - name: do_keyword_loc + type: location? + - name: closing_loc + type: location? + - name: predicate + type: node + kind: non-void expression + - 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 + flags: EncodingFlags + fields: + - 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/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/build_system.md b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/build_system.md new file mode 100644 index 0000000..3595c69 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/build_system.md @@ -0,0 +1,119 @@ +# Build System + +There are many ways to build prism, which means the build system is a bit more complicated than usual. + +## Requirements + +* It must work to build prism for all 6 uses-cases below. +* It must be possible to build prism without needing ruby/rake/etc. + Because once prism is the single parser in TruffleRuby, JRuby or CRuby there won't be another Ruby parser around to parse such Ruby code. + Most/every Ruby implementations want to avoid depending on another Ruby during the build process as that is very brittle. +* It is desirable to compile prism with the same or very similar compiler flags for all use-cases (e.g. optimization level, warning flags, etc). + Otherwise, there is the risk prism does not work correctly with those different compiler flags. + +The main solution for the second point seems a Makefile, otherwise many of the usages would have to duplicate the logic to build prism. + +## General Design + +1. Templates are generated by `templates/template.rb` +2. The `Makefile` compiles both `libprism.a` and `libprism.{so,dylib,dll}` from the `src/**/*.c` and `include/**/*.h` files +3. The `Rakefile` `:compile` task ensures the above prerequisites are done, then calls `make`, + and uses `Rake::ExtensionTask` to compile the C extension (using its `extconf.rb`) + +This way there is minimal duplication, and each layer builds on the previous one and has its own responsibilities. + +The static library exports no symbols, to avoid any conflict. +The shared library exports some symbols, and this is fine since there should only be one libprism shared library +loaded per process (i.e., at most one version of the prism *gem* loaded in a process, only the gem uses the shared library). + +## The various ways to build prism + +### Building from ruby/prism repository with `bundle exec rake` + +`rake` calls `make` and then uses `Rake::ExtensionTask` to compile the C extension (see above). + +### Building the prism gem by `gem install/bundle install` + +The gem contains the pre-generated templates. + +When installing the gem on CRuby, `extconf.rb` is used and that compiles the C extension with mkmf, including both the extension files and the sources of prism itself. + +When installing the gem on JRuby and TruffleRuby, no C extension is built, so instead the `extconf.rb` runs `make build/libprism.{so,dylib,dll}`. +There is Ruby code using FFI which uses `libprism.{so,dylib,dll}` to implement the same methods as the C extension, but using serialization instead of many native calls/accesses (JRuby does not support C extensions, serialization is faster on TruffleRuby than the C extension). + +### Building the prism gem from git, e.g. `gem "prism", github: "ruby/prism"` + +The same as above, except the `extconf.rb` additionally runs first: +* `templates/template.rb` to generate the templates + +Because of course those files are not part of the git repository. + +### Building prism as part of CRuby + +[This script](https://github.com/ruby/ruby/blob/5124f9ac7513eb590c37717337c430cb93caa151/tool/sync_default_gems.rb#L399-L422) imports prism sources in CRuby. + +The script generates the templates when importing. + +prism's `Makefile` is not used at all in CRuby. Instead, CRuby's `Makefile` is used. + +### Building prism as part of TruffleRuby + +[This script](https://github.com/truffleruby/truffleruby/blob/master/tool/import-prism.sh) imports prism sources in TruffleRuby. +The script generates the templates when importing. + +Then when `mx build` builds TruffleRuby and the `prism` mx project inside, it runs `make`. + +Then the `prism bindings` mx project is built, which contains the [bindings](https://github.com/truffleruby/truffleruby/blob/vm-24.1.1/src/main/c/yarp_bindings/src/yarp_bindings.c) +and links to `libprism.a` (to avoid exporting symbols, so no conflict when installing the prism gem). + +### Building prism as part of JRuby + +TODO, similar to TruffleRuby. + +### Building prism for embedded system + +For instance, you can build a static library `libprism.a` targeting the Arm Cortex-M0+ embedded system by the commands below: + +* `templates/template.rb` +* `CFLAGS="-mcpu=cortex-m0plus" make static CC=arm-none-eabi-gcc` + +The build process internally looks up `_POSIX_MAPPED_FILES` and `_WIN32` macros to determine whether the functions of the memory map are available on the target platform. + +### Building prism with custom memory allocator + +If you need to use memory allocation functions implemented outside of the standard library, follow these steps: + +* Add `-D PRISM_XALLOCATOR` to the build options +* Additionally, include `-I [path/to/custom_allocator]` where your `prism_xallocator.h` is located +* Link the implementation of `prism_xallocator.c` that contains functions declared in `prism_xallocator.h` + +For further clarity, refer to `include/prism/defines.h`. + +### Building prism from source as a C library + +All of the source files match `src/**/*.c` and all of the headers match `include/**/*.h`. + +If you want to build prism as a shared library and link against it, you should compile with: + +* `-fPIC -shared` - Compile as a shared library +* `-DPRISM_EXPORT_SYMBOLS` - Export the symbols (by default nothing is exported) + +#### Flags + +`make` respects the `MAKEFLAGS` environment variable. As such, to speed up the build you can run: + +``` +MAKEFLAGS="-j10" bundle exec rake compile +``` + +## Build options + +* `PRISM_BUILD_DEBUG` - Will cause all file reading to copy into its own allocation to allow easier tracking of reading off the end of the buffer. By default this is off. +* `PRISM_BUILD_MINIMAL` - Define all of the `PRISM_EXCLUDE_*` flags at once. +* `PRISM_ENCODING_EXCLUDE_FULL` - Will cause the library to exclude the full encoding API, and only include the minimal number of encodings to support parsing Ruby code without encoding comments. By default this is off. +* `PRISM_EXPORT_SYMBOLS` - Will cause the shared library to export symbols. By default this is off. +* `PRISM_EXCLUDE_JSON` - Will cause the library to exclude the JSON API. By default this is off. +* `PRISM_EXCLUDE_PACK` - Will cause the library to exclude the pack API. By default this is off. +* `PRISM_EXCLUDE_PRETTYPRINT` - Will cause the library to exclude the prettyprint API. By default this is off. +* `PRISM_EXCLUDE_SERIALIZATION` - Will cause the library to exclude the serialization API. By default this is off. +* `PRISM_XALLOCATOR` - Will cause the library to use the custom memory allocator. By default this is off. diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/configuration.md b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/configuration.md new file mode 100644 index 0000000..2e3ff02 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/configuration.md @@ -0,0 +1,68 @@ +# Configuration + +A lot of code in prism's repository is templated from a single configuration file, [config.yml](../config.yml). This file is used to generate the following files: + +* `ext/prism/api_node.c` - for defining how to build Ruby objects for the nodes out of C structs +* `include/prism/ast.h` - for defining the C structs that represent the nodes +* `include/prism/diagnostic.h` - for defining the diagnostics +* `javascript/src/deserialize.js` - for defining how to deserialize the nodes in JavaScript +* `javascript/src/nodes.js` - for defining the nodes in JavaScript +* `java/org/prism/AbstractNodeVisitor.java` - for defining the visitor interface for the nodes in Java +* `java/org/prism/Loader.java` - for defining how to deserialize the nodes in Java +* `java/org/prism/Nodes.java` - for defining the nodes in Java +* `lib/prism/compiler.rb` - for defining the compiler for the nodes in Ruby +* `lib/prism/dispatcher.rb` - for defining the dispatch visitors for the nodes in Ruby +* `lib/prism/dot_visitor.rb` - for defining the dot visitor for the nodes in Ruby +* `lib/prism/dsl.rb` - for defining the DSL for the nodes in Ruby +* `lib/prism/inspect_visitor.rb` - for defining the `#inspect` methods on nodes in Ruby +* `lib/prism/mutation_compiler.rb` - for defining the mutation compiler for the nodes in Ruby +* `lib/prism/node.rb` - for defining the nodes in Ruby +* `lib/prism/reflection.rb` - for defining the reflection API in Ruby +* `lib/prism/serialize.rb` - for defining how to deserialize the nodes in Ruby +* `lib/prism/visitor.rb` - for defining the visitor interface for the nodes in Ruby +* `src/diagnostic.c` - for defining how to build diagnostics +* `src/node.c` - for defining how to free the nodes in C and calculate the size in memory in C +* `src/prettyprint.c` - for defining how to prettyprint the nodes in C +* `src/serialize.c` - for defining how to serialize the nodes in C +* `src/token_type.c` - for defining the names of the token types + +Whenever the structure of the nodes changes, you can run `rake templates` to regenerate these files. Alternatively tasks like `rake test` should pick up on these changes automatically. Every file that is templated will include a comment at the top indicating that it was generated and that changes should be made to the template and not the generated file. + +`config.yml` has a couple of top level fields, which we'll describe below. + +## `tokens` + +This is a list of tokens to be used by the lexer. It is shared here so that it can be templated out into both an enum and a function that is used for debugging that returns the name of the token. + +Each token is expected to have a `name` key and a `comment` key (both as strings). Optionally they can have a `value` key (an integer) which is used to represent the value in the enum. + +In C these tokens will be templated out with the prefix `PM_TOKEN_`. For example, if you have a `name` key with the value `PERCENT`, you can access this in C through `PM_TOKEN_PERCENT`. + +## `flags` + +Sometimes we need to communicate more information in the tree than can be represented by the types of the nodes themselves. For example, we need to represent the flags passed to a regular expression or the type of call that a call node is performing. In these circumstances, it's helpful to reference a bitset of flags. This field is a list of flags that can be used in the nodes. + +Each flag is expected to have a `name` key (a string) and a `values` key (an array). Each value in the `values` key should be an object that contains both a `name` key (a string) that represents the name of the flag and a `comment` key (a string) that represents the comment for the flag. + +In C these flags will get templated out with a `PM_` prefix, then a snake-case version of the flag name, then the flag itself. For example, if you have a flag with the name `RegularExpressionFlags` and a value with the name `IGNORE_CASE`, you can access this in C through `PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE`. + +## `nodes` + +Every node in the tree is defined in `config.yml`. Each node is expected to have a `name` key (a string) and a `comment` key (a string). By convention, the `comment` key uses the multi-line syntax of `: |` because the newlines will get templated into the comments of various files. + +Optionally, every node can define a `child_nodes` key that is an array. This array represents each part of the node that isn't communicated through the type and location of the node itself. Within the `child_nodes` key, each entry should be an object with a `name` key (a string) and a `type` key (a string). The `name` key represents the name of the child node and the `type` is used to determine how it should be represented in each language. + +The available values for `type` are: + +* `node` - A field that is a node. This is a `pm_node_t *` in C. +* `node?` - A field that is a node that is optionally present. This is also a `pm_node_t *` in C, but can be `NULL`. +* `node[]` - A field that is an array of nodes. This is a `pm_node_list_t` in C. +* `string` - A field that is a string. For example, this is used as the name of the method in a call node, since it cannot directly reference the source string (as in `@-` or `foo=`). This is a `pm_string_t` in C. +* `constant` - A field that is an integer that represents an index in the constant pool. This is a `pm_constant_id_t` in C. +* `constant[]` - A field that is an array of constants. This is a `pm_constant_id_list_t` in C. +* `location` - A field that is a location. This is a `pm_location_t` in C. +* `location?` - A field that is a location that is optionally present. This is a `pm_location_t` in C, but if the value is not present then the `start` and `end` fields will be `NULL`. +* `uint8` - A field that is an 8-bit unsigned integer. This is a `uint8_t` in C. +* `uint32` - A field that is a 32-bit unsigned integer. This is a `uint32_t` in C. + +If the type is `node` or `node?` then the value also accepts an optional `kind` key (a string). This key is expected to match to the name of another node type within `config.yml`. This changes a couple of places where code is templated out to use the more specific struct name instead of the generic `pm_node_t`. For example, with `kind: StatementsNode` the `pm_node_t *` in C becomes a `pm_statements_node_t *`. diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/cruby_compilation.md b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/cruby_compilation.md new file mode 100644 index 0000000..8d98ef9 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/cruby_compilation.md @@ -0,0 +1,27 @@ +# Compiling Prism's AST + +One important class of consumers of Prism's AST is compilers. Currently [CRuby](https://github.com/ruby/ruby), [JRuby](https://github.com/jruby/jruby), [TruffleRuby](https://github.com/truffleruby/truffleruby), and [Natalie](https://github.com/natalie-lang/natalie) have all built compilation code on top of Prism's AST. + +This document will describe, at a high level, how CRuby's compilation of Prism's AST works. + +As described in the [build system documentation](build_system.md), there is a "push" Webhook set up within the Prism repo triggered on each new commit to send information about the commit to [git.ruby-lang.org](https://github.com/ruby/git.ruby-lang.org). This in turn runs [a script](https://github.com/ruby/ruby/blob/master/tool/sync_default_gems.rb) to sync over new changes in Prism to their corresponding files in Ruby. Any failures in this sync script will show alerts in the #alerts-sync channel in the RubyLang Slack. The result of this step is that files are synced from Prism into ruby/ruby for its use. It is also worth noting that [`common.mk`](https://github.com/ruby/ruby/blob/master/common.mk) contains a list of Prism files which it needs to correctly compile. If there are new Prism files added, this file should also be updated. + +ruby/ruby uses the Prism code to generate an AST from which it can generate instruction sequences. Compilation in ruby/ruby has three main steps: + +1. Compute an AST + +Syncing over the Prism code allows ruby/ruby to compute the AST using Prism. It currently does this within [`iseq.c`](https://github.com/ruby/ruby/blob/master/iseq.c) using the `pm_parser_init` function. + +2. Run a first pass of compilation + +Once the AST has been created, it is recursively descended in order to compute the appropriate instruction sequences. This is the crux of compilation, and we go into more detail about nuances in the following paragraphs. + +The code for this step is almost exclusively in [`prism_compile.c`](https://github.com/ruby/ruby/blob/master/prism_compile.c). The main function used for compilation is `pm_compile_node` which is essentially a huge switch statement over practically every node type which computes the appropriate instruction sequences for that node type. There are several convenience helpers, such as `PM_COMPILE`, `PM_COMPILE_POPPED`, `PM_COMPILE_NOT_POPPED` which all call into the `pm_compile_node` function. + +There are also several functions, like `parse_string`, `parse_integer` which consume Prism nodes and return CRuby values. These are all called for their relevant types within the big switch statement. + +The Prism compiler also uses a concept of "scope nodes" which are not standard Prism nodes in the AST, but instead nodes constructed within the compiler for the sole purpose of making compilation easier. Scope nodes are defined in [`prism_compile.h`](https://github.com/ruby/ruby/blob/master/prism_compile.h) and store information such as locals, local table size, local depth offset and the index lookup tables. Scope nodes can be generated for node types which have their own "scope". + +3. Run an optimization pass of compilation + +After the instruction sequences are initially computed, there is an existing (non-Prism based) optimization pass of the instruction sequences. There are several optimizations currently inlined into step 2, however, most of them happen in this step. Specifically, any peephole optimizations happen in this step. By the end of step 2, however, the instruction sequences take the same form regardless of if the initial AST was generated by Prism or not. Therefore, step 3 is agnostic to the parser, and should not require any Prism specific code. diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/design.md b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/design.md new file mode 100644 index 0000000..e1bbe78 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/design.md @@ -0,0 +1,53 @@ +# Design + +There are three overall goals for this project: + +* to provide a documented and maintainable parser +* to provide an error-tolerant parser suitable for use in an IDE +* to provide a portable parser that can be used in projects that don't link against CRuby + +The design of the parser is based around these main goals. + +## Structure + +The first piece to understand about the parser is the design of its syntax tree. This is documented in `config.yml`. Every token and node is defined in that file, along with comments about where they are found in what kinds of syntax. This file is used to template out a lot of different files, all found in the `templates` directory. The `templates/template.rb` script performs the templating and outputs all files matching the directory structure found in the templates directory. + +The templated files contain all of the code required to allocate and initialize nodes, pretty print nodes, and serialize nodes. This means for the most part, you will only need to then hook up the parser to call the templated functions to create the nodes in the correct position. That means editing the parser itself, which is housed in `prism.c`. + +## Pratt parsing + +In order to provide the best possible error tolerance, the parser is hand-written. It is structured using Pratt parsing, a technique developed by Vaughan Pratt back in the 1970s. Below are a bunch of links to articles and papers that explain Pratt parsing in more detail. + +* https://github.com/tdop/tdop.github.io/raw/master/original.pdf +* https://tdop.github.io/ +* https://journal.stuffwithstuff.com/2011/03/19/pratt-parsers-expression-parsing-made-easy/ +* https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html +* https://chidiwilliams.com/posts/on-recursive-descent-and-pratt-parsing + +You can find most of the functions that correspond to constructs in the Pratt parsing algorithm in `prism.c`. As a couple of examples: + +* `parse` corresponds to the `parse_expression` function +* `nud` (null denotation) corresponds to the `parse_expression_prefix` function +* `led` (left denotation) corresponds to the `parse_expression_infix` function +* `lbp` (left binding power) corresponds to accessing the `left` field of an element in the `binding_powers` array +* `rbp` (right binding power) corresponds to accessing the `right` field of an element in the `binding_powers` array + +## Portability + +In order to enable using this parser in other projects, the parser is written in C99, and uses only the standard library. This means it can be embedded in most any other project without having to link against CRuby. It can be used directly through its C API to access individual fields, or it can used to parse a syntax tree and then serialize it to a single blob. For more information on serialization, see the [docs/serialization.md](serialization.md) file. + +## Error tolerance + +The design of the error tolerance of this parser is still very much in flux. We are experimenting with various approaches as the parser is being developed to try to determine the best approach. Below are a bunch of links to articles and papers that explain error tolerance in more detail, as well as document some of the approaches that we're evaluating. + +* https://tratt.net/laurie/blog/2020/automatic_syntax_error_recovery.html +* https://diekmann.uk/diekmann_phd.pdf +* https://eelcovisser.org/publications/2012/JongeKVS12.pdf +* https://www.antlr.org/papers/allstar-techreport.pdf +* https://github.com/microsoft/tolerant-php-parser/blob/main/docs/HowItWorks.md + +Currently, there are a couple of mechanisms for error tolerance that are in place: + +* If the parser expects a token in a particular position (for example the `in` keyword in a for loop or the `{` after `BEGIN` or `END`) then it will insert a missing token if one can't be found and continue parsing. +* If the parser expects an expression in a particular position but encounters a token that can't be used as that expression, it checks up the stack to see if that token would close out a parent node. If so, it will close out all of its parent nodes using missing nodes wherever necessary and continue parsing. +* If the parser cannot understand a token in any capacity, it will skip past the token. diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/encoding.md b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/encoding.md new file mode 100644 index 0000000..a9090a9 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/encoding.md @@ -0,0 +1,121 @@ +# Encoding + +When parsing a Ruby file, there are times when the parser must parse identifiers. Identifiers are names of variables, methods, classes, etc. To determine the start of an identifier, the parser must be able to tell if the subsequent bytes form an alphabetic character. To determine the rest of the identifier, the parser must look forward through all alphanumeric characters. + +Determining if a set of bytes comprise an alphabetic or alphanumeric character is encoding-dependent. By default, the parser assumes that all source files are encoded UTF-8. If the file is not encoded in UTF-8, it must be encoded using an encoding that is "ASCII compatible" (i.e., all of the codepoints below 128 match the corresponding codepoints in ASCII and the minimum number of bytes required to represent a codepoint is 1 byte). + +If the file is not encoded in UTF-8, the user must specify the encoding in a "magic" comment at the top of the file. The comment looks like: + +```ruby +# encoding: iso-8859-9 +``` + +The key of the comment can be either "encoding" or "coding". The value of the comment must be a string that is a valid encoding name. The encodings that prism supports by default are: + +* `ASCII-8BIT` +* `Big5` +* `Big5-HKSCS` +* `Big5-UAO` +* `CESU-8` +* `CP51932` +* `CP850` +* `CP852` +* `CP855` +* `CP949` +* `CP950` +* `CP951` +* `Emacs-Mule` +* `EUC-JP` +* `eucJP-ms` +* `EUC-JIS-2004` +* `EUC-KR` +* `EUC-TW` +* `GB12345` +* `GB18030` +* `GB1988` +* `GB2312` +* `GBK` +* `IBM437` +* `IBM720` +* `IBM737` +* `IBM775` +* `IBM852` +* `IBM855` +* `IBM857` +* `IBM860` +* `IBM861` +* `IBM862` +* `IBM863` +* `IBM864` +* `IBM865` +* `IBM866` +* `IBM869` +* `ISO-8859-1` +* `ISO-8859-2` +* `ISO-8859-3` +* `ISO-8859-4` +* `ISO-8859-5` +* `ISO-8859-6` +* `ISO-8859-7` +* `ISO-8859-8` +* `ISO-8859-9` +* `ISO-8859-10` +* `ISO-8859-11` +* `ISO-8859-13` +* `ISO-8859-14` +* `ISO-8859-15` +* `ISO-8859-16` +* `KOI8-R` +* `KOI8-U` +* `macCentEuro` +* `macCroatian` +* `macCyrillic` +* `macGreek` +* `macIceland` +* `MacJapanese` +* `macRoman` +* `macRomania` +* `macThai` +* `macTurkish` +* `macUkraine` +* `Shift_JIS` +* `SJIS-DoCoMo` +* `SJIS-KDDI` +* `SJIS-SoftBank` +* `stateless-ISO-2022-JP` +* `stateless-ISO-2022-JP-KDDI` +* `TIS-620` +* `US-ASCII` +* `UTF-8` +* `UTF8-MAC` +* `UTF8-DoCoMo` +* `UTF8-KDDI` +* `UTF8-SoftBank` +* `Windows-1250` +* `Windows-1251` +* `Windows-1252` +* `Windows-1253` +* `Windows-1254` +* `Windows-1255` +* `Windows-1256` +* `Windows-1257` +* `Windows-1258` +* `Windows-31J` +* `Windows-874` + +For each of these encodings, prism provides functions for checking if the subsequent bytes can be interpreted as a character, and then if that character is alphabetic, alphanumeric, or uppercase. + +## Getting notified when the encoding changes + +You may want to get notified when the encoding changes based on the result of parsing an encoding comment. We use this internally for our `lex` function in order to provide the correct encodings for the tokens that are returned. For that you can register a callback with `pm_parser_register_encoding_changed_callback`. The callback will be called with a pointer to the parser. The encoding can be accessed through `parser->encoding`. + +```c +// 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); + +// 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); +``` diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/fuzzing.md b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/fuzzing.md new file mode 100644 index 0000000..b6ec611 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/fuzzing.md @@ -0,0 +1,88 @@ +# Fuzzing + +We use fuzzing to test the various entrypoints to the library. The fuzzer we use is [AFL++](https://aflplus.plus). All files related to fuzzing live within the `fuzz` directory, which has the following structure: + +``` +fuzz +├── corpus +│   ├── parse fuzzing corpus for parsing (a symlink to our fixtures) +│   └── regexp fuzzing corpus for regexp +├── dict a AFL++ dictionary containing various tokens +├── docker +│   └── Dockerfile for building a container with the fuzzer toolchain +├── fuzz.c generic entrypoint for fuzzing +├── heisenbug.c entrypoint for reproducing a crash or hang +├── parse.c fuzz handler for parsing +├── parse.sh script to run parsing fuzzer +├── regexp.c fuzz handler for regular expression parsing +├── regexp.sh script to run regexp fuzzer +└── tools +    ├── backtrace.sh generates backtrace files for a crash directory +    └── minimize.sh generates minimized crash or hang files +``` + +## Usage + +There are currently three fuzzing targets + +- `pm_serialize_parse` (parse) +- `pm_regexp_parse` (regexp) + +Respectively, fuzzing can be performed with + +``` +make fuzz-run-parse +make fuzz-run-regexp +``` + +To end a fuzzing job, interrupt with CTRL+C. To enter a container with the fuzzing toolchain and debug utilities, run + +``` +make fuzz-debug +``` + +# Out-of-bounds reads + +Currently, encoding functionality implementing the `pm_encoding_t` interface can read outside of inputs. For the time being, ASAN instrumentation is disabled for functions from src/enc. See `fuzz/asan.ignore`. + +To disable ASAN read instrumentation globally, use the `FUZZ_FLAGS` environment variable e.g. + +``` +FUZZ_FLAGS="-mllvm -asan-instrument-reads=false" make fuzz-run-parse +``` + +Note, that this may make reproducing bugs difficult as they may depend on memory outside of the input buffer. In that case, try + +``` +make fuzz-debug # enter the docker container with build tools +make build/fuzz.heisenbug.parse # or .regexp +./build/fuzz.heisenbug.parse path-to-problem-input +``` + +# Triaging Crashes and Hangs + +Triaging crashes and hangs is easier when the inputs are as short as possible. In the fuzz container, an entire crash or hang directory can be minimized using + +``` +./fuzz/tools/minimize.sh +``` + +e.g. +``` +./fuzz/tools/minimize.sh fuzz/output/parse/default/crashes +``` + +This may take a long time. In the crash/hang directory, for each input file there will appear a minimized version with the extension `.min` appended. + +Backtraces for crashes (not hangs) can be generated en masse with + +``` +./fuzz/tools/backtrace.sh +``` + +Files with basename equal to the input file name with extension `.bt` will be created e.g. + +``` +id:000000,sig:06,src:000006+000190,time:8480,execs:18929,op:splice,rep:4 +id:000000,sig:06,src:000006+000190,time:8480,execs:18929,op:splice,rep:4.bt +``` diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/heredocs.md b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/heredocs.md new file mode 100644 index 0000000..ea7e1fc --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/heredocs.md @@ -0,0 +1,36 @@ +# Heredocs + +Heredocs are one of the most complicated pieces of this parser. There are many different forms, there can be multiple open at the same time, and they can be nested. In order to support parsing them, we keep track of a lot of metadata. Below is a basic overview of how it works. + +## 1. Lexing the identifier + +When a heredoc identifier is encountered in the regular process of lexing, we push the `PM_LEX_HEREDOC` mode onto the stack with the following metadata: + +* `ident_start`: A pointer to the start of the identifier for the heredoc. We need this to match against the end of the heredoc. +* `ident_length`: The length of the identifier for the heredoc. We also need this to match. +* `next_start`: A pointer to the place in source that the parser should resume lexing once it has completed this heredoc. + +We also set the special `parser.next_start` field which is a pointer to the place in the source where we should start lexing the next token. This is set to the pointer of the character immediately following the next newline. + +Note that if the `parser.heredoc_end` field is already set, then it means we have already encountered a heredoc on this line. In that case the `parser.next_start` field will be set to the `parser.heredoc_end` field. This is because we want to skip past the previous heredocs on this line and instead lex the body of this heredoc. + +## 2. Lexing the body + +The next time the lexer is asked for a token, it will be in the `PM_LEX_HEREDOC` mode. In this mode we are lexing the body of the heredoc. It will start by checking if the `next_start` field is set. If it is, then this is the first token within the body of the heredoc so we'll start lexing from there. Otherwise we'll start lexing from the end of the previous token. + +Lexing these fields is extremely similar to lexing an interpolated string. The only difference is that we also do an additional check at the beginning of each line to check if we have hit the terminator. + +## 3. Lexing the terminator + +On every newline within the body of a heredoc, we check to see if it matches the terminator followed by a newline or a carriage return and a newline. If it does, then we pop the lex mode off the stack and set a couple of fields on the parser: + +* `next_start`: This is set to the value that we previously stored on the heredoc to indicate where the lexer should resume lexing when it is done with this heredoc. +* `heredoc_end`: This is set to the end of the heredoc. When a newline character is found, this indicates that the lexer should skip past to this next point. + +## 4. Lexing the rest of the line + +Once the heredoc has been lexed, the lexer will resume lexing from the `next_start` field. Lexing will continue until the next newline character. When the next newline character is found, it will check to see if the `heredoc_end` field is set. If it is it will skip to that point, unset the field, and continue lexing. + +## Compatibility with Ripper + +The order in which tokens are emitted is different from that of Ripper. Ripper emits each token in the file in the order in which it appears. prism instead will emit the tokens that makes the most sense for the lexer, using the process described above. Therefore to line things up, `Prism.lex_compat` will shuffle the tokens around to match Ripper's output. diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/javascript.md b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/javascript.md new file mode 100644 index 0000000..2ccd5f8 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/javascript.md @@ -0,0 +1,118 @@ +# JavaScript + +Prism provides bindings to JavaScript out of the box. + +## Node + +To use the package from node, install the `@ruby/prism` dependency: + +```sh +npm install @ruby/prism +``` + +Then import the package: + +```js +import { loadPrism } from "@ruby/prism"; +``` + +Then call the load function to get a parse function: + +```js +const parse = await loadPrism(); +``` + +## Browser + +To use the package from the browser, you will need to do some additional work. The [javascript/example.html](../javascript/example.html) file shows an example of running Prism in the browser. You will need to instantiate the WebAssembly module yourself and then pass it to the `parsePrism` function. + +First, get a shim for WASI since not all browsers support it yet. + +```js +import { WASI } from "https://unpkg.com/@bjorn3/browser_wasi_shim@latest/dist/index.js"; +``` + +Next, import the `parsePrism` function from `@ruby/prism`, either through a CDN or by bundling it with your application. + +```js +import { parsePrism } from "https://unpkg.com/@ruby/prism@latest/src/parsePrism.js"; +``` + +Next, fetch and instantiate the WebAssembly module. You can access it through a CDN or by bundling it with your application. + +```js +const wasm = await WebAssembly.compileStreaming(fetch("https://unpkg.com/@ruby/prism@latest/src/prism.wasm")); +``` + +Next, instantiate the module and initialize WASI. + +```js +const wasi = new WASI([], [], []); +const instance = await WebAssembly.instantiate(wasm, { wasi_snapshot_preview1: wasi.wasiImport }); +wasi.initialize(instance); +``` + +Finally, you can create a function that will parse a string of Ruby code. + +```js +function parse(source) { + return parsePrism(instance.exports, source); +} +``` + +## API + +Now that we have access to a `parse` function, we can use it to parse Ruby code: + +```js +const parseResult = parse("1 + 2"); +``` + +A ParseResult object is very similar to the Prism::ParseResult object from Ruby. It has the same properties: `value`, `comments`, `magicComments`, `errors`, and `warnings`. Here we can serialize the AST to JSON. + +```js +console.log(JSON.stringify(parseResult.value, null, 2)); +``` + +## Visitors + +Prism allows you to traverse the AST of parsed Ruby code using visitors. + +Here's an example of a custom `FooCalls` visitor: + +```js +import { loadPrism, Visitor } from "@ruby/prism" + +const parse = await loadPrism(); +const parseResult = parse("foo()"); + +class FooCalls extends Visitor { + visitCallNode(node) { + if (node.name === "foo") { + // Do something with the node + } + + // Call super so that the visitor continues walking the tree + super.visitCallNode(node); + } +} + +const fooVisitor = new FooCalls(); + +parseResult.value.accept(fooVisitor); +``` + +## Building + +To build the WASM package yourself, first obtain a copy of `wasi-sdk`. You can retrieve this here: . Next, run: + +```sh +make wasm WASI_SDK_PATH=path/to/wasi-sdk +``` + +This will generate `javascript/src/prism.wasm`. From there, you can run the tests to verify everything was generated correctly. + +```sh +cd javascript +node test +``` diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/local_variable_depth.md b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/local_variable_depth.md new file mode 100644 index 0000000..42509c6 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/local_variable_depth.md @@ -0,0 +1,229 @@ +# Local variable depth + +One feature of Prism is that it resolves local variables as it parses. It's necessary to do this because of ambiguities in the grammar. For example, consider the following code: + +```ruby +foo / bar#/ +``` + +If `foo` is a local variable, this is a call to `/` with `bar` as an argument, followed by a comment. If it's not a local variable, this is a method call to `foo` with a regular expression argument. + +"Depth" refers to the number of visible scopes that Prism has to go up to find the declaration of a local variable. +Note that this follows the same scoping rules as Ruby, so a local variable is only visible in the scope it is declared in and in blocks nested in that scope. +The rules for calculating the depth are very important to understand because they may differ from individual Ruby implementations since they are not specified by the language. + +Prism uses the minimum number of scopes, i.e., it only creates scopes when necessary semantically, in other words when there must be distinct scopes (which can be observed through `binding.local_variables`). +That are no "transparent/invisible" scopes in Prism. +Some Ruby implementations use those for some language constructs and need to adjust by maintaining a depth offset. + +Below are the places where a local variable can be written/targeted, along with how the depth is calculated at that point. + +## General + +In the course of general Ruby code when reading a local variable, the depth is equal to the number of scopes to go up to find the declaration of that variable. For example: + +```ruby +foo = 1 +bar = 2 +baz = 3 + +foo # depth 0 +tap { bar } # depth 1 +tap { tap { baz } } # depth 2 +``` + +This also includes writing to a local variable, which could be writing to a local variable that is already declared. For example: + +```ruby +foo = 1 +bar = 2 + +foo = 3 # depth 0 +tap { bar = 4 } # depth 1 +``` + +This includes multiple assignment, where the same principle applies. For example: + +```ruby +foo = 1 +bar = 2 + +foo, bar = 3, 4 # depth 0 +tap { foo, bar = 5, 6 } # depth 1 +``` + +## `for` loops + +`for` loops in Ruby break down to calls to `.each` with a block. +However in that case local variable reads and writes within the block will be in the same scope as the scope surrounding the `for` and not in a deeper/separate scope (surprising, but this is Ruby semantics). +For example: + +```ruby +foo = 1 + +for e in baz + foo # depth 0 + bar = 2 # depth 0 +end + +p bar # depth 0, prints 2 +``` + +The local variable(s) used for the index of the `for` are also at the same depth (as variables inside and outside the `for`): + +```ruby +for e in [1, 2] # depth 0 + e # depth 0 +end + +p e # depth 0, prints 2 +``` + +## Pattern matching captures + +You can target a local variable in a pattern matching expression using capture syntax. Using this syntax, you can target local variables in the current scope or in visible parent scopes. For example: + +```ruby +42 => bar # depth 0 +``` + +The example above writes to a local variable in the current scope. If the variable is already declared in a higher visible scope, it will be written to that scope instead. For example: + +```ruby +foo = 1 +tap { 42 => foo } # depth 1 +``` + +## Named capture groups + +You can target local variables through named capture groups in regular expressions if they are used on the left-hand side of a `=~` operator. For example: + +```ruby +/(?\d+)/ =~ "42" # depth 0 +``` + +This will write to a `foo` local variable. If the variable is already declared in a higher visible scope, it will be written to that scope instead. For example: + +```ruby +foo = 1 +tap { /(?\d+)/ =~ "42" } # depth 1 +``` + +## "interpolated once" regular expressions + +Regular expressions that interpolate local variables (unrelated to capture group local variables) and have the `o` flag will only interpolate the local variables once for the runtime of the program. +In CRuby, this is implemented by compiling the regular expression within a nested instruction sequence, which means CRuby thinks the depth is one more than prism does. For example: + +``` +$ ruby --dump=insns -e 'foo = 1; /#{foo}/o' +== disasm: #@-e:1 (1,0)-(1,18)> (catch: false) +local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1]) +[ 1] foo@0 +0000 putobject_INT2FIX_1_ ( 1)[Li] +0001 setlocal_WC_0 foo@0 +0003 once block in
, +0006 leave + +== disasm: #@-e:1 (1,9)-(1,18)> (catch: false) +0000 putobject "" ( 1) +0002 getlocal_WC_1 foo@0 +0004 dup +0005 objtostring +0007 anytostring +0008 toregexp 0, 2 +0011 leave +``` + +In this case CRuby fetches the local variable with `getlocal_WC_1` as the second instruction to the "once" instruction sequence. When compiling CRuby, prism therefore will adjust the depth to account for this difference. + +## `rescue` clauses + +In CRuby, `rescue` clauses are implemented as their own instruction sequence, and therefore CRuby thinks the depth is one more than prism does. For example: + +``` +$ ruby --dump=insns -e 'begin; foo = 1; rescue; foo; end' +== disasm: #@-e:1 (1,0)-(1,32)> (catch: true) +== catch table +| catch type: rescue st: 0000 ed: 0004 sp: 0000 cont: 0005 +| == disasm: #@-e:1 (1,16)-(1,28)> (catch: true) +| local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1]) +| [ 1] $!@0 +| 0000 getlocal_WC_0 $!@0 ( 1) +| 0002 putobject StandardError +| 0004 checkmatch 3 +| 0006 branchunless 11 +| 0008 getlocal_WC_1 foo@0[Li] +| 0010 leave +| 0011 getlocal_WC_0 $!@0 +| 0013 throw 0 +| catch type: retry st: 0004 ed: 0005 sp: 0000 cont: 0000 +|------------------------------------------------------------------------ +local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1]) +[ 1] foo@0 +0000 putobject_INT2FIX_1_ ( 1)[Li] +0001 dup +0002 setlocal_WC_0 foo@0 +0004 nop +0005 leave +``` + +In the catch table, CRuby is reading the `foo` local variable using `getlocal_WC_1` as the fifth instruction to the "rescue" instruction sequence. When compiling CRuby, prism therefore will adjust the depth to account for this difference. + +Note that this includes the error reference, which can target local variables, as in: + +``` +$ ruby --dump=insns -e 'foo = 1; begin; rescue => foo; end' +== disasm: #@-e:1 (1,0)-(1,34)> (catch: true) +== catch table +| catch type: rescue st: 0003 ed: 0004 sp: 0000 cont: 0005 +| == disasm: #@-e:1 (1,16)-(1,30)> (catch: true) +| local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1]) +| [ 1] $!@0 +| 0000 getlocal_WC_0 $!@0 ( 1) +| 0002 putobject StandardError +| 0004 checkmatch 3 +| 0006 branchunless 14 +| 0008 getlocal_WC_0 $!@0 +| 0010 setlocal_WC_1 foo@0 +| 0012 putnil +| 0013 leave +| 0014 getlocal_WC_0 $!@0 +| 0016 throw 0 +| catch type: retry st: 0004 ed: 0005 sp: 0000 cont: 0003 +|------------------------------------------------------------------------ +local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1]) +[ 1] foo@0 +0000 putobject_INT2FIX_1_ ( 1)[Li] +0001 setlocal_WC_0 foo@0 +0003 putnil +0004 nop +0005 leave +``` + +Note that CRuby is writing to the `foo` local variable using the `setlocal_WC_1` instruction as the sixth instruction to the "rescue" instruction sequence. When compiling CRuby, prism therefore will adjust the depth to account for this difference. + +## Post execution blocks + +The `END {}` syntax allows executing code when the program exits. In CRuby, this is implemented as two nested instruction sequences. CRuby therefore thinks the depth is two more than prism does. For example: + +``` +$ ruby --dump=insns -e 'foo = 1; END { foo }' +== disasm: #@-e:1 (1,0)-(1,20)> (catch: false) +local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1]) +[ 1] foo@0 +0000 putobject_INT2FIX_1_ ( 1)[Li] +0001 setlocal_WC_0 foo@0 +0003 once block in
, +0006 leave + +== disasm: #@-e:0 (0,0)-(-1,-1)> (catch: false) +0000 putspecialobject 1 ( 1) +0002 send , block in
+0005 leave + +== disasm: #@-e:1 (1,9)-(1,20)> (catch: false) +0000 getlocal foo@0, 2 ( 1)[LiBc] +0003 leave [Br] +``` + +In the instruction sequence corresponding to the code that gets executed inside the `END` block, CRuby is reading the `foo` local variable using `getlocal` as the second instruction to the `"block in
"` instruction sequence. When compiling CRuby, prism therefore will adjust the depth to account for this difference. diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/mapping.md b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/mapping.md new file mode 100644 index 0000000..b61e9e9 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/mapping.md @@ -0,0 +1,117 @@ +# Mapping + +When considering the previous CRuby parser versus prism, this document should be helpful to understand how various concepts are mapped. + +## Nodes + +The following table shows how the various CRuby nodes are mapped to prism nodes. + +| CRuby | prism | +| --- | --- | +| `NODE_SCOPE` | | +| `NODE_BLOCK` | | +| `NODE_IF` | `PM_IF_NODE` | +| `NODE_UNLESS` | `PM_UNLESS_NODE` | +| `NODE_CASE` | `PM_CASE_NODE` | +| `NODE_CASE2` | `PM_CASE_NODE` (with a null predicate) | +| `NODE_CASE3` | | +| `NODE_WHEN` | `PM_WHEN_NODE` | +| `NODE_IN` | `PM_IN_NODE` | +| `NODE_WHILE` | `PM_WHILE_NODE` | +| `NODE_UNTIL` | `PM_UNTIL_NODE` | +| `NODE_ITER` | `PM_CALL_NODE` (with a non-null block) | +| `NODE_FOR` | `PM_FOR_NODE` | +| `NODE_FOR_MASGN` | `PM_FOR_NODE` (with a multi-write node as the index) | +| `NODE_BREAK` | `PM_BREAK_NODE` | +| `NODE_NEXT` | `PM_NEXT_NODE` | +| `NODE_REDO` | `PM_REDO_NODE` | +| `NODE_RETRY` | `PM_RETRY_NODE` | +| `NODE_BEGIN` | `PM_BEGIN_NODE` | +| `NODE_RESCUE` | `PM_RESCUE_NODE` | +| `NODE_RESBODY` | | +| `NODE_ENSURE` | `PM_ENSURE_NODE` | +| `NODE_AND` | `PM_AND_NODE` | +| `NODE_OR` | `PM_OR_NODE` | +| `NODE_MASGN` | `PM_MULTI_WRITE_NODE` | +| `NODE_LASGN` | `PM_LOCAL_VARIABLE_WRITE_NODE` | +| `NODE_DASGN` | `PM_LOCAL_VARIABLE_WRITE_NODE` | +| `NODE_GASGN` | `PM_GLOBAL_VARIABLE_WRITE_NODE` | +| `NODE_IASGN` | `PM_INSTANCE_VARIABLE_WRITE_NODE` | +| `NODE_CDECL` | `PM_CONSTANT_PATH_WRITE_NODE` | +| `NODE_CVASGN` | `PM_CLASS_VARIABLE_WRITE_NODE` | +| `NODE_OP_ASGN1` | | +| `NODE_OP_ASGN2` | | +| `NODE_OP_ASGN_AND` | `PM_OPERATOR_AND_ASSIGNMENT_NODE` | +| `NODE_OP_ASGN_OR` | `PM_OPERATOR_OR_ASSIGNMENT_NODE` | +| `NODE_OP_CDECL` | | +| `NODE_CALL` | `PM_CALL_NODE` | +| `NODE_OPCALL` | `PM_CALL_NODE` (with an operator as the method) | +| `NODE_FCALL` | `PM_CALL_NODE` (with a null receiver and parentheses) | +| `NODE_VCALL` | `PM_CALL_NODE` (with a null receiver and parentheses or arguments) | +| `NODE_QCALL` | `PM_CALL_NODE` (with a &. operator) | +| `NODE_SUPER` | `PM_SUPER_NODE` | +| `NODE_ZSUPER` | `PM_FORWARDING_SUPER_NODE` | +| `NODE_LIST` | `PM_ARRAY_NODE` | +| `NODE_ZLIST` | `PM_ARRAY_NODE` (with no child elements) | +| `NODE_VALUES` | `PM_ARGUMENTS_NODE` | +| `NODE_HASH` | `PM_HASH_NODE` | +| `NODE_RETURN` | `PM_RETURN_NODE` | +| `NODE_YIELD` | `PM_YIELD_NODE` | +| `NODE_LVAR` | `PM_LOCAL_VARIABLE_READ_NODE` | +| `NODE_DVAR` | `PM_LOCAL_VARIABLE_READ_NODE` | +| `NODE_GVAR` | `PM_GLOBAL_VARIABLE_READ_NODE` | +| `NODE_IVAR` | `PM_INSTANCE_VARIABLE_READ_NODE` | +| `NODE_CONST` | `PM_CONSTANT_PATH_READ_NODE` | +| `NODE_CVAR` | `PM_CLASS_VARIABLE_READ_NODE` | +| `NODE_NTH_REF` | `PM_NUMBERED_REFERENCE_READ_NODE` | +| `NODE_BACK_REF` | `PM_BACK_REFERENCE_READ_NODE` | +| `NODE_MATCH` | | +| `NODE_MATCH2` | `PM_CALL_NODE` (with regular expression as receiver) | +| `NODE_MATCH3` | `PM_CALL_NODE` (with regular expression as only argument) | +| `NODE_LIT` | | +| `NODE_STR` | `PM_STRING_NODE` | +| `NODE_DSTR` | `PM_INTERPOLATED_STRING_NODE` | +| `NODE_XSTR` | `PM_X_STRING_NODE` | +| `NODE_DXSTR` | `PM_INTERPOLATED_X_STRING_NODE` | +| `NODE_EVSTR` | `PM_STRING_INTERPOLATED_NODE` | +| `NODE_DREGX` | `PM_INTERPOLATED_REGULAR_EXPRESSION_NODE` | +| `NODE_ONCE` | | +| `NODE_ARGS` | `PM_PARAMETERS_NODE` | +| `NODE_ARGS_AUX` | | +| `NODE_OPT_ARG` | `PM_OPTIONAL_PARAMETER_NODE` | +| `NODE_KW_ARG` | `PM_KEYWORD_PARAMETER_NODE` | +| `NODE_POSTARG` | `PM_REQUIRED_PARAMETER_NODE` | +| `NODE_ARGSCAT` | | +| `NODE_ARGSPUSH` | | +| `NODE_SPLAT` | `PM_SPLAT_NODE` | +| `NODE_BLOCK_PASS` | `PM_BLOCK_ARGUMENT_NODE` | +| `NODE_DEFN` | `PM_DEF_NODE` (with a null receiver) | +| `NODE_DEFS` | `PM_DEF_NODE` (with a non-null receiver) | +| `NODE_ALIAS` | `PM_ALIAS_NODE` | +| `NODE_VALIAS` | `PM_ALIAS_NODE` (with a global variable first argument) | +| `NODE_UNDEF` | `PM_UNDEF_NODE` | +| `NODE_CLASS` | `PM_CLASS_NODE` | +| `NODE_MODULE` | `PM_MODULE_NODE` | +| `NODE_SCLASS` | `PM_S_CLASS_NODE` | +| `NODE_COLON2` | `PM_CONSTANT_PATH_NODE` | +| `NODE_COLON3` | `PM_CONSTANT_PATH_NODE` (with a null receiver) | +| `NODE_DOT2` | `PM_RANGE_NODE` (with a .. operator) | +| `NODE_DOT3` | `PM_RANGE_NODE` (with a ... operator) | +| `NODE_FLIP2` | `PM_RANGE_NODE` (with a .. operator) | +| `NODE_FLIP3` | `PM_RANGE_NODE` (with a ... operator) | +| `NODE_SELF` | `PM_SELF_NODE` | +| `NODE_NIL` | `PM_NIL_NODE` | +| `NODE_TRUE` | `PM_TRUE_NODE` | +| `NODE_FALSE` | `PM_FALSE_NODE` | +| `NODE_ERRINFO` | | +| `NODE_DEFINED` | `PM_DEFINED_NODE` | +| `NODE_POSTEXE` | `PM_POST_EXECUTION_NODE` | +| `NODE_DSYM` | `PM_INTERPOLATED_SYMBOL_NODE` | +| `NODE_ATTRASGN` | `PM_CALL_NODE` (with a message that ends with =) | +| `NODE_LAMBDA` | `PM_LAMBDA_NODE` | +| `NODE_ARYPTN` | `PM_ARRAY_PATTERN_NODE` | +| `NODE_HSHPTN` | `PM_HASH_PATTERN_NODE` | +| `NODE_FNDPTN` | `PM_FIND_PATTERN_NODE` | +| `NODE_ERROR` | `PM_MISSING_NODE` | +| `NODE_LAST` | | +``` diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/parser_translation.md b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/parser_translation.md new file mode 100644 index 0000000..8c08007 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/parser_translation.md @@ -0,0 +1,24 @@ +# parser translation + +Prism ships with the ability to translate its syntax tree into the syntax tree used by the [whitequark/parser](https://github.com/whitequark/parser) gem. This allows you to use tools built on top of the `parser` gem with the `prism` parser. + +## Usage + +The `parser` gem provides multiple parsers to support different versions of the Ruby grammar. This includes all of the Ruby versions going back to 1.8, as well as third-party parsers like MacRuby and RubyMotion. The `prism` gem provides another parser that uses the `prism` parser to build the syntax tree. + +You can use the `prism` parser like you would any other. After requiring `prism`, you should be able to call any of the regular `Parser::Base` APIs that you would normally use. + +```ruby +require "prism" + +# Same as `Parser::Ruby34` +Prism::Translation::Parser34.parse_file("path/to/file.rb") + +# Same as `Parser::CurrentRuby` +Prism::Translation::ParserCurrent.parse("puts 'Hello World!'") +``` + +All the parsers are autoloaded, so you don't have to worry about requiring them yourself. + +If you also need to parse Ruby versions below 3.3 (for which the `prism` translation layer does not have explicit support), check out +[this guide](https://github.com/whitequark/parser/blob/master/doc/PRISM_TRANSLATION.md) from the `parser` gem on how to use both in conjunction. diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/parsing_rules.md b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/parsing_rules.md new file mode 100644 index 0000000..36475ba --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/parsing_rules.md @@ -0,0 +1,22 @@ +# Rules + +This document contains information related to the rules of the parser for Ruby source code. + +As an example, in the documentation of many of the fields of nodes, it's mentioned that a field follows the lexing rules for `identifier` or `constant`. This document describes what those rules are. + +## Constants + +Constants in Ruby begin with an upper-case letter. This is followed by any number of underscores, alphanumeric, or non-ASCII characters. The definition of "alphanumeric" and "upper-case letter" are encoding-dependent. + +## Non-void expression + +Most expressions in CRuby are non-void. This means the expression they represent resolves to a value. For example, `1 + 2` is a non-void expression, because it resolves to a method call. Even things like `class Foo; end` is a non-void expression, because it returns the last evaluated expression in the body of the class (or `nil`). + +Certain nodes, however, are void expressions, and cannot be combined to form larger expressions. +* `BEGIN {}`, `END {}`, `alias foo bar`, and `undef foo` can only be at a statement position. +* The "jumps": `return`, `break`, `next`, `redo`, `retry` are void expressions. +* `value => pattern` is also considered a void expression. + +## Identifiers + +Identifiers in Ruby begin with an underscore or lower-case letter. This is followed by any number of underscores, alphanumeric, or non-ASCII characters. The definition of "alphanumeric" and "lower-case letter" are encoding-dependent. diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/releasing.md b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/releasing.md new file mode 100644 index 0000000..a7d3cf5 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/releasing.md @@ -0,0 +1,75 @@ +# Releasing + +To release a new version of Prism, perform the following steps: + +## Preparation + +* Update the `CHANGELOG.md` file. + * Add a new section for the new version at the top of the file. + * Fill in the relevant changes — it may be easiest to click the link for the `Unreleased` heading to find the commits. + * Update the links at the bottom of the file. +* Update the version numbers in the various files that reference them: + +```sh +export PRISM_MAJOR="x" +export PRISM_MINOR="y" +export PRISM_PATCH="z" +export PRISM_VERSION="$PRISM_MAJOR.$PRISM_MINOR.$PRISM_PATCH" +ruby -pi -e 'gsub(/spec\.version = ".+?"/, %Q{spec.version = "#{ENV["PRISM_VERSION"]}"})' prism.gemspec +ruby -pi -e 'gsub(/EXPECTED_PRISM_VERSION ".+?"/, %Q{EXPECTED_PRISM_VERSION "#{ENV["PRISM_VERSION"]}"})' ext/prism/extension.h +ruby -pi -e 'gsub(/PRISM_VERSION_MAJOR \d+/, %Q{PRISM_VERSION_MAJOR #{ENV["PRISM_MAJOR"]}})' include/prism/version.h +ruby -pi -e 'gsub(/PRISM_VERSION_MINOR \d+/, %Q{PRISM_VERSION_MINOR #{ENV["PRISM_MINOR"]}})' include/prism/version.h +ruby -pi -e 'gsub(/PRISM_VERSION_PATCH \d+/, %Q{PRISM_VERSION_PATCH #{ENV["PRISM_PATCH"]}})' include/prism/version.h +ruby -pi -e 'gsub(/PRISM_VERSION ".+?"/, %Q{PRISM_VERSION "#{ENV["PRISM_VERSION"]}"})' include/prism/version.h +ruby -pi -e 'gsub(/"version": ".+?"/, %Q{"version": "#{ENV["PRISM_VERSION"]}"})' javascript/package.json +ruby -pi -e 'gsub(/lossy\(\), ".+?"/, %Q{lossy(), "#{ENV["PRISM_VERSION"]}"})' rust/ruby-prism-sys/tests/utils_tests.rs +ruby -pi -e 'gsub(/\d+, "prism major/, %Q{#{ENV["PRISM_MAJOR"]}, "prism major})' templates/java/org/prism/Loader.java.erb +ruby -pi -e 'gsub(/\d+, "prism minor/, %Q{#{ENV["PRISM_MINOR"]}, "prism minor})' templates/java/org/prism/Loader.java.erb +ruby -pi -e 'gsub(/\d+, "prism patch/, %Q{#{ENV["PRISM_PATCH"]}, "prism patch})' templates/java/org/prism/Loader.java.erb +ruby -pi -e 'gsub(/MAJOR_VERSION = \d+/, %Q{MAJOR_VERSION = #{ENV["PRISM_MAJOR"]}})' templates/javascript/src/deserialize.js.erb +ruby -pi -e 'gsub(/MINOR_VERSION = \d+/, %Q{MINOR_VERSION = #{ENV["PRISM_MINOR"]}})' templates/javascript/src/deserialize.js.erb +ruby -pi -e 'gsub(/PATCH_VERSION = \d+/, %Q{PATCH_VERSION = #{ENV["PRISM_PATCH"]}})' templates/javascript/src/deserialize.js.erb +ruby -pi -e 'gsub(/MAJOR_VERSION = \d+/, %Q{MAJOR_VERSION = #{ENV["PRISM_MAJOR"]}})' templates/lib/prism/serialize.rb.erb +ruby -pi -e 'gsub(/MINOR_VERSION = \d+/, %Q{MINOR_VERSION = #{ENV["PRISM_MINOR"]}})' templates/lib/prism/serialize.rb.erb +ruby -pi -e 'gsub(/PATCH_VERSION = \d+/, %Q{PATCH_VERSION = #{ENV["PRISM_PATCH"]}})' templates/lib/prism/serialize.rb.erb +ruby -pi -e 'gsub(/^version = ".+?"/, %Q{version = "#{ENV["PRISM_VERSION"]}"})' rust/ruby-prism-sys/Cargo.toml +ruby -pi -e 'gsub(/^version = ".+?"/, %Q{version = "#{ENV["PRISM_VERSION"]}"})' rust/ruby-prism/Cargo.toml +ruby -pi -e 'gsub(/^ruby-prism-sys = \{ version = ".+?"/, %Q{ruby-prism-sys = \{ version = "#{ENV["PRISM_VERSION"]}"})' rust/ruby-prism/Cargo.toml +``` + +* Update the `Gemfile.lock` file: + +```sh +chruby ruby-4.0.0-dev +bundle install +``` + +* Update the version-specific lockfiles: + +```sh +for VERSION in "2.7" "3.0" "3.1" "3.2" "3.3" "3.4" "4.0"; do docker run -it --rm -v "$PWD":/usr/src/app -w /usr/src/app -e BUNDLE_GEMFILE="gemfiles/$VERSION/Gemfile" "ruby:$VERSION" bundle update; done +chruby ruby-4.1.0-dev && BUNDLE_GEMFILE=gemfiles/4.1/Gemfile bundle install +``` + +* Update the cargo lockfiles: + +```sh +bundle exec rake cargo:build +``` + +* Commit all of the updated files: + +```sh +git commit -am "Bump to v$PRISM_VERSION" +``` + +* Push up the changes: + +```sh +git push +``` + +## Publishing + +* Update the GitHub release page with a copy of the latest entry in the `CHANGELOG.md` file. +* Push a new tag to the GitHub repository, following the `vX.Y.Z` format. diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/relocation.md b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/relocation.md new file mode 100644 index 0000000..0515f56 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/relocation.md @@ -0,0 +1,34 @@ +# Relocation + +Prism parses deterministically for the same input. This provides a nice property that is exposed through the `#node_id` API on nodes. Effectively this means that for the same input, these values will remain consistent every time the source is parsed. This means we can reparse the source same with a `#node_id` value and find the exact same node again. + +The `Relocation` module provides an API around this property. It allows you to "save" nodes and locations using a minimal amount of memory (just the node_id and a field identifier) and then reify them later. This minimizes the amount of memory you need to allocate to store this information because it does not keep around a pointer to the source string. + +## Getting started + +To get started with the `Relocation` module, you would first instantiate a `Repository` object. You do this through a DSL that chains method calls for configuration. For example, if for every entry in the repository you want to store the start and end lines, the start and end code unit columns for in UTF-16, and the leading comments, you would: + +```ruby +repository = Prism::Relocation.filepath("path/to/file").lines.code_unit_columns(Encoding::UTF_16).leading_comments +``` + +Now that you have the repository, you can pass it into any of the `save*` APIs on nodes or locations to create entries in the repository that will be lazily reified. + +```ruby +# assume that node is a Prism::ClassNode object +entry = node.constant_path.save(repository) +``` + +Now that you have the entry object, you do not need to keep around a reference to the repository, it will be cleaned up on its own when the last entry is reified. Now, whenever you need to, you may call the associated field methods on the entry object, as in: + +```ruby +entry.start_line +entry.end_line + +entry.start_code_units_column +entry.end_code_units_column + +entry.leading_comments +``` + +Note that if you had configured other fields to be saved, you would be able to access them as well. The first time one of these fields is accessed, the repository will reify every entry it knows about and then clean itself up. In this way, you can effectively treat them as if you had kept around lightweight versions of `Prism::Node` or `Prism::Location` objects. diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/ripper_translation.md b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/ripper_translation.md new file mode 100644 index 0000000..92bbe74 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/ripper_translation.md @@ -0,0 +1,63 @@ +# Ripper translation + +Prism provides the ability to mirror the `Ripper` standard library. It is available under `Prism::Translation::Ripper`. You can use the entire public API, and also some undocumented features that are commonly used. + +Briefly, `Ripper` is a streaming parser that allows you to construct your own syntax tree. As an example: + +```ruby +class ArithmeticRipper < Prism::Translation::Ripper + def on_binary(left, operator, right) + left.public_send(operator, right) + end + + def on_int(value) + value.to_i + end + + def on_program(stmts) + stmts + end + + def on_stmts_new + [] + end + + def on_stmts_add(stmts, stmt) + stmts << stmt + stmts + end +end + +ArithmeticRipper.new("1 + 2 - 3").parse # => [0] +``` + +The exact names of the `on_*` methods are listed in the `Ripper` source. + +You can can also automatically use the ripper translation in places that don't explicitly use the translation layer by doing the following: + +```ruby +# Will redirect access of the `Ripper` constant to `Prism::Translation::Ripper`. +require "prism/translation/ripper/shim" +``` + +## Background + +It is helpful to understand the differences between the `Ripper` library and the `Prism` library. Both libraries perform parsing and provide you with APIs to manipulate and understand the resulting syntax tree. However, there are a few key differences. + +### Design + +`Ripper` is a streaming parser. This means as it is parsing Ruby code, it dispatches events back to the consumer. This allows quite a bit of flexibility. You can use it to build your own syntax tree or to find specific patterns in the code. `Prism` on the other hand returns to you the completed syntax tree _before_ it allows you to manipulate it. This means the tree that you get back is the only representation that can be generated by the parser _at parse time_ (but of course can be manipulated later). + +### Fields + +We use the term "field" to mean a piece of information on a syntax tree node. `Ripper` provides the minimal number of fields to accurately represent the syntax tree for the purposes of compilation/interpretation. For example, in the callbacks for nodes that are based on keywords (`class`, `module`, `for`, `while`, etc.) you are not given the keyword itself, you need to attach it on your own. In other cases, tokens are not necessarily dispatched at all, meaning you need to find them yourself. `Prism` provides the opposite: the maximum number of fields on nodes is provided. As a tradeoff, this requires more memory, but this is chosen to make it easier on consumers. + +### Maintainability + +The `Ripper` interface is not guaranteed in any way, and tends to change between patch versions of CRuby. This is largely due to the fact that `Ripper` is a by-product of the generated parser, as opposed to its own parser. As an example, in the expression `foo::bar = baz`, there are three different represents possible for the call operator, including: + +* `:"::"` - Ruby 1.9 to Ruby 3.1.4 +* `73` - Ruby 3.1.5 to Ruby 3.1.6 +* `[:@op, "::", [lineno, column]]` - Ruby 3.2.0 and later + +The `Prism` interface is guaranteed going forward to be the consistent, and the official Ruby syntax tree interface. This means you can rely on this interface without having to worry about individual changes between Ruby versions. It also is a gem, which means it is versioned based on the gem version, as opposed to being versioned based on the Ruby version. Finally, you can use `Prism` to parse multiple versions of Ruby, whereas `Ripper` is tied to the Ruby version it is running on. diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/ruby_api.md b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/ruby_api.md new file mode 100644 index 0000000..c378417 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/ruby_api.md @@ -0,0 +1,45 @@ +# Ruby API + +The `prism` gem provides a Ruby API for accessing the syntax tree. + +For the most part, the API for accessing the tree mirrors that found in the [Syntax Tree](https://github.com/ruby-syntax-tree/syntax_tree) project. This means: + +* Walking the tree involves creating a visitor and passing it to the `#accept` method on any node in the tree +* Nodes in the tree respond to named methods for accessing their children as well as `#child_nodes` +* Nodes respond to the pattern matching interfaces `#deconstruct` and `#deconstruct_keys` + +Every entry in `config.yml` will generate a Ruby class as well as the code that builds the nodes themselves. +Creating a syntax tree involves calling one of the class methods on the `Prism` module. +The full API is documented below. + +## API + +* `Prism.dump(source)` - parse the syntax tree corresponding to the given source string, and serialize it to a string +* `Prism.dump_file(filepath)` - parse the syntax tree corresponding to the given source file and serialize it to a string +* `Prism.lex(source)` - parse the tokens corresponding to the given source string and return them as an array within a parse result +* `Prism.lex_file(filepath)` - parse the tokens corresponding to the given source file and return them as an array within a parse result +* `Prism.parse(source)` - parse the syntax tree corresponding to the given source string and return it within a parse result +* `Prism.parse_file(filepath)` - parse the syntax tree corresponding to the given source file and return it within a parse result +* `Prism.parse_stream(io)` - parse the syntax tree corresponding to the source that is read out of the given IO object using the `#gets` method and return it within a parse result +* `Prism.parse_lex(source)` - parse the syntax tree corresponding to the given source string and return it within a parse result, along with the tokens +* `Prism.parse_lex_file(filepath)` - parse the syntax tree corresponding to the given source file and return it within a parse result, along with the tokens +* `Prism.load(source, serialized, freeze = false)` - load the serialized syntax tree using the source as a reference into a syntax tree +* `Prism.parse_comments(source)` - parse the comments corresponding to the given source string and return them +* `Prism.parse_file_comments(source)` - parse the comments corresponding to the given source file and return them +* `Prism.parse_success?(source)` - parse the syntax tree corresponding to the given source string and return true if it was parsed without errors +* `Prism.parse_file_success?(filepath)` - parse the syntax tree corresponding to the given source file and return true if it was parsed without errors + +## Nodes + +Once you have nodes in hand coming out of a parse result, there are a number of common APIs that are available on each instance. They are: + +* `#accept(visitor)` - a method that will immediately call `visit_*` to specialize for the node type +* `#child_nodes` - a positional array of the child nodes of the node, with `nil` values for any missing children +* `#compact_child_nodes` - a positional array of the child nodes of the node with no `nil` values +* `#each_child_node` - with a block given yields all child nodes, without a block return an enumerator containing all child nodes +* `#copy(**keys)` - a method that allows creating a shallow copy of the node with the given keys overridden +* `#deconstruct`/`#deconstruct_keys(keys)` - the pattern matching interface for nodes +* `#inspect` - a string representation that looks like the syntax tree of the node +* `#location` - a `Location` object that describes the location of the node in the source file +* `#to_dot` - convert the node's syntax tree into graphviz dot notation +* `#type` - a symbol that represents the type of the node, useful for quick comparisons diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/ruby_parser_translation.md b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/ruby_parser_translation.md new file mode 100644 index 0000000..35a4cd3 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/ruby_parser_translation.md @@ -0,0 +1,19 @@ +# ruby_parser translation + +Prism ships with the ability to translate its syntax tree into the syntax tree used by the [seattlerb/ruby_parser](https://github.com/seattlerb/ruby_parser) gem. This allows you to use tools built on top of the `ruby_parser` gem with the `prism` parser. + +## Usage + +You can call the `parse` and `parse_file` methods on the `Prism::Translation::RubyParser` module: + +```ruby +filepath = "path/to/file.rb" +Prism::Translation::RubyParser.parse_file(filepath) +``` + +This will return to you `Sexp` objects that mirror the result of calling `RubyParser` methods, as in: + +```ruby +filepath = "path/to/file.rb" +RubyParser.new.parse(File.read(filepath), filepath) +``` diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/serialization.md b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/serialization.md new file mode 100644 index 0000000..ec395f8 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/serialization.md @@ -0,0 +1,233 @@ +# Serialization + +Prism ships with the ability to serialize a syntax tree to a single string. +The string can then be deserialized back into a syntax tree using a language other than C. +This is useful for using the parsing logic in other tools without having to write a parser in that language. +The syntax tree still requires a copy of the original source, as for the most part it just contains byte offsets into the source string. + +## Types + +Let us define some simple types for readability. + +### varuint + +A variable-length unsigned integer with the value fitting in `uint32_t` using between 1 and 5 bytes, using the [LEB128](https://en.wikipedia.org/wiki/LEB128) encoding. +This drastically cuts down on the size of the serialized string, especially when the source file is large. + +### varsint + +A variable-length signed integer with the value fitting in `int32_t` using between 1 and 5 bytes, using [ZigZag encoding](https://protobuf.dev/programming-guides/encoding/#signed-ints) into [LEB128]. + +### string + +| # bytes | field | +| --- | --- | +| varuint | the length of the string in bytes | +| ... | the string bytes | + +### location + +| # bytes | field | +| --- | --- | +| varuint | byte offset into the source string where this location begins | +| varuint | length of the location in bytes in the source string | + +### comment + +The comment type is one of: + +* 0=`INLINE` (`# comment`) +* 1=`EMBEDDED_DOCUMENT` (`=begin`/`=end`) + +| # bytes | field | +| --- | --- | +| `1` | comment type | +| location | the location in the source of this comment | + +### magic comment + +| # bytes | field | +| --- | --- | +| location | the location of the key of the magic comment | +| location | the location of the value of the magic comment | + +### error + +| # bytes | field | +| --- | --- | +| varuint | type | +| string | error message (ASCII-only characters) | +| location | the location in the source this error applies to | +| `1` | the level of the error: `0` for `fatal`, `1` for `argument`, `2` for `load` | + +### warning + +| # bytes | field | +| --- | --- | +| varuint | type | +| string | warning message (ASCII-only characters) | +| location | the location in the source this warning applies to | +| `1` | the level of the warning: `0` for `default` and `1` for `verbose` | + +### integer + +| # bytes | field | +| --- | --- | +| `1` | `1` if the integer is negative, `0` if the integer is positive | +| varuint | the number of words in this integer | +| varuint+ | the words of the integer, least-significant to most-significant | + +## Structure + +The serialized string representing the syntax tree is composed of three parts: the header, the body, and the constant pool. +The header contains information like the version of prism that serialized the tree. +The body contains the actual nodes in the tree. +The constant pool contains constants that were interned while parsing. + +The header is structured like the following table: + +| # bytes | field | +| --- | --- | +| `5` | "PRISM" | +| `1` | major version number | +| `1` | minor version number | +| `1` | patch version number | +| `1` | 1 indicates only semantics fields were serialized, 0 indicates all fields were serialized (including location fields) | +| string | the encoding name | +| varsint | the start line | +| varuint | number of newline offsets | +| varuint* | newline offsets | +| varuint | number of comments | +| comment* | comments | +| varuint | number of magic comments | +| magic comment* | magic comments | +| location? | the optional location of the `__END__` keyword and its contents | +| varuint | number of errors | +| error* | errors | +| varuint | number of warnings | +| warning* | warnings | +| `4` | content pool offset | +| varuint | content pool size | + +After the header comes the body of the serialized string. +The body consists of a sequence of nodes that is built using a prefix traversal order of the syntax tree. +Each node is structured like the following table: + +| # bytes | field | +| --- | --- | +| `1` | node type | +| varuint | node identifier | +| location | node location | +| varuint | node flags | + +Every field on the node is then appended to the serialized string. The fields can be determined by referencing `config.yml`. Depending on the type of field, it could take a couple of different forms, described below: + +* `double` - A field that is a `double`. This is structured as a sequence of 8 bytes in native endian order. +* `node` - A field that is a node. This is structured just as like parent node. +* `node?` - A field that is a node that is optionally present. If the node is not present, then a single `0` byte will be written in its place. If it is present, then it will be structured just as like parent node. +* `node[]` - A field that is an array of nodes. This is structured as a variable-length integer length, followed by the child nodes themselves. +* `string` - A field that is a string. For example, this is used as the name of the method in a call node, since it cannot directly reference the source string (as in `@-` or `foo=`). This is structured as a variable-length integer byte length, followed by the string itself (_without_ a trailing null byte). +* `constant` - A variable-length integer that represents an index in the constant pool. +* `constant?` - An optional variable-length integer that represents an index in the constant pool. If it's not present, then a single `0` byte will be written in its place. +* `integer` - A field that represents an arbitrary-sized integer. The structure is listed above. +* `location` - A field that is a location. This is structured as a variable-length integer start followed by a variable-length integer length. +* `location?` - A field that is a location that is optionally present. If the location is not present, then a single `0` byte will be written in its place. If it is present, then it will be structured just like the `location` child node. +* `uint8` - A field that is an 8-bit unsigned integer. This is structured as a single byte. +* `uint32` - A field that is a 32-bit unsigned integer. This is structured as a variable-length integer. + +After the syntax tree, the content pool is serialized. This is a list of constants that were referenced from within the tree. The content pool begins at the offset specified in the header. Constants can be either "owned" (in which case their contents are embedded in the serialization) or "shared" (in which case their contents represent a slice of the source string). The most significant bit of the constant indicates whether it is owned or shared. + +In the case that it is owned, the constant is structured as follows: + +| # bytes | field | +| --- | --- | +| `4` | the byte offset in the serialization for the contents of the constant | +| `4` | the byte length in the serialization | + +Note that you will need to mask off the most significant bit for the byte offset in the serialization. In the case that it is shared, the constant is structured as follows: + +| # bytes | field | +| --- | --- | +| `4` | the byte offset in the source string for the contents of the constant | +| `4` | the byte length in the source string | + +After the constant pool, the contents of the owned constants are serialized. This is just a sequence of bytes that represent the contents of the constants. At the end of the serialization, the buffer is null terminated. + +## APIs + +The relevant APIs and struct definitions are listed below: + +```c +// A pm_buffer_t is a simple memory buffer that stores data in a contiguous +// block of memory. It is used to store the serialized representation of a +// prism tree. +typedef struct { + char *value; + size_t length; + size_t capacity; +} pm_buffer_t; + +// Free the memory associated with the buffer. +void pm_buffer_free(pm_buffer_t *); + +// Parse and serialize the AST represented by the given source to the given +// buffer. +void pm_serialize_parse(pm_buffer_t *buffer, const uint8_t *source, size_t length, const char *data); +``` + +Typically you would use a stack-allocated `pm_buffer_t` and call `pm_serialize_parse`, as in: + +```c +void +serialize(const uint8_t *source, size_t length) { + pm_buffer_t buffer = { 0 }; + pm_serialize_parse(&buffer, source, length, NULL); + + // Do something with the serialized string. + + pm_buffer_free(&buffer); +} +``` + +The final argument to `pm_serialize_parse` is an optional string that controls the options to the parse function. This includes all of the normal options that could be passed to `pm_parser_init` through a `pm_options_t` struct, but serialized as a string to make it easier for callers through FFI. Note that no `varuint` are used here to make it easier to produce the data for the caller, and also serialized size is less important here. The format of the data is structured as follows: + +| # 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` | command line flags | +| `1` | syntax version, see [pm_options_version_t](https://github.com/ruby/prism/blob/main/include/prism/options.h) for valid values | +| `1` | whether or not the encoding is locked (should almost always be false) | +| `4` | the number of scopes | +| ... | the scopes | + +Command line flags are a bitset. By default every flag is `0`. It includes the following values: + +* `0x1` - the `-a` option +* `0x2` - the `-e` option +* `0x4` - the `-l` option +* `0x8` - the `-n` option +* `0x10` - the `-p` option +* `0x20` - the `-x` option + +Scopes are ordered from the outermost scope to the innermost one. + +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 | + +The data can be `NULL` (as seen in the example above). diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/testing.md b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/testing.md new file mode 100644 index 0000000..a42ee2e --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/docs/testing.md @@ -0,0 +1,55 @@ +# Testing + +This document explains how to test prism, both locally, and against existing test suites. + +## Test suite + +`rake test` will run all of the files in the `test/` directory. This can be conceived of as two parts: unit tests, and snapshot tests. + +### Unit tests + +These test specific prism implementation details like comments, errors, and regular expressions. There are corresponding files for each thing being tested (like `test/errors_test.rb`). + +### Snapshot tests + +Snapshot tests ensure that parsed output is equivalent to previous parsed output. There are many categorized examples of valid syntax within the `test/prism/fixtures/` directory. When the test suite runs, it will parse all of this syntax, and compare it against corresponding files in the `test/prism/snapshots/` directory. For example, `test/prism/fixtures/strings.txt` has a corresponding `test/prism/snapshots/strings.txt`. + +If the parsed files do not match, it will raise an error. If there is not a corresponding file in the `test/prism/snapshots/` directory, one will be created so that it exists for the next test run. + +### Testing against repositories + +To test the parser against a repository, you can run `FILEPATHS='/path/to/repository/**/*.rb' rake lex`. This will run the parser against every file matched by the glob pattern and check its generated tokens against those generated by ripper. + +## Local testing + +As you are working, you will likely want to test your code locally. `test.rb` is ignored by git, so it can be used for local testing. There are also two executables which may help you: + +1. **bin/lex** takes a filepath and compares prism's lexed output to Ripper's lexed output. It prints any lexed output that doesn't match. It does some minor transformations to the lexed output in order to compare them, like split prism's heredoc tokens to mirror Ripper's. + +``` +$ bin/lex test.rb +``` + +If you would like to see the full lexed comparison, and not only the output that doesn't match, you can run with `VERBOSE=1`: + +``` +$ VERBOSE=1 bin/lex test.rb +``` + +`bin/lex` can also be used with `-e` and then source code, like this: + +``` +$ bin/lex -e "1 + 2" +``` + +2. **bin/parse** takes a filepath and outputs prism's parsed node structure generated from reading the file. + +``` +$ bin/parse test.rb +``` + +`bin/parse` can also be used with `-e` and then source code, like this: + +``` +$ bin/parse -e "1 + 2" +``` diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/ext/prism/Makefile b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/ext/prism/Makefile new file mode 100644 index 0000000..6446943 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/ext/prism/Makefile @@ -0,0 +1,269 @@ + +SHELL = /bin/sh + +# V=0 quiet, V=1 verbose. other values don't work. +V = 0 +V0 = $(V:0=) +Q1 = $(V:1=) +Q = $(Q1:0=@) +ECHO1 = $(V:1=@ :) +ECHO = $(ECHO1:0=@ echo) +NULLCMD = : + +#### Start of system configuration section. #### + +srcdir = . +topdir = /opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0 +hdrdir = $(topdir) +arch_hdrdir = /opt/hostedtoolcache/Ruby/3.3.10/x64/include/ruby-3.3.0/x86_64-linux +PATH_SEPARATOR = : +VPATH = $(srcdir):$(arch_hdrdir)/ruby:$(hdrdir)/ruby:$(srcdir)/../../src:$(srcdir)/../../src/util +prefix = $(DESTDIR)/opt/hostedtoolcache/Ruby/3.3.10/x64 +rubysitearchprefix = $(rubylibprefix)/$(sitearch) +rubyarchprefix = $(rubylibprefix)/$(arch) +rubylibprefix = $(libdir)/$(RUBY_BASE_NAME) +exec_prefix = $(prefix) +vendorarchhdrdir = $(vendorhdrdir)/$(sitearch) +sitearchhdrdir = $(sitehdrdir)/$(sitearch) +rubyarchhdrdir = $(rubyhdrdir)/$(arch) +vendorhdrdir = $(rubyhdrdir)/vendor_ruby +sitehdrdir = $(rubyhdrdir)/site_ruby +rubyhdrdir = $(includedir)/$(RUBY_VERSION_NAME) +vendorarchdir = $(vendorlibdir)/$(sitearch) +vendorlibdir = $(vendordir)/$(ruby_version) +vendordir = $(rubylibprefix)/vendor_ruby +sitearchdir = $(sitelibdir)/$(sitearch) +sitelibdir = $(sitedir)/$(ruby_version) +sitedir = $(rubylibprefix)/site_ruby +rubyarchdir = $(rubylibdir)/$(arch) +rubylibdir = $(rubylibprefix)/$(ruby_version) +sitearchincludedir = $(includedir)/$(sitearch) +archincludedir = $(includedir)/$(arch) +sitearchlibdir = $(libdir)/$(sitearch) +archlibdir = $(libdir)/$(arch) +ridir = $(datarootdir)/$(RI_BASE_NAME) +mandir = $(datarootdir)/man +localedir = $(datarootdir)/locale +libdir = $(exec_prefix)/lib +psdir = $(docdir) +pdfdir = $(docdir) +dvidir = $(docdir) +htmldir = $(docdir) +infodir = $(datarootdir)/info +docdir = $(datarootdir)/doc/$(PACKAGE) +oldincludedir = $(DESTDIR)/usr/include +includedir = $(prefix)/include +runstatedir = $(localstatedir)/run +localstatedir = $(prefix)/var +sharedstatedir = $(prefix)/com +sysconfdir = $(prefix)/etc +datadir = $(datarootdir) +datarootdir = $(prefix)/share +libexecdir = $(exec_prefix)/libexec +sbindir = $(exec_prefix)/sbin +bindir = $(exec_prefix)/bin +archdir = $(rubyarchdir) + + +CC_WRAPPER = +CC = gcc +CXX = g++ +LIBRUBY = $(LIBRUBY_SO) +LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a +LIBRUBYARG_SHARED = -Wl,-rpath,$(libdir) -L$(libdir) -l$(RUBY_SO_NAME) +LIBRUBYARG_STATIC = -Wl,-rpath,$(libdir) -L$(libdir) -l$(RUBY_SO_NAME)-static $(MAINLIBS) +empty = +OUTFLAG = -o $(empty) +COUTFLAG = -o $(empty) +CSRCFLAG = $(empty) + +RUBY_EXTCONF_H = +cflags = $(optflags) $(debugflags) $(warnflags) +cxxflags = +optflags = -O3 -fno-fast-math +debugflags = -ggdb3 +warnflags = -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wmisleading-indentation -Wundef +cppflags = +CCDLFLAGS = -fPIC +CFLAGS = $(CCDLFLAGS) $(cflags) -fPIC -fvisibility=hidden $(ARCH_FLAG) +INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir)/ruby/backward -I$(hdrdir) -I$(srcdir) -I/home/runner/work/devcontainer/devcontainer/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include -I/home/runner/work/devcontainer/devcontainer/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/ext +DEFS = +CPPFLAGS = -DENABLE_PATH_CHECK=0 $(DEFS) $(cppflags) +CXXFLAGS = $(CCDLFLAGS) $(ARCH_FLAG) +ldflags = -L. -fstack-protector-strong -rdynamic -Wl,-export-dynamic -Wl,--no-as-needed +dldflags = -Wl,--compress-debug-sections=zlib +ARCH_FLAG = +DLDFLAGS = $(ldflags) $(dldflags) $(ARCH_FLAG) +LDSHARED = $(CC) -shared +LDSHAREDXX = $(CXX) -shared +AR = gcc-ar +EXEEXT = + +RUBY_INSTALL_NAME = $(RUBY_BASE_NAME) +RUBY_SO_NAME = ruby +RUBYW_INSTALL_NAME = +RUBY_VERSION_NAME = $(RUBY_BASE_NAME)-$(ruby_version) +RUBYW_BASE_NAME = rubyw +RUBY_BASE_NAME = ruby + +arch = x86_64-linux +sitearch = $(arch) +ruby_version = 3.3.0 +ruby = $(bindir)/$(RUBY_BASE_NAME) +RUBY = $(ruby) +BUILTRUBY = $(bindir)/$(RUBY_BASE_NAME) +ruby_headers = $(hdrdir)/ruby.h $(hdrdir)/ruby/backward.h $(hdrdir)/ruby/ruby.h $(hdrdir)/ruby/defines.h $(hdrdir)/ruby/missing.h $(hdrdir)/ruby/intern.h $(hdrdir)/ruby/st.h $(hdrdir)/ruby/subst.h $(arch_hdrdir)/ruby/config.h + +RM = rm -f +RM_RF = rm -fr +RMDIRS = rmdir --ignore-fail-on-non-empty -p +MAKEDIRS = /usr/bin/mkdir -p +INSTALL = /usr/bin/install -c +INSTALL_PROG = $(INSTALL) -m 0755 +INSTALL_DATA = $(INSTALL) -m 644 +COPY = cp +TOUCH = exit > + +#### End of system configuration section. #### + +preload = +libpath = . $(libdir) +LIBPATH = -L. -L$(libdir) -Wl,-rpath,$(libdir) +DEFFILE = + +CLEANFILES = mkmf.log +DISTCLEANFILES = +DISTCLEANDIRS = + +extout = +extout_prefix = +target_prefix = /prism +LOCAL_LIBS = +LIBS = $(LIBRUBYARG_SHARED) -lm -lpthread -lc +ORIG_SRCS = api_node.c api_pack.c extension.c +SRCS = $(ORIG_SRCS) diagnostic.c encoding.c node.c options.c pack.c prettyprint.c prism.c regexp.c serialize.c static_literals.c token_type.c pm_buffer.c pm_char.c pm_constant_pool.c pm_integer.c pm_list.c pm_memchr.c pm_newline_list.c pm_string.c pm_strncasecmp.c pm_strpbrk.c +OBJS = api_node.o api_pack.o extension.o diagnostic.o encoding.o node.o options.o pack.o prettyprint.o prism.o regexp.o serialize.o static_literals.o token_type.o pm_buffer.o pm_char.o pm_constant_pool.o pm_integer.o pm_list.o pm_memchr.o pm_newline_list.o pm_string.o pm_strncasecmp.o pm_strpbrk.o +HDRS = $(srcdir)/extension.h +LOCAL_HDRS = +TARGET = prism +TARGET_NAME = prism +TARGET_ENTRY = Init_$(TARGET_NAME) +DLLIB = $(TARGET).so +EXTSTATIC = +STATIC_LIB = + +TIMESTAMP_DIR = . +BINDIR = $(bindir) +RUBYCOMMONDIR = $(sitedir)$(target_prefix) +RUBYLIBDIR = $(sitelibdir)$(target_prefix) +RUBYARCHDIR = $(sitearchdir)$(target_prefix) +HDRDIR = $(sitehdrdir)$(target_prefix) +ARCHHDRDIR = $(sitearchhdrdir)$(target_prefix) +TARGET_SO_DIR = +TARGET_SO = $(TARGET_SO_DIR)$(DLLIB) +CLEANLIBS = $(TARGET_SO) false +CLEANOBJS = $(OBJS) *.bak +TARGET_SO_DIR_TIMESTAMP = $(TIMESTAMP_DIR)/.sitearchdir.-.prism.time + +all: $(DLLIB) +static: $(STATIC_LIB) +.PHONY: all install static install-so install-rb +.PHONY: clean clean-so clean-static clean-rb + +clean-static:: +clean-rb-default:: +clean-rb:: +clean-so:: +clean: clean-so clean-static clean-rb-default clean-rb + -$(Q)$(RM_RF) $(CLEANLIBS) $(CLEANOBJS) $(CLEANFILES) .*.time + +distclean-rb-default:: +distclean-rb:: +distclean-so:: +distclean-static:: +distclean: clean distclean-so distclean-static distclean-rb-default distclean-rb + -$(Q)$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log + -$(Q)$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES) + -$(Q)$(RMDIRS) $(DISTCLEANDIRS) 2> /dev/null || true + +realclean: distclean +install: install-so install-rb + +install-so: $(DLLIB) $(TARGET_SO_DIR_TIMESTAMP) + $(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR) +clean-static:: + -$(Q)$(RM) $(STATIC_LIB) +install-rb: pre-install-rb do-install-rb install-rb-default +install-rb-default: pre-install-rb-default do-install-rb-default +pre-install-rb: Makefile +pre-install-rb-default: Makefile +do-install-rb: +do-install-rb-default: +pre-install-rb-default: + @$(NULLCMD) +$(TARGET_SO_DIR_TIMESTAMP): + $(Q) $(MAKEDIRS) $(@D) $(RUBYARCHDIR) + $(Q) $(TOUCH) $@ + +site-install: site-install-so site-install-rb +site-install-so: install-so +site-install-rb: install-rb + +.SUFFIXES: .c .m .cc .mm .cxx .cpp .o .S + +.cc.o: + $(ECHO) compiling $(<) + $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$< + +.cc.S: + $(ECHO) translating $(<) + $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$< + +.mm.o: + $(ECHO) compiling $(<) + $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$< + +.mm.S: + $(ECHO) translating $(<) + $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$< + +.cxx.o: + $(ECHO) compiling $(<) + $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$< + +.cxx.S: + $(ECHO) translating $(<) + $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$< + +.cpp.o: + $(ECHO) compiling $(<) + $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$< + +.cpp.S: + $(ECHO) translating $(<) + $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$< + +.c.o: + $(ECHO) compiling $(<) + $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$< + +.c.S: + $(ECHO) translating $(<) + $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$< + +.m.o: + $(ECHO) compiling $(<) + $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$< + +.m.S: + $(ECHO) translating $(<) + $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$< + +$(TARGET_SO): $(OBJS) Makefile + $(ECHO) linking shared-object prism/$(DLLIB) + -$(Q)$(RM) $(@) + $(Q) $(LDSHARED) -o $@ $(OBJS) $(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS) + + + +$(OBJS): $(HDRS) $(ruby_headers) diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/ext/prism/api_node.c b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/ext/prism/api_node.c new file mode 100644 index 0000000..c9515de --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/ext/prism/api_node.c @@ -0,0 +1,6945 @@ +/* :markup: markdown */ + +/*----------------------------------------------------------------------------*/ +/* This file is generated by the templates/template.rb script and should not */ +/* be modified manually. See */ +/* templates/ext/prism/api_node.c.erb */ +/* if you are looking to modify the */ +/* template */ +/*----------------------------------------------------------------------------*/ + +#line 2 "prism/templates/ext/prism/api_node.c.erb" +#include "prism/extension.h" + +extern VALUE rb_cPrism; +extern VALUE rb_cPrismNode; +extern VALUE rb_cPrismSource; +extern VALUE rb_cPrismToken; +extern VALUE rb_cPrismLocation; + +static VALUE rb_cPrismAliasGlobalVariableNode; +static VALUE rb_cPrismAliasMethodNode; +static VALUE rb_cPrismAlternationPatternNode; +static VALUE rb_cPrismAndNode; +static VALUE rb_cPrismArgumentsNode; +static VALUE rb_cPrismArrayNode; +static VALUE rb_cPrismArrayPatternNode; +static VALUE rb_cPrismAssocNode; +static VALUE rb_cPrismAssocSplatNode; +static VALUE rb_cPrismBackReferenceReadNode; +static VALUE rb_cPrismBeginNode; +static VALUE rb_cPrismBlockArgumentNode; +static VALUE rb_cPrismBlockLocalVariableNode; +static VALUE rb_cPrismBlockNode; +static VALUE rb_cPrismBlockParameterNode; +static VALUE rb_cPrismBlockParametersNode; +static VALUE rb_cPrismBreakNode; +static VALUE rb_cPrismCallAndWriteNode; +static VALUE rb_cPrismCallNode; +static VALUE rb_cPrismCallOperatorWriteNode; +static VALUE rb_cPrismCallOrWriteNode; +static VALUE rb_cPrismCallTargetNode; +static VALUE rb_cPrismCapturePatternNode; +static VALUE rb_cPrismCaseMatchNode; +static VALUE rb_cPrismCaseNode; +static VALUE rb_cPrismClassNode; +static VALUE rb_cPrismClassVariableAndWriteNode; +static VALUE rb_cPrismClassVariableOperatorWriteNode; +static VALUE rb_cPrismClassVariableOrWriteNode; +static VALUE rb_cPrismClassVariableReadNode; +static VALUE rb_cPrismClassVariableTargetNode; +static VALUE rb_cPrismClassVariableWriteNode; +static VALUE rb_cPrismConstantAndWriteNode; +static VALUE rb_cPrismConstantOperatorWriteNode; +static VALUE rb_cPrismConstantOrWriteNode; +static VALUE rb_cPrismConstantPathAndWriteNode; +static VALUE rb_cPrismConstantPathNode; +static VALUE rb_cPrismConstantPathOperatorWriteNode; +static VALUE rb_cPrismConstantPathOrWriteNode; +static VALUE rb_cPrismConstantPathTargetNode; +static VALUE rb_cPrismConstantPathWriteNode; +static VALUE rb_cPrismConstantReadNode; +static VALUE rb_cPrismConstantTargetNode; +static VALUE rb_cPrismConstantWriteNode; +static VALUE rb_cPrismDefNode; +static VALUE rb_cPrismDefinedNode; +static VALUE rb_cPrismElseNode; +static VALUE rb_cPrismEmbeddedStatementsNode; +static VALUE rb_cPrismEmbeddedVariableNode; +static VALUE rb_cPrismEnsureNode; +static VALUE rb_cPrismFalseNode; +static VALUE rb_cPrismFindPatternNode; +static VALUE rb_cPrismFlipFlopNode; +static VALUE rb_cPrismFloatNode; +static VALUE rb_cPrismForNode; +static VALUE rb_cPrismForwardingArgumentsNode; +static VALUE rb_cPrismForwardingParameterNode; +static VALUE rb_cPrismForwardingSuperNode; +static VALUE rb_cPrismGlobalVariableAndWriteNode; +static VALUE rb_cPrismGlobalVariableOperatorWriteNode; +static VALUE rb_cPrismGlobalVariableOrWriteNode; +static VALUE rb_cPrismGlobalVariableReadNode; +static VALUE rb_cPrismGlobalVariableTargetNode; +static VALUE rb_cPrismGlobalVariableWriteNode; +static VALUE rb_cPrismHashNode; +static VALUE rb_cPrismHashPatternNode; +static VALUE rb_cPrismIfNode; +static VALUE rb_cPrismImaginaryNode; +static VALUE rb_cPrismImplicitNode; +static VALUE rb_cPrismImplicitRestNode; +static VALUE rb_cPrismInNode; +static VALUE rb_cPrismIndexAndWriteNode; +static VALUE rb_cPrismIndexOperatorWriteNode; +static VALUE rb_cPrismIndexOrWriteNode; +static VALUE rb_cPrismIndexTargetNode; +static VALUE rb_cPrismInstanceVariableAndWriteNode; +static VALUE rb_cPrismInstanceVariableOperatorWriteNode; +static VALUE rb_cPrismInstanceVariableOrWriteNode; +static VALUE rb_cPrismInstanceVariableReadNode; +static VALUE rb_cPrismInstanceVariableTargetNode; +static VALUE rb_cPrismInstanceVariableWriteNode; +static VALUE rb_cPrismIntegerNode; +static VALUE rb_cPrismInterpolatedMatchLastLineNode; +static VALUE rb_cPrismInterpolatedRegularExpressionNode; +static VALUE rb_cPrismInterpolatedStringNode; +static VALUE rb_cPrismInterpolatedSymbolNode; +static VALUE rb_cPrismInterpolatedXStringNode; +static VALUE rb_cPrismItLocalVariableReadNode; +static VALUE rb_cPrismItParametersNode; +static VALUE rb_cPrismKeywordHashNode; +static VALUE rb_cPrismKeywordRestParameterNode; +static VALUE rb_cPrismLambdaNode; +static VALUE rb_cPrismLocalVariableAndWriteNode; +static VALUE rb_cPrismLocalVariableOperatorWriteNode; +static VALUE rb_cPrismLocalVariableOrWriteNode; +static VALUE rb_cPrismLocalVariableReadNode; +static VALUE rb_cPrismLocalVariableTargetNode; +static VALUE rb_cPrismLocalVariableWriteNode; +static VALUE rb_cPrismMatchLastLineNode; +static VALUE rb_cPrismMatchPredicateNode; +static VALUE rb_cPrismMatchRequiredNode; +static VALUE rb_cPrismMatchWriteNode; +static VALUE rb_cPrismMissingNode; +static VALUE rb_cPrismModuleNode; +static VALUE rb_cPrismMultiTargetNode; +static VALUE rb_cPrismMultiWriteNode; +static VALUE rb_cPrismNextNode; +static VALUE rb_cPrismNilNode; +static VALUE rb_cPrismNoKeywordsParameterNode; +static VALUE rb_cPrismNumberedParametersNode; +static VALUE rb_cPrismNumberedReferenceReadNode; +static VALUE rb_cPrismOptionalKeywordParameterNode; +static VALUE rb_cPrismOptionalParameterNode; +static VALUE rb_cPrismOrNode; +static VALUE rb_cPrismParametersNode; +static VALUE rb_cPrismParenthesesNode; +static VALUE rb_cPrismPinnedExpressionNode; +static VALUE rb_cPrismPinnedVariableNode; +static VALUE rb_cPrismPostExecutionNode; +static VALUE rb_cPrismPreExecutionNode; +static VALUE rb_cPrismProgramNode; +static VALUE rb_cPrismRangeNode; +static VALUE rb_cPrismRationalNode; +static VALUE rb_cPrismRedoNode; +static VALUE rb_cPrismRegularExpressionNode; +static VALUE rb_cPrismRequiredKeywordParameterNode; +static VALUE rb_cPrismRequiredParameterNode; +static VALUE rb_cPrismRescueModifierNode; +static VALUE rb_cPrismRescueNode; +static VALUE rb_cPrismRestParameterNode; +static VALUE rb_cPrismRetryNode; +static VALUE rb_cPrismReturnNode; +static VALUE rb_cPrismSelfNode; +static VALUE rb_cPrismShareableConstantNode; +static VALUE rb_cPrismSingletonClassNode; +static VALUE rb_cPrismSourceEncodingNode; +static VALUE rb_cPrismSourceFileNode; +static VALUE rb_cPrismSourceLineNode; +static VALUE rb_cPrismSplatNode; +static VALUE rb_cPrismStatementsNode; +static VALUE rb_cPrismStringNode; +static VALUE rb_cPrismSuperNode; +static VALUE rb_cPrismSymbolNode; +static VALUE rb_cPrismTrueNode; +static VALUE rb_cPrismUndefNode; +static VALUE rb_cPrismUnlessNode; +static VALUE rb_cPrismUntilNode; +static VALUE rb_cPrismWhenNode; +static VALUE rb_cPrismWhileNode; +static VALUE rb_cPrismXStringNode; +static VALUE rb_cPrismYieldNode; + +static VALUE +pm_location_new(const pm_parser_t *parser, const uint8_t *start, const uint8_t *end, VALUE source, bool freeze) { + if (freeze) { + VALUE location_argv[] = { + source, + LONG2FIX(start - parser->start), + LONG2FIX(end - start) + }; + + return rb_obj_freeze(rb_class_new_instance(3, location_argv, rb_cPrismLocation)); + } else { + 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, bool freeze) { + ID type = rb_intern(pm_token_type_name(token->type)); + VALUE location = pm_location_new(parser, token->start, token->end, source, freeze); + + VALUE slice = rb_enc_str_new((const char *) token->start, token->end - token->start, encoding); + if (freeze) rb_obj_freeze(slice); + + VALUE argv[] = { source, ID2SYM(type), slice, location }; + VALUE value = rb_class_new_instance(4, argv, rb_cPrismToken); + if (freeze) rb_obj_freeze(value); + + return value; +} + +static VALUE +pm_string_new(const pm_string_t *string, rb_encoding *encoding) { + return rb_obj_freeze(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, bool freeze) { + 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])); + } + + if (freeze) { + rb_obj_freeze(source_string); + rb_obj_freeze(offsets); + } + + VALUE source = rb_funcall(rb_cPrismSource, rb_intern("for"), 3, source_string, LONG2NUM(parser->start_line), offsets); + if (freeze) rb_obj_freeze(source); + + return source; +} + +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, bool freeze) { + 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)) { +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_ALIAS_GLOBAL_VARIABLE_NODE: { + pm_alias_global_variable_node_t *cast = (pm_alias_global_variable_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->new_name); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->old_name); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_ALIAS_METHOD_NODE: { + pm_alias_method_node_t *cast = (pm_alias_method_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->new_name); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->old_name); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_ALTERNATION_PATTERN_NODE: { + pm_alternation_pattern_node_t *cast = (pm_alternation_pattern_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->left); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->right); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_AND_NODE: { + pm_and_node_t *cast = (pm_and_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->left); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->right); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_ARGUMENTS_NODE: { + pm_arguments_node_t *cast = (pm_arguments_node_t *) node; + for (size_t index = 0; index < cast->arguments.size; index++) { + pm_node_stack_push(&node_stack, (pm_node_t *) cast->arguments.nodes[index]); + } + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_ARRAY_NODE: { + pm_array_node_t *cast = (pm_array_node_t *) node; + for (size_t index = 0; index < cast->elements.size; index++) { + pm_node_stack_push(&node_stack, (pm_node_t *) cast->elements.nodes[index]); + } + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_ARRAY_PATTERN_NODE: { + pm_array_pattern_node_t *cast = (pm_array_pattern_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->constant); + for (size_t index = 0; index < cast->requireds.size; index++) { + pm_node_stack_push(&node_stack, (pm_node_t *) cast->requireds.nodes[index]); + } + pm_node_stack_push(&node_stack, (pm_node_t *) cast->rest); + for (size_t index = 0; index < cast->posts.size; index++) { + pm_node_stack_push(&node_stack, (pm_node_t *) cast->posts.nodes[index]); + } + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_ASSOC_NODE: { + pm_assoc_node_t *cast = (pm_assoc_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->key); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->value); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_ASSOC_SPLAT_NODE: { + pm_assoc_splat_node_t *cast = (pm_assoc_splat_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->value); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_BEGIN_NODE: { + pm_begin_node_t *cast = (pm_begin_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->statements); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->rescue_clause); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->else_clause); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->ensure_clause); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_BLOCK_ARGUMENT_NODE: { + pm_block_argument_node_t *cast = (pm_block_argument_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->expression); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_BLOCK_NODE: { + pm_block_node_t *cast = (pm_block_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->parameters); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->body); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_BLOCK_PARAMETERS_NODE: { + pm_block_parameters_node_t *cast = (pm_block_parameters_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->parameters); + for (size_t index = 0; index < cast->locals.size; index++) { + pm_node_stack_push(&node_stack, (pm_node_t *) cast->locals.nodes[index]); + } + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_BREAK_NODE: { + pm_break_node_t *cast = (pm_break_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->arguments); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_CALL_AND_WRITE_NODE: { + pm_call_and_write_node_t *cast = (pm_call_and_write_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->receiver); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->value); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_CALL_NODE: { + pm_call_node_t *cast = (pm_call_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->receiver); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->arguments); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->block); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_CALL_OPERATOR_WRITE_NODE: { + pm_call_operator_write_node_t *cast = (pm_call_operator_write_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->receiver); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->value); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_CALL_OR_WRITE_NODE: { + pm_call_or_write_node_t *cast = (pm_call_or_write_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->receiver); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->value); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_CALL_TARGET_NODE: { + pm_call_target_node_t *cast = (pm_call_target_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->receiver); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_CAPTURE_PATTERN_NODE: { + pm_capture_pattern_node_t *cast = (pm_capture_pattern_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->value); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->target); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_CASE_MATCH_NODE: { + pm_case_match_node_t *cast = (pm_case_match_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->predicate); + for (size_t index = 0; index < cast->conditions.size; index++) { + pm_node_stack_push(&node_stack, (pm_node_t *) cast->conditions.nodes[index]); + } + pm_node_stack_push(&node_stack, (pm_node_t *) cast->else_clause); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_CASE_NODE: { + pm_case_node_t *cast = (pm_case_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->predicate); + for (size_t index = 0; index < cast->conditions.size; index++) { + pm_node_stack_push(&node_stack, (pm_node_t *) cast->conditions.nodes[index]); + } + pm_node_stack_push(&node_stack, (pm_node_t *) cast->else_clause); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_CLASS_NODE: { + pm_class_node_t *cast = (pm_class_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->constant_path); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->superclass); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->body); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_CLASS_VARIABLE_AND_WRITE_NODE: { + pm_class_variable_and_write_node_t *cast = (pm_class_variable_and_write_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->value); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE: { + pm_class_variable_operator_write_node_t *cast = (pm_class_variable_operator_write_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->value); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_CLASS_VARIABLE_OR_WRITE_NODE: { + pm_class_variable_or_write_node_t *cast = (pm_class_variable_or_write_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->value); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_CLASS_VARIABLE_WRITE_NODE: { + pm_class_variable_write_node_t *cast = (pm_class_variable_write_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->value); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_CONSTANT_AND_WRITE_NODE: { + pm_constant_and_write_node_t *cast = (pm_constant_and_write_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->value); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_CONSTANT_OPERATOR_WRITE_NODE: { + pm_constant_operator_write_node_t *cast = (pm_constant_operator_write_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->value); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_CONSTANT_OR_WRITE_NODE: { + pm_constant_or_write_node_t *cast = (pm_constant_or_write_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->value); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_CONSTANT_PATH_AND_WRITE_NODE: { + pm_constant_path_and_write_node_t *cast = (pm_constant_path_and_write_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->target); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->value); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_CONSTANT_PATH_NODE: { + pm_constant_path_node_t *cast = (pm_constant_path_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->parent); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_CONSTANT_PATH_OPERATOR_WRITE_NODE: { + pm_constant_path_operator_write_node_t *cast = (pm_constant_path_operator_write_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->target); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->value); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_CONSTANT_PATH_OR_WRITE_NODE: { + pm_constant_path_or_write_node_t *cast = (pm_constant_path_or_write_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->target); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->value); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_CONSTANT_PATH_TARGET_NODE: { + pm_constant_path_target_node_t *cast = (pm_constant_path_target_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->parent); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_CONSTANT_PATH_WRITE_NODE: { + pm_constant_path_write_node_t *cast = (pm_constant_path_write_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->target); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->value); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_CONSTANT_WRITE_NODE: { + pm_constant_write_node_t *cast = (pm_constant_write_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->value); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_DEF_NODE: { + pm_def_node_t *cast = (pm_def_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->receiver); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->parameters); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->body); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_DEFINED_NODE: { + pm_defined_node_t *cast = (pm_defined_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->value); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_ELSE_NODE: { + pm_else_node_t *cast = (pm_else_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->statements); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_EMBEDDED_STATEMENTS_NODE: { + pm_embedded_statements_node_t *cast = (pm_embedded_statements_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->statements); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_EMBEDDED_VARIABLE_NODE: { + pm_embedded_variable_node_t *cast = (pm_embedded_variable_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->variable); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_ENSURE_NODE: { + pm_ensure_node_t *cast = (pm_ensure_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->statements); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_FIND_PATTERN_NODE: { + pm_find_pattern_node_t *cast = (pm_find_pattern_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->constant); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->left); + for (size_t index = 0; index < cast->requireds.size; index++) { + pm_node_stack_push(&node_stack, (pm_node_t *) cast->requireds.nodes[index]); + } + pm_node_stack_push(&node_stack, (pm_node_t *) cast->right); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_FLIP_FLOP_NODE: { + pm_flip_flop_node_t *cast = (pm_flip_flop_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->left); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->right); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_FOR_NODE: { + pm_for_node_t *cast = (pm_for_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->index); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->collection); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->statements); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_FORWARDING_SUPER_NODE: { + pm_forwarding_super_node_t *cast = (pm_forwarding_super_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->block); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_GLOBAL_VARIABLE_AND_WRITE_NODE: { + pm_global_variable_and_write_node_t *cast = (pm_global_variable_and_write_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->value); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE: { + pm_global_variable_operator_write_node_t *cast = (pm_global_variable_operator_write_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->value); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_GLOBAL_VARIABLE_OR_WRITE_NODE: { + pm_global_variable_or_write_node_t *cast = (pm_global_variable_or_write_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->value); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_GLOBAL_VARIABLE_WRITE_NODE: { + pm_global_variable_write_node_t *cast = (pm_global_variable_write_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->value); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_HASH_NODE: { + pm_hash_node_t *cast = (pm_hash_node_t *) node; + for (size_t index = 0; index < cast->elements.size; index++) { + pm_node_stack_push(&node_stack, (pm_node_t *) cast->elements.nodes[index]); + } + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_HASH_PATTERN_NODE: { + pm_hash_pattern_node_t *cast = (pm_hash_pattern_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->constant); + for (size_t index = 0; index < cast->elements.size; index++) { + pm_node_stack_push(&node_stack, (pm_node_t *) cast->elements.nodes[index]); + } + pm_node_stack_push(&node_stack, (pm_node_t *) cast->rest); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_IF_NODE: { + pm_if_node_t *cast = (pm_if_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->predicate); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->statements); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->subsequent); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_IMAGINARY_NODE: { + pm_imaginary_node_t *cast = (pm_imaginary_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->numeric); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_IMPLICIT_NODE: { + pm_implicit_node_t *cast = (pm_implicit_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->value); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_IN_NODE: { + pm_in_node_t *cast = (pm_in_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->pattern); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->statements); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_INDEX_AND_WRITE_NODE: { + pm_index_and_write_node_t *cast = (pm_index_and_write_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->receiver); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->arguments); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->block); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->value); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_INDEX_OPERATOR_WRITE_NODE: { + pm_index_operator_write_node_t *cast = (pm_index_operator_write_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->receiver); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->arguments); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->block); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->value); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_INDEX_OR_WRITE_NODE: { + pm_index_or_write_node_t *cast = (pm_index_or_write_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->receiver); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->arguments); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->block); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->value); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_INDEX_TARGET_NODE: { + pm_index_target_node_t *cast = (pm_index_target_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->receiver); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->arguments); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->block); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_INSTANCE_VARIABLE_AND_WRITE_NODE: { + pm_instance_variable_and_write_node_t *cast = (pm_instance_variable_and_write_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->value); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE: { + pm_instance_variable_operator_write_node_t *cast = (pm_instance_variable_operator_write_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->value); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_INSTANCE_VARIABLE_OR_WRITE_NODE: { + pm_instance_variable_or_write_node_t *cast = (pm_instance_variable_or_write_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->value); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_INSTANCE_VARIABLE_WRITE_NODE: { + pm_instance_variable_write_node_t *cast = (pm_instance_variable_write_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->value); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_INTERPOLATED_MATCH_LAST_LINE_NODE: { + pm_interpolated_match_last_line_node_t *cast = (pm_interpolated_match_last_line_node_t *) node; + for (size_t index = 0; index < cast->parts.size; index++) { + pm_node_stack_push(&node_stack, (pm_node_t *) cast->parts.nodes[index]); + } + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: { + pm_interpolated_regular_expression_node_t *cast = (pm_interpolated_regular_expression_node_t *) node; + for (size_t index = 0; index < cast->parts.size; index++) { + pm_node_stack_push(&node_stack, (pm_node_t *) cast->parts.nodes[index]); + } + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_INTERPOLATED_STRING_NODE: { + pm_interpolated_string_node_t *cast = (pm_interpolated_string_node_t *) node; + for (size_t index = 0; index < cast->parts.size; index++) { + pm_node_stack_push(&node_stack, (pm_node_t *) cast->parts.nodes[index]); + } + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_INTERPOLATED_SYMBOL_NODE: { + pm_interpolated_symbol_node_t *cast = (pm_interpolated_symbol_node_t *) node; + for (size_t index = 0; index < cast->parts.size; index++) { + pm_node_stack_push(&node_stack, (pm_node_t *) cast->parts.nodes[index]); + } + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_INTERPOLATED_X_STRING_NODE: { + pm_interpolated_x_string_node_t *cast = (pm_interpolated_x_string_node_t *) node; + for (size_t index = 0; index < cast->parts.size; index++) { + pm_node_stack_push(&node_stack, (pm_node_t *) cast->parts.nodes[index]); + } + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_KEYWORD_HASH_NODE: { + pm_keyword_hash_node_t *cast = (pm_keyword_hash_node_t *) node; + for (size_t index = 0; index < cast->elements.size; index++) { + pm_node_stack_push(&node_stack, (pm_node_t *) cast->elements.nodes[index]); + } + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_LAMBDA_NODE: { + pm_lambda_node_t *cast = (pm_lambda_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->parameters); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->body); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_LOCAL_VARIABLE_AND_WRITE_NODE: { + pm_local_variable_and_write_node_t *cast = (pm_local_variable_and_write_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->value); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE: { + pm_local_variable_operator_write_node_t *cast = (pm_local_variable_operator_write_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->value); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_LOCAL_VARIABLE_OR_WRITE_NODE: { + pm_local_variable_or_write_node_t *cast = (pm_local_variable_or_write_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->value); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_LOCAL_VARIABLE_WRITE_NODE: { + pm_local_variable_write_node_t *cast = (pm_local_variable_write_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->value); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_MATCH_PREDICATE_NODE: { + pm_match_predicate_node_t *cast = (pm_match_predicate_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->value); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->pattern); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_MATCH_REQUIRED_NODE: { + pm_match_required_node_t *cast = (pm_match_required_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->value); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->pattern); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_MATCH_WRITE_NODE: { + pm_match_write_node_t *cast = (pm_match_write_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->call); + for (size_t index = 0; index < cast->targets.size; index++) { + pm_node_stack_push(&node_stack, (pm_node_t *) cast->targets.nodes[index]); + } + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_MODULE_NODE: { + pm_module_node_t *cast = (pm_module_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->constant_path); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->body); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_MULTI_TARGET_NODE: { + pm_multi_target_node_t *cast = (pm_multi_target_node_t *) node; + for (size_t index = 0; index < cast->lefts.size; index++) { + pm_node_stack_push(&node_stack, (pm_node_t *) cast->lefts.nodes[index]); + } + pm_node_stack_push(&node_stack, (pm_node_t *) cast->rest); + for (size_t index = 0; index < cast->rights.size; index++) { + pm_node_stack_push(&node_stack, (pm_node_t *) cast->rights.nodes[index]); + } + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_MULTI_WRITE_NODE: { + pm_multi_write_node_t *cast = (pm_multi_write_node_t *) node; + for (size_t index = 0; index < cast->lefts.size; index++) { + pm_node_stack_push(&node_stack, (pm_node_t *) cast->lefts.nodes[index]); + } + pm_node_stack_push(&node_stack, (pm_node_t *) cast->rest); + for (size_t index = 0; index < cast->rights.size; index++) { + pm_node_stack_push(&node_stack, (pm_node_t *) cast->rights.nodes[index]); + } + pm_node_stack_push(&node_stack, (pm_node_t *) cast->value); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_NEXT_NODE: { + pm_next_node_t *cast = (pm_next_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->arguments); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_OPTIONAL_KEYWORD_PARAMETER_NODE: { + pm_optional_keyword_parameter_node_t *cast = (pm_optional_keyword_parameter_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->value); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_OPTIONAL_PARAMETER_NODE: { + pm_optional_parameter_node_t *cast = (pm_optional_parameter_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->value); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_OR_NODE: { + pm_or_node_t *cast = (pm_or_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->left); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->right); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_PARAMETERS_NODE: { + pm_parameters_node_t *cast = (pm_parameters_node_t *) node; + for (size_t index = 0; index < cast->requireds.size; index++) { + pm_node_stack_push(&node_stack, (pm_node_t *) cast->requireds.nodes[index]); + } + for (size_t index = 0; index < cast->optionals.size; index++) { + pm_node_stack_push(&node_stack, (pm_node_t *) cast->optionals.nodes[index]); + } + pm_node_stack_push(&node_stack, (pm_node_t *) cast->rest); + for (size_t index = 0; index < cast->posts.size; index++) { + pm_node_stack_push(&node_stack, (pm_node_t *) cast->posts.nodes[index]); + } + for (size_t index = 0; index < cast->keywords.size; index++) { + pm_node_stack_push(&node_stack, (pm_node_t *) cast->keywords.nodes[index]); + } + pm_node_stack_push(&node_stack, (pm_node_t *) cast->keyword_rest); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->block); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_PARENTHESES_NODE: { + pm_parentheses_node_t *cast = (pm_parentheses_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->body); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_PINNED_EXPRESSION_NODE: { + pm_pinned_expression_node_t *cast = (pm_pinned_expression_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->expression); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_PINNED_VARIABLE_NODE: { + pm_pinned_variable_node_t *cast = (pm_pinned_variable_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->variable); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_POST_EXECUTION_NODE: { + pm_post_execution_node_t *cast = (pm_post_execution_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->statements); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_PRE_EXECUTION_NODE: { + pm_pre_execution_node_t *cast = (pm_pre_execution_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->statements); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_PROGRAM_NODE: { + pm_program_node_t *cast = (pm_program_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->statements); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_RANGE_NODE: { + pm_range_node_t *cast = (pm_range_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->left); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->right); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_RESCUE_MODIFIER_NODE: { + pm_rescue_modifier_node_t *cast = (pm_rescue_modifier_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->expression); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->rescue_expression); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_RESCUE_NODE: { + pm_rescue_node_t *cast = (pm_rescue_node_t *) node; + for (size_t index = 0; index < cast->exceptions.size; index++) { + pm_node_stack_push(&node_stack, (pm_node_t *) cast->exceptions.nodes[index]); + } + pm_node_stack_push(&node_stack, (pm_node_t *) cast->reference); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->statements); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->subsequent); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_RETURN_NODE: { + pm_return_node_t *cast = (pm_return_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->arguments); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_SHAREABLE_CONSTANT_NODE: { + pm_shareable_constant_node_t *cast = (pm_shareable_constant_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->write); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_SINGLETON_CLASS_NODE: { + pm_singleton_class_node_t *cast = (pm_singleton_class_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->expression); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->body); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_SPLAT_NODE: { + pm_splat_node_t *cast = (pm_splat_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->expression); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_STATEMENTS_NODE: { + pm_statements_node_t *cast = (pm_statements_node_t *) node; + for (size_t index = 0; index < cast->body.size; index++) { + pm_node_stack_push(&node_stack, (pm_node_t *) cast->body.nodes[index]); + } + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_SUPER_NODE: { + pm_super_node_t *cast = (pm_super_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->arguments); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->block); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_UNDEF_NODE: { + pm_undef_node_t *cast = (pm_undef_node_t *) node; + for (size_t index = 0; index < cast->names.size; index++) { + pm_node_stack_push(&node_stack, (pm_node_t *) cast->names.nodes[index]); + } + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_UNLESS_NODE: { + pm_unless_node_t *cast = (pm_unless_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->predicate); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->statements); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->else_clause); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_UNTIL_NODE: { + pm_until_node_t *cast = (pm_until_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->predicate); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->statements); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_WHEN_NODE: { + pm_when_node_t *cast = (pm_when_node_t *) node; + for (size_t index = 0; index < cast->conditions.size; index++) { + pm_node_stack_push(&node_stack, (pm_node_t *) cast->conditions.nodes[index]); + } + pm_node_stack_push(&node_stack, (pm_node_t *) cast->statements); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_WHILE_NODE: { + pm_while_node_t *cast = (pm_while_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->predicate); + pm_node_stack_push(&node_stack, (pm_node_t *) cast->statements); + break; + } +#line 164 "prism/templates/ext/prism/api_node.c.erb" + case PM_YIELD_NODE: { + pm_yield_node_t *cast = (pm_yield_node_t *) node; + pm_node_stack_push(&node_stack, (pm_node_t *) cast->arguments); + break; + } + default: + break; + } +#line 184 "prism/templates/ext/prism/api_node.c.erb" + } else { + const pm_node_t *node = pm_node_stack_pop(&node_stack); + + switch (PM_NODE_TYPE(node)) { +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_ALIAS_GLOBAL_VARIABLE_NODE: { + pm_alias_global_variable_node_t *cast = (pm_alias_global_variable_node_t *) node; + VALUE argv[7]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // new_name +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // old_name +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = rb_ary_pop(value_stack); + + // keyword_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->keyword_loc.start, cast->keyword_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(7, argv, rb_cPrismAliasGlobalVariableNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_ALIAS_METHOD_NODE: { + pm_alias_method_node_t *cast = (pm_alias_method_node_t *) node; + VALUE argv[7]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // new_name +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // old_name +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = rb_ary_pop(value_stack); + + // keyword_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->keyword_loc.start, cast->keyword_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(7, argv, rb_cPrismAliasMethodNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_ALTERNATION_PATTERN_NODE: { + pm_alternation_pattern_node_t *cast = (pm_alternation_pattern_node_t *) node; + VALUE argv[7]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // left +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // right +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = rb_ary_pop(value_stack); + + // operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(7, argv, rb_cPrismAlternationPatternNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_AND_NODE: { + pm_and_node_t *cast = (pm_and_node_t *) node; + VALUE argv[7]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // left +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // right +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = rb_ary_pop(value_stack); + + // operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(7, argv, rb_cPrismAndNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_ARGUMENTS_NODE: { + pm_arguments_node_t *cast = (pm_arguments_node_t *) node; + VALUE argv[5]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // arguments +#line 216 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_new_capa(cast->arguments.size); + for (size_t index = 0; index < cast->arguments.size; index++) { + rb_ary_push(argv[4], rb_ary_pop(value_stack)); + } + if (freeze) rb_obj_freeze(argv[4]); + + VALUE value = rb_class_new_instance(5, argv, rb_cPrismArgumentsNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_ARRAY_NODE: { + pm_array_node_t *cast = (pm_array_node_t *) node; + VALUE argv[7]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // elements +#line 216 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_new_capa(cast->elements.size); + for (size_t index = 0; index < cast->elements.size; index++) { + rb_ary_push(argv[4], rb_ary_pop(value_stack)); + } + if (freeze) rb_obj_freeze(argv[4]); + + // opening_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = cast->opening_loc.start == NULL ? Qnil : pm_location_new(parser, cast->opening_loc.start, cast->opening_loc.end, source, freeze); + + // closing_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = cast->closing_loc.start == NULL ? Qnil : pm_location_new(parser, cast->closing_loc.start, cast->closing_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(7, argv, rb_cPrismArrayNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_ARRAY_PATTERN_NODE: { + pm_array_pattern_node_t *cast = (pm_array_pattern_node_t *) node; + VALUE argv[10]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // constant +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // requireds +#line 216 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = rb_ary_new_capa(cast->requireds.size); + for (size_t index = 0; index < cast->requireds.size; index++) { + rb_ary_push(argv[5], rb_ary_pop(value_stack)); + } + if (freeze) rb_obj_freeze(argv[5]); + + // rest +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = rb_ary_pop(value_stack); + + // posts +#line 216 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = rb_ary_new_capa(cast->posts.size); + for (size_t index = 0; index < cast->posts.size; index++) { + rb_ary_push(argv[7], rb_ary_pop(value_stack)); + } + if (freeze) rb_obj_freeze(argv[7]); + + // opening_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[8] = cast->opening_loc.start == NULL ? Qnil : pm_location_new(parser, cast->opening_loc.start, cast->opening_loc.end, source, freeze); + + // closing_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[9] = cast->closing_loc.start == NULL ? Qnil : pm_location_new(parser, cast->closing_loc.start, cast->closing_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(10, argv, rb_cPrismArrayPatternNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_ASSOC_NODE: { + pm_assoc_node_t *cast = (pm_assoc_node_t *) node; + VALUE argv[7]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // key +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // value +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = rb_ary_pop(value_stack); + + // operator_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = cast->operator_loc.start == NULL ? Qnil : pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(7, argv, rb_cPrismAssocNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_ASSOC_SPLAT_NODE: { + pm_assoc_splat_node_t *cast = (pm_assoc_splat_node_t *) node; + VALUE argv[6]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // value +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(6, argv, rb_cPrismAssocSplatNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_BACK_REFERENCE_READ_NODE: { + pm_back_reference_read_node_t *cast = (pm_back_reference_read_node_t *) node; + VALUE argv[5]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->name != 0); + argv[4] = RARRAY_AREF(constants, cast->name - 1); + + VALUE value = rb_class_new_instance(5, argv, rb_cPrismBackReferenceReadNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_BEGIN_NODE: { + pm_begin_node_t *cast = (pm_begin_node_t *) node; + VALUE argv[10]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // begin_keyword_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = cast->begin_keyword_loc.start == NULL ? Qnil : pm_location_new(parser, cast->begin_keyword_loc.start, cast->begin_keyword_loc.end, source, freeze); + + // statements +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = rb_ary_pop(value_stack); + + // rescue_clause +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = rb_ary_pop(value_stack); + + // else_clause +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = rb_ary_pop(value_stack); + + // ensure_clause +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[8] = rb_ary_pop(value_stack); + + // end_keyword_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[9] = cast->end_keyword_loc.start == NULL ? Qnil : pm_location_new(parser, cast->end_keyword_loc.start, cast->end_keyword_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(10, argv, rb_cPrismBeginNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_BLOCK_ARGUMENT_NODE: { + pm_block_argument_node_t *cast = (pm_block_argument_node_t *) node; + VALUE argv[6]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // expression +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(6, argv, rb_cPrismBlockArgumentNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_BLOCK_LOCAL_VARIABLE_NODE: { + pm_block_local_variable_node_t *cast = (pm_block_local_variable_node_t *) node; + VALUE argv[5]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->name != 0); + argv[4] = RARRAY_AREF(constants, cast->name - 1); + + VALUE value = rb_class_new_instance(5, argv, rb_cPrismBlockLocalVariableNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_BLOCK_NODE: { + pm_block_node_t *cast = (pm_block_node_t *) node; + VALUE argv[9]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // locals +#line 232 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_new_capa(cast->locals.size); + for (size_t index = 0; index < cast->locals.size; index++) { + assert(cast->locals.ids[index] != 0); + rb_ary_push(argv[4], RARRAY_AREF(constants, cast->locals.ids[index] - 1)); + } + if (freeze) rb_obj_freeze(argv[4]); + + // parameters +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = rb_ary_pop(value_stack); + + // body +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = rb_ary_pop(value_stack); + + // opening_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = pm_location_new(parser, cast->opening_loc.start, cast->opening_loc.end, source, freeze); + + // closing_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[8] = pm_location_new(parser, cast->closing_loc.start, cast->closing_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(9, argv, rb_cPrismBlockNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_BLOCK_PARAMETER_NODE: { + pm_block_parameter_node_t *cast = (pm_block_parameter_node_t *) node; + VALUE argv[7]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // name + argv[4] = cast->name == 0 ? Qnil : RARRAY_AREF(constants, cast->name - 1); + + // name_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = cast->name_loc.start == NULL ? Qnil : pm_location_new(parser, cast->name_loc.start, cast->name_loc.end, source, freeze); + + // operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(7, argv, rb_cPrismBlockParameterNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_BLOCK_PARAMETERS_NODE: { + pm_block_parameters_node_t *cast = (pm_block_parameters_node_t *) node; + VALUE argv[8]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // parameters +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // locals +#line 216 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = rb_ary_new_capa(cast->locals.size); + for (size_t index = 0; index < cast->locals.size; index++) { + rb_ary_push(argv[5], rb_ary_pop(value_stack)); + } + if (freeze) rb_obj_freeze(argv[5]); + + // opening_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = cast->opening_loc.start == NULL ? Qnil : pm_location_new(parser, cast->opening_loc.start, cast->opening_loc.end, source, freeze); + + // closing_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = cast->closing_loc.start == NULL ? Qnil : pm_location_new(parser, cast->closing_loc.start, cast->closing_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(8, argv, rb_cPrismBlockParametersNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_BREAK_NODE: { + pm_break_node_t *cast = (pm_break_node_t *) node; + VALUE argv[6]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // arguments +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // keyword_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->keyword_loc.start, cast->keyword_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(6, argv, rb_cPrismBreakNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_CALL_AND_WRITE_NODE: { + pm_call_and_write_node_t *cast = (pm_call_and_write_node_t *) node; + VALUE argv[11]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // receiver +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // call_operator_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = cast->call_operator_loc.start == NULL ? Qnil : pm_location_new(parser, cast->call_operator_loc.start, cast->call_operator_loc.end, source, freeze); + + // message_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = cast->message_loc.start == NULL ? Qnil : pm_location_new(parser, cast->message_loc.start, cast->message_loc.end, source, freeze); + + // read_name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->read_name != 0); + argv[7] = RARRAY_AREF(constants, cast->read_name - 1); + + // write_name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->write_name != 0); + argv[8] = RARRAY_AREF(constants, cast->write_name - 1); + + // operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[9] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + // value +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[10] = rb_ary_pop(value_stack); + + VALUE value = rb_class_new_instance(11, argv, rb_cPrismCallAndWriteNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_CALL_NODE: { + pm_call_node_t *cast = (pm_call_node_t *) node; + VALUE argv[13]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // receiver +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // call_operator_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = cast->call_operator_loc.start == NULL ? Qnil : pm_location_new(parser, cast->call_operator_loc.start, cast->call_operator_loc.end, source, freeze); + + // name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->name != 0); + argv[6] = RARRAY_AREF(constants, cast->name - 1); + + // message_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = cast->message_loc.start == NULL ? Qnil : pm_location_new(parser, cast->message_loc.start, cast->message_loc.end, source, freeze); + + // opening_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[8] = cast->opening_loc.start == NULL ? Qnil : pm_location_new(parser, cast->opening_loc.start, cast->opening_loc.end, source, freeze); + + // arguments +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[9] = rb_ary_pop(value_stack); + + // closing_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[10] = cast->closing_loc.start == NULL ? Qnil : pm_location_new(parser, cast->closing_loc.start, cast->closing_loc.end, source, freeze); + + // equal_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[11] = cast->equal_loc.start == NULL ? Qnil : pm_location_new(parser, cast->equal_loc.start, cast->equal_loc.end, source, freeze); + + // block +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[12] = rb_ary_pop(value_stack); + + VALUE value = rb_class_new_instance(13, argv, rb_cPrismCallNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_CALL_OPERATOR_WRITE_NODE: { + pm_call_operator_write_node_t *cast = (pm_call_operator_write_node_t *) node; + VALUE argv[12]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // receiver +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // call_operator_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = cast->call_operator_loc.start == NULL ? Qnil : pm_location_new(parser, cast->call_operator_loc.start, cast->call_operator_loc.end, source, freeze); + + // message_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = cast->message_loc.start == NULL ? Qnil : pm_location_new(parser, cast->message_loc.start, cast->message_loc.end, source, freeze); + + // read_name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->read_name != 0); + argv[7] = RARRAY_AREF(constants, cast->read_name - 1); + + // write_name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->write_name != 0); + argv[8] = RARRAY_AREF(constants, cast->write_name - 1); + + // binary_operator +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->binary_operator != 0); + argv[9] = RARRAY_AREF(constants, cast->binary_operator - 1); + + // binary_operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[10] = pm_location_new(parser, cast->binary_operator_loc.start, cast->binary_operator_loc.end, source, freeze); + + // value +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[11] = rb_ary_pop(value_stack); + + VALUE value = rb_class_new_instance(12, argv, rb_cPrismCallOperatorWriteNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_CALL_OR_WRITE_NODE: { + pm_call_or_write_node_t *cast = (pm_call_or_write_node_t *) node; + VALUE argv[11]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // receiver +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // call_operator_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = cast->call_operator_loc.start == NULL ? Qnil : pm_location_new(parser, cast->call_operator_loc.start, cast->call_operator_loc.end, source, freeze); + + // message_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = cast->message_loc.start == NULL ? Qnil : pm_location_new(parser, cast->message_loc.start, cast->message_loc.end, source, freeze); + + // read_name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->read_name != 0); + argv[7] = RARRAY_AREF(constants, cast->read_name - 1); + + // write_name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->write_name != 0); + argv[8] = RARRAY_AREF(constants, cast->write_name - 1); + + // operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[9] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + // value +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[10] = rb_ary_pop(value_stack); + + VALUE value = rb_class_new_instance(11, argv, rb_cPrismCallOrWriteNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_CALL_TARGET_NODE: { + pm_call_target_node_t *cast = (pm_call_target_node_t *) node; + VALUE argv[8]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // receiver +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // call_operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->call_operator_loc.start, cast->call_operator_loc.end, source, freeze); + + // name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->name != 0); + argv[6] = RARRAY_AREF(constants, cast->name - 1); + + // message_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = pm_location_new(parser, cast->message_loc.start, cast->message_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(8, argv, rb_cPrismCallTargetNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_CAPTURE_PATTERN_NODE: { + pm_capture_pattern_node_t *cast = (pm_capture_pattern_node_t *) node; + VALUE argv[7]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // value +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // target +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = rb_ary_pop(value_stack); + + // operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(7, argv, rb_cPrismCapturePatternNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_CASE_MATCH_NODE: { + pm_case_match_node_t *cast = (pm_case_match_node_t *) node; + VALUE argv[9]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // predicate +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // conditions +#line 216 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = rb_ary_new_capa(cast->conditions.size); + for (size_t index = 0; index < cast->conditions.size; index++) { + rb_ary_push(argv[5], rb_ary_pop(value_stack)); + } + if (freeze) rb_obj_freeze(argv[5]); + + // else_clause +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = rb_ary_pop(value_stack); + + // case_keyword_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = pm_location_new(parser, cast->case_keyword_loc.start, cast->case_keyword_loc.end, source, freeze); + + // end_keyword_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[8] = pm_location_new(parser, cast->end_keyword_loc.start, cast->end_keyword_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(9, argv, rb_cPrismCaseMatchNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_CASE_NODE: { + pm_case_node_t *cast = (pm_case_node_t *) node; + VALUE argv[9]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // predicate +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // conditions +#line 216 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = rb_ary_new_capa(cast->conditions.size); + for (size_t index = 0; index < cast->conditions.size; index++) { + rb_ary_push(argv[5], rb_ary_pop(value_stack)); + } + if (freeze) rb_obj_freeze(argv[5]); + + // else_clause +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = rb_ary_pop(value_stack); + + // case_keyword_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = pm_location_new(parser, cast->case_keyword_loc.start, cast->case_keyword_loc.end, source, freeze); + + // end_keyword_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[8] = pm_location_new(parser, cast->end_keyword_loc.start, cast->end_keyword_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(9, argv, rb_cPrismCaseNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_CLASS_NODE: { + pm_class_node_t *cast = (pm_class_node_t *) node; + VALUE argv[12]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // locals +#line 232 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_new_capa(cast->locals.size); + for (size_t index = 0; index < cast->locals.size; index++) { + assert(cast->locals.ids[index] != 0); + rb_ary_push(argv[4], RARRAY_AREF(constants, cast->locals.ids[index] - 1)); + } + if (freeze) rb_obj_freeze(argv[4]); + + // class_keyword_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->class_keyword_loc.start, cast->class_keyword_loc.end, source, freeze); + + // constant_path +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = rb_ary_pop(value_stack); + + // inheritance_operator_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = cast->inheritance_operator_loc.start == NULL ? Qnil : pm_location_new(parser, cast->inheritance_operator_loc.start, cast->inheritance_operator_loc.end, source, freeze); + + // superclass +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[8] = rb_ary_pop(value_stack); + + // body +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[9] = rb_ary_pop(value_stack); + + // end_keyword_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[10] = pm_location_new(parser, cast->end_keyword_loc.start, cast->end_keyword_loc.end, source, freeze); + + // name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->name != 0); + argv[11] = RARRAY_AREF(constants, cast->name - 1); + + VALUE value = rb_class_new_instance(12, argv, rb_cPrismClassNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_CLASS_VARIABLE_AND_WRITE_NODE: { + pm_class_variable_and_write_node_t *cast = (pm_class_variable_and_write_node_t *) node; + VALUE argv[8]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->name != 0); + argv[4] = RARRAY_AREF(constants, cast->name - 1); + + // name_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->name_loc.start, cast->name_loc.end, source, freeze); + + // operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + // value +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = rb_ary_pop(value_stack); + + VALUE value = rb_class_new_instance(8, argv, rb_cPrismClassVariableAndWriteNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE: { + pm_class_variable_operator_write_node_t *cast = (pm_class_variable_operator_write_node_t *) node; + VALUE argv[9]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->name != 0); + argv[4] = RARRAY_AREF(constants, cast->name - 1); + + // name_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->name_loc.start, cast->name_loc.end, source, freeze); + + // binary_operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->binary_operator_loc.start, cast->binary_operator_loc.end, source, freeze); + + // value +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = rb_ary_pop(value_stack); + + // binary_operator +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->binary_operator != 0); + argv[8] = RARRAY_AREF(constants, cast->binary_operator - 1); + + VALUE value = rb_class_new_instance(9, argv, rb_cPrismClassVariableOperatorWriteNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_CLASS_VARIABLE_OR_WRITE_NODE: { + pm_class_variable_or_write_node_t *cast = (pm_class_variable_or_write_node_t *) node; + VALUE argv[8]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->name != 0); + argv[4] = RARRAY_AREF(constants, cast->name - 1); + + // name_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->name_loc.start, cast->name_loc.end, source, freeze); + + // operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + // value +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = rb_ary_pop(value_stack); + + VALUE value = rb_class_new_instance(8, argv, rb_cPrismClassVariableOrWriteNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_CLASS_VARIABLE_READ_NODE: { + pm_class_variable_read_node_t *cast = (pm_class_variable_read_node_t *) node; + VALUE argv[5]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->name != 0); + argv[4] = RARRAY_AREF(constants, cast->name - 1); + + VALUE value = rb_class_new_instance(5, argv, rb_cPrismClassVariableReadNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_CLASS_VARIABLE_TARGET_NODE: { + pm_class_variable_target_node_t *cast = (pm_class_variable_target_node_t *) node; + VALUE argv[5]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->name != 0); + argv[4] = RARRAY_AREF(constants, cast->name - 1); + + VALUE value = rb_class_new_instance(5, argv, rb_cPrismClassVariableTargetNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_CLASS_VARIABLE_WRITE_NODE: { + pm_class_variable_write_node_t *cast = (pm_class_variable_write_node_t *) node; + VALUE argv[8]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->name != 0); + argv[4] = RARRAY_AREF(constants, cast->name - 1); + + // name_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->name_loc.start, cast->name_loc.end, source, freeze); + + // value +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = rb_ary_pop(value_stack); + + // operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(8, argv, rb_cPrismClassVariableWriteNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_CONSTANT_AND_WRITE_NODE: { + pm_constant_and_write_node_t *cast = (pm_constant_and_write_node_t *) node; + VALUE argv[8]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->name != 0); + argv[4] = RARRAY_AREF(constants, cast->name - 1); + + // name_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->name_loc.start, cast->name_loc.end, source, freeze); + + // operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + // value +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = rb_ary_pop(value_stack); + + VALUE value = rb_class_new_instance(8, argv, rb_cPrismConstantAndWriteNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_CONSTANT_OPERATOR_WRITE_NODE: { + pm_constant_operator_write_node_t *cast = (pm_constant_operator_write_node_t *) node; + VALUE argv[9]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->name != 0); + argv[4] = RARRAY_AREF(constants, cast->name - 1); + + // name_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->name_loc.start, cast->name_loc.end, source, freeze); + + // binary_operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->binary_operator_loc.start, cast->binary_operator_loc.end, source, freeze); + + // value +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = rb_ary_pop(value_stack); + + // binary_operator +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->binary_operator != 0); + argv[8] = RARRAY_AREF(constants, cast->binary_operator - 1); + + VALUE value = rb_class_new_instance(9, argv, rb_cPrismConstantOperatorWriteNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_CONSTANT_OR_WRITE_NODE: { + pm_constant_or_write_node_t *cast = (pm_constant_or_write_node_t *) node; + VALUE argv[8]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->name != 0); + argv[4] = RARRAY_AREF(constants, cast->name - 1); + + // name_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->name_loc.start, cast->name_loc.end, source, freeze); + + // operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + // value +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = rb_ary_pop(value_stack); + + VALUE value = rb_class_new_instance(8, argv, rb_cPrismConstantOrWriteNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_CONSTANT_PATH_AND_WRITE_NODE: { + pm_constant_path_and_write_node_t *cast = (pm_constant_path_and_write_node_t *) node; + VALUE argv[7]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // target +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + // value +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = rb_ary_pop(value_stack); + + VALUE value = rb_class_new_instance(7, argv, rb_cPrismConstantPathAndWriteNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_CONSTANT_PATH_NODE: { + pm_constant_path_node_t *cast = (pm_constant_path_node_t *) node; + VALUE argv[8]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // parent +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // name + argv[5] = cast->name == 0 ? Qnil : RARRAY_AREF(constants, cast->name - 1); + + // delimiter_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->delimiter_loc.start, cast->delimiter_loc.end, source, freeze); + + // name_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = pm_location_new(parser, cast->name_loc.start, cast->name_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(8, argv, rb_cPrismConstantPathNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_CONSTANT_PATH_OPERATOR_WRITE_NODE: { + pm_constant_path_operator_write_node_t *cast = (pm_constant_path_operator_write_node_t *) node; + VALUE argv[8]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // target +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // binary_operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->binary_operator_loc.start, cast->binary_operator_loc.end, source, freeze); + + // value +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = rb_ary_pop(value_stack); + + // binary_operator +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->binary_operator != 0); + argv[7] = RARRAY_AREF(constants, cast->binary_operator - 1); + + VALUE value = rb_class_new_instance(8, argv, rb_cPrismConstantPathOperatorWriteNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_CONSTANT_PATH_OR_WRITE_NODE: { + pm_constant_path_or_write_node_t *cast = (pm_constant_path_or_write_node_t *) node; + VALUE argv[7]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // target +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + // value +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = rb_ary_pop(value_stack); + + VALUE value = rb_class_new_instance(7, argv, rb_cPrismConstantPathOrWriteNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_CONSTANT_PATH_TARGET_NODE: { + pm_constant_path_target_node_t *cast = (pm_constant_path_target_node_t *) node; + VALUE argv[8]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // parent +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // name + argv[5] = cast->name == 0 ? Qnil : RARRAY_AREF(constants, cast->name - 1); + + // delimiter_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->delimiter_loc.start, cast->delimiter_loc.end, source, freeze); + + // name_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = pm_location_new(parser, cast->name_loc.start, cast->name_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(8, argv, rb_cPrismConstantPathTargetNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_CONSTANT_PATH_WRITE_NODE: { + pm_constant_path_write_node_t *cast = (pm_constant_path_write_node_t *) node; + VALUE argv[7]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // target +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + // value +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = rb_ary_pop(value_stack); + + VALUE value = rb_class_new_instance(7, argv, rb_cPrismConstantPathWriteNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_CONSTANT_READ_NODE: { + pm_constant_read_node_t *cast = (pm_constant_read_node_t *) node; + VALUE argv[5]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->name != 0); + argv[4] = RARRAY_AREF(constants, cast->name - 1); + + VALUE value = rb_class_new_instance(5, argv, rb_cPrismConstantReadNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_CONSTANT_TARGET_NODE: { + pm_constant_target_node_t *cast = (pm_constant_target_node_t *) node; + VALUE argv[5]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->name != 0); + argv[4] = RARRAY_AREF(constants, cast->name - 1); + + VALUE value = rb_class_new_instance(5, argv, rb_cPrismConstantTargetNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_CONSTANT_WRITE_NODE: { + pm_constant_write_node_t *cast = (pm_constant_write_node_t *) node; + VALUE argv[8]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->name != 0); + argv[4] = RARRAY_AREF(constants, cast->name - 1); + + // name_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->name_loc.start, cast->name_loc.end, source, freeze); + + // value +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = rb_ary_pop(value_stack); + + // operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(8, argv, rb_cPrismConstantWriteNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_DEF_NODE: { + pm_def_node_t *cast = (pm_def_node_t *) node; + VALUE argv[16]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->name != 0); + argv[4] = RARRAY_AREF(constants, cast->name - 1); + + // name_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->name_loc.start, cast->name_loc.end, source, freeze); + + // receiver +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = rb_ary_pop(value_stack); + + // parameters +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = rb_ary_pop(value_stack); + + // body +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[8] = rb_ary_pop(value_stack); + + // locals +#line 232 "prism/templates/ext/prism/api_node.c.erb" + argv[9] = rb_ary_new_capa(cast->locals.size); + for (size_t index = 0; index < cast->locals.size; index++) { + assert(cast->locals.ids[index] != 0); + rb_ary_push(argv[9], RARRAY_AREF(constants, cast->locals.ids[index] - 1)); + } + if (freeze) rb_obj_freeze(argv[9]); + + // def_keyword_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[10] = pm_location_new(parser, cast->def_keyword_loc.start, cast->def_keyword_loc.end, source, freeze); + + // operator_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[11] = cast->operator_loc.start == NULL ? Qnil : pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + // lparen_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[12] = cast->lparen_loc.start == NULL ? Qnil : pm_location_new(parser, cast->lparen_loc.start, cast->lparen_loc.end, source, freeze); + + // rparen_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[13] = cast->rparen_loc.start == NULL ? Qnil : pm_location_new(parser, cast->rparen_loc.start, cast->rparen_loc.end, source, freeze); + + // equal_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[14] = cast->equal_loc.start == NULL ? Qnil : pm_location_new(parser, cast->equal_loc.start, cast->equal_loc.end, source, freeze); + + // end_keyword_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[15] = cast->end_keyword_loc.start == NULL ? Qnil : pm_location_new(parser, cast->end_keyword_loc.start, cast->end_keyword_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(16, argv, rb_cPrismDefNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_DEFINED_NODE: { + pm_defined_node_t *cast = (pm_defined_node_t *) node; + VALUE argv[8]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // lparen_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = cast->lparen_loc.start == NULL ? Qnil : pm_location_new(parser, cast->lparen_loc.start, cast->lparen_loc.end, source, freeze); + + // value +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = rb_ary_pop(value_stack); + + // rparen_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = cast->rparen_loc.start == NULL ? Qnil : pm_location_new(parser, cast->rparen_loc.start, cast->rparen_loc.end, source, freeze); + + // keyword_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = pm_location_new(parser, cast->keyword_loc.start, cast->keyword_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(8, argv, rb_cPrismDefinedNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_ELSE_NODE: { + pm_else_node_t *cast = (pm_else_node_t *) node; + VALUE argv[7]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // else_keyword_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = pm_location_new(parser, cast->else_keyword_loc.start, cast->else_keyword_loc.end, source, freeze); + + // statements +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = rb_ary_pop(value_stack); + + // end_keyword_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = cast->end_keyword_loc.start == NULL ? Qnil : pm_location_new(parser, cast->end_keyword_loc.start, cast->end_keyword_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(7, argv, rb_cPrismElseNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_EMBEDDED_STATEMENTS_NODE: { + pm_embedded_statements_node_t *cast = (pm_embedded_statements_node_t *) node; + VALUE argv[7]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // opening_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = pm_location_new(parser, cast->opening_loc.start, cast->opening_loc.end, source, freeze); + + // statements +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = rb_ary_pop(value_stack); + + // closing_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->closing_loc.start, cast->closing_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(7, argv, rb_cPrismEmbeddedStatementsNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_EMBEDDED_VARIABLE_NODE: { + pm_embedded_variable_node_t *cast = (pm_embedded_variable_node_t *) node; + VALUE argv[6]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + // variable +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = rb_ary_pop(value_stack); + + VALUE value = rb_class_new_instance(6, argv, rb_cPrismEmbeddedVariableNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_ENSURE_NODE: { + pm_ensure_node_t *cast = (pm_ensure_node_t *) node; + VALUE argv[7]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // ensure_keyword_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = pm_location_new(parser, cast->ensure_keyword_loc.start, cast->ensure_keyword_loc.end, source, freeze); + + // statements +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = rb_ary_pop(value_stack); + + // end_keyword_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->end_keyword_loc.start, cast->end_keyword_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(7, argv, rb_cPrismEnsureNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_FALSE_NODE: { + VALUE argv[4]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + VALUE value = rb_class_new_instance(4, argv, rb_cPrismFalseNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_FIND_PATTERN_NODE: { + pm_find_pattern_node_t *cast = (pm_find_pattern_node_t *) node; + VALUE argv[10]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // constant +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // left +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = rb_ary_pop(value_stack); + + // requireds +#line 216 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = rb_ary_new_capa(cast->requireds.size); + for (size_t index = 0; index < cast->requireds.size; index++) { + rb_ary_push(argv[6], rb_ary_pop(value_stack)); + } + if (freeze) rb_obj_freeze(argv[6]); + + // right +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = rb_ary_pop(value_stack); + + // opening_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[8] = cast->opening_loc.start == NULL ? Qnil : pm_location_new(parser, cast->opening_loc.start, cast->opening_loc.end, source, freeze); + + // closing_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[9] = cast->closing_loc.start == NULL ? Qnil : pm_location_new(parser, cast->closing_loc.start, cast->closing_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(10, argv, rb_cPrismFindPatternNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_FLIP_FLOP_NODE: { + pm_flip_flop_node_t *cast = (pm_flip_flop_node_t *) node; + VALUE argv[7]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // left +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // right +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = rb_ary_pop(value_stack); + + // operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(7, argv, rb_cPrismFlipFlopNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_FLOAT_NODE: { + pm_float_node_t *cast = (pm_float_node_t *) node; + VALUE argv[5]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // value +#line 255 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = DBL2NUM(cast->value); + + VALUE value = rb_class_new_instance(5, argv, rb_cPrismFloatNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_FOR_NODE: { + pm_for_node_t *cast = (pm_for_node_t *) node; + VALUE argv[11]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // index +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // collection +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = rb_ary_pop(value_stack); + + // statements +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = rb_ary_pop(value_stack); + + // for_keyword_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = pm_location_new(parser, cast->for_keyword_loc.start, cast->for_keyword_loc.end, source, freeze); + + // in_keyword_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[8] = pm_location_new(parser, cast->in_keyword_loc.start, cast->in_keyword_loc.end, source, freeze); + + // do_keyword_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[9] = cast->do_keyword_loc.start == NULL ? Qnil : pm_location_new(parser, cast->do_keyword_loc.start, cast->do_keyword_loc.end, source, freeze); + + // end_keyword_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[10] = pm_location_new(parser, cast->end_keyword_loc.start, cast->end_keyword_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(11, argv, rb_cPrismForNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_FORWARDING_ARGUMENTS_NODE: { + VALUE argv[4]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + VALUE value = rb_class_new_instance(4, argv, rb_cPrismForwardingArgumentsNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_FORWARDING_PARAMETER_NODE: { + VALUE argv[4]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + VALUE value = rb_class_new_instance(4, argv, rb_cPrismForwardingParameterNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_FORWARDING_SUPER_NODE: { + VALUE argv[5]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // block +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + VALUE value = rb_class_new_instance(5, argv, rb_cPrismForwardingSuperNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_GLOBAL_VARIABLE_AND_WRITE_NODE: { + pm_global_variable_and_write_node_t *cast = (pm_global_variable_and_write_node_t *) node; + VALUE argv[8]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->name != 0); + argv[4] = RARRAY_AREF(constants, cast->name - 1); + + // name_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->name_loc.start, cast->name_loc.end, source, freeze); + + // operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + // value +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = rb_ary_pop(value_stack); + + VALUE value = rb_class_new_instance(8, argv, rb_cPrismGlobalVariableAndWriteNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE: { + pm_global_variable_operator_write_node_t *cast = (pm_global_variable_operator_write_node_t *) node; + VALUE argv[9]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->name != 0); + argv[4] = RARRAY_AREF(constants, cast->name - 1); + + // name_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->name_loc.start, cast->name_loc.end, source, freeze); + + // binary_operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->binary_operator_loc.start, cast->binary_operator_loc.end, source, freeze); + + // value +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = rb_ary_pop(value_stack); + + // binary_operator +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->binary_operator != 0); + argv[8] = RARRAY_AREF(constants, cast->binary_operator - 1); + + VALUE value = rb_class_new_instance(9, argv, rb_cPrismGlobalVariableOperatorWriteNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_GLOBAL_VARIABLE_OR_WRITE_NODE: { + pm_global_variable_or_write_node_t *cast = (pm_global_variable_or_write_node_t *) node; + VALUE argv[8]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->name != 0); + argv[4] = RARRAY_AREF(constants, cast->name - 1); + + // name_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->name_loc.start, cast->name_loc.end, source, freeze); + + // operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + // value +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = rb_ary_pop(value_stack); + + VALUE value = rb_class_new_instance(8, argv, rb_cPrismGlobalVariableOrWriteNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_GLOBAL_VARIABLE_READ_NODE: { + pm_global_variable_read_node_t *cast = (pm_global_variable_read_node_t *) node; + VALUE argv[5]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->name != 0); + argv[4] = RARRAY_AREF(constants, cast->name - 1); + + VALUE value = rb_class_new_instance(5, argv, rb_cPrismGlobalVariableReadNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_GLOBAL_VARIABLE_TARGET_NODE: { + pm_global_variable_target_node_t *cast = (pm_global_variable_target_node_t *) node; + VALUE argv[5]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->name != 0); + argv[4] = RARRAY_AREF(constants, cast->name - 1); + + VALUE value = rb_class_new_instance(5, argv, rb_cPrismGlobalVariableTargetNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_GLOBAL_VARIABLE_WRITE_NODE: { + pm_global_variable_write_node_t *cast = (pm_global_variable_write_node_t *) node; + VALUE argv[8]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->name != 0); + argv[4] = RARRAY_AREF(constants, cast->name - 1); + + // name_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->name_loc.start, cast->name_loc.end, source, freeze); + + // value +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = rb_ary_pop(value_stack); + + // operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(8, argv, rb_cPrismGlobalVariableWriteNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_HASH_NODE: { + pm_hash_node_t *cast = (pm_hash_node_t *) node; + VALUE argv[7]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // opening_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = pm_location_new(parser, cast->opening_loc.start, cast->opening_loc.end, source, freeze); + + // elements +#line 216 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = rb_ary_new_capa(cast->elements.size); + for (size_t index = 0; index < cast->elements.size; index++) { + rb_ary_push(argv[5], rb_ary_pop(value_stack)); + } + if (freeze) rb_obj_freeze(argv[5]); + + // closing_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->closing_loc.start, cast->closing_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(7, argv, rb_cPrismHashNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_HASH_PATTERN_NODE: { + pm_hash_pattern_node_t *cast = (pm_hash_pattern_node_t *) node; + VALUE argv[9]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // constant +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // elements +#line 216 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = rb_ary_new_capa(cast->elements.size); + for (size_t index = 0; index < cast->elements.size; index++) { + rb_ary_push(argv[5], rb_ary_pop(value_stack)); + } + if (freeze) rb_obj_freeze(argv[5]); + + // rest +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = rb_ary_pop(value_stack); + + // opening_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = cast->opening_loc.start == NULL ? Qnil : pm_location_new(parser, cast->opening_loc.start, cast->opening_loc.end, source, freeze); + + // closing_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[8] = cast->closing_loc.start == NULL ? Qnil : pm_location_new(parser, cast->closing_loc.start, cast->closing_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(9, argv, rb_cPrismHashPatternNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_IF_NODE: { + pm_if_node_t *cast = (pm_if_node_t *) node; + VALUE argv[10]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // if_keyword_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = cast->if_keyword_loc.start == NULL ? Qnil : pm_location_new(parser, cast->if_keyword_loc.start, cast->if_keyword_loc.end, source, freeze); + + // predicate +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = rb_ary_pop(value_stack); + + // then_keyword_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = cast->then_keyword_loc.start == NULL ? Qnil : pm_location_new(parser, cast->then_keyword_loc.start, cast->then_keyword_loc.end, source, freeze); + + // statements +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = rb_ary_pop(value_stack); + + // subsequent +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[8] = rb_ary_pop(value_stack); + + // end_keyword_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[9] = cast->end_keyword_loc.start == NULL ? Qnil : pm_location_new(parser, cast->end_keyword_loc.start, cast->end_keyword_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(10, argv, rb_cPrismIfNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_IMAGINARY_NODE: { + VALUE argv[5]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // numeric +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + VALUE value = rb_class_new_instance(5, argv, rb_cPrismImaginaryNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_IMPLICIT_NODE: { + VALUE argv[5]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // value +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + VALUE value = rb_class_new_instance(5, argv, rb_cPrismImplicitNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_IMPLICIT_REST_NODE: { + VALUE argv[4]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + VALUE value = rb_class_new_instance(4, argv, rb_cPrismImplicitRestNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_IN_NODE: { + pm_in_node_t *cast = (pm_in_node_t *) node; + VALUE argv[8]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // pattern +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // statements +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = rb_ary_pop(value_stack); + + // in_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->in_loc.start, cast->in_loc.end, source, freeze); + + // then_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = cast->then_loc.start == NULL ? Qnil : pm_location_new(parser, cast->then_loc.start, cast->then_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(8, argv, rb_cPrismInNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_INDEX_AND_WRITE_NODE: { + pm_index_and_write_node_t *cast = (pm_index_and_write_node_t *) node; + VALUE argv[12]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // receiver +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // call_operator_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = cast->call_operator_loc.start == NULL ? Qnil : pm_location_new(parser, cast->call_operator_loc.start, cast->call_operator_loc.end, source, freeze); + + // opening_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->opening_loc.start, cast->opening_loc.end, source, freeze); + + // arguments +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = rb_ary_pop(value_stack); + + // closing_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[8] = pm_location_new(parser, cast->closing_loc.start, cast->closing_loc.end, source, freeze); + + // block +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[9] = rb_ary_pop(value_stack); + + // operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[10] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + // value +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[11] = rb_ary_pop(value_stack); + + VALUE value = rb_class_new_instance(12, argv, rb_cPrismIndexAndWriteNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_INDEX_OPERATOR_WRITE_NODE: { + pm_index_operator_write_node_t *cast = (pm_index_operator_write_node_t *) node; + VALUE argv[13]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // receiver +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // call_operator_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = cast->call_operator_loc.start == NULL ? Qnil : pm_location_new(parser, cast->call_operator_loc.start, cast->call_operator_loc.end, source, freeze); + + // opening_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->opening_loc.start, cast->opening_loc.end, source, freeze); + + // arguments +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = rb_ary_pop(value_stack); + + // closing_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[8] = pm_location_new(parser, cast->closing_loc.start, cast->closing_loc.end, source, freeze); + + // block +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[9] = rb_ary_pop(value_stack); + + // binary_operator +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->binary_operator != 0); + argv[10] = RARRAY_AREF(constants, cast->binary_operator - 1); + + // binary_operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[11] = pm_location_new(parser, cast->binary_operator_loc.start, cast->binary_operator_loc.end, source, freeze); + + // value +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[12] = rb_ary_pop(value_stack); + + VALUE value = rb_class_new_instance(13, argv, rb_cPrismIndexOperatorWriteNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_INDEX_OR_WRITE_NODE: { + pm_index_or_write_node_t *cast = (pm_index_or_write_node_t *) node; + VALUE argv[12]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // receiver +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // call_operator_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = cast->call_operator_loc.start == NULL ? Qnil : pm_location_new(parser, cast->call_operator_loc.start, cast->call_operator_loc.end, source, freeze); + + // opening_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->opening_loc.start, cast->opening_loc.end, source, freeze); + + // arguments +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = rb_ary_pop(value_stack); + + // closing_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[8] = pm_location_new(parser, cast->closing_loc.start, cast->closing_loc.end, source, freeze); + + // block +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[9] = rb_ary_pop(value_stack); + + // operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[10] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + // value +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[11] = rb_ary_pop(value_stack); + + VALUE value = rb_class_new_instance(12, argv, rb_cPrismIndexOrWriteNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_INDEX_TARGET_NODE: { + pm_index_target_node_t *cast = (pm_index_target_node_t *) node; + VALUE argv[9]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // receiver +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // opening_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->opening_loc.start, cast->opening_loc.end, source, freeze); + + // arguments +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = rb_ary_pop(value_stack); + + // closing_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = pm_location_new(parser, cast->closing_loc.start, cast->closing_loc.end, source, freeze); + + // block +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[8] = rb_ary_pop(value_stack); + + VALUE value = rb_class_new_instance(9, argv, rb_cPrismIndexTargetNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_INSTANCE_VARIABLE_AND_WRITE_NODE: { + pm_instance_variable_and_write_node_t *cast = (pm_instance_variable_and_write_node_t *) node; + VALUE argv[8]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->name != 0); + argv[4] = RARRAY_AREF(constants, cast->name - 1); + + // name_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->name_loc.start, cast->name_loc.end, source, freeze); + + // operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + // value +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = rb_ary_pop(value_stack); + + VALUE value = rb_class_new_instance(8, argv, rb_cPrismInstanceVariableAndWriteNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE: { + pm_instance_variable_operator_write_node_t *cast = (pm_instance_variable_operator_write_node_t *) node; + VALUE argv[9]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->name != 0); + argv[4] = RARRAY_AREF(constants, cast->name - 1); + + // name_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->name_loc.start, cast->name_loc.end, source, freeze); + + // binary_operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->binary_operator_loc.start, cast->binary_operator_loc.end, source, freeze); + + // value +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = rb_ary_pop(value_stack); + + // binary_operator +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->binary_operator != 0); + argv[8] = RARRAY_AREF(constants, cast->binary_operator - 1); + + VALUE value = rb_class_new_instance(9, argv, rb_cPrismInstanceVariableOperatorWriteNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_INSTANCE_VARIABLE_OR_WRITE_NODE: { + pm_instance_variable_or_write_node_t *cast = (pm_instance_variable_or_write_node_t *) node; + VALUE argv[8]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->name != 0); + argv[4] = RARRAY_AREF(constants, cast->name - 1); + + // name_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->name_loc.start, cast->name_loc.end, source, freeze); + + // operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + // value +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = rb_ary_pop(value_stack); + + VALUE value = rb_class_new_instance(8, argv, rb_cPrismInstanceVariableOrWriteNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_INSTANCE_VARIABLE_READ_NODE: { + pm_instance_variable_read_node_t *cast = (pm_instance_variable_read_node_t *) node; + VALUE argv[5]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->name != 0); + argv[4] = RARRAY_AREF(constants, cast->name - 1); + + VALUE value = rb_class_new_instance(5, argv, rb_cPrismInstanceVariableReadNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_INSTANCE_VARIABLE_TARGET_NODE: { + pm_instance_variable_target_node_t *cast = (pm_instance_variable_target_node_t *) node; + VALUE argv[5]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->name != 0); + argv[4] = RARRAY_AREF(constants, cast->name - 1); + + VALUE value = rb_class_new_instance(5, argv, rb_cPrismInstanceVariableTargetNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_INSTANCE_VARIABLE_WRITE_NODE: { + pm_instance_variable_write_node_t *cast = (pm_instance_variable_write_node_t *) node; + VALUE argv[8]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->name != 0); + argv[4] = RARRAY_AREF(constants, cast->name - 1); + + // name_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->name_loc.start, cast->name_loc.end, source, freeze); + + // value +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = rb_ary_pop(value_stack); + + // operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(8, argv, rb_cPrismInstanceVariableWriteNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_INTEGER_NODE: { + pm_integer_node_t *cast = (pm_integer_node_t *) node; + VALUE argv[5]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // value +#line 252 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = pm_integer_new(&cast->value); + + VALUE value = rb_class_new_instance(5, argv, rb_cPrismIntegerNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_INTERPOLATED_MATCH_LAST_LINE_NODE: { + pm_interpolated_match_last_line_node_t *cast = (pm_interpolated_match_last_line_node_t *) node; + VALUE argv[7]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // opening_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = pm_location_new(parser, cast->opening_loc.start, cast->opening_loc.end, source, freeze); + + // parts +#line 216 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = rb_ary_new_capa(cast->parts.size); + for (size_t index = 0; index < cast->parts.size; index++) { + rb_ary_push(argv[5], rb_ary_pop(value_stack)); + } + if (freeze) rb_obj_freeze(argv[5]); + + // closing_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->closing_loc.start, cast->closing_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(7, argv, rb_cPrismInterpolatedMatchLastLineNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: { + pm_interpolated_regular_expression_node_t *cast = (pm_interpolated_regular_expression_node_t *) node; + VALUE argv[7]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // opening_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = pm_location_new(parser, cast->opening_loc.start, cast->opening_loc.end, source, freeze); + + // parts +#line 216 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = rb_ary_new_capa(cast->parts.size); + for (size_t index = 0; index < cast->parts.size; index++) { + rb_ary_push(argv[5], rb_ary_pop(value_stack)); + } + if (freeze) rb_obj_freeze(argv[5]); + + // closing_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->closing_loc.start, cast->closing_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(7, argv, rb_cPrismInterpolatedRegularExpressionNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_INTERPOLATED_STRING_NODE: { + pm_interpolated_string_node_t *cast = (pm_interpolated_string_node_t *) node; + VALUE argv[7]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // opening_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = cast->opening_loc.start == NULL ? Qnil : pm_location_new(parser, cast->opening_loc.start, cast->opening_loc.end, source, freeze); + + // parts +#line 216 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = rb_ary_new_capa(cast->parts.size); + for (size_t index = 0; index < cast->parts.size; index++) { + rb_ary_push(argv[5], rb_ary_pop(value_stack)); + } + if (freeze) rb_obj_freeze(argv[5]); + + // closing_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = cast->closing_loc.start == NULL ? Qnil : pm_location_new(parser, cast->closing_loc.start, cast->closing_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(7, argv, rb_cPrismInterpolatedStringNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_INTERPOLATED_SYMBOL_NODE: { + pm_interpolated_symbol_node_t *cast = (pm_interpolated_symbol_node_t *) node; + VALUE argv[7]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // opening_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = cast->opening_loc.start == NULL ? Qnil : pm_location_new(parser, cast->opening_loc.start, cast->opening_loc.end, source, freeze); + + // parts +#line 216 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = rb_ary_new_capa(cast->parts.size); + for (size_t index = 0; index < cast->parts.size; index++) { + rb_ary_push(argv[5], rb_ary_pop(value_stack)); + } + if (freeze) rb_obj_freeze(argv[5]); + + // closing_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = cast->closing_loc.start == NULL ? Qnil : pm_location_new(parser, cast->closing_loc.start, cast->closing_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(7, argv, rb_cPrismInterpolatedSymbolNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_INTERPOLATED_X_STRING_NODE: { + pm_interpolated_x_string_node_t *cast = (pm_interpolated_x_string_node_t *) node; + VALUE argv[7]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // opening_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = pm_location_new(parser, cast->opening_loc.start, cast->opening_loc.end, source, freeze); + + // parts +#line 216 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = rb_ary_new_capa(cast->parts.size); + for (size_t index = 0; index < cast->parts.size; index++) { + rb_ary_push(argv[5], rb_ary_pop(value_stack)); + } + if (freeze) rb_obj_freeze(argv[5]); + + // closing_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->closing_loc.start, cast->closing_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(7, argv, rb_cPrismInterpolatedXStringNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_IT_LOCAL_VARIABLE_READ_NODE: { + VALUE argv[4]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + VALUE value = rb_class_new_instance(4, argv, rb_cPrismItLocalVariableReadNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_IT_PARAMETERS_NODE: { + VALUE argv[4]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + VALUE value = rb_class_new_instance(4, argv, rb_cPrismItParametersNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_KEYWORD_HASH_NODE: { + pm_keyword_hash_node_t *cast = (pm_keyword_hash_node_t *) node; + VALUE argv[5]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // elements +#line 216 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_new_capa(cast->elements.size); + for (size_t index = 0; index < cast->elements.size; index++) { + rb_ary_push(argv[4], rb_ary_pop(value_stack)); + } + if (freeze) rb_obj_freeze(argv[4]); + + VALUE value = rb_class_new_instance(5, argv, rb_cPrismKeywordHashNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_KEYWORD_REST_PARAMETER_NODE: { + pm_keyword_rest_parameter_node_t *cast = (pm_keyword_rest_parameter_node_t *) node; + VALUE argv[7]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // name + argv[4] = cast->name == 0 ? Qnil : RARRAY_AREF(constants, cast->name - 1); + + // name_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = cast->name_loc.start == NULL ? Qnil : pm_location_new(parser, cast->name_loc.start, cast->name_loc.end, source, freeze); + + // operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(7, argv, rb_cPrismKeywordRestParameterNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_LAMBDA_NODE: { + pm_lambda_node_t *cast = (pm_lambda_node_t *) node; + VALUE argv[10]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // locals +#line 232 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_new_capa(cast->locals.size); + for (size_t index = 0; index < cast->locals.size; index++) { + assert(cast->locals.ids[index] != 0); + rb_ary_push(argv[4], RARRAY_AREF(constants, cast->locals.ids[index] - 1)); + } + if (freeze) rb_obj_freeze(argv[4]); + + // operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + // opening_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->opening_loc.start, cast->opening_loc.end, source, freeze); + + // closing_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = pm_location_new(parser, cast->closing_loc.start, cast->closing_loc.end, source, freeze); + + // parameters +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[8] = rb_ary_pop(value_stack); + + // body +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[9] = rb_ary_pop(value_stack); + + VALUE value = rb_class_new_instance(10, argv, rb_cPrismLambdaNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_LOCAL_VARIABLE_AND_WRITE_NODE: { + pm_local_variable_and_write_node_t *cast = (pm_local_variable_and_write_node_t *) node; + VALUE argv[9]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // name_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = pm_location_new(parser, cast->name_loc.start, cast->name_loc.end, source, freeze); + + // operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + // value +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = rb_ary_pop(value_stack); + + // name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->name != 0); + argv[7] = RARRAY_AREF(constants, cast->name - 1); + + // depth +#line 249 "prism/templates/ext/prism/api_node.c.erb" + argv[8] = ULONG2NUM(cast->depth); + + VALUE value = rb_class_new_instance(9, argv, rb_cPrismLocalVariableAndWriteNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE: { + pm_local_variable_operator_write_node_t *cast = (pm_local_variable_operator_write_node_t *) node; + VALUE argv[10]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // name_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = pm_location_new(parser, cast->name_loc.start, cast->name_loc.end, source, freeze); + + // binary_operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->binary_operator_loc.start, cast->binary_operator_loc.end, source, freeze); + + // value +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = rb_ary_pop(value_stack); + + // name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->name != 0); + argv[7] = RARRAY_AREF(constants, cast->name - 1); + + // binary_operator +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->binary_operator != 0); + argv[8] = RARRAY_AREF(constants, cast->binary_operator - 1); + + // depth +#line 249 "prism/templates/ext/prism/api_node.c.erb" + argv[9] = ULONG2NUM(cast->depth); + + VALUE value = rb_class_new_instance(10, argv, rb_cPrismLocalVariableOperatorWriteNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_LOCAL_VARIABLE_OR_WRITE_NODE: { + pm_local_variable_or_write_node_t *cast = (pm_local_variable_or_write_node_t *) node; + VALUE argv[9]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // name_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = pm_location_new(parser, cast->name_loc.start, cast->name_loc.end, source, freeze); + + // operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + // value +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = rb_ary_pop(value_stack); + + // name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->name != 0); + argv[7] = RARRAY_AREF(constants, cast->name - 1); + + // depth +#line 249 "prism/templates/ext/prism/api_node.c.erb" + argv[8] = ULONG2NUM(cast->depth); + + VALUE value = rb_class_new_instance(9, argv, rb_cPrismLocalVariableOrWriteNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_LOCAL_VARIABLE_READ_NODE: { + pm_local_variable_read_node_t *cast = (pm_local_variable_read_node_t *) node; + VALUE argv[6]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->name != 0); + argv[4] = RARRAY_AREF(constants, cast->name - 1); + + // depth +#line 249 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = ULONG2NUM(cast->depth); + + VALUE value = rb_class_new_instance(6, argv, rb_cPrismLocalVariableReadNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_LOCAL_VARIABLE_TARGET_NODE: { + pm_local_variable_target_node_t *cast = (pm_local_variable_target_node_t *) node; + VALUE argv[6]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->name != 0); + argv[4] = RARRAY_AREF(constants, cast->name - 1); + + // depth +#line 249 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = ULONG2NUM(cast->depth); + + VALUE value = rb_class_new_instance(6, argv, rb_cPrismLocalVariableTargetNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_LOCAL_VARIABLE_WRITE_NODE: { + pm_local_variable_write_node_t *cast = (pm_local_variable_write_node_t *) node; + VALUE argv[9]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->name != 0); + argv[4] = RARRAY_AREF(constants, cast->name - 1); + + // depth +#line 249 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = ULONG2NUM(cast->depth); + + // name_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->name_loc.start, cast->name_loc.end, source, freeze); + + // value +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = rb_ary_pop(value_stack); + + // operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[8] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(9, argv, rb_cPrismLocalVariableWriteNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_MATCH_LAST_LINE_NODE: { + pm_match_last_line_node_t *cast = (pm_match_last_line_node_t *) node; + VALUE argv[8]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // opening_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = pm_location_new(parser, cast->opening_loc.start, cast->opening_loc.end, source, freeze); + + // content_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->content_loc.start, cast->content_loc.end, source, freeze); + + // closing_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->closing_loc.start, cast->closing_loc.end, source, freeze); + + // unescaped +#line 223 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = pm_string_new(&cast->unescaped, encoding); + + VALUE value = rb_class_new_instance(8, argv, rb_cPrismMatchLastLineNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_MATCH_PREDICATE_NODE: { + pm_match_predicate_node_t *cast = (pm_match_predicate_node_t *) node; + VALUE argv[7]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // value +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // pattern +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = rb_ary_pop(value_stack); + + // operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(7, argv, rb_cPrismMatchPredicateNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_MATCH_REQUIRED_NODE: { + pm_match_required_node_t *cast = (pm_match_required_node_t *) node; + VALUE argv[7]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // value +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // pattern +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = rb_ary_pop(value_stack); + + // operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(7, argv, rb_cPrismMatchRequiredNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_MATCH_WRITE_NODE: { + pm_match_write_node_t *cast = (pm_match_write_node_t *) node; + VALUE argv[6]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // call +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // targets +#line 216 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = rb_ary_new_capa(cast->targets.size); + for (size_t index = 0; index < cast->targets.size; index++) { + rb_ary_push(argv[5], rb_ary_pop(value_stack)); + } + if (freeze) rb_obj_freeze(argv[5]); + + VALUE value = rb_class_new_instance(6, argv, rb_cPrismMatchWriteNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_MISSING_NODE: { + VALUE argv[4]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + VALUE value = rb_class_new_instance(4, argv, rb_cPrismMissingNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_MODULE_NODE: { + pm_module_node_t *cast = (pm_module_node_t *) node; + VALUE argv[10]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // locals +#line 232 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_new_capa(cast->locals.size); + for (size_t index = 0; index < cast->locals.size; index++) { + assert(cast->locals.ids[index] != 0); + rb_ary_push(argv[4], RARRAY_AREF(constants, cast->locals.ids[index] - 1)); + } + if (freeze) rb_obj_freeze(argv[4]); + + // module_keyword_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->module_keyword_loc.start, cast->module_keyword_loc.end, source, freeze); + + // constant_path +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = rb_ary_pop(value_stack); + + // body +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = rb_ary_pop(value_stack); + + // end_keyword_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[8] = pm_location_new(parser, cast->end_keyword_loc.start, cast->end_keyword_loc.end, source, freeze); + + // name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->name != 0); + argv[9] = RARRAY_AREF(constants, cast->name - 1); + + VALUE value = rb_class_new_instance(10, argv, rb_cPrismModuleNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_MULTI_TARGET_NODE: { + pm_multi_target_node_t *cast = (pm_multi_target_node_t *) node; + VALUE argv[9]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // lefts +#line 216 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_new_capa(cast->lefts.size); + for (size_t index = 0; index < cast->lefts.size; index++) { + rb_ary_push(argv[4], rb_ary_pop(value_stack)); + } + if (freeze) rb_obj_freeze(argv[4]); + + // rest +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = rb_ary_pop(value_stack); + + // rights +#line 216 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = rb_ary_new_capa(cast->rights.size); + for (size_t index = 0; index < cast->rights.size; index++) { + rb_ary_push(argv[6], rb_ary_pop(value_stack)); + } + if (freeze) rb_obj_freeze(argv[6]); + + // lparen_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = cast->lparen_loc.start == NULL ? Qnil : pm_location_new(parser, cast->lparen_loc.start, cast->lparen_loc.end, source, freeze); + + // rparen_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[8] = cast->rparen_loc.start == NULL ? Qnil : pm_location_new(parser, cast->rparen_loc.start, cast->rparen_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(9, argv, rb_cPrismMultiTargetNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_MULTI_WRITE_NODE: { + pm_multi_write_node_t *cast = (pm_multi_write_node_t *) node; + VALUE argv[11]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // lefts +#line 216 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_new_capa(cast->lefts.size); + for (size_t index = 0; index < cast->lefts.size; index++) { + rb_ary_push(argv[4], rb_ary_pop(value_stack)); + } + if (freeze) rb_obj_freeze(argv[4]); + + // rest +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = rb_ary_pop(value_stack); + + // rights +#line 216 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = rb_ary_new_capa(cast->rights.size); + for (size_t index = 0; index < cast->rights.size; index++) { + rb_ary_push(argv[6], rb_ary_pop(value_stack)); + } + if (freeze) rb_obj_freeze(argv[6]); + + // lparen_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = cast->lparen_loc.start == NULL ? Qnil : pm_location_new(parser, cast->lparen_loc.start, cast->lparen_loc.end, source, freeze); + + // rparen_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[8] = cast->rparen_loc.start == NULL ? Qnil : pm_location_new(parser, cast->rparen_loc.start, cast->rparen_loc.end, source, freeze); + + // operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[9] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + // value +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[10] = rb_ary_pop(value_stack); + + VALUE value = rb_class_new_instance(11, argv, rb_cPrismMultiWriteNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_NEXT_NODE: { + pm_next_node_t *cast = (pm_next_node_t *) node; + VALUE argv[6]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // arguments +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // keyword_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->keyword_loc.start, cast->keyword_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(6, argv, rb_cPrismNextNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_NIL_NODE: { + VALUE argv[4]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + VALUE value = rb_class_new_instance(4, argv, rb_cPrismNilNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_NO_KEYWORDS_PARAMETER_NODE: { + pm_no_keywords_parameter_node_t *cast = (pm_no_keywords_parameter_node_t *) node; + VALUE argv[6]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + // keyword_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->keyword_loc.start, cast->keyword_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(6, argv, rb_cPrismNoKeywordsParameterNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_NUMBERED_PARAMETERS_NODE: { + pm_numbered_parameters_node_t *cast = (pm_numbered_parameters_node_t *) node; + VALUE argv[5]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // maximum +#line 246 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = UINT2NUM(cast->maximum); + + VALUE value = rb_class_new_instance(5, argv, rb_cPrismNumberedParametersNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_NUMBERED_REFERENCE_READ_NODE: { + pm_numbered_reference_read_node_t *cast = (pm_numbered_reference_read_node_t *) node; + VALUE argv[5]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // number +#line 249 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = ULONG2NUM(cast->number); + + VALUE value = rb_class_new_instance(5, argv, rb_cPrismNumberedReferenceReadNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_OPTIONAL_KEYWORD_PARAMETER_NODE: { + pm_optional_keyword_parameter_node_t *cast = (pm_optional_keyword_parameter_node_t *) node; + VALUE argv[7]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->name != 0); + argv[4] = RARRAY_AREF(constants, cast->name - 1); + + // name_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->name_loc.start, cast->name_loc.end, source, freeze); + + // value +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = rb_ary_pop(value_stack); + + VALUE value = rb_class_new_instance(7, argv, rb_cPrismOptionalKeywordParameterNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_OPTIONAL_PARAMETER_NODE: { + pm_optional_parameter_node_t *cast = (pm_optional_parameter_node_t *) node; + VALUE argv[8]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->name != 0); + argv[4] = RARRAY_AREF(constants, cast->name - 1); + + // name_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->name_loc.start, cast->name_loc.end, source, freeze); + + // operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + // value +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = rb_ary_pop(value_stack); + + VALUE value = rb_class_new_instance(8, argv, rb_cPrismOptionalParameterNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_OR_NODE: { + pm_or_node_t *cast = (pm_or_node_t *) node; + VALUE argv[7]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // left +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // right +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = rb_ary_pop(value_stack); + + // operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(7, argv, rb_cPrismOrNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_PARAMETERS_NODE: { + pm_parameters_node_t *cast = (pm_parameters_node_t *) node; + VALUE argv[11]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // requireds +#line 216 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_new_capa(cast->requireds.size); + for (size_t index = 0; index < cast->requireds.size; index++) { + rb_ary_push(argv[4], rb_ary_pop(value_stack)); + } + if (freeze) rb_obj_freeze(argv[4]); + + // optionals +#line 216 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = rb_ary_new_capa(cast->optionals.size); + for (size_t index = 0; index < cast->optionals.size; index++) { + rb_ary_push(argv[5], rb_ary_pop(value_stack)); + } + if (freeze) rb_obj_freeze(argv[5]); + + // rest +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = rb_ary_pop(value_stack); + + // posts +#line 216 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = rb_ary_new_capa(cast->posts.size); + for (size_t index = 0; index < cast->posts.size; index++) { + rb_ary_push(argv[7], rb_ary_pop(value_stack)); + } + if (freeze) rb_obj_freeze(argv[7]); + + // keywords +#line 216 "prism/templates/ext/prism/api_node.c.erb" + argv[8] = rb_ary_new_capa(cast->keywords.size); + for (size_t index = 0; index < cast->keywords.size; index++) { + rb_ary_push(argv[8], rb_ary_pop(value_stack)); + } + if (freeze) rb_obj_freeze(argv[8]); + + // keyword_rest +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[9] = rb_ary_pop(value_stack); + + // block +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[10] = rb_ary_pop(value_stack); + + VALUE value = rb_class_new_instance(11, argv, rb_cPrismParametersNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_PARENTHESES_NODE: { + pm_parentheses_node_t *cast = (pm_parentheses_node_t *) node; + VALUE argv[7]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // body +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // opening_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->opening_loc.start, cast->opening_loc.end, source, freeze); + + // closing_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->closing_loc.start, cast->closing_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(7, argv, rb_cPrismParenthesesNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_PINNED_EXPRESSION_NODE: { + pm_pinned_expression_node_t *cast = (pm_pinned_expression_node_t *) node; + VALUE argv[8]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // expression +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + // lparen_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->lparen_loc.start, cast->lparen_loc.end, source, freeze); + + // rparen_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = pm_location_new(parser, cast->rparen_loc.start, cast->rparen_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(8, argv, rb_cPrismPinnedExpressionNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_PINNED_VARIABLE_NODE: { + pm_pinned_variable_node_t *cast = (pm_pinned_variable_node_t *) node; + VALUE argv[6]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // variable +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(6, argv, rb_cPrismPinnedVariableNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_POST_EXECUTION_NODE: { + pm_post_execution_node_t *cast = (pm_post_execution_node_t *) node; + VALUE argv[8]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // statements +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // keyword_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->keyword_loc.start, cast->keyword_loc.end, source, freeze); + + // opening_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->opening_loc.start, cast->opening_loc.end, source, freeze); + + // closing_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = pm_location_new(parser, cast->closing_loc.start, cast->closing_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(8, argv, rb_cPrismPostExecutionNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_PRE_EXECUTION_NODE: { + pm_pre_execution_node_t *cast = (pm_pre_execution_node_t *) node; + VALUE argv[8]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // statements +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // keyword_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->keyword_loc.start, cast->keyword_loc.end, source, freeze); + + // opening_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->opening_loc.start, cast->opening_loc.end, source, freeze); + + // closing_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = pm_location_new(parser, cast->closing_loc.start, cast->closing_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(8, argv, rb_cPrismPreExecutionNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_PROGRAM_NODE: { + pm_program_node_t *cast = (pm_program_node_t *) node; + VALUE argv[6]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // locals +#line 232 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_new_capa(cast->locals.size); + for (size_t index = 0; index < cast->locals.size; index++) { + assert(cast->locals.ids[index] != 0); + rb_ary_push(argv[4], RARRAY_AREF(constants, cast->locals.ids[index] - 1)); + } + if (freeze) rb_obj_freeze(argv[4]); + + // statements +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = rb_ary_pop(value_stack); + + VALUE value = rb_class_new_instance(6, argv, rb_cPrismProgramNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_RANGE_NODE: { + pm_range_node_t *cast = (pm_range_node_t *) node; + VALUE argv[7]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // left +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // right +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = rb_ary_pop(value_stack); + + // operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(7, argv, rb_cPrismRangeNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_RATIONAL_NODE: { + pm_rational_node_t *cast = (pm_rational_node_t *) node; + VALUE argv[6]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // numerator +#line 252 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = pm_integer_new(&cast->numerator); + + // denominator +#line 252 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_integer_new(&cast->denominator); + + VALUE value = rb_class_new_instance(6, argv, rb_cPrismRationalNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_REDO_NODE: { + VALUE argv[4]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + VALUE value = rb_class_new_instance(4, argv, rb_cPrismRedoNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_REGULAR_EXPRESSION_NODE: { + pm_regular_expression_node_t *cast = (pm_regular_expression_node_t *) node; + VALUE argv[8]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // opening_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = pm_location_new(parser, cast->opening_loc.start, cast->opening_loc.end, source, freeze); + + // content_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->content_loc.start, cast->content_loc.end, source, freeze); + + // closing_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->closing_loc.start, cast->closing_loc.end, source, freeze); + + // unescaped +#line 223 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = pm_string_new(&cast->unescaped, encoding); + + VALUE value = rb_class_new_instance(8, argv, rb_cPrismRegularExpressionNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_REQUIRED_KEYWORD_PARAMETER_NODE: { + pm_required_keyword_parameter_node_t *cast = (pm_required_keyword_parameter_node_t *) node; + VALUE argv[6]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->name != 0); + argv[4] = RARRAY_AREF(constants, cast->name - 1); + + // name_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->name_loc.start, cast->name_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(6, argv, rb_cPrismRequiredKeywordParameterNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_REQUIRED_PARAMETER_NODE: { + pm_required_parameter_node_t *cast = (pm_required_parameter_node_t *) node; + VALUE argv[5]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // name +#line 226 "prism/templates/ext/prism/api_node.c.erb" + assert(cast->name != 0); + argv[4] = RARRAY_AREF(constants, cast->name - 1); + + VALUE value = rb_class_new_instance(5, argv, rb_cPrismRequiredParameterNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_RESCUE_MODIFIER_NODE: { + pm_rescue_modifier_node_t *cast = (pm_rescue_modifier_node_t *) node; + VALUE argv[7]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // expression +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + // keyword_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->keyword_loc.start, cast->keyword_loc.end, source, freeze); + + // rescue_expression +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = rb_ary_pop(value_stack); + + VALUE value = rb_class_new_instance(7, argv, rb_cPrismRescueModifierNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_RESCUE_NODE: { + pm_rescue_node_t *cast = (pm_rescue_node_t *) node; + VALUE argv[11]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // keyword_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = pm_location_new(parser, cast->keyword_loc.start, cast->keyword_loc.end, source, freeze); + + // exceptions +#line 216 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = rb_ary_new_capa(cast->exceptions.size); + for (size_t index = 0; index < cast->exceptions.size; index++) { + rb_ary_push(argv[5], rb_ary_pop(value_stack)); + } + if (freeze) rb_obj_freeze(argv[5]); + + // operator_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = cast->operator_loc.start == NULL ? Qnil : pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + // reference +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = rb_ary_pop(value_stack); + + // then_keyword_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[8] = cast->then_keyword_loc.start == NULL ? Qnil : pm_location_new(parser, cast->then_keyword_loc.start, cast->then_keyword_loc.end, source, freeze); + + // statements +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[9] = rb_ary_pop(value_stack); + + // subsequent +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[10] = rb_ary_pop(value_stack); + + VALUE value = rb_class_new_instance(11, argv, rb_cPrismRescueNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_REST_PARAMETER_NODE: { + pm_rest_parameter_node_t *cast = (pm_rest_parameter_node_t *) node; + VALUE argv[7]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // name + argv[4] = cast->name == 0 ? Qnil : RARRAY_AREF(constants, cast->name - 1); + + // name_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = cast->name_loc.start == NULL ? Qnil : pm_location_new(parser, cast->name_loc.start, cast->name_loc.end, source, freeze); + + // operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(7, argv, rb_cPrismRestParameterNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_RETRY_NODE: { + VALUE argv[4]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + VALUE value = rb_class_new_instance(4, argv, rb_cPrismRetryNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_RETURN_NODE: { + pm_return_node_t *cast = (pm_return_node_t *) node; + VALUE argv[6]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // keyword_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = pm_location_new(parser, cast->keyword_loc.start, cast->keyword_loc.end, source, freeze); + + // arguments +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = rb_ary_pop(value_stack); + + VALUE value = rb_class_new_instance(6, argv, rb_cPrismReturnNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_SELF_NODE: { + VALUE argv[4]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + VALUE value = rb_class_new_instance(4, argv, rb_cPrismSelfNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_SHAREABLE_CONSTANT_NODE: { + VALUE argv[5]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // write +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_pop(value_stack); + + VALUE value = rb_class_new_instance(5, argv, rb_cPrismShareableConstantNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_SINGLETON_CLASS_NODE: { + pm_singleton_class_node_t *cast = (pm_singleton_class_node_t *) node; + VALUE argv[10]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // locals +#line 232 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_new_capa(cast->locals.size); + for (size_t index = 0; index < cast->locals.size; index++) { + assert(cast->locals.ids[index] != 0); + rb_ary_push(argv[4], RARRAY_AREF(constants, cast->locals.ids[index] - 1)); + } + if (freeze) rb_obj_freeze(argv[4]); + + // class_keyword_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->class_keyword_loc.start, cast->class_keyword_loc.end, source, freeze); + + // operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + // expression +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = rb_ary_pop(value_stack); + + // body +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[8] = rb_ary_pop(value_stack); + + // end_keyword_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[9] = pm_location_new(parser, cast->end_keyword_loc.start, cast->end_keyword_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(10, argv, rb_cPrismSingletonClassNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_SOURCE_ENCODING_NODE: { + VALUE argv[4]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + VALUE value = rb_class_new_instance(4, argv, rb_cPrismSourceEncodingNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_SOURCE_FILE_NODE: { + pm_source_file_node_t *cast = (pm_source_file_node_t *) node; + VALUE argv[5]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // filepath +#line 223 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = pm_string_new(&cast->filepath, encoding); + + VALUE value = rb_class_new_instance(5, argv, rb_cPrismSourceFileNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_SOURCE_LINE_NODE: { + VALUE argv[4]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + VALUE value = rb_class_new_instance(4, argv, rb_cPrismSourceLineNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_SPLAT_NODE: { + pm_splat_node_t *cast = (pm_splat_node_t *) node; + VALUE argv[6]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // operator_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source, freeze); + + // expression +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = rb_ary_pop(value_stack); + + VALUE value = rb_class_new_instance(6, argv, rb_cPrismSplatNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_STATEMENTS_NODE: { + pm_statements_node_t *cast = (pm_statements_node_t *) node; + VALUE argv[5]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // body +#line 216 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_new_capa(cast->body.size); + for (size_t index = 0; index < cast->body.size; index++) { + rb_ary_push(argv[4], rb_ary_pop(value_stack)); + } + if (freeze) rb_obj_freeze(argv[4]); + + VALUE value = rb_class_new_instance(5, argv, rb_cPrismStatementsNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_STRING_NODE: { + pm_string_node_t *cast = (pm_string_node_t *) node; + VALUE argv[8]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // opening_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = cast->opening_loc.start == NULL ? Qnil : pm_location_new(parser, cast->opening_loc.start, cast->opening_loc.end, source, freeze); + + // content_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->content_loc.start, cast->content_loc.end, source, freeze); + + // closing_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = cast->closing_loc.start == NULL ? Qnil : pm_location_new(parser, cast->closing_loc.start, cast->closing_loc.end, source, freeze); + + // unescaped +#line 223 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = pm_string_new(&cast->unescaped, encoding); + + VALUE value = rb_class_new_instance(8, argv, rb_cPrismStringNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_SUPER_NODE: { + pm_super_node_t *cast = (pm_super_node_t *) node; + VALUE argv[9]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // keyword_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = pm_location_new(parser, cast->keyword_loc.start, cast->keyword_loc.end, source, freeze); + + // lparen_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = cast->lparen_loc.start == NULL ? Qnil : pm_location_new(parser, cast->lparen_loc.start, cast->lparen_loc.end, source, freeze); + + // arguments +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = rb_ary_pop(value_stack); + + // rparen_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = cast->rparen_loc.start == NULL ? Qnil : pm_location_new(parser, cast->rparen_loc.start, cast->rparen_loc.end, source, freeze); + + // block +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[8] = rb_ary_pop(value_stack); + + VALUE value = rb_class_new_instance(9, argv, rb_cPrismSuperNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_SYMBOL_NODE: { + pm_symbol_node_t *cast = (pm_symbol_node_t *) node; + VALUE argv[8]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // opening_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = cast->opening_loc.start == NULL ? Qnil : pm_location_new(parser, cast->opening_loc.start, cast->opening_loc.end, source, freeze); + + // value_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = cast->value_loc.start == NULL ? Qnil : pm_location_new(parser, cast->value_loc.start, cast->value_loc.end, source, freeze); + + // closing_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = cast->closing_loc.start == NULL ? Qnil : pm_location_new(parser, cast->closing_loc.start, cast->closing_loc.end, source, freeze); + + // unescaped +#line 223 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = pm_string_new(&cast->unescaped, encoding); + + VALUE value = rb_class_new_instance(8, argv, rb_cPrismSymbolNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_TRUE_NODE: { + VALUE argv[4]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + VALUE value = rb_class_new_instance(4, argv, rb_cPrismTrueNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_UNDEF_NODE: { + pm_undef_node_t *cast = (pm_undef_node_t *) node; + VALUE argv[6]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // names +#line 216 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = rb_ary_new_capa(cast->names.size); + for (size_t index = 0; index < cast->names.size; index++) { + rb_ary_push(argv[4], rb_ary_pop(value_stack)); + } + if (freeze) rb_obj_freeze(argv[4]); + + // keyword_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->keyword_loc.start, cast->keyword_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(6, argv, rb_cPrismUndefNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_UNLESS_NODE: { + pm_unless_node_t *cast = (pm_unless_node_t *) node; + VALUE argv[10]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // keyword_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = pm_location_new(parser, cast->keyword_loc.start, cast->keyword_loc.end, source, freeze); + + // predicate +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = rb_ary_pop(value_stack); + + // then_keyword_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = cast->then_keyword_loc.start == NULL ? Qnil : pm_location_new(parser, cast->then_keyword_loc.start, cast->then_keyword_loc.end, source, freeze); + + // statements +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = rb_ary_pop(value_stack); + + // else_clause +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[8] = rb_ary_pop(value_stack); + + // end_keyword_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[9] = cast->end_keyword_loc.start == NULL ? Qnil : pm_location_new(parser, cast->end_keyword_loc.start, cast->end_keyword_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(10, argv, rb_cPrismUnlessNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_UNTIL_NODE: { + pm_until_node_t *cast = (pm_until_node_t *) node; + VALUE argv[9]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // keyword_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = pm_location_new(parser, cast->keyword_loc.start, cast->keyword_loc.end, source, freeze); + + // do_keyword_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = cast->do_keyword_loc.start == NULL ? Qnil : pm_location_new(parser, cast->do_keyword_loc.start, cast->do_keyword_loc.end, source, freeze); + + // closing_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = cast->closing_loc.start == NULL ? Qnil : pm_location_new(parser, cast->closing_loc.start, cast->closing_loc.end, source, freeze); + + // predicate +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = rb_ary_pop(value_stack); + + // statements +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[8] = rb_ary_pop(value_stack); + + VALUE value = rb_class_new_instance(9, argv, rb_cPrismUntilNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_WHEN_NODE: { + pm_when_node_t *cast = (pm_when_node_t *) node; + VALUE argv[8]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // keyword_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = pm_location_new(parser, cast->keyword_loc.start, cast->keyword_loc.end, source, freeze); + + // conditions +#line 216 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = rb_ary_new_capa(cast->conditions.size); + for (size_t index = 0; index < cast->conditions.size; index++) { + rb_ary_push(argv[5], rb_ary_pop(value_stack)); + } + if (freeze) rb_obj_freeze(argv[5]); + + // then_keyword_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = cast->then_keyword_loc.start == NULL ? Qnil : pm_location_new(parser, cast->then_keyword_loc.start, cast->then_keyword_loc.end, source, freeze); + + // statements +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = rb_ary_pop(value_stack); + + VALUE value = rb_class_new_instance(8, argv, rb_cPrismWhenNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_WHILE_NODE: { + pm_while_node_t *cast = (pm_while_node_t *) node; + VALUE argv[9]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // keyword_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = pm_location_new(parser, cast->keyword_loc.start, cast->keyword_loc.end, source, freeze); + + // do_keyword_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = cast->do_keyword_loc.start == NULL ? Qnil : pm_location_new(parser, cast->do_keyword_loc.start, cast->do_keyword_loc.end, source, freeze); + + // closing_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = cast->closing_loc.start == NULL ? Qnil : pm_location_new(parser, cast->closing_loc.start, cast->closing_loc.end, source, freeze); + + // predicate +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = rb_ary_pop(value_stack); + + // statements +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[8] = rb_ary_pop(value_stack); + + VALUE value = rb_class_new_instance(9, argv, rb_cPrismWhileNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_X_STRING_NODE: { + pm_x_string_node_t *cast = (pm_x_string_node_t *) node; + VALUE argv[8]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // opening_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = pm_location_new(parser, cast->opening_loc.start, cast->opening_loc.end, source, freeze); + + // content_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = pm_location_new(parser, cast->content_loc.start, cast->content_loc.end, source, freeze); + + // closing_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = pm_location_new(parser, cast->closing_loc.start, cast->closing_loc.end, source, freeze); + + // unescaped +#line 223 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = pm_string_new(&cast->unescaped, encoding); + + VALUE value = rb_class_new_instance(8, argv, rb_cPrismXStringNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } +#line 190 "prism/templates/ext/prism/api_node.c.erb" + case PM_YIELD_NODE: { + pm_yield_node_t *cast = (pm_yield_node_t *) node; + VALUE argv[8]; + + // source + argv[0] = source; + + // node_id + argv[1] = ULONG2NUM(node->node_id); + + // location + argv[2] = pm_location_new(parser, node->location.start, node->location.end, source, freeze); + + // flags + argv[3] = ULONG2NUM(node->flags); + + // keyword_loc +#line 240 "prism/templates/ext/prism/api_node.c.erb" + argv[4] = pm_location_new(parser, cast->keyword_loc.start, cast->keyword_loc.end, source, freeze); + + // lparen_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[5] = cast->lparen_loc.start == NULL ? Qnil : pm_location_new(parser, cast->lparen_loc.start, cast->lparen_loc.end, source, freeze); + + // arguments +#line 213 "prism/templates/ext/prism/api_node.c.erb" + argv[6] = rb_ary_pop(value_stack); + + // rparen_loc +#line 243 "prism/templates/ext/prism/api_node.c.erb" + argv[7] = cast->rparen_loc.start == NULL ? Qnil : pm_location_new(parser, cast->rparen_loc.start, cast->rparen_loc.end, source, freeze); + + VALUE value = rb_class_new_instance(8, argv, rb_cPrismYieldNode); + if (freeze) rb_obj_freeze(value); + + rb_ary_push(value_stack, value); + break; + } + 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) { + rb_cPrismAliasGlobalVariableNode = rb_define_class_under(rb_cPrism, "AliasGlobalVariableNode", rb_cPrismNode); + rb_cPrismAliasMethodNode = rb_define_class_under(rb_cPrism, "AliasMethodNode", rb_cPrismNode); + rb_cPrismAlternationPatternNode = rb_define_class_under(rb_cPrism, "AlternationPatternNode", rb_cPrismNode); + rb_cPrismAndNode = rb_define_class_under(rb_cPrism, "AndNode", rb_cPrismNode); + rb_cPrismArgumentsNode = rb_define_class_under(rb_cPrism, "ArgumentsNode", rb_cPrismNode); + rb_cPrismArrayNode = rb_define_class_under(rb_cPrism, "ArrayNode", rb_cPrismNode); + rb_cPrismArrayPatternNode = rb_define_class_under(rb_cPrism, "ArrayPatternNode", rb_cPrismNode); + rb_cPrismAssocNode = rb_define_class_under(rb_cPrism, "AssocNode", rb_cPrismNode); + rb_cPrismAssocSplatNode = rb_define_class_under(rb_cPrism, "AssocSplatNode", rb_cPrismNode); + rb_cPrismBackReferenceReadNode = rb_define_class_under(rb_cPrism, "BackReferenceReadNode", rb_cPrismNode); + rb_cPrismBeginNode = rb_define_class_under(rb_cPrism, "BeginNode", rb_cPrismNode); + rb_cPrismBlockArgumentNode = rb_define_class_under(rb_cPrism, "BlockArgumentNode", rb_cPrismNode); + rb_cPrismBlockLocalVariableNode = rb_define_class_under(rb_cPrism, "BlockLocalVariableNode", rb_cPrismNode); + rb_cPrismBlockNode = rb_define_class_under(rb_cPrism, "BlockNode", rb_cPrismNode); + rb_cPrismBlockParameterNode = rb_define_class_under(rb_cPrism, "BlockParameterNode", rb_cPrismNode); + rb_cPrismBlockParametersNode = rb_define_class_under(rb_cPrism, "BlockParametersNode", rb_cPrismNode); + rb_cPrismBreakNode = rb_define_class_under(rb_cPrism, "BreakNode", rb_cPrismNode); + rb_cPrismCallAndWriteNode = rb_define_class_under(rb_cPrism, "CallAndWriteNode", rb_cPrismNode); + rb_cPrismCallNode = rb_define_class_under(rb_cPrism, "CallNode", rb_cPrismNode); + rb_cPrismCallOperatorWriteNode = rb_define_class_under(rb_cPrism, "CallOperatorWriteNode", rb_cPrismNode); + rb_cPrismCallOrWriteNode = rb_define_class_under(rb_cPrism, "CallOrWriteNode", rb_cPrismNode); + rb_cPrismCallTargetNode = rb_define_class_under(rb_cPrism, "CallTargetNode", rb_cPrismNode); + rb_cPrismCapturePatternNode = rb_define_class_under(rb_cPrism, "CapturePatternNode", rb_cPrismNode); + rb_cPrismCaseMatchNode = rb_define_class_under(rb_cPrism, "CaseMatchNode", rb_cPrismNode); + rb_cPrismCaseNode = rb_define_class_under(rb_cPrism, "CaseNode", rb_cPrismNode); + rb_cPrismClassNode = rb_define_class_under(rb_cPrism, "ClassNode", rb_cPrismNode); + rb_cPrismClassVariableAndWriteNode = rb_define_class_under(rb_cPrism, "ClassVariableAndWriteNode", rb_cPrismNode); + rb_cPrismClassVariableOperatorWriteNode = rb_define_class_under(rb_cPrism, "ClassVariableOperatorWriteNode", rb_cPrismNode); + rb_cPrismClassVariableOrWriteNode = rb_define_class_under(rb_cPrism, "ClassVariableOrWriteNode", rb_cPrismNode); + rb_cPrismClassVariableReadNode = rb_define_class_under(rb_cPrism, "ClassVariableReadNode", rb_cPrismNode); + rb_cPrismClassVariableTargetNode = rb_define_class_under(rb_cPrism, "ClassVariableTargetNode", rb_cPrismNode); + rb_cPrismClassVariableWriteNode = rb_define_class_under(rb_cPrism, "ClassVariableWriteNode", rb_cPrismNode); + rb_cPrismConstantAndWriteNode = rb_define_class_under(rb_cPrism, "ConstantAndWriteNode", rb_cPrismNode); + rb_cPrismConstantOperatorWriteNode = rb_define_class_under(rb_cPrism, "ConstantOperatorWriteNode", rb_cPrismNode); + rb_cPrismConstantOrWriteNode = rb_define_class_under(rb_cPrism, "ConstantOrWriteNode", rb_cPrismNode); + rb_cPrismConstantPathAndWriteNode = rb_define_class_under(rb_cPrism, "ConstantPathAndWriteNode", rb_cPrismNode); + rb_cPrismConstantPathNode = rb_define_class_under(rb_cPrism, "ConstantPathNode", rb_cPrismNode); + rb_cPrismConstantPathOperatorWriteNode = rb_define_class_under(rb_cPrism, "ConstantPathOperatorWriteNode", rb_cPrismNode); + rb_cPrismConstantPathOrWriteNode = rb_define_class_under(rb_cPrism, "ConstantPathOrWriteNode", rb_cPrismNode); + rb_cPrismConstantPathTargetNode = rb_define_class_under(rb_cPrism, "ConstantPathTargetNode", rb_cPrismNode); + rb_cPrismConstantPathWriteNode = rb_define_class_under(rb_cPrism, "ConstantPathWriteNode", rb_cPrismNode); + rb_cPrismConstantReadNode = rb_define_class_under(rb_cPrism, "ConstantReadNode", rb_cPrismNode); + rb_cPrismConstantTargetNode = rb_define_class_under(rb_cPrism, "ConstantTargetNode", rb_cPrismNode); + rb_cPrismConstantWriteNode = rb_define_class_under(rb_cPrism, "ConstantWriteNode", rb_cPrismNode); + rb_cPrismDefNode = rb_define_class_under(rb_cPrism, "DefNode", rb_cPrismNode); + rb_cPrismDefinedNode = rb_define_class_under(rb_cPrism, "DefinedNode", rb_cPrismNode); + rb_cPrismElseNode = rb_define_class_under(rb_cPrism, "ElseNode", rb_cPrismNode); + rb_cPrismEmbeddedStatementsNode = rb_define_class_under(rb_cPrism, "EmbeddedStatementsNode", rb_cPrismNode); + rb_cPrismEmbeddedVariableNode = rb_define_class_under(rb_cPrism, "EmbeddedVariableNode", rb_cPrismNode); + rb_cPrismEnsureNode = rb_define_class_under(rb_cPrism, "EnsureNode", rb_cPrismNode); + rb_cPrismFalseNode = rb_define_class_under(rb_cPrism, "FalseNode", rb_cPrismNode); + rb_cPrismFindPatternNode = rb_define_class_under(rb_cPrism, "FindPatternNode", rb_cPrismNode); + rb_cPrismFlipFlopNode = rb_define_class_under(rb_cPrism, "FlipFlopNode", rb_cPrismNode); + rb_cPrismFloatNode = rb_define_class_under(rb_cPrism, "FloatNode", rb_cPrismNode); + rb_cPrismForNode = rb_define_class_under(rb_cPrism, "ForNode", rb_cPrismNode); + rb_cPrismForwardingArgumentsNode = rb_define_class_under(rb_cPrism, "ForwardingArgumentsNode", rb_cPrismNode); + rb_cPrismForwardingParameterNode = rb_define_class_under(rb_cPrism, "ForwardingParameterNode", rb_cPrismNode); + rb_cPrismForwardingSuperNode = rb_define_class_under(rb_cPrism, "ForwardingSuperNode", rb_cPrismNode); + rb_cPrismGlobalVariableAndWriteNode = rb_define_class_under(rb_cPrism, "GlobalVariableAndWriteNode", rb_cPrismNode); + rb_cPrismGlobalVariableOperatorWriteNode = rb_define_class_under(rb_cPrism, "GlobalVariableOperatorWriteNode", rb_cPrismNode); + rb_cPrismGlobalVariableOrWriteNode = rb_define_class_under(rb_cPrism, "GlobalVariableOrWriteNode", rb_cPrismNode); + rb_cPrismGlobalVariableReadNode = rb_define_class_under(rb_cPrism, "GlobalVariableReadNode", rb_cPrismNode); + rb_cPrismGlobalVariableTargetNode = rb_define_class_under(rb_cPrism, "GlobalVariableTargetNode", rb_cPrismNode); + rb_cPrismGlobalVariableWriteNode = rb_define_class_under(rb_cPrism, "GlobalVariableWriteNode", rb_cPrismNode); + rb_cPrismHashNode = rb_define_class_under(rb_cPrism, "HashNode", rb_cPrismNode); + rb_cPrismHashPatternNode = rb_define_class_under(rb_cPrism, "HashPatternNode", rb_cPrismNode); + rb_cPrismIfNode = rb_define_class_under(rb_cPrism, "IfNode", rb_cPrismNode); + rb_cPrismImaginaryNode = rb_define_class_under(rb_cPrism, "ImaginaryNode", rb_cPrismNode); + rb_cPrismImplicitNode = rb_define_class_under(rb_cPrism, "ImplicitNode", rb_cPrismNode); + rb_cPrismImplicitRestNode = rb_define_class_under(rb_cPrism, "ImplicitRestNode", rb_cPrismNode); + rb_cPrismInNode = rb_define_class_under(rb_cPrism, "InNode", rb_cPrismNode); + rb_cPrismIndexAndWriteNode = rb_define_class_under(rb_cPrism, "IndexAndWriteNode", rb_cPrismNode); + rb_cPrismIndexOperatorWriteNode = rb_define_class_under(rb_cPrism, "IndexOperatorWriteNode", rb_cPrismNode); + rb_cPrismIndexOrWriteNode = rb_define_class_under(rb_cPrism, "IndexOrWriteNode", rb_cPrismNode); + rb_cPrismIndexTargetNode = rb_define_class_under(rb_cPrism, "IndexTargetNode", rb_cPrismNode); + rb_cPrismInstanceVariableAndWriteNode = rb_define_class_under(rb_cPrism, "InstanceVariableAndWriteNode", rb_cPrismNode); + rb_cPrismInstanceVariableOperatorWriteNode = rb_define_class_under(rb_cPrism, "InstanceVariableOperatorWriteNode", rb_cPrismNode); + rb_cPrismInstanceVariableOrWriteNode = rb_define_class_under(rb_cPrism, "InstanceVariableOrWriteNode", rb_cPrismNode); + rb_cPrismInstanceVariableReadNode = rb_define_class_under(rb_cPrism, "InstanceVariableReadNode", rb_cPrismNode); + rb_cPrismInstanceVariableTargetNode = rb_define_class_under(rb_cPrism, "InstanceVariableTargetNode", rb_cPrismNode); + rb_cPrismInstanceVariableWriteNode = rb_define_class_under(rb_cPrism, "InstanceVariableWriteNode", rb_cPrismNode); + rb_cPrismIntegerNode = rb_define_class_under(rb_cPrism, "IntegerNode", rb_cPrismNode); + rb_cPrismInterpolatedMatchLastLineNode = rb_define_class_under(rb_cPrism, "InterpolatedMatchLastLineNode", rb_cPrismNode); + rb_cPrismInterpolatedRegularExpressionNode = rb_define_class_under(rb_cPrism, "InterpolatedRegularExpressionNode", rb_cPrismNode); + rb_cPrismInterpolatedStringNode = rb_define_class_under(rb_cPrism, "InterpolatedStringNode", rb_cPrismNode); + rb_cPrismInterpolatedSymbolNode = rb_define_class_under(rb_cPrism, "InterpolatedSymbolNode", rb_cPrismNode); + rb_cPrismInterpolatedXStringNode = rb_define_class_under(rb_cPrism, "InterpolatedXStringNode", rb_cPrismNode); + rb_cPrismItLocalVariableReadNode = rb_define_class_under(rb_cPrism, "ItLocalVariableReadNode", rb_cPrismNode); + rb_cPrismItParametersNode = rb_define_class_under(rb_cPrism, "ItParametersNode", rb_cPrismNode); + rb_cPrismKeywordHashNode = rb_define_class_under(rb_cPrism, "KeywordHashNode", rb_cPrismNode); + rb_cPrismKeywordRestParameterNode = rb_define_class_under(rb_cPrism, "KeywordRestParameterNode", rb_cPrismNode); + rb_cPrismLambdaNode = rb_define_class_under(rb_cPrism, "LambdaNode", rb_cPrismNode); + rb_cPrismLocalVariableAndWriteNode = rb_define_class_under(rb_cPrism, "LocalVariableAndWriteNode", rb_cPrismNode); + rb_cPrismLocalVariableOperatorWriteNode = rb_define_class_under(rb_cPrism, "LocalVariableOperatorWriteNode", rb_cPrismNode); + rb_cPrismLocalVariableOrWriteNode = rb_define_class_under(rb_cPrism, "LocalVariableOrWriteNode", rb_cPrismNode); + rb_cPrismLocalVariableReadNode = rb_define_class_under(rb_cPrism, "LocalVariableReadNode", rb_cPrismNode); + rb_cPrismLocalVariableTargetNode = rb_define_class_under(rb_cPrism, "LocalVariableTargetNode", rb_cPrismNode); + rb_cPrismLocalVariableWriteNode = rb_define_class_under(rb_cPrism, "LocalVariableWriteNode", rb_cPrismNode); + rb_cPrismMatchLastLineNode = rb_define_class_under(rb_cPrism, "MatchLastLineNode", rb_cPrismNode); + rb_cPrismMatchPredicateNode = rb_define_class_under(rb_cPrism, "MatchPredicateNode", rb_cPrismNode); + rb_cPrismMatchRequiredNode = rb_define_class_under(rb_cPrism, "MatchRequiredNode", rb_cPrismNode); + rb_cPrismMatchWriteNode = rb_define_class_under(rb_cPrism, "MatchWriteNode", rb_cPrismNode); + rb_cPrismMissingNode = rb_define_class_under(rb_cPrism, "MissingNode", rb_cPrismNode); + rb_cPrismModuleNode = rb_define_class_under(rb_cPrism, "ModuleNode", rb_cPrismNode); + rb_cPrismMultiTargetNode = rb_define_class_under(rb_cPrism, "MultiTargetNode", rb_cPrismNode); + rb_cPrismMultiWriteNode = rb_define_class_under(rb_cPrism, "MultiWriteNode", rb_cPrismNode); + rb_cPrismNextNode = rb_define_class_under(rb_cPrism, "NextNode", rb_cPrismNode); + rb_cPrismNilNode = rb_define_class_under(rb_cPrism, "NilNode", rb_cPrismNode); + rb_cPrismNoKeywordsParameterNode = rb_define_class_under(rb_cPrism, "NoKeywordsParameterNode", rb_cPrismNode); + rb_cPrismNumberedParametersNode = rb_define_class_under(rb_cPrism, "NumberedParametersNode", rb_cPrismNode); + rb_cPrismNumberedReferenceReadNode = rb_define_class_under(rb_cPrism, "NumberedReferenceReadNode", rb_cPrismNode); + rb_cPrismOptionalKeywordParameterNode = rb_define_class_under(rb_cPrism, "OptionalKeywordParameterNode", rb_cPrismNode); + rb_cPrismOptionalParameterNode = rb_define_class_under(rb_cPrism, "OptionalParameterNode", rb_cPrismNode); + rb_cPrismOrNode = rb_define_class_under(rb_cPrism, "OrNode", rb_cPrismNode); + rb_cPrismParametersNode = rb_define_class_under(rb_cPrism, "ParametersNode", rb_cPrismNode); + rb_cPrismParenthesesNode = rb_define_class_under(rb_cPrism, "ParenthesesNode", rb_cPrismNode); + rb_cPrismPinnedExpressionNode = rb_define_class_under(rb_cPrism, "PinnedExpressionNode", rb_cPrismNode); + rb_cPrismPinnedVariableNode = rb_define_class_under(rb_cPrism, "PinnedVariableNode", rb_cPrismNode); + rb_cPrismPostExecutionNode = rb_define_class_under(rb_cPrism, "PostExecutionNode", rb_cPrismNode); + rb_cPrismPreExecutionNode = rb_define_class_under(rb_cPrism, "PreExecutionNode", rb_cPrismNode); + rb_cPrismProgramNode = rb_define_class_under(rb_cPrism, "ProgramNode", rb_cPrismNode); + rb_cPrismRangeNode = rb_define_class_under(rb_cPrism, "RangeNode", rb_cPrismNode); + rb_cPrismRationalNode = rb_define_class_under(rb_cPrism, "RationalNode", rb_cPrismNode); + rb_cPrismRedoNode = rb_define_class_under(rb_cPrism, "RedoNode", rb_cPrismNode); + rb_cPrismRegularExpressionNode = rb_define_class_under(rb_cPrism, "RegularExpressionNode", rb_cPrismNode); + rb_cPrismRequiredKeywordParameterNode = rb_define_class_under(rb_cPrism, "RequiredKeywordParameterNode", rb_cPrismNode); + rb_cPrismRequiredParameterNode = rb_define_class_under(rb_cPrism, "RequiredParameterNode", rb_cPrismNode); + rb_cPrismRescueModifierNode = rb_define_class_under(rb_cPrism, "RescueModifierNode", rb_cPrismNode); + rb_cPrismRescueNode = rb_define_class_under(rb_cPrism, "RescueNode", rb_cPrismNode); + rb_cPrismRestParameterNode = rb_define_class_under(rb_cPrism, "RestParameterNode", rb_cPrismNode); + rb_cPrismRetryNode = rb_define_class_under(rb_cPrism, "RetryNode", rb_cPrismNode); + rb_cPrismReturnNode = rb_define_class_under(rb_cPrism, "ReturnNode", rb_cPrismNode); + rb_cPrismSelfNode = rb_define_class_under(rb_cPrism, "SelfNode", rb_cPrismNode); + rb_cPrismShareableConstantNode = rb_define_class_under(rb_cPrism, "ShareableConstantNode", rb_cPrismNode); + rb_cPrismSingletonClassNode = rb_define_class_under(rb_cPrism, "SingletonClassNode", rb_cPrismNode); + rb_cPrismSourceEncodingNode = rb_define_class_under(rb_cPrism, "SourceEncodingNode", rb_cPrismNode); + rb_cPrismSourceFileNode = rb_define_class_under(rb_cPrism, "SourceFileNode", rb_cPrismNode); + rb_cPrismSourceLineNode = rb_define_class_under(rb_cPrism, "SourceLineNode", rb_cPrismNode); + rb_cPrismSplatNode = rb_define_class_under(rb_cPrism, "SplatNode", rb_cPrismNode); + rb_cPrismStatementsNode = rb_define_class_under(rb_cPrism, "StatementsNode", rb_cPrismNode); + rb_cPrismStringNode = rb_define_class_under(rb_cPrism, "StringNode", rb_cPrismNode); + rb_cPrismSuperNode = rb_define_class_under(rb_cPrism, "SuperNode", rb_cPrismNode); + rb_cPrismSymbolNode = rb_define_class_under(rb_cPrism, "SymbolNode", rb_cPrismNode); + rb_cPrismTrueNode = rb_define_class_under(rb_cPrism, "TrueNode", rb_cPrismNode); + rb_cPrismUndefNode = rb_define_class_under(rb_cPrism, "UndefNode", rb_cPrismNode); + rb_cPrismUnlessNode = rb_define_class_under(rb_cPrism, "UnlessNode", rb_cPrismNode); + rb_cPrismUntilNode = rb_define_class_under(rb_cPrism, "UntilNode", rb_cPrismNode); + rb_cPrismWhenNode = rb_define_class_under(rb_cPrism, "WhenNode", rb_cPrismNode); + rb_cPrismWhileNode = rb_define_class_under(rb_cPrism, "WhileNode", rb_cPrismNode); + rb_cPrismXStringNode = rb_define_class_under(rb_cPrism, "XStringNode", rb_cPrismNode); + rb_cPrismYieldNode = rb_define_class_under(rb_cPrism, "YieldNode", rb_cPrismNode); +} diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/ext/prism/api_pack.c b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/ext/prism/api_pack.c new file mode 100644 index 0000000..98509ae --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/ext/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/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/ext/prism/extconf.rb b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/ext/prism/extconf.rb new file mode 100644 index 0000000..ae1691c --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/ext/prism/extconf.rb @@ -0,0 +1,127 @@ +# frozen_string_literal: true + +require "rbconfig" + +if ARGV.delete("--help") + print(<<~TEXT) + USAGE: ruby #{$PROGRAM_NAME} [options] + + Flags that are always valid: + + --enable-build-debug + Enable debug build. + You may also set the PRISM_BUILD_DEBUG environment variable. + + --enable-build-minimal + Enable minimal build. + You may also set the PRISM_BUILD_MINIMAL environment variable. + + --help + Display this message. + + Environment variables used: + + PRISM_BUILD_DEBUG + Equivalent to `--enable-build-debug` when set, even if nil or blank. + + PRISM_BUILD_MINIMAL + Equivalent to `--enable-build-minimal` when set, even if nil or blank. + + TEXT + exit!(0) +end + +# If this gem is being build from a git source, then we need to run +# templating if it hasn't been run yet. In normal packaging, we would have +# shipped the templated files with the gem, so this wouldn't be necessary. +def generate_templates + Dir.chdir(File.expand_path("../..", __dir__)) do + if !File.exist?("include/prism/ast.h") && Dir.exist?(".git") + system(RbConfig.ruby, "templates/template.rb", exception: true) + end + end +end + +# Runs `make` in the root directory of the project. Note that this is the +# `Makefile` for the overall project, not the `Makefile` that is being generated +# by this script.` +def make(env, target) + puts "Running make #{target} with #{env.inspect}" + Dir.chdir(File.expand_path("../..", __dir__)) do + system( + env, + RUBY_PLATFORM.match?(/openbsd|freebsd/) ? "gmake" : "make", + target, + exception: true + ) + end +end + +# On non-CRuby we only need the shared library since we'll interface with it +# through FFI, so we'll build only that and not the C extension. We also avoid +# `require "mkmf"` as that prepends the GraalVM LLVM toolchain to PATH on TruffleRuby < 24.0, +# but we want to use the system toolchain here since libprism is run natively. +if RUBY_ENGINE != "ruby" + generate_templates + soext = RbConfig::CONFIG["SOEXT"] + # Pass SOEXT to avoid an extra subprocess just to query that + make({ "SOEXT" => soext }, "build/libprism.#{soext}") + File.write("Makefile", "all install clean:\n\t@#{RbConfig::CONFIG["NULLCMD"]}\n") + return +end + +require "mkmf" + +# First, ensure that we can find the header for the prism library. +generate_templates # Templates should be generated before find_header. +unless find_header("prism.h", File.expand_path("../../include", __dir__)) + raise "prism.h is required" +end + +# Next, ensure we can find the header for the C extension. Explicitly look for +# the extension header in the parent directory because we want to consistently +# look for `prism/extension.h` in our source files to line up with our mirroring +# in CRuby. +unless find_header("prism/extension.h", File.expand_path("..", __dir__)) + raise "prism/extension.h is required" +end + +# If `--enable-build-debug` is passed to this script or the +# `PRISM_BUILD_DEBUG` environment variable is defined, we'll build with the +# `PRISM_BUILD_DEBUG` macro defined. This causes parse functions to +# duplicate their input so that they have clearly set bounds, which is useful +# for finding bugs that cause the parser to read off the end of the input. +if enable_config("build-debug", ENV["PRISM_BUILD_DEBUG"] || false) + append_cflags("-DPRISM_BUILD_DEBUG") +end + +# If `--enable-build-minimal` is passed to this script or the +# `PRISM_BUILD_MINIMAL` environment variable is defined, we'll build with the +# set of defines that comprise the minimal set. This causes the parser to be +# built with minimal features, necessary for stripping out functionality when +# the size of the final built artifact is a concern. +if enable_config("build-minimal", ENV["PRISM_BUILD_MINIMAL"] || false) + append_cflags("-DPRISM_BUILD_MINIMAL") +end + +# By default, all symbols are hidden in the shared library. +append_cflags("-fvisibility=hidden") + +def src_list(path) + srcdir = path.dup + RbConfig.expand(srcdir) # mutates srcdir :-/ + Dir[File.join(srcdir, "*.{#{SRC_EXT.join(%q{,})}}")] +end + +def add_libprism_source(path) + $VPATH << path + src_list path +end + +$srcs = src_list("$(srcdir)") + + add_libprism_source("$(srcdir)/../../src") + + add_libprism_source("$(srcdir)/../../src/util") + +# Finally, we'll create the `Makefile` that is going to be used to configure and +# build the C extension. +create_makefile("prism/prism") diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/ext/prism/extension.c b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/ext/prism/extension.c new file mode 100644 index 0000000..71c2d91 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/ext/prism/extension.c @@ -0,0 +1,1427 @@ +#include "prism/extension.h" + +#ifdef _WIN32 +#include +#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_cPrismLexResult; +VALUE rb_cPrismParseLexResult; +VALUE rb_cPrismStringQuery; +VALUE rb_cPrismScope; +VALUE rb_cPrismCurrentVersionError; + +VALUE rb_cPrismDebugEncoding; + +ID rb_id_option_command_line; +ID rb_id_option_encoding; +ID rb_id_option_filepath; +ID rb_id_option_freeze; +ID rb_id_option_frozen_string_literal; +ID rb_id_option_line; +ID rb_id_option_main_script; +ID rb_id_option_partial_script; +ID rb_id_option_scopes; +ID rb_id_option_version; +ID rb_id_source_for; +ID rb_id_forwarding_positionals; +ID rb_id_forwarding_keywords; +ID rb_id_forwarding_block; +ID rb_id_forwarding_all; + +/******************************************************************************/ +/* IO of Ruby code */ +/******************************************************************************/ + +/** + * Check if the given VALUE is a string. If it's not a string, then raise a + * TypeError. Otherwise return the VALUE as a C string. + */ +static const char * +check_string(VALUE value) { + // 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); + + // The scope can be either an array or it can be a Prism::Scope object. + // Parse out the correct values here from either. + VALUE locals; + uint8_t forwarding = PM_OPTIONS_SCOPE_FORWARDING_NONE; + + if (RB_TYPE_P(scope, T_ARRAY)) { + locals = scope; + } else if (rb_obj_is_kind_of(scope, rb_cPrismScope)) { + locals = rb_ivar_get(scope, rb_intern("@locals")); + if (!RB_TYPE_P(locals, T_ARRAY)) { + rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Array)", rb_obj_class(locals)); + } + + VALUE names = rb_ivar_get(scope, rb_intern("@forwarding")); + if (!RB_TYPE_P(names, T_ARRAY)) { + rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Array)", rb_obj_class(names)); + } + + size_t names_count = RARRAY_LEN(names); + for (size_t name_index = 0; name_index < names_count; name_index++) { + VALUE name = rb_ary_entry(names, name_index); + + // Check that the name is a symbol. If it's not, then raise + // a type error. + if (!RB_TYPE_P(name, T_SYMBOL)) { + rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Symbol)", rb_obj_class(name)); + } + + ID id = SYM2ID(name); + if (id == rb_id_forwarding_positionals) { + forwarding |= PM_OPTIONS_SCOPE_FORWARDING_POSITIONALS; + } else if (id == rb_id_forwarding_keywords) { + forwarding |= PM_OPTIONS_SCOPE_FORWARDING_KEYWORDS; + } else if (id == rb_id_forwarding_block) { + forwarding |= PM_OPTIONS_SCOPE_FORWARDING_BLOCK; + } else if (id == rb_id_forwarding_all) { + forwarding |= PM_OPTIONS_SCOPE_FORWARDING_ALL; + } else { + rb_raise(rb_eArgError, "invalid forwarding value: %" PRIsVALUE, name); + } + } + } else { + rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Array or Prism::Scope)", rb_obj_class(scope)); + } + + // Initialize the scope array. + size_t locals_count = RARRAY_LEN(locals); + 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(locals, 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)); + } + + // Now set the forwarding options. + pm_options_scope_forwarding_set(options_scope, forwarding); + } +} + +/** + * 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_id_option_filepath) { + if (!NIL_P(value)) pm_options_filepath_set(options, check_string(value)); + } else if (key_id == rb_id_option_encoding) { + if (!NIL_P(value)) { + if (value == Qfalse) { + pm_options_encoding_locked_set(options, true); + } else { + pm_options_encoding_set(options, rb_enc_name(rb_to_encoding(value))); + } + } + } else if (key_id == rb_id_option_line) { + if (!NIL_P(value)) pm_options_line_set(options, NUM2INT(value)); + } else if (key_id == rb_id_option_frozen_string_literal) { + if (!NIL_P(value)) pm_options_frozen_string_literal_set(options, RTEST(value)); + } else if (key_id == rb_id_option_version) { + if (!NIL_P(value)) { + const char *version = check_string(value); + + if (RSTRING_LEN(value) == 7 && strncmp(version, "current", 7) == 0) { + const char *current_version = RSTRING_PTR(rb_const_get(rb_cObject, rb_intern("RUBY_VERSION"))); + if (!pm_options_version_set(options, current_version, 3)) { + rb_exc_raise(rb_exc_new_cstr(rb_cPrismCurrentVersionError, current_version)); + } + } else if (!pm_options_version_set(options, version, RSTRING_LEN(value))) { + rb_raise(rb_eArgError, "invalid version: %" PRIsVALUE, value); + } + } + } else if (key_id == rb_id_option_scopes) { + if (!NIL_P(value)) build_options_scopes(options, value); + } else if (key_id == rb_id_option_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 if (key_id == rb_id_option_main_script) { + if (!NIL_P(value)) pm_options_main_script_set(options, RTEST(value)); + } else if (key_id == rb_id_option_partial_script) { + if (!NIL_P(value)) pm_options_partial_script_set(options, RTEST(value)); + } else if (key_id == rb_id_option_freeze) { + if (!NIL_P(value)) pm_options_freeze_set(options, RTEST(value)); + } 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 *encoded_filepath) { + VALUE filepath; + VALUE keywords; + rb_scan_args(argc, argv, "1:", &filepath, &keywords); + + Check_Type(filepath, T_STRING); + *encoded_filepath = rb_str_encode_ospath(filepath); + extract_options(options, *encoded_filepath, keywords); + + const char *source = (const char *) pm_string_source(&options->filepath); + pm_string_init_result_t result; + + switch (result = pm_string_file_init(input, source)) { + case PM_STRING_INIT_SUCCESS: + break; + case PM_STRING_INIT_ERROR_GENERIC: { + pm_options_free(options); + +#ifdef _WIN32 + int e = rb_w32_map_errno(GetLastError()); +#else + int e = errno; +#endif + + rb_syserr_fail(e, source); + break; + } + case PM_STRING_INIT_ERROR_DIRECTORY: + pm_options_free(options); + rb_syserr_fail(EISDIR, source); + break; + default: + pm_options_free(options); + rb_raise(rb_eRuntimeError, "Unknown error (%d) initializing file: %s", result, source); + break; + } +} + +#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); + if (options.freeze) rb_obj_freeze(value); + +#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 }; + + VALUE encoded_filepath; + file_options(argc, argv, &input, &options, &encoded_filepath); + + VALUE value = dump_input(&input, &options); + pm_string_free(&input); + pm_options_free(&options); + + return value; +} + +#endif + +/******************************************************************************/ +/* Extracting values for the parse result */ +/******************************************************************************/ + +/** + * The same as rb_class_new_instance, but accepts an additional boolean to + * indicate whether or not the resulting class instance should be frozen. + */ +static inline VALUE +rb_class_new_instance_freeze(int argc, const VALUE *argv, VALUE klass, bool freeze) { + VALUE value = rb_class_new_instance(argc, argv, klass); + if (freeze) rb_obj_freeze(value); + return value; +} + +/** + * Create a new Location instance from the given parser and bounds. + */ +static inline VALUE +parser_location(const pm_parser_t *parser, VALUE source, bool freeze, const uint8_t *start, size_t length) { + VALUE argv[] = { source, LONG2FIX(start - parser->start), LONG2FIX(length) }; + return rb_class_new_instance_freeze(3, argv, rb_cPrismLocation, freeze); +} + +/** + * Create a new Location instance from the given parser and location. + */ +#define PARSER_LOCATION_LOC(parser, source, freeze, loc) \ + parser_location(parser, source, freeze, loc.start, (size_t) (loc.end - loc.start)) + +/** + * Build a new Comment instance from the given parser and comment. + */ +static inline VALUE +parser_comment(const pm_parser_t *parser, VALUE source, bool freeze, const pm_comment_t *comment) { + VALUE argv[] = { PARSER_LOCATION_LOC(parser, source, freeze, comment->location) }; + VALUE type = (comment->type == PM_COMMENT_EMBDOC) ? rb_cPrismEmbDocComment : rb_cPrismInlineComment; + return rb_class_new_instance_freeze(1, argv, type, freeze); +} + +/** + * Extract the comments out of the parser into an array. + */ +static VALUE +parser_comments(const pm_parser_t *parser, VALUE source, bool freeze) { + VALUE comments = rb_ary_new_capa(parser->comment_list.size); + + for ( + const pm_comment_t *comment = (const pm_comment_t *) parser->comment_list.head; + comment != NULL; + comment = (const pm_comment_t *) comment->node.next + ) { + VALUE value = parser_comment(parser, source, freeze, comment); + rb_ary_push(comments, value); + } + + if (freeze) rb_obj_freeze(comments); + return comments; +} + +/** + * Build a new MagicComment instance from the given parser and magic comment. + */ +static inline VALUE +parser_magic_comment(const pm_parser_t *parser, VALUE source, bool freeze, const pm_magic_comment_t *magic_comment) { + VALUE key_loc = parser_location(parser, source, freeze, magic_comment->key_start, magic_comment->key_length); + VALUE value_loc = parser_location(parser, source, freeze, magic_comment->value_start, magic_comment->value_length); + VALUE argv[] = { key_loc, value_loc }; + return rb_class_new_instance_freeze(2, argv, rb_cPrismMagicComment, freeze); +} + +/** + * Extract the magic comments out of the parser into an array. + */ +static VALUE +parser_magic_comments(const pm_parser_t *parser, VALUE source, bool freeze) { + VALUE magic_comments = rb_ary_new_capa(parser->magic_comment_list.size); + + for ( + const pm_magic_comment_t *magic_comment = (const pm_magic_comment_t *) parser->magic_comment_list.head; + magic_comment != NULL; + magic_comment = (const pm_magic_comment_t *) magic_comment->node.next + ) { + VALUE value = parser_magic_comment(parser, source, freeze, magic_comment); + rb_ary_push(magic_comments, value); + } + + if (freeze) rb_obj_freeze(magic_comments); + 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, bool freeze) { + if (parser->data_loc.end == NULL) { + return Qnil; + } else { + return PARSER_LOCATION_LOC(parser, source, freeze, parser->data_loc); + } +} + +/** + * Extract the errors out of the parser into an array. + */ +static VALUE +parser_errors(const pm_parser_t *parser, rb_encoding *encoding, VALUE source, bool freeze) { + VALUE errors = rb_ary_new_capa(parser->error_list.size); + + for ( + const pm_diagnostic_t *error = (const pm_diagnostic_t *) parser->error_list.head; + error != NULL; + error = (const pm_diagnostic_t *) error->node.next + ) { + VALUE type = ID2SYM(rb_intern(pm_diagnostic_id_human(error->diag_id))); + VALUE message = rb_obj_freeze(rb_enc_str_new_cstr(error->message, encoding)); + VALUE location = PARSER_LOCATION_LOC(parser, source, freeze, error->location); + + 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 argv[] = { type, message, location, level }; + VALUE value = rb_class_new_instance_freeze(4, argv, rb_cPrismParseError, freeze); + rb_ary_push(errors, value); + } + + if (freeze) rb_obj_freeze(errors); + return errors; +} + +/** + * Extract the warnings out of the parser into an array. + */ +static VALUE +parser_warnings(const pm_parser_t *parser, rb_encoding *encoding, VALUE source, bool freeze) { + VALUE warnings = rb_ary_new_capa(parser->warning_list.size); + + for ( + const pm_diagnostic_t *warning = (const pm_diagnostic_t *) parser->warning_list.head; + warning != NULL; + warning = (const pm_diagnostic_t *) warning->node.next + ) { + VALUE type = ID2SYM(rb_intern(pm_diagnostic_id_human(warning->diag_id))); + VALUE message = rb_obj_freeze(rb_enc_str_new_cstr(warning->message, encoding)); + VALUE location = PARSER_LOCATION_LOC(parser, source, freeze, warning->location); + + 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 argv[] = { type, message, location, level }; + VALUE value = rb_class_new_instance_freeze(4, argv, rb_cPrismParseWarning, freeze); + rb_ary_push(warnings, value); + } + + if (freeze) rb_obj_freeze(warnings); + return warnings; +} + +/** + * Create a new parse result from the given parser, value, encoding, and source. + */ +static VALUE +parse_result_create(VALUE class, const pm_parser_t *parser, VALUE value, rb_encoding *encoding, VALUE source, bool freeze) { + VALUE result_argv[] = { + value, + parser_comments(parser, source, freeze), + parser_magic_comments(parser, source, freeze), + parser_data_loc(parser, source, freeze), + parser_errors(parser, encoding, source, freeze), + parser_warnings(parser, encoding, source, freeze), + source + }; + + return rb_class_new_instance_freeze(7, result_argv, class, freeze); +} + +/******************************************************************************/ +/* 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; + bool freeze; +} 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 value = pm_token_new(parser, token, parse_lex_data->encoding, parse_lex_data->source, parse_lex_data->freeze); + VALUE yields = rb_assoc_new(value, INT2FIX(parser->lex_state)); + + if (parse_lex_data->freeze) { + rb_obj_freeze(value); + rb_obj_freeze(yields); + } + + 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; + VALUE next_tokens = rb_ary_new(); + + 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")); + VALUE next_value = rb_str_dup(value); + + rb_enc_associate(next_value, parse_lex_data->encoding); + if (parse_lex_data->freeze) rb_obj_freeze(next_value); + + VALUE next_token_argv[] = { + parse_lex_data->source, + rb_ivar_get(token, rb_intern("@type")), + next_value, + rb_ivar_get(token, rb_intern("@location")) + }; + + VALUE next_token = rb_class_new_instance(4, next_token_argv, rb_cPrismToken); + VALUE next_yields = rb_assoc_new(next_token, rb_ary_entry(yields, 1)); + + if (parse_lex_data->freeze) { + rb_obj_freeze(next_token); + rb_obj_freeze(next_yields); + } + + rb_ary_push(next_tokens, next_yields); + } + + rb_ary_replace(parse_lex_data->tokens, next_tokens); +} + +/** + * 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_capa(parser.newline_list.size); + VALUE source = rb_funcall(rb_cPrismSource, rb_id_source_for, 3, source_string, LONG2NUM(parser.start_line), offsets); + + parse_lex_data_t parse_lex_data = { + .source = source, + .tokens = rb_ary_new(), + .encoding = rb_utf8_encoding(), + .freeze = options->freeze, + }; + + 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])); + } + + if (options->freeze) { + rb_obj_freeze(source_string); + rb_obj_freeze(offsets); + rb_obj_freeze(source); + rb_obj_freeze(parse_lex_data.tokens); + } + + VALUE result; + if (return_nodes) { + VALUE value = rb_ary_new_capa(2); + rb_ary_push(value, pm_ast_new(&parser, node, parse_lex_data.encoding, source, options->freeze)); + rb_ary_push(value, parse_lex_data.tokens); + if (options->freeze) rb_obj_freeze(value); + result = parse_result_create(rb_cPrismParseLexResult, &parser, value, parse_lex_data.encoding, source, options->freeze); + } else { + result = parse_result_create(rb_cPrismLexResult, &parser, parse_lex_data.tokens, parse_lex_data.encoding, source, options->freeze); + } + + pm_node_destroy(&parser, node); + pm_parser_free(&parser); + + return result; +} + +/** + * call-seq: + * Prism::lex(source, **options) -> LexResult + * + * Return a LexResult instance that contains 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) -> LexResult + * + * Return a LexResult instance that contains 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 }; + + VALUE encoded_filepath; + file_options(argc, argv, &input, &options, &encoded_filepath); + + 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, options->freeze); + VALUE value = pm_ast_new(&parser, node, encoding, source, options->freeze); + VALUE result = parse_result_create(rb_cPrismParseResult, &parser, value, encoding, source, options->freeze); + + if (options->freeze) { + rb_obj_freeze(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. + * * `freeze` - whether or not to deeply freeze the AST. This should be a + * boolean 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. + * * `main_script` - a boolean indicating whether or not the source being parsed + * is the main script being run by the interpreter. This controls whether + * or not shebangs are parsed for additional flags and whether or not the + * parser will attempt to find a matching shebang if the first one does + * not contain the word "ruby". + * * `partial_script` - when the file being parsed is considered a "partial" + * script, jumps will not be marked as errors if they are not contained + * within loops/blocks. This is used in the case that you're parsing a + * script that you know will be embedded inside another script later, but + * you do not have that context yet. For example, when parsing an ERB + * template that will be evaluated inside another script. + * * `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, e.g., with `"3.3.0"`. To parse with the same syntax version that + * the current Ruby is running use `version: "current"`. Raises + * ArgumentError if the version is not currently supported by Prism. + */ +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; +} + +/** + * 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 }; + + VALUE encoded_filepath; + file_options(argc, argv, &input, &options, &encoded_filepath); + + VALUE value = parse_input(&input, &options); + pm_string_free(&input); + pm_options_free(&options); + + return value; +} + +/** + * Parse the given input and return nothing. + */ +static void +profile_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); + pm_node_destroy(&parser, node); + pm_parser_free(&parser); +} + +/** + * call-seq: + * Prism::profile(source, **options) -> nil + * + * Parse the given string and return nothing. This method is meant to allow + * profilers to avoid the overhead of reifying the AST to Ruby. For supported + * options, see Prism::parse. + */ +static VALUE +profile(int argc, VALUE *argv, VALUE self) { + pm_string_t input; + pm_options_t options = { 0 }; + + string_options(argc, argv, &input, &options); + profile_input(&input, &options); + pm_string_free(&input); + pm_options_free(&options); + + return Qnil; +} + +/** + * call-seq: + * Prism::profile_file(filepath, **options) -> nil + * + * Parse the given file and return nothing. This method is meant to allow + * profilers to avoid the overhead of reifying the AST to Ruby. For supported + * options, see Prism::parse. + */ +static VALUE +profile_file(int argc, VALUE *argv, VALUE self) { + pm_string_t input; + pm_options_t options = { 0 }; + + VALUE encoded_filepath; + file_options(argc, argv, &input, &options, &encoded_filepath); + + profile_input(&input, &options); + pm_string_free(&input); + pm_options_free(&options); + + return Qnil; +} + +static int +parse_stream_eof(void *stream) { + if (rb_funcall((VALUE) stream, rb_intern("eof?"), 0)) { + return 1; + } + return 0; +} + +/** + * 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 = RSTRING_PTR(line); + long length = RSTRING_LEN(line); + + 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, parse_stream_eof, &options); + rb_encoding *encoding = rb_enc_find(parser.encoding->name); + + VALUE source = pm_source_new(&parser, encoding, options.freeze); + VALUE value = pm_ast_new(&parser, node, encoding, source, options.freeze); + VALUE result = parse_result_create(rb_cPrismParseResult, &parser, value, encoding, source, options.freeze); + + pm_node_destroy(&parser, node); + pm_buffer_free(&buffer); + pm_parser_free(&parser); + + return result; +} + +/** + * 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, options->freeze); + VALUE comments = parser_comments(&parser, source, options->freeze); + + 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 }; + + VALUE encoded_filepath; + file_options(argc, argv, &input, &options, &encoded_filepath); + + VALUE value = parse_input_comments(&input, &options); + pm_string_free(&input); + pm_options_free(&options); + + return value; +} + +/** + * call-seq: + * Prism::parse_lex(source, **options) -> ParseLexResult + * + * Parse the given string and return a ParseLexResult 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) -> ParseLexResult + * + * Parse the given file and return a ParseLexResult 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 }; + + VALUE encoded_filepath; + file_options(argc, argv, &input, &options, &encoded_filepath); + + 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 }; + + VALUE encoded_filepath; + file_options(argc, argv, &input, &options, &encoded_filepath); + + 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; +} + +/******************************************************************************/ +/* String query methods */ +/******************************************************************************/ + +/** + * Process the result of a call to a string query method and return an + * appropriate value. + */ +static VALUE +string_query(pm_string_query_t result) { + switch (result) { + case PM_STRING_QUERY_ERROR: + rb_raise(rb_eArgError, "Invalid or non ascii-compatible encoding"); + return Qfalse; + case PM_STRING_QUERY_FALSE: + return Qfalse; + case PM_STRING_QUERY_TRUE: + return Qtrue; + } + return Qfalse; +} + +/** + * call-seq: + * Prism::StringQuery::local?(string) -> bool + * + * Returns true if the string constitutes a valid local variable name. Note that + * this means the names that can be set through Binding#local_variable_set, not + * necessarily the ones that can be set through a local variable assignment. + */ +static VALUE +string_query_local_p(VALUE self, VALUE string) { + const uint8_t *source = (const uint8_t *) check_string(string); + return string_query(pm_string_query_local(source, RSTRING_LEN(string), rb_enc_get(string)->name)); +} + +/** + * call-seq: + * Prism::StringQuery::constant?(string) -> bool + * + * Returns true if the string constitutes a valid constant name. Note that this + * means the names that can be set through Module#const_set, not necessarily the + * ones that can be set through a constant assignment. + */ +static VALUE +string_query_constant_p(VALUE self, VALUE string) { + const uint8_t *source = (const uint8_t *) check_string(string); + return string_query(pm_string_query_constant(source, RSTRING_LEN(string), rb_enc_get(string)->name)); +} + +/** + * call-seq: + * Prism::StringQuery::method_name?(string) -> bool + * + * Returns true if the string constitutes a valid method name. + */ +static VALUE +string_query_method_name_p(VALUE self, VALUE string) { + const uint8_t *source = (const uint8_t *) check_string(string); + return string_query(pm_string_query_method_name(source, RSTRING_LEN(string), rb_enc_get(string)->name)); +} + +/******************************************************************************/ +/* 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 + ); + } + +#ifdef HAVE_RB_EXT_RACTOR_SAFE + // Mark this extension as Ractor-safe. + rb_ext_ractor_safe(true); +#endif + + // 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_cPrismLexResult = rb_define_class_under(rb_cPrism, "LexResult", rb_cPrismResult); + rb_cPrismParseLexResult = rb_define_class_under(rb_cPrism, "ParseLexResult", rb_cPrismResult); + rb_cPrismStringQuery = rb_define_class_under(rb_cPrism, "StringQuery", rb_cObject); + rb_cPrismScope = rb_define_class_under(rb_cPrism, "Scope", rb_cObject); + + rb_cPrismCurrentVersionError = rb_const_get(rb_cPrism, rb_intern("CurrentVersionError")); + + // Intern all of the IDs eagerly that we support so that we don't have to do + // it every time we parse. + rb_id_option_command_line = rb_intern_const("command_line"); + rb_id_option_encoding = rb_intern_const("encoding"); + rb_id_option_filepath = rb_intern_const("filepath"); + rb_id_option_freeze = rb_intern_const("freeze"); + rb_id_option_frozen_string_literal = rb_intern_const("frozen_string_literal"); + rb_id_option_line = rb_intern_const("line"); + rb_id_option_main_script = rb_intern_const("main_script"); + rb_id_option_partial_script = rb_intern_const("partial_script"); + rb_id_option_scopes = rb_intern_const("scopes"); + rb_id_option_version = rb_intern_const("version"); + rb_id_source_for = rb_intern("for"); + rb_id_forwarding_positionals = rb_intern("*"); + rb_id_forwarding_keywords = rb_intern("**"); + rb_id_forwarding_block = rb_intern("&"); + rb_id_forwarding_all = rb_intern("..."); + + /** + * The version of the prism library. + */ + rb_define_const(rb_cPrism, "VERSION", rb_str_freeze(rb_str_new_cstr(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_file", parse_file, -1); + rb_define_singleton_method(rb_cPrism, "profile", profile, -1); + rb_define_singleton_method(rb_cPrism, "profile_file", profile_file, -1); + rb_define_singleton_method(rb_cPrism, "parse_stream", parse_stream, -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 + + rb_define_singleton_method(rb_cPrismStringQuery, "local?", string_query_local_p, 1); + rb_define_singleton_method(rb_cPrismStringQuery, "constant?", string_query_constant_p, 1); + rb_define_singleton_method(rb_cPrismStringQuery, "method_name?", string_query_method_name_p, 1); + + // Next, initialize the other APIs. + Init_prism_api_node(); + Init_prism_pack(); +} diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/ext/prism/extension.h b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/ext/prism/extension.h new file mode 100644 index 0000000..4ddc3a7 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/ext/prism/extension.h @@ -0,0 +1,19 @@ +#ifndef PRISM_EXT_NODE_H +#define PRISM_EXT_NODE_H + +#define EXPECTED_PRISM_VERSION "1.9.0" + +#include +#include +#include "prism.h" + +VALUE pm_source_new(const pm_parser_t *parser, rb_encoding *encoding, bool freeze); +VALUE pm_token_new(const pm_parser_t *parser, const pm_token_t *token, rb_encoding *encoding, VALUE source, bool freeze); +VALUE pm_ast_new(const pm_parser_t *parser, const pm_node_t *node, rb_encoding *encoding, VALUE source, bool freeze); +VALUE pm_integer_new(const pm_integer_t *integer); + +void Init_prism_api_node(void); +void Init_prism_pack(void); +RUBY_FUNC_EXPORTED void Init_prism(void); + +#endif diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism.h b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism.h new file mode 100644 index 0000000..c468db1 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism.h @@ -0,0 +1,408 @@ +/** + * @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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef _WIN32 +#include +#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. + * + * The resulting parser must eventually be freed with `pm_parser_free()`. + * + * @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. These options must + * live for the whole lifetime of this parser. + * + * \public \memberof pm_parser + */ +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. + * + * \public \memberof pm_parser + */ +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. + * + * This does not free the `pm_options_t` object that was used to initialize the + * parser. + * + * @param parser The parser to free. + * + * \public \memberof pm_parser + */ +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. + * + * \public \memberof pm_parser + */ +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); + +/** + * This function is used in pm_parse_stream to check whether a stream is EOF. + * It closely mirrors that of feof so that feof can be used as the + * default implementation. + */ +typedef int (pm_parse_stream_feof_t)(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 stream_fgets The function to use to read from the stream. + * @param stream_feof The function to use to determine if the stream has hit eof. + * @param options The optional options to use when parsing. + * @return The AST representing the source. + * + * \public \memberof pm_parser + */ +PRISM_EXPORTED_FUNCTION pm_node_t * pm_parse_stream(pm_parser_t *parser, pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *stream_fgets, pm_parse_stream_feof_t *stream_feof, 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 stream_fgets The function to use to read from the stream. + * @param stream_feof The function to use to tell if the stream has hit eof. + * @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 *stream_fgets, pm_parse_stream_feof_t *stream_feof, 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); + +// 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 + +/** + * Represents the results of a slice query. + */ +typedef enum { + /** Returned if the encoding given to a slice query was invalid. */ + PM_STRING_QUERY_ERROR = -1, + + /** Returned if the result of the slice query is false. */ + PM_STRING_QUERY_FALSE, + + /** Returned if the result of the slice query is true. */ + PM_STRING_QUERY_TRUE +} pm_string_query_t; + +/** + * Check that the slice is a valid local variable name. + * + * @param source The source to check. + * @param length The length of the source. + * @param encoding_name The name of the encoding of the source. + * @return PM_STRING_QUERY_TRUE if the query is true, PM_STRING_QUERY_FALSE if + * the query is false, and PM_STRING_QUERY_ERROR if the encoding was invalid. + */ +PRISM_EXPORTED_FUNCTION pm_string_query_t pm_string_query_local(const uint8_t *source, size_t length, const char *encoding_name); + +/** + * Check that the slice is a valid constant name. + * + * @param source The source to check. + * @param length The length of the source. + * @param encoding_name The name of the encoding of the source. + * @return PM_STRING_QUERY_TRUE if the query is true, PM_STRING_QUERY_FALSE if + * the query is false, and PM_STRING_QUERY_ERROR if the encoding was invalid. + */ +PRISM_EXPORTED_FUNCTION pm_string_query_t pm_string_query_constant(const uint8_t *source, size_t length, const char *encoding_name); + +/** + * Check that the slice is a valid method name. + * + * @param source The source to check. + * @param length The length of the source. + * @param encoding_name The name of the encoding of the source. + * @return PM_STRING_QUERY_TRUE if the query is true, PM_STRING_QUERY_FALSE if + * the query is false, and PM_STRING_QUERY_ERROR if the encoding was invalid. + */ +PRISM_EXPORTED_FUNCTION pm_string_query_t pm_string_query_method_name(const uint8_t *source, size_t length, const char *encoding_name); + +/** + * @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/truffleruby/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/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/ast.h b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/ast.h new file mode 100644 index 0000000..6911006 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/ast.h @@ -0,0 +1,8254 @@ +/* :markup: markdown */ + +/*----------------------------------------------------------------------------*/ +/* This file is generated by the templates/template.rb script and should not */ +/* be modified manually. See */ +/* templates/include/prism/ast.h.erb */ +/* if you are looking to modify the */ +/* template */ +/*----------------------------------------------------------------------------*/ + +/** + * @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 +#include +#include + +/** + * This enum represents every type of token in the Ruby source. + */ +typedef enum pm_token_type { + /** final token in the file */ + PM_TOKEN_EOF = 1, + + /** } */ + PM_TOKEN_BRACE_RIGHT, + + /** , */ + PM_TOKEN_COMMA, + + /** } */ + PM_TOKEN_EMBEXPR_END, + + /** do */ + PM_TOKEN_KEYWORD_DO, + + /** else */ + PM_TOKEN_KEYWORD_ELSE, + + /** elsif */ + PM_TOKEN_KEYWORD_ELSIF, + + /** end */ + PM_TOKEN_KEYWORD_END, + + /** ensure */ + PM_TOKEN_KEYWORD_ENSURE, + + /** in */ + PM_TOKEN_KEYWORD_IN, + + /** rescue */ + PM_TOKEN_KEYWORD_RESCUE, + + /** then */ + PM_TOKEN_KEYWORD_THEN, + + /** when */ + PM_TOKEN_KEYWORD_WHEN, + + /** a newline character outside of other tokens */ + PM_TOKEN_NEWLINE, + + /** ) */ + PM_TOKEN_PARENTHESIS_RIGHT, + + /** | */ + PM_TOKEN_PIPE, + + /** ; */ + PM_TOKEN_SEMICOLON, + + /** & */ + PM_TOKEN_AMPERSAND, + + /** && */ + PM_TOKEN_AMPERSAND_AMPERSAND, + + /** &&= */ + PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL, + + /** &. */ + PM_TOKEN_AMPERSAND_DOT, + + /** &= */ + PM_TOKEN_AMPERSAND_EQUAL, + + /** ` */ + PM_TOKEN_BACKTICK, + + /** a back reference */ + PM_TOKEN_BACK_REFERENCE, + + /** ! or !@ */ + PM_TOKEN_BANG, + + /** != */ + PM_TOKEN_BANG_EQUAL, + + /** !~ */ + PM_TOKEN_BANG_TILDE, + + /** { */ + PM_TOKEN_BRACE_LEFT, + + /** [ */ + PM_TOKEN_BRACKET_LEFT, + + /** [ for the beginning of an array */ + PM_TOKEN_BRACKET_LEFT_ARRAY, + + /** [] */ + PM_TOKEN_BRACKET_LEFT_RIGHT, + + /** []= */ + PM_TOKEN_BRACKET_LEFT_RIGHT_EQUAL, + + /** ] */ + PM_TOKEN_BRACKET_RIGHT, + + /** ^ */ + PM_TOKEN_CARET, + + /** ^= */ + PM_TOKEN_CARET_EQUAL, + + /** a character literal */ + PM_TOKEN_CHARACTER_LITERAL, + + /** a class variable */ + PM_TOKEN_CLASS_VARIABLE, + + /** : */ + PM_TOKEN_COLON, + + /** :: */ + PM_TOKEN_COLON_COLON, + + /** a comment */ + PM_TOKEN_COMMENT, + + /** a constant */ + PM_TOKEN_CONSTANT, + + /** the . call operator */ + PM_TOKEN_DOT, + + /** the .. range operator */ + PM_TOKEN_DOT_DOT, + + /** the ... range operator or forwarding parameter */ + PM_TOKEN_DOT_DOT_DOT, + + /** =begin */ + PM_TOKEN_EMBDOC_BEGIN, + + /** =end */ + PM_TOKEN_EMBDOC_END, + + /** a line inside of embedded documentation */ + PM_TOKEN_EMBDOC_LINE, + + /** #{ */ + PM_TOKEN_EMBEXPR_BEGIN, + + /** # */ + PM_TOKEN_EMBVAR, + + /** = */ + PM_TOKEN_EQUAL, + + /** == */ + PM_TOKEN_EQUAL_EQUAL, + + /** === */ + PM_TOKEN_EQUAL_EQUAL_EQUAL, + + /** => */ + PM_TOKEN_EQUAL_GREATER, + + /** =~ */ + PM_TOKEN_EQUAL_TILDE, + + /** a floating point number */ + PM_TOKEN_FLOAT, + + /** a floating pointer number with an imaginary suffix */ + PM_TOKEN_FLOAT_IMAGINARY, + + /** a floating pointer number with a rational suffix */ + PM_TOKEN_FLOAT_RATIONAL, + + /** a floating pointer number with a rational and imaginary suffix */ + PM_TOKEN_FLOAT_RATIONAL_IMAGINARY, + + /** a global variable */ + PM_TOKEN_GLOBAL_VARIABLE, + + /** > */ + PM_TOKEN_GREATER, + + /** >= */ + PM_TOKEN_GREATER_EQUAL, + + /** >> */ + PM_TOKEN_GREATER_GREATER, + + /** >>= */ + PM_TOKEN_GREATER_GREATER_EQUAL, + + /** the end of a heredoc */ + PM_TOKEN_HEREDOC_END, + + /** the start of a heredoc */ + PM_TOKEN_HEREDOC_START, + + /** an identifier */ + PM_TOKEN_IDENTIFIER, + + /** an ignored newline */ + PM_TOKEN_IGNORED_NEWLINE, + + /** an instance variable */ + PM_TOKEN_INSTANCE_VARIABLE, + + /** an integer (any base) */ + PM_TOKEN_INTEGER, + + /** an integer with an imaginary suffix */ + PM_TOKEN_INTEGER_IMAGINARY, + + /** an integer with a rational suffix */ + PM_TOKEN_INTEGER_RATIONAL, + + /** an integer with a rational and imaginary suffix */ + PM_TOKEN_INTEGER_RATIONAL_IMAGINARY, + + /** alias */ + PM_TOKEN_KEYWORD_ALIAS, + + /** and */ + PM_TOKEN_KEYWORD_AND, + + /** begin */ + PM_TOKEN_KEYWORD_BEGIN, + + /** BEGIN */ + PM_TOKEN_KEYWORD_BEGIN_UPCASE, + + /** break */ + PM_TOKEN_KEYWORD_BREAK, + + /** case */ + PM_TOKEN_KEYWORD_CASE, + + /** class */ + PM_TOKEN_KEYWORD_CLASS, + + /** def */ + PM_TOKEN_KEYWORD_DEF, + + /** defined? */ + PM_TOKEN_KEYWORD_DEFINED, + + /** do keyword for a predicate in a while, until, or for loop */ + PM_TOKEN_KEYWORD_DO_LOOP, + + /** END */ + PM_TOKEN_KEYWORD_END_UPCASE, + + /** false */ + PM_TOKEN_KEYWORD_FALSE, + + /** for */ + PM_TOKEN_KEYWORD_FOR, + + /** if */ + PM_TOKEN_KEYWORD_IF, + + /** if in the modifier form */ + PM_TOKEN_KEYWORD_IF_MODIFIER, + + /** module */ + PM_TOKEN_KEYWORD_MODULE, + + /** next */ + PM_TOKEN_KEYWORD_NEXT, + + /** nil */ + PM_TOKEN_KEYWORD_NIL, + + /** not */ + PM_TOKEN_KEYWORD_NOT, + + /** or */ + PM_TOKEN_KEYWORD_OR, + + /** redo */ + PM_TOKEN_KEYWORD_REDO, + + /** rescue in the modifier form */ + PM_TOKEN_KEYWORD_RESCUE_MODIFIER, + + /** retry */ + PM_TOKEN_KEYWORD_RETRY, + + /** return */ + PM_TOKEN_KEYWORD_RETURN, + + /** self */ + PM_TOKEN_KEYWORD_SELF, + + /** super */ + PM_TOKEN_KEYWORD_SUPER, + + /** true */ + PM_TOKEN_KEYWORD_TRUE, + + /** undef */ + PM_TOKEN_KEYWORD_UNDEF, + + /** unless */ + PM_TOKEN_KEYWORD_UNLESS, + + /** unless in the modifier form */ + PM_TOKEN_KEYWORD_UNLESS_MODIFIER, + + /** until */ + PM_TOKEN_KEYWORD_UNTIL, + + /** until in the modifier form */ + PM_TOKEN_KEYWORD_UNTIL_MODIFIER, + + /** while */ + PM_TOKEN_KEYWORD_WHILE, + + /** while in the modifier form */ + PM_TOKEN_KEYWORD_WHILE_MODIFIER, + + /** yield */ + PM_TOKEN_KEYWORD_YIELD, + + /** __ENCODING__ */ + PM_TOKEN_KEYWORD___ENCODING__, + + /** __FILE__ */ + PM_TOKEN_KEYWORD___FILE__, + + /** __LINE__ */ + PM_TOKEN_KEYWORD___LINE__, + + /** a label */ + PM_TOKEN_LABEL, + + /** the end of a label */ + PM_TOKEN_LABEL_END, + + /** { */ + PM_TOKEN_LAMBDA_BEGIN, + + /** < */ + PM_TOKEN_LESS, + + /** <= */ + PM_TOKEN_LESS_EQUAL, + + /** <=> */ + PM_TOKEN_LESS_EQUAL_GREATER, + + /** << */ + PM_TOKEN_LESS_LESS, + + /** <<= */ + PM_TOKEN_LESS_LESS_EQUAL, + + /** a method name */ + PM_TOKEN_METHOD_NAME, + + /** - */ + PM_TOKEN_MINUS, + + /** -= */ + PM_TOKEN_MINUS_EQUAL, + + /** -> */ + PM_TOKEN_MINUS_GREATER, + + /** a numbered reference to a capture group in the previous regular expression match */ + PM_TOKEN_NUMBERED_REFERENCE, + + /** ( */ + PM_TOKEN_PARENTHESIS_LEFT, + + /** ( for a parentheses node */ + PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES, + + /** % */ + PM_TOKEN_PERCENT, + + /** %= */ + PM_TOKEN_PERCENT_EQUAL, + + /** %i */ + PM_TOKEN_PERCENT_LOWER_I, + + /** %w */ + PM_TOKEN_PERCENT_LOWER_W, + + /** %x */ + PM_TOKEN_PERCENT_LOWER_X, + + /** %I */ + PM_TOKEN_PERCENT_UPPER_I, + + /** %W */ + PM_TOKEN_PERCENT_UPPER_W, + + /** |= */ + PM_TOKEN_PIPE_EQUAL, + + /** || */ + PM_TOKEN_PIPE_PIPE, + + /** ||= */ + PM_TOKEN_PIPE_PIPE_EQUAL, + + /** + */ + PM_TOKEN_PLUS, + + /** += */ + PM_TOKEN_PLUS_EQUAL, + + /** ? */ + PM_TOKEN_QUESTION_MARK, + + /** the beginning of a regular expression */ + PM_TOKEN_REGEXP_BEGIN, + + /** the end of a regular expression */ + PM_TOKEN_REGEXP_END, + + /** / */ + PM_TOKEN_SLASH, + + /** /= */ + PM_TOKEN_SLASH_EQUAL, + + /** * */ + PM_TOKEN_STAR, + + /** *= */ + PM_TOKEN_STAR_EQUAL, + + /** ** */ + PM_TOKEN_STAR_STAR, + + /** **= */ + PM_TOKEN_STAR_STAR_EQUAL, + + /** the beginning of a string */ + PM_TOKEN_STRING_BEGIN, + + /** the contents of a string */ + PM_TOKEN_STRING_CONTENT, + + /** the end of a string */ + PM_TOKEN_STRING_END, + + /** the beginning of a symbol */ + PM_TOKEN_SYMBOL_BEGIN, + + /** ~ or ~@ */ + PM_TOKEN_TILDE, + + /** unary & */ + PM_TOKEN_UAMPERSAND, + + /** unary :: */ + PM_TOKEN_UCOLON_COLON, + + /** unary .. operator */ + PM_TOKEN_UDOT_DOT, + + /** unary ... operator */ + PM_TOKEN_UDOT_DOT_DOT, + + /** -@ */ + PM_TOKEN_UMINUS, + + /** -@ for a number */ + PM_TOKEN_UMINUS_NUM, + + /** +@ */ + PM_TOKEN_UPLUS, + + /** unary * */ + PM_TOKEN_USTAR, + + /** unary ** */ + PM_TOKEN_USTAR_STAR, + + /** a separator between words in a list */ + PM_TOKEN_WORDS_SEP, + + /** marker for the point in the file at which the parser should stop */ + PM_TOKEN___END__, + + /** a token that was expected but not found */ + PM_TOKEN_MISSING, + + /** a token that was not present but it is okay */ + PM_TOKEN_NOT_PROVIDED, + + /** 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 { + /** AliasGlobalVariableNode */ + PM_ALIAS_GLOBAL_VARIABLE_NODE = 1, + + /** AliasMethodNode */ + PM_ALIAS_METHOD_NODE = 2, + + /** AlternationPatternNode */ + PM_ALTERNATION_PATTERN_NODE = 3, + + /** AndNode */ + PM_AND_NODE = 4, + + /** ArgumentsNode */ + PM_ARGUMENTS_NODE = 5, + + /** ArrayNode */ + PM_ARRAY_NODE = 6, + + /** ArrayPatternNode */ + PM_ARRAY_PATTERN_NODE = 7, + + /** AssocNode */ + PM_ASSOC_NODE = 8, + + /** AssocSplatNode */ + PM_ASSOC_SPLAT_NODE = 9, + + /** BackReferenceReadNode */ + PM_BACK_REFERENCE_READ_NODE = 10, + + /** BeginNode */ + PM_BEGIN_NODE = 11, + + /** BlockArgumentNode */ + PM_BLOCK_ARGUMENT_NODE = 12, + + /** BlockLocalVariableNode */ + PM_BLOCK_LOCAL_VARIABLE_NODE = 13, + + /** BlockNode */ + PM_BLOCK_NODE = 14, + + /** BlockParameterNode */ + PM_BLOCK_PARAMETER_NODE = 15, + + /** BlockParametersNode */ + PM_BLOCK_PARAMETERS_NODE = 16, + + /** BreakNode */ + PM_BREAK_NODE = 17, + + /** CallAndWriteNode */ + PM_CALL_AND_WRITE_NODE = 18, + + /** CallNode */ + PM_CALL_NODE = 19, + + /** CallOperatorWriteNode */ + PM_CALL_OPERATOR_WRITE_NODE = 20, + + /** CallOrWriteNode */ + PM_CALL_OR_WRITE_NODE = 21, + + /** CallTargetNode */ + PM_CALL_TARGET_NODE = 22, + + /** CapturePatternNode */ + PM_CAPTURE_PATTERN_NODE = 23, + + /** CaseMatchNode */ + PM_CASE_MATCH_NODE = 24, + + /** CaseNode */ + PM_CASE_NODE = 25, + + /** ClassNode */ + PM_CLASS_NODE = 26, + + /** ClassVariableAndWriteNode */ + PM_CLASS_VARIABLE_AND_WRITE_NODE = 27, + + /** ClassVariableOperatorWriteNode */ + PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE = 28, + + /** ClassVariableOrWriteNode */ + PM_CLASS_VARIABLE_OR_WRITE_NODE = 29, + + /** ClassVariableReadNode */ + PM_CLASS_VARIABLE_READ_NODE = 30, + + /** ClassVariableTargetNode */ + PM_CLASS_VARIABLE_TARGET_NODE = 31, + + /** ClassVariableWriteNode */ + PM_CLASS_VARIABLE_WRITE_NODE = 32, + + /** ConstantAndWriteNode */ + PM_CONSTANT_AND_WRITE_NODE = 33, + + /** ConstantOperatorWriteNode */ + PM_CONSTANT_OPERATOR_WRITE_NODE = 34, + + /** ConstantOrWriteNode */ + PM_CONSTANT_OR_WRITE_NODE = 35, + + /** ConstantPathAndWriteNode */ + PM_CONSTANT_PATH_AND_WRITE_NODE = 36, + + /** ConstantPathNode */ + PM_CONSTANT_PATH_NODE = 37, + + /** ConstantPathOperatorWriteNode */ + PM_CONSTANT_PATH_OPERATOR_WRITE_NODE = 38, + + /** ConstantPathOrWriteNode */ + PM_CONSTANT_PATH_OR_WRITE_NODE = 39, + + /** ConstantPathTargetNode */ + PM_CONSTANT_PATH_TARGET_NODE = 40, + + /** ConstantPathWriteNode */ + PM_CONSTANT_PATH_WRITE_NODE = 41, + + /** ConstantReadNode */ + PM_CONSTANT_READ_NODE = 42, + + /** ConstantTargetNode */ + PM_CONSTANT_TARGET_NODE = 43, + + /** ConstantWriteNode */ + PM_CONSTANT_WRITE_NODE = 44, + + /** DefNode */ + PM_DEF_NODE = 45, + + /** DefinedNode */ + PM_DEFINED_NODE = 46, + + /** ElseNode */ + PM_ELSE_NODE = 47, + + /** EmbeddedStatementsNode */ + PM_EMBEDDED_STATEMENTS_NODE = 48, + + /** EmbeddedVariableNode */ + PM_EMBEDDED_VARIABLE_NODE = 49, + + /** EnsureNode */ + PM_ENSURE_NODE = 50, + + /** FalseNode */ + PM_FALSE_NODE = 51, + + /** FindPatternNode */ + PM_FIND_PATTERN_NODE = 52, + + /** FlipFlopNode */ + PM_FLIP_FLOP_NODE = 53, + + /** FloatNode */ + PM_FLOAT_NODE = 54, + + /** ForNode */ + PM_FOR_NODE = 55, + + /** ForwardingArgumentsNode */ + PM_FORWARDING_ARGUMENTS_NODE = 56, + + /** ForwardingParameterNode */ + PM_FORWARDING_PARAMETER_NODE = 57, + + /** ForwardingSuperNode */ + PM_FORWARDING_SUPER_NODE = 58, + + /** GlobalVariableAndWriteNode */ + PM_GLOBAL_VARIABLE_AND_WRITE_NODE = 59, + + /** GlobalVariableOperatorWriteNode */ + PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE = 60, + + /** GlobalVariableOrWriteNode */ + PM_GLOBAL_VARIABLE_OR_WRITE_NODE = 61, + + /** GlobalVariableReadNode */ + PM_GLOBAL_VARIABLE_READ_NODE = 62, + + /** GlobalVariableTargetNode */ + PM_GLOBAL_VARIABLE_TARGET_NODE = 63, + + /** GlobalVariableWriteNode */ + PM_GLOBAL_VARIABLE_WRITE_NODE = 64, + + /** HashNode */ + PM_HASH_NODE = 65, + + /** HashPatternNode */ + PM_HASH_PATTERN_NODE = 66, + + /** IfNode */ + PM_IF_NODE = 67, + + /** ImaginaryNode */ + PM_IMAGINARY_NODE = 68, + + /** ImplicitNode */ + PM_IMPLICIT_NODE = 69, + + /** ImplicitRestNode */ + PM_IMPLICIT_REST_NODE = 70, + + /** InNode */ + PM_IN_NODE = 71, + + /** IndexAndWriteNode */ + PM_INDEX_AND_WRITE_NODE = 72, + + /** IndexOperatorWriteNode */ + PM_INDEX_OPERATOR_WRITE_NODE = 73, + + /** IndexOrWriteNode */ + PM_INDEX_OR_WRITE_NODE = 74, + + /** IndexTargetNode */ + PM_INDEX_TARGET_NODE = 75, + + /** InstanceVariableAndWriteNode */ + PM_INSTANCE_VARIABLE_AND_WRITE_NODE = 76, + + /** InstanceVariableOperatorWriteNode */ + PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE = 77, + + /** InstanceVariableOrWriteNode */ + PM_INSTANCE_VARIABLE_OR_WRITE_NODE = 78, + + /** InstanceVariableReadNode */ + PM_INSTANCE_VARIABLE_READ_NODE = 79, + + /** InstanceVariableTargetNode */ + PM_INSTANCE_VARIABLE_TARGET_NODE = 80, + + /** InstanceVariableWriteNode */ + PM_INSTANCE_VARIABLE_WRITE_NODE = 81, + + /** IntegerNode */ + PM_INTEGER_NODE = 82, + + /** InterpolatedMatchLastLineNode */ + PM_INTERPOLATED_MATCH_LAST_LINE_NODE = 83, + + /** InterpolatedRegularExpressionNode */ + PM_INTERPOLATED_REGULAR_EXPRESSION_NODE = 84, + + /** InterpolatedStringNode */ + PM_INTERPOLATED_STRING_NODE = 85, + + /** InterpolatedSymbolNode */ + PM_INTERPOLATED_SYMBOL_NODE = 86, + + /** InterpolatedXStringNode */ + PM_INTERPOLATED_X_STRING_NODE = 87, + + /** ItLocalVariableReadNode */ + PM_IT_LOCAL_VARIABLE_READ_NODE = 88, + + /** ItParametersNode */ + PM_IT_PARAMETERS_NODE = 89, + + /** KeywordHashNode */ + PM_KEYWORD_HASH_NODE = 90, + + /** KeywordRestParameterNode */ + PM_KEYWORD_REST_PARAMETER_NODE = 91, + + /** LambdaNode */ + PM_LAMBDA_NODE = 92, + + /** LocalVariableAndWriteNode */ + PM_LOCAL_VARIABLE_AND_WRITE_NODE = 93, + + /** LocalVariableOperatorWriteNode */ + PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE = 94, + + /** LocalVariableOrWriteNode */ + PM_LOCAL_VARIABLE_OR_WRITE_NODE = 95, + + /** LocalVariableReadNode */ + PM_LOCAL_VARIABLE_READ_NODE = 96, + + /** LocalVariableTargetNode */ + PM_LOCAL_VARIABLE_TARGET_NODE = 97, + + /** LocalVariableWriteNode */ + PM_LOCAL_VARIABLE_WRITE_NODE = 98, + + /** MatchLastLineNode */ + PM_MATCH_LAST_LINE_NODE = 99, + + /** MatchPredicateNode */ + PM_MATCH_PREDICATE_NODE = 100, + + /** MatchRequiredNode */ + PM_MATCH_REQUIRED_NODE = 101, + + /** MatchWriteNode */ + PM_MATCH_WRITE_NODE = 102, + + /** MissingNode */ + PM_MISSING_NODE = 103, + + /** ModuleNode */ + PM_MODULE_NODE = 104, + + /** MultiTargetNode */ + PM_MULTI_TARGET_NODE = 105, + + /** MultiWriteNode */ + PM_MULTI_WRITE_NODE = 106, + + /** NextNode */ + PM_NEXT_NODE = 107, + + /** NilNode */ + PM_NIL_NODE = 108, + + /** NoKeywordsParameterNode */ + PM_NO_KEYWORDS_PARAMETER_NODE = 109, + + /** NumberedParametersNode */ + PM_NUMBERED_PARAMETERS_NODE = 110, + + /** NumberedReferenceReadNode */ + PM_NUMBERED_REFERENCE_READ_NODE = 111, + + /** OptionalKeywordParameterNode */ + PM_OPTIONAL_KEYWORD_PARAMETER_NODE = 112, + + /** OptionalParameterNode */ + PM_OPTIONAL_PARAMETER_NODE = 113, + + /** OrNode */ + PM_OR_NODE = 114, + + /** ParametersNode */ + PM_PARAMETERS_NODE = 115, + + /** ParenthesesNode */ + PM_PARENTHESES_NODE = 116, + + /** PinnedExpressionNode */ + PM_PINNED_EXPRESSION_NODE = 117, + + /** PinnedVariableNode */ + PM_PINNED_VARIABLE_NODE = 118, + + /** PostExecutionNode */ + PM_POST_EXECUTION_NODE = 119, + + /** PreExecutionNode */ + PM_PRE_EXECUTION_NODE = 120, + + /** ProgramNode */ + PM_PROGRAM_NODE = 121, + + /** RangeNode */ + PM_RANGE_NODE = 122, + + /** RationalNode */ + PM_RATIONAL_NODE = 123, + + /** RedoNode */ + PM_REDO_NODE = 124, + + /** RegularExpressionNode */ + PM_REGULAR_EXPRESSION_NODE = 125, + + /** RequiredKeywordParameterNode */ + PM_REQUIRED_KEYWORD_PARAMETER_NODE = 126, + + /** RequiredParameterNode */ + PM_REQUIRED_PARAMETER_NODE = 127, + + /** RescueModifierNode */ + PM_RESCUE_MODIFIER_NODE = 128, + + /** RescueNode */ + PM_RESCUE_NODE = 129, + + /** RestParameterNode */ + PM_REST_PARAMETER_NODE = 130, + + /** RetryNode */ + PM_RETRY_NODE = 131, + + /** ReturnNode */ + PM_RETURN_NODE = 132, + + /** SelfNode */ + PM_SELF_NODE = 133, + + /** ShareableConstantNode */ + PM_SHAREABLE_CONSTANT_NODE = 134, + + /** SingletonClassNode */ + PM_SINGLETON_CLASS_NODE = 135, + + /** SourceEncodingNode */ + PM_SOURCE_ENCODING_NODE = 136, + + /** SourceFileNode */ + PM_SOURCE_FILE_NODE = 137, + + /** SourceLineNode */ + PM_SOURCE_LINE_NODE = 138, + + /** SplatNode */ + PM_SPLAT_NODE = 139, + + /** StatementsNode */ + PM_STATEMENTS_NODE = 140, + + /** StringNode */ + PM_STRING_NODE = 141, + + /** SuperNode */ + PM_SUPER_NODE = 142, + + /** SymbolNode */ + PM_SYMBOL_NODE = 143, + + /** TrueNode */ + PM_TRUE_NODE = 144, + + /** UndefNode */ + PM_UNDEF_NODE = 145, + + /** UnlessNode */ + PM_UNLESS_NODE = 146, + + /** UntilNode */ + PM_UNTIL_NODE = 147, + + /** WhenNode */ + PM_WHEN_NODE = 148, + + /** WhileNode */ + PM_WHILE_NODE = 149, + + /** XStringNode */ + PM_X_STRING_NODE = 150, + + /** YieldNode */ + PM_YIELD_NODE = 151, + + /** 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. + */ +static const pm_node_flags_t PM_NODE_FLAG_NEWLINE = 0x1; +static const pm_node_flags_t PM_NODE_FLAG_STATIC_LITERAL = 0x2; + +/** + * 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; + + /** + * The unique identifier for this node, which is deterministic based on the + * source. It is used to identify unique nodes across parses. + */ + uint32_t node_id; + + /** + * 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; + +/** + * Cast the given node to the base pm_node_t type. + */ +#define PM_NODE_UPCAST(node_) ((pm_node_t *) (node_)) + +/** + * 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 the flags associated with the given node. + */ +#define PM_NODE_FLAGS(node_) (PM_NODE_UPCAST(node_)->flags) + +/** + * Return true if the given flag is set on the given node. + */ +#define PM_NODE_FLAG_P(node_, flag_) ((PM_NODE_FLAGS(node_) & (flag_)) != 0) + +/** + * AliasGlobalVariableNode + * + * Represents the use of the `alias` keyword to alias a global variable. + * + * alias $foo $bar + * ^^^^^^^^^^^^^^^ + * + * Type: ::PM_ALIAS_GLOBAL_VARIABLE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_alias_global_variable_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * AliasGlobalVariableNode#new_name + * + * Represents the new name of the global variable that can be used after aliasing. + * + * alias $foo $bar + * ^^^^ + */ + struct pm_node *new_name; + + /** + * AliasGlobalVariableNode#old_name + * + * Represents the old name of the global variable that can be used before aliasing. + * + * alias $foo $bar + * ^^^^ + */ + struct pm_node *old_name; + + /** + * AliasGlobalVariableNode#keyword_loc + * + * The location of the `alias` keyword. + * + * alias $foo $bar + * ^^^^^ + */ + pm_location_t keyword_loc; +} pm_alias_global_variable_node_t; + +/** + * AliasMethodNode + * + * Represents the use of the `alias` keyword to alias a method. + * + * alias foo bar + * ^^^^^^^^^^^^^ + * + * Type: ::PM_ALIAS_METHOD_NODE + * + * @extends pm_node_t + */ +typedef struct pm_alias_method_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * AliasMethodNode#new_name + * + * Represents the new name of the method that will be aliased. + * + * alias foo bar + * ^^^ + * + * alias :foo :bar + * ^^^^ + * + * alias :"#{foo}" :"#{bar}" + * ^^^^^^^^^ + */ + struct pm_node *new_name; + + /** + * AliasMethodNode#old_name + * + * Represents the old name of the method that will be aliased. + * + * alias foo bar + * ^^^ + * + * alias :foo :bar + * ^^^^ + * + * alias :"#{foo}" :"#{bar}" + * ^^^^^^^^^ + */ + struct pm_node *old_name; + + /** + * AliasMethodNode#keyword_loc + * + * Represents the location of the `alias` keyword. + * + * alias foo bar + * ^^^^^ + */ + pm_location_t keyword_loc; +} pm_alias_method_node_t; + +/** + * AlternationPatternNode + * + * Represents an alternation pattern in pattern matching. + * + * foo => bar | baz + * ^^^^^^^^^ + * + * Type: ::PM_ALTERNATION_PATTERN_NODE + * + * @extends pm_node_t + */ +typedef struct pm_alternation_pattern_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * AlternationPatternNode#left + * + * Represents the left side of the expression. + * + * foo => bar | baz + * ^^^ + */ + struct pm_node *left; + + /** + * AlternationPatternNode#right + * + * Represents the right side of the expression. + * + * foo => bar | baz + * ^^^ + */ + struct pm_node *right; + + /** + * AlternationPatternNode#operator_loc + * + * Represents the alternation operator location. + * + * foo => bar | baz + * ^ + */ + pm_location_t operator_loc; +} pm_alternation_pattern_node_t; + +/** + * AndNode + * + * Represents the use of the `&&` operator or the `and` keyword. + * + * left and right + * ^^^^^^^^^^^^^^ + * + * Type: ::PM_AND_NODE + * + * @extends pm_node_t + */ +typedef struct pm_and_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * AndNode#left + * + * 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 + * ^ + */ + struct pm_node *left; + + /** + * AndNode#right + * + * Represents the right side of the expression. + * + * left && right + * ^^^^^ + * + * 1 and 2 + * ^ + */ + struct pm_node *right; + + /** + * AndNode#operator_loc + * + * The location of the `and` keyword or the `&&` operator. + * + * left and right + * ^^^ + */ + pm_location_t operator_loc; +} pm_and_node_t; + +/** + * ArgumentsNode + * + * Represents a set of arguments to a method or a keyword. + * + * return foo, bar, baz + * ^^^^^^^^^^^^^ + * + * Type: ::PM_ARGUMENTS_NODE + + * Flags (#pm_arguments_node_flags): + * * ::PM_ARGUMENTS_NODE_FLAGS_CONTAINS_FORWARDING + * * ::PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS + * * ::PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT + * * ::PM_ARGUMENTS_NODE_FLAGS_CONTAINS_SPLAT + * * ::PM_ARGUMENTS_NODE_FLAGS_CONTAINS_MULTIPLE_SPLATS + * + * @extends pm_node_t + */ +typedef struct pm_arguments_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * ArgumentsNode#arguments + * + * The list of arguments, if present. These can be any [non-void expressions](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + * + * foo(bar, baz) + * ^^^^^^^^ + */ + struct pm_node_list arguments; +} pm_arguments_node_t; + +/** + * ArrayNode + * + * Represents an array literal. This can be a regular array using brackets or a special array using % like %w or %i. + * + * [1, 2, 3] + * ^^^^^^^^^ + * + * Type: ::PM_ARRAY_NODE + + * Flags (#pm_array_node_flags): + * * ::PM_ARRAY_NODE_FLAGS_CONTAINS_SPLAT + * + * @extends pm_node_t + */ +typedef struct pm_array_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * ArrayNode#elements + * + * 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. + */ + struct pm_node_list elements; + + /** + * ArrayNode#opening_loc + * + * 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 + */ + pm_location_t opening_loc; + + /** + * ArrayNode#closing_loc + * + * 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 + */ + pm_location_t closing_loc; +} pm_array_node_t; + +/** + * ArrayPatternNode + * + * Represents an array pattern in pattern matching. + * + * foo in 1, 2 + * ^^^^^^^^^^^ + * + * foo in [1, 2] + * ^^^^^^^^^^^^^ + * + * foo in *bar + * ^^^^^^^^^^^ + * + * foo in Bar[] + * ^^^^^^^^^^^^ + * + * foo in Bar[1, 2, 3] + * ^^^^^^^^^^^^^^^^^^^ + * + * Type: ::PM_ARRAY_PATTERN_NODE + * + * @extends pm_node_t + */ +typedef struct pm_array_pattern_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * ArrayPatternNode#constant + * + * Represents the optional constant preceding the Array + * + * foo in Bar[] + * ^^^ + * + * foo in Bar[1, 2, 3] + * ^^^ + * + * foo in Bar::Baz[1, 2, 3] + * ^^^^^^^^ + */ + struct pm_node *constant; + + /** + * ArrayPatternNode#requireds + * + * Represents the required elements of the array pattern. + * + * foo in [1, 2] + * ^ ^ + */ + struct pm_node_list requireds; + + /** + * ArrayPatternNode#rest + * + * Represents the rest element of the array pattern. + * + * foo in *bar + * ^^^^ + */ + struct pm_node *rest; + + /** + * ArrayPatternNode#posts + * + * Represents the elements after the rest element of the array pattern. + * + * foo in *bar, baz + * ^^^ + */ + struct pm_node_list posts; + + /** + * ArrayPatternNode#opening_loc + * + * Represents the opening location of the array pattern. + * + * foo in [1, 2] + * ^ + */ + pm_location_t opening_loc; + + /** + * ArrayPatternNode#closing_loc + * + * Represents the closing location of the array pattern. + * + * foo in [1, 2] + * ^ + */ + pm_location_t closing_loc; +} pm_array_pattern_node_t; + +/** + * AssocNode + * + * Represents a hash key/value pair. + * + * { a => b } + * ^^^^^^ + * + * Type: ::PM_ASSOC_NODE + * + * @extends pm_node_t + */ +typedef struct pm_assoc_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * AssocNode#key + * + * 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 } + * ^^^^^^^^^^ + */ + struct pm_node *key; + + /** + * AssocNode#value + * + * 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 } + * ^ + */ + struct pm_node *value; + + /** + * AssocNode#operator_loc + * + * The location of the `=>` operator, if present. + * + * { foo => bar } + * ^^ + */ + pm_location_t operator_loc; +} pm_assoc_node_t; + +/** + * AssocSplatNode + * + * Represents a splat in a hash literal. + * + * { **foo } + * ^^^^^ + * + * Type: ::PM_ASSOC_SPLAT_NODE + * + * @extends pm_node_t + */ +typedef struct pm_assoc_splat_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * AssocSplatNode#value + * + * The value to be splatted, if present. Will be missing when keyword rest argument forwarding is used. + * + * { **foo } + * ^^^ + */ + struct pm_node *value; + + /** + * AssocSplatNode#operator_loc + * + * The location of the `**` operator. + * + * { **x } + * ^^ + */ + pm_location_t operator_loc; +} pm_assoc_splat_node_t; + +/** + * BackReferenceReadNode + * + * Represents reading a reference to a field in the previous match. + * + * $' + * ^^ + * + * Type: ::PM_BACK_REFERENCE_READ_NODE + * + * @extends pm_node_t + */ +typedef struct pm_back_reference_read_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * BackReferenceReadNode#name + * + * The name of the back-reference variable, including the leading `$`. + * + * $& # name `:$&` + * + * $+ # name `:$+` + */ + pm_constant_id_t name; +} pm_back_reference_read_node_t; + +/** + * BeginNode + * + * Represents a begin statement. + * + * begin + * foo + * end + * ^^^^^ + * + * Type: ::PM_BEGIN_NODE + * + * @extends pm_node_t + */ +typedef struct pm_begin_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * BeginNode#begin_keyword_loc + * + * Represents the location of the `begin` keyword. + * + * begin x end + * ^^^^^ + */ + pm_location_t begin_keyword_loc; + + /** + * BeginNode#statements + * + * Represents the statements within the begin block. + * + * begin x end + * ^ + */ + struct pm_statements_node *statements; + + /** + * BeginNode#rescue_clause + * + * Represents the rescue clause within the begin block. + * + * begin x; rescue y; end + * ^^^^^^^^ + */ + struct pm_rescue_node *rescue_clause; + + /** + * BeginNode#else_clause + * + * Represents the else clause within the begin block. + * + * begin x; rescue y; else z; end + * ^^^^^^ + */ + struct pm_else_node *else_clause; + + /** + * BeginNode#ensure_clause + * + * Represents the ensure clause within the begin block. + * + * begin x; ensure y; end + * ^^^^^^^^ + */ + struct pm_ensure_node *ensure_clause; + + /** + * BeginNode#end_keyword_loc + * + * Represents the location of the `end` keyword. + * + * begin x end + * ^^^ + */ + pm_location_t end_keyword_loc; +} pm_begin_node_t; + +/** + * BlockArgumentNode + * + * Represents a block argument using `&`. + * + * bar(&args) + * ^^^^^^^^^^ + * + * Type: ::PM_BLOCK_ARGUMENT_NODE + * + * @extends pm_node_t + */ +typedef struct pm_block_argument_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * BlockArgumentNode#expression + * + * The expression that is being passed as a block argument. This can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + * + * foo(&args) + * ^^^^^ + */ + struct pm_node *expression; + + /** + * BlockArgumentNode#operator_loc + * + * Represents the location of the `&` operator. + * + * foo(&args) + * ^ + */ + pm_location_t operator_loc; +} pm_block_argument_node_t; + +/** + * BlockLocalVariableNode + * + * Represents a block local variable. + * + * a { |; b| } + * ^ + * + * Type: ::PM_BLOCK_LOCAL_VARIABLE_NODE + + * Flags (#pm_parameter_flags): + * * ::PM_PARAMETER_FLAGS_REPEATED_PARAMETER + * + * @extends pm_node_t + */ +typedef struct pm_block_local_variable_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * BlockLocalVariableNode#name + * + * The name of the block local variable. + * + * a { |; b| } # name `:b` + * ^ + */ + pm_constant_id_t name; +} pm_block_local_variable_node_t; + +/** + * BlockNode + * + * Represents a block of ruby code. + * + * [1, 2, 3].each { |i| puts x } + * ^^^^^^^^^^^^^^ + * + * Type: ::PM_BLOCK_NODE + * + * @extends pm_node_t + */ +typedef struct pm_block_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * BlockNode#locals + * + * The local variables declared in the block. + * + * [1, 2, 3].each { |i| puts x } # locals: [:i] + * ^ + */ + pm_constant_id_list_t locals; + + /** + * BlockNode#parameters + * + * The parameters of the block. + * + * [1, 2, 3].each { |i| puts x } + * ^^^ + * [1, 2, 3].each { puts _1 } + * ^^^^^^^^^^^ + * [1, 2, 3].each { puts it } + * ^^^^^^^^^^^ + */ + struct pm_node *parameters; + + /** + * BlockNode#body + * + * The body of the block. + * + * [1, 2, 3].each { |i| puts x } + * ^^^^^^ + */ + struct pm_node *body; + + /** + * BlockNode#opening_loc + * + * Represents the location of the opening `{` or `do`. + * + * [1, 2, 3].each { |i| puts x } + * ^ + */ + pm_location_t opening_loc; + + /** + * BlockNode#closing_loc + * + * Represents the location of the closing `}` or `end`. + * + * [1, 2, 3].each { |i| puts x } + * ^ + */ + pm_location_t closing_loc; +} pm_block_node_t; + +/** + * BlockParameterNode + * + * Represents a block parameter of a method, block, or lambda definition. + * + * def a(&b) + * ^^ + * end + * + * Type: ::PM_BLOCK_PARAMETER_NODE + + * Flags (#pm_parameter_flags): + * * ::PM_PARAMETER_FLAGS_REPEATED_PARAMETER + * + * @extends pm_node_t + */ +typedef struct pm_block_parameter_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * BlockParameterNode#name + * + * The name of the block parameter. + * + * def a(&b) # name `:b` + * ^ + * end + */ + pm_constant_id_t name; + + /** + * BlockParameterNode#name_loc + * + * Represents the location of the block parameter name. + * + * def a(&b) + * ^ + */ + pm_location_t name_loc; + + /** + * BlockParameterNode#operator_loc + * + * Represents the location of the `&` operator. + * + * def a(&b) + * ^ + * end + */ + pm_location_t operator_loc; +} pm_block_parameter_node_t; + +/** + * BlockParametersNode + * + * Represents a block's parameters declaration. + * + * -> (a, b = 1; local) { } + * ^^^^^^^^^^^^^^^^^ + * + * foo do |a, b = 1; local| + * ^^^^^^^^^^^^^^^^^ + * end + * + * Type: ::PM_BLOCK_PARAMETERS_NODE + * + * @extends pm_node_t + */ +typedef struct pm_block_parameters_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * BlockParametersNode#parameters + * + * Represents the parameters of the block. + * + * -> (a, b = 1; local) { } + * ^^^^^^^^ + * + * foo do |a, b = 1; local| + * ^^^^^^^^ + * end + */ + struct pm_parameters_node *parameters; + + /** + * BlockParametersNode#locals + * + * Represents the local variables of the block. + * + * -> (a, b = 1; local) { } + * ^^^^^ + * + * foo do |a, b = 1; local| + * ^^^^^ + * end + */ + struct pm_node_list locals; + + /** + * BlockParametersNode#opening_loc + * + * Represents the opening location of the block parameters. + * + * -> (a, b = 1; local) { } + * ^ + * + * foo do |a, b = 1; local| + * ^ + * end + */ + pm_location_t opening_loc; + + /** + * BlockParametersNode#closing_loc + * + * Represents the closing location of the block parameters. + * + * -> (a, b = 1; local) { } + * ^ + * + * foo do |a, b = 1; local| + * ^ + * end + */ + pm_location_t closing_loc; +} pm_block_parameters_node_t; + +/** + * BreakNode + * + * Represents the use of the `break` keyword. + * + * break foo + * ^^^^^^^^^ + * + * Type: ::PM_BREAK_NODE + * + * @extends pm_node_t + */ +typedef struct pm_break_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * BreakNode#arguments + * + * 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 + * ^^^ + */ + struct pm_arguments_node *arguments; + + /** + * BreakNode#keyword_loc + * + * The location of the `break` keyword. + * + * break foo + * ^^^^^ + */ + pm_location_t keyword_loc; +} pm_break_node_t; + +/** + * CallAndWriteNode + * + * Represents the use of the `&&=` operator on a call. + * + * foo.bar &&= value + * ^^^^^^^^^^^^^^^^^ + * + * Type: ::PM_CALL_AND_WRITE_NODE + + * Flags (#pm_call_node_flags): + * * ::PM_CALL_NODE_FLAGS_SAFE_NAVIGATION + * * ::PM_CALL_NODE_FLAGS_VARIABLE_CALL + * * ::PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE + * * ::PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY + * + * @extends pm_node_t + */ +typedef struct pm_call_and_write_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * CallAndWriteNode#receiver + * + * 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 &&= value + * ^^^ + */ + struct pm_node *receiver; + + /** + * CallAndWriteNode#call_operator_loc + * + * Represents the location of the call operator. + * + * foo.bar &&= value + * ^ + */ + pm_location_t call_operator_loc; + + /** + * CallAndWriteNode#message_loc + * + * Represents the location of the message. + * + * foo.bar &&= value + * ^^^ + */ + pm_location_t message_loc; + + /** + * CallAndWriteNode#read_name + * + * Represents the name of the method being called. + * + * foo.bar &&= value # read_name `:bar` + * ^^^ + */ + pm_constant_id_t read_name; + + /** + * CallAndWriteNode#write_name + * + * Represents the name of the method being written to. + * + * foo.bar &&= value # write_name `:bar=` + * ^^^ + */ + pm_constant_id_t write_name; + + /** + * CallAndWriteNode#operator_loc + * + * Represents the location of the operator. + * + * foo.bar &&= value + * ^^^ + */ + pm_location_t operator_loc; + + /** + * CallAndWriteNode#value + * + * Represents the value being assigned. + * + * foo.bar &&= value + * ^^^^^ + */ + struct pm_node *value; +} pm_call_and_write_node_t; + +/** + * CallNode + * + * Represents a method call, in all of the various forms that can take. + * + * foo + * ^^^ + * + * foo() + * ^^^^^ + * + * +foo + * ^^^^ + * + * foo + bar + * ^^^^^^^^^ + * + * foo.bar + * ^^^^^^^ + * + * foo&.bar + * ^^^^^^^^ + * + * Type: ::PM_CALL_NODE + + * Flags (#pm_call_node_flags): + * * ::PM_CALL_NODE_FLAGS_SAFE_NAVIGATION + * * ::PM_CALL_NODE_FLAGS_VARIABLE_CALL + * * ::PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE + * * ::PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY + * + * @extends pm_node_t + */ +typedef struct pm_call_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * CallNode#receiver + * + * 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 + * ^^^ + */ + struct pm_node *receiver; + + /** + * CallNode#call_operator_loc + * + * Represents the location of the call operator. + * + * foo.bar + * ^ + * + * foo&.bar + * ^^ + */ + pm_location_t call_operator_loc; + + /** + * CallNode#name + * + * Represents the name of the method being called. + * + * foo.bar # name `:foo` + * ^^^ + */ + pm_constant_id_t name; + + /** + * CallNode#message_loc + * + * Represents the location of the message. + * + * foo.bar + * ^^^ + */ + pm_location_t message_loc; + + /** + * CallNode#opening_loc + * + * Represents the location of the left parenthesis. + * foo(bar) + * ^ + */ + pm_location_t opening_loc; + + /** + * CallNode#arguments + * + * Represents the arguments to the method call. These can be any [non-void expressions](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + * + * foo(bar) + * ^^^ + */ + struct pm_arguments_node *arguments; + + /** + * CallNode#closing_loc + * + * Represents the location of the right parenthesis. + * + * foo(bar) + * ^ + */ + pm_location_t closing_loc; + + /** + * CallNode#equal_loc + * + * Represents the location of the equal sign, in the case that this is an attribute write. + * + * foo.bar = value + * ^ + * + * foo[bar] = value + * ^ + */ + pm_location_t equal_loc; + + /** + * CallNode#block + * + * Represents the block that is being passed to the method. + * + * foo { |a| a } + * ^^^^^^^^^ + */ + struct pm_node *block; +} pm_call_node_t; + +/** + * CallOperatorWriteNode + * + * Represents the use of an assignment operator on a call. + * + * foo.bar += baz + * ^^^^^^^^^^^^^^ + * + * Type: ::PM_CALL_OPERATOR_WRITE_NODE + + * Flags (#pm_call_node_flags): + * * ::PM_CALL_NODE_FLAGS_SAFE_NAVIGATION + * * ::PM_CALL_NODE_FLAGS_VARIABLE_CALL + * * ::PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE + * * ::PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY + * + * @extends pm_node_t + */ +typedef struct pm_call_operator_write_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * CallOperatorWriteNode#receiver + * + * The object that the method is being called on. This can be either `nil` or any [non-void expressions](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + * + * foo.bar += value + * ^^^ + */ + struct pm_node *receiver; + + /** + * CallOperatorWriteNode#call_operator_loc + * + * Represents the location of the call operator. + * + * foo.bar += value + * ^ + */ + pm_location_t call_operator_loc; + + /** + * CallOperatorWriteNode#message_loc + * + * Represents the location of the message. + * + * foo.bar += value + * ^^^ + */ + pm_location_t message_loc; + + /** + * CallOperatorWriteNode#read_name + * + * Represents the name of the method being called. + * + * foo.bar += value # read_name `:bar` + * ^^^ + */ + pm_constant_id_t read_name; + + /** + * CallOperatorWriteNode#write_name + * + * Represents the name of the method being written to. + * + * foo.bar += value # write_name `:bar=` + * ^^^ + */ + pm_constant_id_t write_name; + + /** + * CallOperatorWriteNode#binary_operator + * + * Represents the binary operator being used. + * + * foo.bar += value # binary_operator `:+` + * ^ + */ + pm_constant_id_t binary_operator; + + /** + * CallOperatorWriteNode#binary_operator_loc + * + * Represents the location of the binary operator. + * + * foo.bar += value + * ^^ + */ + pm_location_t binary_operator_loc; + + /** + * CallOperatorWriteNode#value + * + * Represents the value being assigned. + * + * foo.bar += value + * ^^^^^ + */ + struct pm_node *value; +} pm_call_operator_write_node_t; + +/** + * CallOrWriteNode + * + * Represents the use of the `||=` operator on a call. + * + * foo.bar ||= value + * ^^^^^^^^^^^^^^^^^ + * + * Type: ::PM_CALL_OR_WRITE_NODE + + * Flags (#pm_call_node_flags): + * * ::PM_CALL_NODE_FLAGS_SAFE_NAVIGATION + * * ::PM_CALL_NODE_FLAGS_VARIABLE_CALL + * * ::PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE + * * ::PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY + * + * @extends pm_node_t + */ +typedef struct pm_call_or_write_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * CallOrWriteNode#receiver + * + * The object that the method is being called on. This can be either `nil` or any [non-void expressions](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + * + * foo.bar ||= value + * ^^^ + */ + struct pm_node *receiver; + + /** + * CallOrWriteNode#call_operator_loc + * + * Represents the location of the call operator. + * + * foo.bar ||= value + * ^ + */ + pm_location_t call_operator_loc; + + /** + * CallOrWriteNode#message_loc + * + * Represents the location of the message. + * + * foo.bar ||= value + * ^^^ + */ + pm_location_t message_loc; + + /** + * CallOrWriteNode#read_name + * + * Represents the name of the method being called. + * + * foo.bar ||= value # read_name `:bar` + * ^^^ + */ + pm_constant_id_t read_name; + + /** + * CallOrWriteNode#write_name + * + * Represents the name of the method being written to. + * + * foo.bar ||= value # write_name `:bar=` + * ^^^ + */ + pm_constant_id_t write_name; + + /** + * CallOrWriteNode#operator_loc + * + * Represents the location of the operator. + * + * foo.bar ||= value + * ^^^ + */ + pm_location_t operator_loc; + + /** + * CallOrWriteNode#value + * + * Represents the value being assigned. + * + * foo.bar ||= value + * ^^^^^ + */ + struct pm_node *value; +} pm_call_or_write_node_t; + +/** + * CallTargetNode + * + * Represents assigning to a method call. + * + * foo.bar, = 1 + * ^^^^^^^ + * + * begin + * rescue => foo.bar + * ^^^^^^^ + * end + * + * for foo.bar in baz do end + * ^^^^^^^ + * + * Type: ::PM_CALL_TARGET_NODE + + * Flags (#pm_call_node_flags): + * * ::PM_CALL_NODE_FLAGS_SAFE_NAVIGATION + * * ::PM_CALL_NODE_FLAGS_VARIABLE_CALL + * * ::PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE + * * ::PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY + * + * @extends pm_node_t + */ +typedef struct pm_call_target_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * CallTargetNode#receiver + * + * The object that the method is being called on. This can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + * + * foo.bar = 1 + * ^^^ + */ + struct pm_node *receiver; + + /** + * CallTargetNode#call_operator_loc + * + * Represents the location of the call operator. + * + * foo.bar = 1 + * ^ + */ + pm_location_t call_operator_loc; + + /** + * CallTargetNode#name + * + * Represents the name of the method being called. + * + * foo.bar = 1 # name `:foo` + * ^^^ + */ + pm_constant_id_t name; + + /** + * CallTargetNode#message_loc + * + * Represents the location of the message. + * + * foo.bar = 1 + * ^^^ + */ + pm_location_t message_loc; +} pm_call_target_node_t; + +/** + * CapturePatternNode + * + * Represents assigning to a local variable in pattern matching. + * + * foo => [bar => baz] + * ^^^^^^^^^^^^ + * + * Type: ::PM_CAPTURE_PATTERN_NODE + * + * @extends pm_node_t + */ +typedef struct pm_capture_pattern_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * CapturePatternNode#value + * + * Represents the value to capture. + * + * foo => bar + * ^^^ + */ + struct pm_node *value; + + /** + * CapturePatternNode#target + * + * Represents the target of the capture. + * + * foo => bar + * ^^^ + */ + struct pm_local_variable_target_node *target; + + /** + * CapturePatternNode#operator_loc + * + * Represents the location of the `=>` operator. + * + * foo => bar + * ^^ + */ + pm_location_t operator_loc; +} pm_capture_pattern_node_t; + +/** + * CaseMatchNode + * + * Represents the use of a case statement for pattern matching. + * + * case true + * in false + * end + * ^^^^^^^^^ + * + * Type: ::PM_CASE_MATCH_NODE + * + * @extends pm_node_t + */ +typedef struct pm_case_match_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * CaseMatchNode#predicate + * + * Represents the predicate of the case match. This can be either `nil` or any [non-void expressions](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + * + * case true; in false; end + * ^^^^ + */ + struct pm_node *predicate; + + /** + * CaseMatchNode#conditions + * + * Represents the conditions of the case match. + * + * case true; in false; end + * ^^^^^^^^ + */ + struct pm_node_list conditions; + + /** + * CaseMatchNode#else_clause + * + * Represents the else clause of the case match. + * + * case true; in false; else; end + * ^^^^ + */ + struct pm_else_node *else_clause; + + /** + * CaseMatchNode#case_keyword_loc + * + * Represents the location of the `case` keyword. + * + * case true; in false; end + * ^^^^ + */ + pm_location_t case_keyword_loc; + + /** + * CaseMatchNode#end_keyword_loc + * + * Represents the location of the `end` keyword. + * + * case true; in false; end + * ^^^ + */ + pm_location_t end_keyword_loc; +} pm_case_match_node_t; + +/** + * CaseNode + * + * Represents the use of a case statement. + * + * case true + * when false + * end + * ^^^^^^^^^^ + * + * Type: ::PM_CASE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_case_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * CaseNode#predicate + * + * Represents the predicate of the case statement. This can be either `nil` or any [non-void expressions](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + * + * case true; when false; end + * ^^^^ + */ + struct pm_node *predicate; + + /** + * CaseNode#conditions + * + * Represents the conditions of the case statement. + * + * case true; when false; end + * ^^^^^^^^^^ + */ + struct pm_node_list conditions; + + /** + * CaseNode#else_clause + * + * Represents the else clause of the case statement. + * + * case true; when false; else; end + * ^^^^ + */ + struct pm_else_node *else_clause; + + /** + * CaseNode#case_keyword_loc + * + * Represents the location of the `case` keyword. + * + * case true; when false; end + * ^^^^ + */ + pm_location_t case_keyword_loc; + + /** + * CaseNode#end_keyword_loc + * + * Represents the location of the `end` keyword. + * + * case true; when false; end + * ^^^ + */ + pm_location_t end_keyword_loc; +} pm_case_node_t; + +/** + * ClassNode + * + * Represents a class declaration involving the `class` keyword. + * + * class Foo end + * ^^^^^^^^^^^^^ + * + * Type: ::PM_CLASS_NODE + * + * @extends pm_node_t + */ +typedef struct pm_class_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * ClassNode#locals + */ + pm_constant_id_list_t locals; + + /** + * ClassNode#class_keyword_loc + * + * Represents the location of the `class` keyword. + * + * class Foo end + * ^^^^^ + */ + pm_location_t class_keyword_loc; + + /** + * ClassNode#constant_path + */ + struct pm_node *constant_path; + + /** + * ClassNode#inheritance_operator_loc + * + * Represents the location of the `<` operator. + * + * class Foo < Bar + * ^ + */ + pm_location_t inheritance_operator_loc; + + /** + * ClassNode#superclass + * + * Represents the superclass of the class. + * + * class Foo < Bar + * ^^^ + */ + struct pm_node *superclass; + + /** + * ClassNode#body + * + * Represents the body of the class. + * + * class Foo + * foo + * ^^^ + */ + struct pm_node *body; + + /** + * ClassNode#end_keyword_loc + * + * Represents the location of the `end` keyword. + * + * class Foo end + * ^^^ + */ + pm_location_t end_keyword_loc; + + /** + * ClassNode#name + * + * The name of the class. + * + * class Foo end # name `:Foo` + */ + pm_constant_id_t name; +} pm_class_node_t; + +/** + * ClassVariableAndWriteNode + * + * Represents the use of the `&&=` operator for assignment to a class variable. + * + * @@target &&= value + * ^^^^^^^^^^^^^^^^^^ + * + * Type: ::PM_CLASS_VARIABLE_AND_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_class_variable_and_write_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * ClassVariableAndWriteNode#name + * + * 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). + * + * @@target &&= value # name `:@@target` + * ^^^^^^^^ + */ + pm_constant_id_t name; + + /** + * ClassVariableAndWriteNode#name_loc + * + * Represents the location of the variable name. + * + * @@target &&= value + * ^^^^^^^^ + */ + pm_location_t name_loc; + + /** + * ClassVariableAndWriteNode#operator_loc + * + * Represents the location of the `&&=` operator. + * + * @@target &&= value + * ^^^ + */ + pm_location_t operator_loc; + + /** + * ClassVariableAndWriteNode#value + * + * Represents the value being assigned. This can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + * + * @@target &&= value + * ^^^^^ + */ + struct pm_node *value; +} pm_class_variable_and_write_node_t; + +/** + * ClassVariableOperatorWriteNode + * + * Represents assigning to a class variable using an operator that isn't `=`. + * + * @@target += value + * ^^^^^^^^^^^^^^^^^ + * + * Type: ::PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_class_variable_operator_write_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * ClassVariableOperatorWriteNode#name + */ + pm_constant_id_t name; + + /** + * ClassVariableOperatorWriteNode#name_loc + */ + pm_location_t name_loc; + + /** + * ClassVariableOperatorWriteNode#binary_operator_loc + */ + pm_location_t binary_operator_loc; + + /** + * ClassVariableOperatorWriteNode#value + */ + struct pm_node *value; + + /** + * ClassVariableOperatorWriteNode#binary_operator + */ + pm_constant_id_t binary_operator; +} pm_class_variable_operator_write_node_t; + +/** + * ClassVariableOrWriteNode + * + * Represents the use of the `||=` operator for assignment to a class variable. + * + * @@target ||= value + * ^^^^^^^^^^^^^^^^^^ + * + * Type: ::PM_CLASS_VARIABLE_OR_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_class_variable_or_write_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * ClassVariableOrWriteNode#name + */ + pm_constant_id_t name; + + /** + * ClassVariableOrWriteNode#name_loc + */ + pm_location_t name_loc; + + /** + * ClassVariableOrWriteNode#operator_loc + */ + pm_location_t operator_loc; + + /** + * ClassVariableOrWriteNode#value + */ + struct pm_node *value; +} pm_class_variable_or_write_node_t; + +/** + * ClassVariableReadNode + * + * Represents referencing a class variable. + * + * @@foo + * ^^^^^ + * + * Type: ::PM_CLASS_VARIABLE_READ_NODE + * + * @extends pm_node_t + */ +typedef struct pm_class_variable_read_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * ClassVariableReadNode#name + * + * 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` + */ + pm_constant_id_t name; +} pm_class_variable_read_node_t; + +/** + * ClassVariableTargetNode + * + * Represents writing to a class variable in a context that doesn't have an explicit value. + * + * @@foo, @@bar = baz + * ^^^^^ ^^^^^ + * + * Type: ::PM_CLASS_VARIABLE_TARGET_NODE + * + * @extends pm_node_t + */ +typedef struct pm_class_variable_target_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * ClassVariableTargetNode#name + */ + pm_constant_id_t name; +} pm_class_variable_target_node_t; + +/** + * ClassVariableWriteNode + * + * Represents writing to a class variable. + * + * @@foo = 1 + * ^^^^^^^^^ + * + * Type: ::PM_CLASS_VARIABLE_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_class_variable_write_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * ClassVariableWriteNode#name + * + * 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` + */ + pm_constant_id_t name; + + /** + * ClassVariableWriteNode#name_loc + * + * The location of the variable name. + * + * @@foo = :bar + * ^^^^^ + */ + pm_location_t name_loc; + + /** + * ClassVariableWriteNode#value + * + * 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 + * ^^^ + */ + struct pm_node *value; + + /** + * ClassVariableWriteNode#operator_loc + * + * The location of the `=` operator. + * + * @@foo = :bar + * ^ + */ + pm_location_t operator_loc; +} pm_class_variable_write_node_t; + +/** + * ConstantAndWriteNode + * + * Represents the use of the `&&=` operator for assignment to a constant. + * + * Target &&= value + * ^^^^^^^^^^^^^^^^ + * + * Type: ::PM_CONSTANT_AND_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_constant_and_write_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * ConstantAndWriteNode#name + */ + pm_constant_id_t name; + + /** + * ConstantAndWriteNode#name_loc + */ + pm_location_t name_loc; + + /** + * ConstantAndWriteNode#operator_loc + */ + pm_location_t operator_loc; + + /** + * ConstantAndWriteNode#value + */ + struct pm_node *value; +} pm_constant_and_write_node_t; + +/** + * ConstantOperatorWriteNode + * + * Represents assigning to a constant using an operator that isn't `=`. + * + * Target += value + * ^^^^^^^^^^^^^^^ + * + * Type: ::PM_CONSTANT_OPERATOR_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_constant_operator_write_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * ConstantOperatorWriteNode#name + */ + pm_constant_id_t name; + + /** + * ConstantOperatorWriteNode#name_loc + */ + pm_location_t name_loc; + + /** + * ConstantOperatorWriteNode#binary_operator_loc + */ + pm_location_t binary_operator_loc; + + /** + * ConstantOperatorWriteNode#value + */ + struct pm_node *value; + + /** + * ConstantOperatorWriteNode#binary_operator + */ + pm_constant_id_t binary_operator; +} pm_constant_operator_write_node_t; + +/** + * ConstantOrWriteNode + * + * Represents the use of the `||=` operator for assignment to a constant. + * + * Target ||= value + * ^^^^^^^^^^^^^^^^ + * + * Type: ::PM_CONSTANT_OR_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_constant_or_write_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * ConstantOrWriteNode#name + */ + pm_constant_id_t name; + + /** + * ConstantOrWriteNode#name_loc + */ + pm_location_t name_loc; + + /** + * ConstantOrWriteNode#operator_loc + */ + pm_location_t operator_loc; + + /** + * ConstantOrWriteNode#value + */ + struct pm_node *value; +} pm_constant_or_write_node_t; + +/** + * ConstantPathAndWriteNode + * + * Represents the use of the `&&=` operator for assignment to a constant path. + * + * Parent::Child &&= value + * ^^^^^^^^^^^^^^^^^^^^^^^ + * + * Type: ::PM_CONSTANT_PATH_AND_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_constant_path_and_write_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * ConstantPathAndWriteNode#target + */ + struct pm_constant_path_node *target; + + /** + * ConstantPathAndWriteNode#operator_loc + */ + pm_location_t operator_loc; + + /** + * ConstantPathAndWriteNode#value + */ + struct pm_node *value; +} pm_constant_path_and_write_node_t; + +/** + * ConstantPathNode + * + * Represents accessing a constant through a path of `::` operators. + * + * Foo::Bar + * ^^^^^^^^ + * + * Type: ::PM_CONSTANT_PATH_NODE + * + * @extends pm_node_t + */ +typedef struct pm_constant_path_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * ConstantPathNode#parent + * + * 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 + * ^^^ + */ + struct pm_node *parent; + + /** + * ConstantPathNode#name + * + * The name of the constant being accessed. This could be `nil` in the event of a syntax error. + */ + pm_constant_id_t name; + + /** + * ConstantPathNode#delimiter_loc + * + * The location of the `::` delimiter. + * + * ::Foo + * ^^ + * + * One::Two + * ^^ + */ + pm_location_t delimiter_loc; + + /** + * ConstantPathNode#name_loc + * + * The location of the name of the constant. + * + * ::Foo + * ^^^ + * + * One::Two + * ^^^ + */ + pm_location_t name_loc; +} pm_constant_path_node_t; + +/** + * ConstantPathOperatorWriteNode + * + * Represents assigning to a constant path using an operator that isn't `=`. + * + * Parent::Child += value + * ^^^^^^^^^^^^^^^^^^^^^^ + * + * Type: ::PM_CONSTANT_PATH_OPERATOR_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_constant_path_operator_write_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * ConstantPathOperatorWriteNode#target + */ + struct pm_constant_path_node *target; + + /** + * ConstantPathOperatorWriteNode#binary_operator_loc + */ + pm_location_t binary_operator_loc; + + /** + * ConstantPathOperatorWriteNode#value + */ + struct pm_node *value; + + /** + * ConstantPathOperatorWriteNode#binary_operator + */ + pm_constant_id_t binary_operator; +} pm_constant_path_operator_write_node_t; + +/** + * ConstantPathOrWriteNode + * + * Represents the use of the `||=` operator for assignment to a constant path. + * + * Parent::Child ||= value + * ^^^^^^^^^^^^^^^^^^^^^^^ + * + * Type: ::PM_CONSTANT_PATH_OR_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_constant_path_or_write_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * ConstantPathOrWriteNode#target + */ + struct pm_constant_path_node *target; + + /** + * ConstantPathOrWriteNode#operator_loc + */ + pm_location_t operator_loc; + + /** + * ConstantPathOrWriteNode#value + */ + struct pm_node *value; +} pm_constant_path_or_write_node_t; + +/** + * ConstantPathTargetNode + * + * Represents writing to a constant path in a context that doesn't have an explicit value. + * + * Foo::Foo, Bar::Bar = baz + * ^^^^^^^^ ^^^^^^^^ + * + * Type: ::PM_CONSTANT_PATH_TARGET_NODE + * + * @extends pm_node_t + */ +typedef struct pm_constant_path_target_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * ConstantPathTargetNode#parent + */ + struct pm_node *parent; + + /** + * ConstantPathTargetNode#name + */ + pm_constant_id_t name; + + /** + * ConstantPathTargetNode#delimiter_loc + */ + pm_location_t delimiter_loc; + + /** + * ConstantPathTargetNode#name_loc + */ + pm_location_t name_loc; +} pm_constant_path_target_node_t; + +/** + * ConstantPathWriteNode + * + * Represents writing to a constant path. + * + * ::Foo = 1 + * ^^^^^^^^^ + * + * Foo::Bar = 1 + * ^^^^^^^^^^^^ + * + * ::Foo::Bar = 1 + * ^^^^^^^^^^^^^^ + * + * Type: ::PM_CONSTANT_PATH_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_constant_path_write_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * ConstantPathWriteNode#target + * + * A node representing the constant path being written to. + * + * Foo::Bar = 1 + * ^^^^^^^^ + * + * ::Foo = :abc + * ^^^^^ + */ + struct pm_constant_path_node *target; + + /** + * ConstantPathWriteNode#operator_loc + * + * The location of the `=` operator. + * + * ::ABC = 123 + * ^ + */ + pm_location_t operator_loc; + + /** + * ConstantPathWriteNode#value + * + * 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 + * ^^^^ + */ + struct pm_node *value; +} pm_constant_path_write_node_t; + +/** + * ConstantReadNode + * + * Represents referencing a constant. + * + * Foo + * ^^^ + * + * Type: ::PM_CONSTANT_READ_NODE + * + * @extends pm_node_t + */ +typedef struct pm_constant_read_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * ConstantReadNode#name + * + * 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` + */ + pm_constant_id_t name; +} pm_constant_read_node_t; + +/** + * ConstantTargetNode + * + * Represents writing to a constant in a context that doesn't have an explicit value. + * + * Foo, Bar = baz + * ^^^ ^^^ + * + * Type: ::PM_CONSTANT_TARGET_NODE + * + * @extends pm_node_t + */ +typedef struct pm_constant_target_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * ConstantTargetNode#name + */ + pm_constant_id_t name; +} pm_constant_target_node_t; + +/** + * ConstantWriteNode + * + * Represents writing to a constant. + * + * Foo = 1 + * ^^^^^^^ + * + * Type: ::PM_CONSTANT_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_constant_write_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * ConstantWriteNode#name + * + * 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` + */ + pm_constant_id_t name; + + /** + * ConstantWriteNode#name_loc + * + * The location of the constant name. + * + * FOO = 1 + * ^^^ + */ + pm_location_t name_loc; + + /** + * ConstantWriteNode#value + * + * 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 + * ^^^^^^^^^ + */ + struct pm_node *value; + + /** + * ConstantWriteNode#operator_loc + * + * The location of the `=` operator. + * + * FOO = :bar + * ^ + */ + pm_location_t operator_loc; +} pm_constant_write_node_t; + +/** + * DefNode + * + * Represents a method definition. + * + * def method + * end + * ^^^^^^^^^^ + * + * Type: ::PM_DEF_NODE + * + * @extends pm_node_t + */ +typedef struct pm_def_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * DefNode#name + */ + pm_constant_id_t name; + + /** + * DefNode#name_loc + */ + pm_location_t name_loc; + + /** + * DefNode#receiver + */ + struct pm_node *receiver; + + /** + * DefNode#parameters + */ + struct pm_parameters_node *parameters; + + /** + * DefNode#body + */ + struct pm_node *body; + + /** + * DefNode#locals + */ + pm_constant_id_list_t locals; + + /** + * DefNode#def_keyword_loc + */ + pm_location_t def_keyword_loc; + + /** + * DefNode#operator_loc + */ + pm_location_t operator_loc; + + /** + * DefNode#lparen_loc + */ + pm_location_t lparen_loc; + + /** + * DefNode#rparen_loc + */ + pm_location_t rparen_loc; + + /** + * DefNode#equal_loc + */ + pm_location_t equal_loc; + + /** + * DefNode#end_keyword_loc + */ + pm_location_t end_keyword_loc; +} pm_def_node_t; + +/** + * DefinedNode + * + * Represents the use of the `defined?` keyword. + * + * defined?(a) + * ^^^^^^^^^^^ + * + * Type: ::PM_DEFINED_NODE + * + * @extends pm_node_t + */ +typedef struct pm_defined_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * DefinedNode#lparen_loc + */ + pm_location_t lparen_loc; + + /** + * DefinedNode#value + */ + struct pm_node *value; + + /** + * DefinedNode#rparen_loc + */ + pm_location_t rparen_loc; + + /** + * DefinedNode#keyword_loc + */ + pm_location_t keyword_loc; +} pm_defined_node_t; + +/** + * ElseNode + * + * Represents an `else` clause in a `case`, `if`, or `unless` statement. + * + * if a then b else c end + * ^^^^^^^^^^ + * + * Type: ::PM_ELSE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_else_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * ElseNode#else_keyword_loc + */ + pm_location_t else_keyword_loc; + + /** + * ElseNode#statements + */ + struct pm_statements_node *statements; + + /** + * ElseNode#end_keyword_loc + */ + pm_location_t end_keyword_loc; +} pm_else_node_t; + +/** + * EmbeddedStatementsNode + * + * Represents an interpolated set of statements. + * + * "foo #{bar}" + * ^^^^^^ + * + * Type: ::PM_EMBEDDED_STATEMENTS_NODE + * + * @extends pm_node_t + */ +typedef struct pm_embedded_statements_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * EmbeddedStatementsNode#opening_loc + */ + pm_location_t opening_loc; + + /** + * EmbeddedStatementsNode#statements + */ + struct pm_statements_node *statements; + + /** + * EmbeddedStatementsNode#closing_loc + */ + pm_location_t closing_loc; +} pm_embedded_statements_node_t; + +/** + * EmbeddedVariableNode + * + * Represents an interpolated variable. + * + * "foo #@bar" + * ^^^^^ + * + * Type: ::PM_EMBEDDED_VARIABLE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_embedded_variable_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * EmbeddedVariableNode#operator_loc + */ + pm_location_t operator_loc; + + /** + * EmbeddedVariableNode#variable + */ + struct pm_node *variable; +} pm_embedded_variable_node_t; + +/** + * EnsureNode + * + * Represents an `ensure` clause in a `begin` statement. + * + * begin + * foo + * ensure + * ^^^^^^ + * bar + * end + * + * Type: ::PM_ENSURE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_ensure_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * EnsureNode#ensure_keyword_loc + */ + pm_location_t ensure_keyword_loc; + + /** + * EnsureNode#statements + */ + struct pm_statements_node *statements; + + /** + * EnsureNode#end_keyword_loc + */ + pm_location_t end_keyword_loc; +} pm_ensure_node_t; + +/** + * FalseNode + * + * Represents the use of the literal `false` keyword. + * + * false + * ^^^^^ + * + * Type: ::PM_FALSE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_false_node { + /** The embedded base node. */ + pm_node_t base; + +} pm_false_node_t; + +/** + * FindPatternNode + * + * Represents a find pattern in pattern matching. + * + * foo in *bar, baz, *qux + * ^^^^^^^^^^^^^^^ + * + * foo in [*bar, baz, *qux] + * ^^^^^^^^^^^^^^^^^ + * + * foo in Foo(*bar, baz, *qux) + * ^^^^^^^^^^^^^^^^^^^^ + * + * foo => *bar, baz, *qux + * ^^^^^^^^^^^^^^^ + * + * Type: ::PM_FIND_PATTERN_NODE + * + * @extends pm_node_t + */ +typedef struct pm_find_pattern_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * FindPatternNode#constant + * + * Represents the optional constant preceding the pattern + * + * foo in Foo(*bar, baz, *qux) + * ^^^ + */ + struct pm_node *constant; + + /** + * FindPatternNode#left + * + * Represents the first wildcard node in the pattern. + * + * foo in *bar, baz, *qux + * ^^^^ + * + * foo in Foo(*bar, baz, *qux) + * ^^^^ + */ + struct pm_splat_node *left; + + /** + * FindPatternNode#requireds + * + * Represents the nodes in between the wildcards. + * + * foo in *bar, baz, *qux + * ^^^ + * + * foo in Foo(*bar, baz, 1, *qux) + * ^^^^^^ + */ + struct pm_node_list requireds; + + /** + * FindPatternNode#right + * + * Represents the second wildcard node in the pattern. + * + * foo in *bar, baz, *qux + * ^^^^ + * + * foo in Foo(*bar, baz, *qux) + * ^^^^ + */ + struct pm_node *right; + + /** + * FindPatternNode#opening_loc + * + * The location of the opening brace. + * + * foo in [*bar, baz, *qux] + * ^ + * + * foo in Foo(*bar, baz, *qux) + * ^ + */ + pm_location_t opening_loc; + + /** + * FindPatternNode#closing_loc + * + * The location of the closing brace. + * + * foo in [*bar, baz, *qux] + * ^ + * + * foo in Foo(*bar, baz, *qux) + * ^ + */ + pm_location_t closing_loc; +} pm_find_pattern_node_t; + +/** + * FlipFlopNode + * + * Represents the use of the `..` or `...` operators to create flip flops. + * + * baz if foo .. bar + * ^^^^^^^^^^ + * + * Type: ::PM_FLIP_FLOP_NODE + + * Flags (#pm_range_flags): + * * ::PM_RANGE_FLAGS_EXCLUDE_END + * + * @extends pm_node_t + */ +typedef struct pm_flip_flop_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * FlipFlopNode#left + */ + struct pm_node *left; + + /** + * FlipFlopNode#right + */ + struct pm_node *right; + + /** + * FlipFlopNode#operator_loc + */ + pm_location_t operator_loc; +} pm_flip_flop_node_t; + +/** + * FloatNode + * + * Represents a floating point number literal. + * + * 1.0 + * ^^^ + * + * Type: ::PM_FLOAT_NODE + * + * @extends pm_node_t + */ +typedef struct pm_float_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * FloatNode#value + * + * The value of the floating point number as a Float. + */ + double value; +} pm_float_node_t; + +/** + * ForNode + * + * Represents the use of the `for` keyword. + * + * for i in a end + * ^^^^^^^^^^^^^^ + * + * Type: ::PM_FOR_NODE + * + * @extends pm_node_t + */ +typedef struct pm_for_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * ForNode#index + * + * The index expression for `for` loops. + * + * for i in a end + * ^ + */ + struct pm_node *index; + + /** + * ForNode#collection + * + * The collection to iterate over. + * + * for i in a end + * ^ + */ + struct pm_node *collection; + + /** + * ForNode#statements + * + * Represents the body of statements to execute for each iteration of the loop. + * + * for i in a + * foo(i) + * ^^^^^^ + * end + */ + struct pm_statements_node *statements; + + /** + * ForNode#for_keyword_loc + * + * The location of the `for` keyword. + * + * for i in a end + * ^^^ + */ + pm_location_t for_keyword_loc; + + /** + * ForNode#in_keyword_loc + * + * The location of the `in` keyword. + * + * for i in a end + * ^^ + */ + pm_location_t in_keyword_loc; + + /** + * ForNode#do_keyword_loc + * + * The location of the `do` keyword, if present. + * + * for i in a do end + * ^^ + */ + pm_location_t do_keyword_loc; + + /** + * ForNode#end_keyword_loc + * + * The location of the `end` keyword. + * + * for i in a end + * ^^^ + */ + pm_location_t end_keyword_loc; +} pm_for_node_t; + +/** + * ForwardingArgumentsNode + * + * Represents forwarding all arguments to this method to another method. + * + * def foo(...) + * bar(...) + * ^^^ + * end + * + * Type: ::PM_FORWARDING_ARGUMENTS_NODE + * + * @extends pm_node_t + */ +typedef struct pm_forwarding_arguments_node { + /** The embedded base node. */ + pm_node_t base; + +} pm_forwarding_arguments_node_t; + +/** + * ForwardingParameterNode + * + * Represents the use of the forwarding parameter in a method, block, or lambda declaration. + * + * def foo(...) + * ^^^ + * end + * + * Type: ::PM_FORWARDING_PARAMETER_NODE + * + * @extends pm_node_t + */ +typedef struct pm_forwarding_parameter_node { + /** The embedded base node. */ + pm_node_t base; + +} pm_forwarding_parameter_node_t; + +/** + * ForwardingSuperNode + * + * Represents the use of the `super` keyword without parentheses or arguments, but which might have a block. + * + * super + * ^^^^^ + * + * super { 123 } + * ^^^^^^^^^^^^^ + * + * If it has any other arguments, it would be a `SuperNode` instead. + * + * Type: ::PM_FORWARDING_SUPER_NODE + * + * @extends pm_node_t + */ +typedef struct pm_forwarding_super_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * ForwardingSuperNode#block + * + * All other arguments are forwarded as normal, except the original block is replaced with the new block. + */ + struct pm_block_node *block; +} pm_forwarding_super_node_t; + +/** + * GlobalVariableAndWriteNode + * + * Represents the use of the `&&=` operator for assignment to a global variable. + * + * $target &&= value + * ^^^^^^^^^^^^^^^^^ + * + * Type: ::PM_GLOBAL_VARIABLE_AND_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_global_variable_and_write_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * GlobalVariableAndWriteNode#name + */ + pm_constant_id_t name; + + /** + * GlobalVariableAndWriteNode#name_loc + */ + pm_location_t name_loc; + + /** + * GlobalVariableAndWriteNode#operator_loc + */ + pm_location_t operator_loc; + + /** + * GlobalVariableAndWriteNode#value + */ + struct pm_node *value; +} pm_global_variable_and_write_node_t; + +/** + * GlobalVariableOperatorWriteNode + * + * Represents assigning to a global variable using an operator that isn't `=`. + * + * $target += value + * ^^^^^^^^^^^^^^^^ + * + * Type: ::PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_global_variable_operator_write_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * GlobalVariableOperatorWriteNode#name + */ + pm_constant_id_t name; + + /** + * GlobalVariableOperatorWriteNode#name_loc + */ + pm_location_t name_loc; + + /** + * GlobalVariableOperatorWriteNode#binary_operator_loc + */ + pm_location_t binary_operator_loc; + + /** + * GlobalVariableOperatorWriteNode#value + */ + struct pm_node *value; + + /** + * GlobalVariableOperatorWriteNode#binary_operator + */ + pm_constant_id_t binary_operator; +} pm_global_variable_operator_write_node_t; + +/** + * GlobalVariableOrWriteNode + * + * Represents the use of the `||=` operator for assignment to a global variable. + * + * $target ||= value + * ^^^^^^^^^^^^^^^^^ + * + * Type: ::PM_GLOBAL_VARIABLE_OR_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_global_variable_or_write_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * GlobalVariableOrWriteNode#name + */ + pm_constant_id_t name; + + /** + * GlobalVariableOrWriteNode#name_loc + */ + pm_location_t name_loc; + + /** + * GlobalVariableOrWriteNode#operator_loc + */ + pm_location_t operator_loc; + + /** + * GlobalVariableOrWriteNode#value + */ + struct pm_node *value; +} pm_global_variable_or_write_node_t; + +/** + * GlobalVariableReadNode + * + * Represents referencing a global variable. + * + * $foo + * ^^^^ + * + * Type: ::PM_GLOBAL_VARIABLE_READ_NODE + * + * @extends pm_node_t + */ +typedef struct pm_global_variable_read_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * GlobalVariableReadNode#name + * + * 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` + */ + pm_constant_id_t name; +} pm_global_variable_read_node_t; + +/** + * GlobalVariableTargetNode + * + * Represents writing to a global variable in a context that doesn't have an explicit value. + * + * $foo, $bar = baz + * ^^^^ ^^^^ + * + * Type: ::PM_GLOBAL_VARIABLE_TARGET_NODE + * + * @extends pm_node_t + */ +typedef struct pm_global_variable_target_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * GlobalVariableTargetNode#name + */ + pm_constant_id_t name; +} pm_global_variable_target_node_t; + +/** + * GlobalVariableWriteNode + * + * Represents writing to a global variable. + * + * $foo = 1 + * ^^^^^^^^ + * + * Type: ::PM_GLOBAL_VARIABLE_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_global_variable_write_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * GlobalVariableWriteNode#name + * + * 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` + */ + pm_constant_id_t name; + + /** + * GlobalVariableWriteNode#name_loc + * + * The location of the global variable's name. + * + * $foo = :bar + * ^^^^ + */ + pm_location_t name_loc; + + /** + * GlobalVariableWriteNode#value + * + * 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 + * ^^^ + */ + struct pm_node *value; + + /** + * GlobalVariableWriteNode#operator_loc + * + * The location of the `=` operator. + * + * $foo = :bar + * ^ + */ + pm_location_t operator_loc; +} pm_global_variable_write_node_t; + +/** + * HashNode + * + * Represents a hash literal. + * + * { a => b } + * ^^^^^^^^^^ + * + * Type: ::PM_HASH_NODE + * + * @extends pm_node_t + */ +typedef struct pm_hash_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * HashNode#opening_loc + * + * The location of the opening brace. + * + * { a => b } + * ^ + */ + pm_location_t opening_loc; + + /** + * HashNode#elements + * + * The elements of the hash. These can be either `AssocNode`s or `AssocSplatNode`s. + * + * { a: b } + * ^^^^ + * + * { **foo } + * ^^^^^ + */ + struct pm_node_list elements; + + /** + * HashNode#closing_loc + * + * The location of the closing brace. + * + * { a => b } + * ^ + */ + pm_location_t closing_loc; +} pm_hash_node_t; + +/** + * HashPatternNode + * + * Represents a hash pattern in pattern matching. + * + * foo => { a: 1, b: 2 } + * ^^^^^^^^^^^^^^ + * + * foo => { a: 1, b: 2, **c } + * ^^^^^^^^^^^^^^^^^^^ + * + * foo => Bar[a: 1, b: 2] + * ^^^^^^^^^^^^^^^ + * + * foo in { a: 1, b: 2 } + * ^^^^^^^^^^^^^^ + * + * Type: ::PM_HASH_PATTERN_NODE + * + * @extends pm_node_t + */ +typedef struct pm_hash_pattern_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * HashPatternNode#constant + * + * Represents the optional constant preceding the Hash. + * + * foo => Bar[a: 1, b: 2] + * ^^^ + * + * foo => Bar::Baz[a: 1, b: 2] + * ^^^^^^^^ + */ + struct pm_node *constant; + + /** + * HashPatternNode#elements + * + * Represents the explicit named hash keys and values. + * + * foo => { a: 1, b:, ** } + * ^^^^^^^^ + */ + struct pm_node_list elements; + + /** + * HashPatternNode#rest + * + * Represents the rest of the Hash keys and values. This can be named, unnamed, or explicitly forbidden via `**nil`, this last one results in a `NoKeywordsParameterNode`. + * + * foo => { a: 1, b:, **c } + * ^^^ + * + * foo => { a: 1, b:, ** } + * ^^ + * + * foo => { a: 1, b:, **nil } + * ^^^^^ + */ + struct pm_node *rest; + + /** + * HashPatternNode#opening_loc + * + * The location of the opening brace. + * + * foo => { a: 1 } + * ^ + * + * foo => Bar[a: 1] + * ^ + */ + pm_location_t opening_loc; + + /** + * HashPatternNode#closing_loc + * + * The location of the closing brace. + * + * foo => { a: 1 } + * ^ + * + * foo => Bar[a: 1] + * ^ + */ + pm_location_t closing_loc; +} pm_hash_pattern_node_t; + +/** + * IfNode + * + * 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 + * ^^^^^^^^^^^^^^^ + * + * Type: ::PM_IF_NODE + * + * @extends pm_node_t + */ +typedef struct pm_if_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * IfNode#if_keyword_loc + * + * 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. + */ + pm_location_t if_keyword_loc; + + /** + * IfNode#predicate + * + * The node for the condition the `IfNode` is testing. + * + * if foo + * ^^^ + * bar + * end + * + * bar if foo + * ^^^ + * + * foo ? bar : baz + * ^^^ + */ + struct pm_node *predicate; + + /** + * IfNode#then_keyword_loc + * + * The location of the `then` keyword (if present) or the `?` in a ternary expression, `nil` otherwise. + * + * if foo then bar end + * ^^^^ + * + * a ? b : c + * ^ + */ + pm_location_t then_keyword_loc; + + /** + * IfNode#statements + * + * 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 + */ + struct pm_statements_node *statements; + + /** + * IfNode#subsequent + * + * 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 + * ^^^^^^^^^^^^ + */ + struct pm_node *subsequent; + + /** + * IfNode#end_keyword_loc + * + * The location of the `end` keyword if present, `nil` otherwise. + * + * if foo + * bar + * end + * ^^^ + */ + pm_location_t end_keyword_loc; +} pm_if_node_t; + +/** + * ImaginaryNode + * + * Represents an imaginary number literal. + * + * 1.0i + * ^^^^ + * + * Type: ::PM_IMAGINARY_NODE + * + * @extends pm_node_t + */ +typedef struct pm_imaginary_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * ImaginaryNode#numeric + */ + struct pm_node *numeric; +} pm_imaginary_node_t; + +/** + * ImplicitNode + * + * 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: } + * ^^^^ + * + * Type: ::PM_IMPLICIT_NODE + * + * @extends pm_node_t + */ +typedef struct pm_implicit_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * ImplicitNode#value + */ + struct pm_node *value; +} pm_implicit_node_t; + +/** + * ImplicitRestNode + * + * Represents using a trailing comma to indicate an implicit rest parameter. + * + * foo { |bar,| } + * ^ + * + * foo in [bar,] + * ^ + * + * for foo, in bar do end + * ^ + * + * foo, = bar + * ^ + * + * Type: ::PM_IMPLICIT_REST_NODE + * + * @extends pm_node_t + */ +typedef struct pm_implicit_rest_node { + /** The embedded base node. */ + pm_node_t base; + +} pm_implicit_rest_node_t; + +/** + * InNode + * + * Represents the use of the `in` keyword in a case statement. + * + * case a; in b then c end + * ^^^^^^^^^^^ + * + * Type: ::PM_IN_NODE + * + * @extends pm_node_t + */ +typedef struct pm_in_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * InNode#pattern + */ + struct pm_node *pattern; + + /** + * InNode#statements + */ + struct pm_statements_node *statements; + + /** + * InNode#in_loc + */ + pm_location_t in_loc; + + /** + * InNode#then_loc + */ + pm_location_t then_loc; +} pm_in_node_t; + +/** + * IndexAndWriteNode + * + * Represents the use of the `&&=` operator on a call to the `[]` method. + * + * foo.bar[baz] &&= value + * ^^^^^^^^^^^^^^^^^^^^^^ + * + * Type: ::PM_INDEX_AND_WRITE_NODE + + * Flags (#pm_call_node_flags): + * * ::PM_CALL_NODE_FLAGS_SAFE_NAVIGATION + * * ::PM_CALL_NODE_FLAGS_VARIABLE_CALL + * * ::PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE + * * ::PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY + * + * @extends pm_node_t + */ +typedef struct pm_index_and_write_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * IndexAndWriteNode#receiver + */ + struct pm_node *receiver; + + /** + * IndexAndWriteNode#call_operator_loc + */ + pm_location_t call_operator_loc; + + /** + * IndexAndWriteNode#opening_loc + */ + pm_location_t opening_loc; + + /** + * IndexAndWriteNode#arguments + */ + struct pm_arguments_node *arguments; + + /** + * IndexAndWriteNode#closing_loc + */ + pm_location_t closing_loc; + + /** + * IndexAndWriteNode#block + */ + struct pm_block_argument_node *block; + + /** + * IndexAndWriteNode#operator_loc + */ + pm_location_t operator_loc; + + /** + * IndexAndWriteNode#value + */ + struct pm_node *value; +} pm_index_and_write_node_t; + +/** + * IndexOperatorWriteNode + * + * Represents the use of an assignment operator on a call to `[]`. + * + * foo.bar[baz] += value + * ^^^^^^^^^^^^^^^^^^^^^ + * + * Type: ::PM_INDEX_OPERATOR_WRITE_NODE + + * Flags (#pm_call_node_flags): + * * ::PM_CALL_NODE_FLAGS_SAFE_NAVIGATION + * * ::PM_CALL_NODE_FLAGS_VARIABLE_CALL + * * ::PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE + * * ::PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY + * + * @extends pm_node_t + */ +typedef struct pm_index_operator_write_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * IndexOperatorWriteNode#receiver + */ + struct pm_node *receiver; + + /** + * IndexOperatorWriteNode#call_operator_loc + */ + pm_location_t call_operator_loc; + + /** + * IndexOperatorWriteNode#opening_loc + */ + pm_location_t opening_loc; + + /** + * IndexOperatorWriteNode#arguments + */ + struct pm_arguments_node *arguments; + + /** + * IndexOperatorWriteNode#closing_loc + */ + pm_location_t closing_loc; + + /** + * IndexOperatorWriteNode#block + */ + struct pm_block_argument_node *block; + + /** + * IndexOperatorWriteNode#binary_operator + */ + pm_constant_id_t binary_operator; + + /** + * IndexOperatorWriteNode#binary_operator_loc + */ + pm_location_t binary_operator_loc; + + /** + * IndexOperatorWriteNode#value + */ + struct pm_node *value; +} pm_index_operator_write_node_t; + +/** + * IndexOrWriteNode + * + * Represents the use of the `||=` operator on a call to `[]`. + * + * foo.bar[baz] ||= value + * ^^^^^^^^^^^^^^^^^^^^^^ + * + * Type: ::PM_INDEX_OR_WRITE_NODE + + * Flags (#pm_call_node_flags): + * * ::PM_CALL_NODE_FLAGS_SAFE_NAVIGATION + * * ::PM_CALL_NODE_FLAGS_VARIABLE_CALL + * * ::PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE + * * ::PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY + * + * @extends pm_node_t + */ +typedef struct pm_index_or_write_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * IndexOrWriteNode#receiver + */ + struct pm_node *receiver; + + /** + * IndexOrWriteNode#call_operator_loc + */ + pm_location_t call_operator_loc; + + /** + * IndexOrWriteNode#opening_loc + */ + pm_location_t opening_loc; + + /** + * IndexOrWriteNode#arguments + */ + struct pm_arguments_node *arguments; + + /** + * IndexOrWriteNode#closing_loc + */ + pm_location_t closing_loc; + + /** + * IndexOrWriteNode#block + */ + struct pm_block_argument_node *block; + + /** + * IndexOrWriteNode#operator_loc + */ + pm_location_t operator_loc; + + /** + * IndexOrWriteNode#value + */ + struct pm_node *value; +} pm_index_or_write_node_t; + +/** + * IndexTargetNode + * + * Represents assigning to an index. + * + * foo[bar], = 1 + * ^^^^^^^^ + * + * begin + * rescue => foo[bar] + * ^^^^^^^^ + * end + * + * for foo[bar] in baz do end + * ^^^^^^^^ + * + * Type: ::PM_INDEX_TARGET_NODE + + * Flags (#pm_call_node_flags): + * * ::PM_CALL_NODE_FLAGS_SAFE_NAVIGATION + * * ::PM_CALL_NODE_FLAGS_VARIABLE_CALL + * * ::PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE + * * ::PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY + * + * @extends pm_node_t + */ +typedef struct pm_index_target_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * IndexTargetNode#receiver + */ + struct pm_node *receiver; + + /** + * IndexTargetNode#opening_loc + */ + pm_location_t opening_loc; + + /** + * IndexTargetNode#arguments + */ + struct pm_arguments_node *arguments; + + /** + * IndexTargetNode#closing_loc + */ + pm_location_t closing_loc; + + /** + * IndexTargetNode#block + */ + struct pm_block_argument_node *block; +} pm_index_target_node_t; + +/** + * InstanceVariableAndWriteNode + * + * Represents the use of the `&&=` operator for assignment to an instance variable. + * + * @target &&= value + * ^^^^^^^^^^^^^^^^^ + * + * Type: ::PM_INSTANCE_VARIABLE_AND_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_instance_variable_and_write_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * InstanceVariableAndWriteNode#name + */ + pm_constant_id_t name; + + /** + * InstanceVariableAndWriteNode#name_loc + */ + pm_location_t name_loc; + + /** + * InstanceVariableAndWriteNode#operator_loc + */ + pm_location_t operator_loc; + + /** + * InstanceVariableAndWriteNode#value + */ + struct pm_node *value; +} pm_instance_variable_and_write_node_t; + +/** + * InstanceVariableOperatorWriteNode + * + * Represents assigning to an instance variable using an operator that isn't `=`. + * + * @target += value + * ^^^^^^^^^^^^^^^^ + * + * Type: ::PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_instance_variable_operator_write_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * InstanceVariableOperatorWriteNode#name + */ + pm_constant_id_t name; + + /** + * InstanceVariableOperatorWriteNode#name_loc + */ + pm_location_t name_loc; + + /** + * InstanceVariableOperatorWriteNode#binary_operator_loc + */ + pm_location_t binary_operator_loc; + + /** + * InstanceVariableOperatorWriteNode#value + */ + struct pm_node *value; + + /** + * InstanceVariableOperatorWriteNode#binary_operator + */ + pm_constant_id_t binary_operator; +} pm_instance_variable_operator_write_node_t; + +/** + * InstanceVariableOrWriteNode + * + * Represents the use of the `||=` operator for assignment to an instance variable. + * + * @target ||= value + * ^^^^^^^^^^^^^^^^^ + * + * Type: ::PM_INSTANCE_VARIABLE_OR_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_instance_variable_or_write_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * InstanceVariableOrWriteNode#name + */ + pm_constant_id_t name; + + /** + * InstanceVariableOrWriteNode#name_loc + */ + pm_location_t name_loc; + + /** + * InstanceVariableOrWriteNode#operator_loc + */ + pm_location_t operator_loc; + + /** + * InstanceVariableOrWriteNode#value + */ + struct pm_node *value; +} pm_instance_variable_or_write_node_t; + +/** + * InstanceVariableReadNode + * + * Represents referencing an instance variable. + * + * @foo + * ^^^^ + * + * Type: ::PM_INSTANCE_VARIABLE_READ_NODE + * + * @extends pm_node_t + */ +typedef struct pm_instance_variable_read_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * InstanceVariableReadNode#name + * + * 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` + */ + pm_constant_id_t name; +} pm_instance_variable_read_node_t; + +/** + * InstanceVariableTargetNode + * + * Represents writing to an instance variable in a context that doesn't have an explicit value. + * + * @foo, @bar = baz + * ^^^^ ^^^^ + * + * Type: ::PM_INSTANCE_VARIABLE_TARGET_NODE + * + * @extends pm_node_t + */ +typedef struct pm_instance_variable_target_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * InstanceVariableTargetNode#name + */ + pm_constant_id_t name; +} pm_instance_variable_target_node_t; + +/** + * InstanceVariableWriteNode + * + * Represents writing to an instance variable. + * + * @foo = 1 + * ^^^^^^^^ + * + * Type: ::PM_INSTANCE_VARIABLE_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_instance_variable_write_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * InstanceVariableWriteNode#name + * + * 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` + */ + pm_constant_id_t name; + + /** + * InstanceVariableWriteNode#name_loc + * + * The location of the variable name. + * + * @_x = 1 + * ^^^ + */ + pm_location_t name_loc; + + /** + * InstanceVariableWriteNode#value + * + * 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 + * ^^^^ + */ + struct pm_node *value; + + /** + * InstanceVariableWriteNode#operator_loc + * + * The location of the `=` operator. + * + * @x = y + * ^ + */ + pm_location_t operator_loc; +} pm_instance_variable_write_node_t; + +/** + * IntegerNode + * + * Represents an integer number literal. + * + * 1 + * ^ + * + * Type: ::PM_INTEGER_NODE + + * Flags (#pm_integer_base_flags): + * * ::PM_INTEGER_BASE_FLAGS_BINARY + * * ::PM_INTEGER_BASE_FLAGS_DECIMAL + * * ::PM_INTEGER_BASE_FLAGS_OCTAL + * * ::PM_INTEGER_BASE_FLAGS_HEXADECIMAL + * + * @extends pm_node_t + */ +typedef struct pm_integer_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * IntegerNode#value + * + * The value of the integer literal as a number. + */ + pm_integer_t value; +} pm_integer_node_t; + +/** + * InterpolatedMatchLastLineNode + * + * 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 + * ^^^^^^^^^^^^^^^^ + * + * Type: ::PM_INTERPOLATED_MATCH_LAST_LINE_NODE + + * Flags (#pm_regular_expression_flags): + * * ::PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE + * * ::PM_REGULAR_EXPRESSION_FLAGS_EXTENDED + * * ::PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE + * * ::PM_REGULAR_EXPRESSION_FLAGS_ONCE + * * ::PM_REGULAR_EXPRESSION_FLAGS_EUC_JP + * * ::PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT + * * ::PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J + * * ::PM_REGULAR_EXPRESSION_FLAGS_UTF_8 + * * ::PM_REGULAR_EXPRESSION_FLAGS_FORCED_UTF8_ENCODING + * * ::PM_REGULAR_EXPRESSION_FLAGS_FORCED_BINARY_ENCODING + * * ::PM_REGULAR_EXPRESSION_FLAGS_FORCED_US_ASCII_ENCODING + * + * @extends pm_node_t + */ +typedef struct pm_interpolated_match_last_line_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * InterpolatedMatchLastLineNode#opening_loc + */ + pm_location_t opening_loc; + + /** + * InterpolatedMatchLastLineNode#parts + */ + struct pm_node_list parts; + + /** + * InterpolatedMatchLastLineNode#closing_loc + */ + pm_location_t closing_loc; +} pm_interpolated_match_last_line_node_t; + +/** + * InterpolatedRegularExpressionNode + * + * Represents a regular expression literal that contains interpolation. + * + * /foo #{bar} baz/ + * ^^^^^^^^^^^^^^^^ + * + * Type: ::PM_INTERPOLATED_REGULAR_EXPRESSION_NODE + + * Flags (#pm_regular_expression_flags): + * * ::PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE + * * ::PM_REGULAR_EXPRESSION_FLAGS_EXTENDED + * * ::PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE + * * ::PM_REGULAR_EXPRESSION_FLAGS_ONCE + * * ::PM_REGULAR_EXPRESSION_FLAGS_EUC_JP + * * ::PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT + * * ::PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J + * * ::PM_REGULAR_EXPRESSION_FLAGS_UTF_8 + * * ::PM_REGULAR_EXPRESSION_FLAGS_FORCED_UTF8_ENCODING + * * ::PM_REGULAR_EXPRESSION_FLAGS_FORCED_BINARY_ENCODING + * * ::PM_REGULAR_EXPRESSION_FLAGS_FORCED_US_ASCII_ENCODING + * + * @extends pm_node_t + */ +typedef struct pm_interpolated_regular_expression_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * InterpolatedRegularExpressionNode#opening_loc + */ + pm_location_t opening_loc; + + /** + * InterpolatedRegularExpressionNode#parts + */ + struct pm_node_list parts; + + /** + * InterpolatedRegularExpressionNode#closing_loc + */ + pm_location_t closing_loc; +} pm_interpolated_regular_expression_node_t; + +/** + * InterpolatedStringNode + * + * Represents a string literal that contains interpolation. + * + * "foo #{bar} baz" + * ^^^^^^^^^^^^^^^^ + * + * Type: ::PM_INTERPOLATED_STRING_NODE + + * Flags (#pm_interpolated_string_node_flags): + * * ::PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN + * * ::PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE + * + * @extends pm_node_t + */ +typedef struct pm_interpolated_string_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * InterpolatedStringNode#opening_loc + */ + pm_location_t opening_loc; + + /** + * InterpolatedStringNode#parts + */ + struct pm_node_list parts; + + /** + * InterpolatedStringNode#closing_loc + */ + pm_location_t closing_loc; +} pm_interpolated_string_node_t; + +/** + * InterpolatedSymbolNode + * + * Represents a symbol literal that contains interpolation. + * + * :"foo #{bar} baz" + * ^^^^^^^^^^^^^^^^^ + * + * Type: ::PM_INTERPOLATED_SYMBOL_NODE + * + * @extends pm_node_t + */ +typedef struct pm_interpolated_symbol_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * InterpolatedSymbolNode#opening_loc + */ + pm_location_t opening_loc; + + /** + * InterpolatedSymbolNode#parts + */ + struct pm_node_list parts; + + /** + * InterpolatedSymbolNode#closing_loc + */ + pm_location_t closing_loc; +} pm_interpolated_symbol_node_t; + +/** + * InterpolatedXStringNode + * + * Represents an xstring literal that contains interpolation. + * + * `foo #{bar} baz` + * ^^^^^^^^^^^^^^^^ + * + * Type: ::PM_INTERPOLATED_X_STRING_NODE + * + * @extends pm_node_t + */ +typedef struct pm_interpolated_x_string_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * InterpolatedXStringNode#opening_loc + */ + pm_location_t opening_loc; + + /** + * InterpolatedXStringNode#parts + */ + struct pm_node_list parts; + + /** + * InterpolatedXStringNode#closing_loc + */ + pm_location_t closing_loc; +} pm_interpolated_x_string_node_t; + +/** + * ItLocalVariableReadNode + * + * Represents reading from the implicit `it` local variable. + * + * -> { it } + * ^^ + * + * Type: ::PM_IT_LOCAL_VARIABLE_READ_NODE + * + * @extends pm_node_t + */ +typedef struct pm_it_local_variable_read_node { + /** The embedded base node. */ + pm_node_t base; + +} pm_it_local_variable_read_node_t; + +/** + * ItParametersNode + * + * Represents an implicit set of parameters through the use of the `it` keyword within a block or lambda. + * + * -> { it + it } + * ^^^^^^^^^^^^^^ + * + * Type: ::PM_IT_PARAMETERS_NODE + * + * @extends pm_node_t + */ +typedef struct pm_it_parameters_node { + /** The embedded base node. */ + pm_node_t base; + +} pm_it_parameters_node_t; + +/** + * KeywordHashNode + * + * Represents a hash literal without opening and closing braces. + * + * foo(a: b) + * ^^^^ + * + * Type: ::PM_KEYWORD_HASH_NODE + + * Flags (#pm_keyword_hash_node_flags): + * * ::PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS + * + * @extends pm_node_t + */ +typedef struct pm_keyword_hash_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * KeywordHashNode#elements + */ + struct pm_node_list elements; +} pm_keyword_hash_node_t; + +/** + * KeywordRestParameterNode + * + * Represents a keyword rest parameter to a method, block, or lambda definition. + * + * def a(**b) + * ^^^ + * end + * + * Type: ::PM_KEYWORD_REST_PARAMETER_NODE + + * Flags (#pm_parameter_flags): + * * ::PM_PARAMETER_FLAGS_REPEATED_PARAMETER + * + * @extends pm_node_t + */ +typedef struct pm_keyword_rest_parameter_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * KeywordRestParameterNode#name + */ + pm_constant_id_t name; + + /** + * KeywordRestParameterNode#name_loc + */ + pm_location_t name_loc; + + /** + * KeywordRestParameterNode#operator_loc + */ + pm_location_t operator_loc; +} pm_keyword_rest_parameter_node_t; + +/** + * LambdaNode + * + * Represents using a lambda literal (not the lambda method call). + * + * ->(value) { value * 2 } + * ^^^^^^^^^^^^^^^^^^^^^^^ + * + * Type: ::PM_LAMBDA_NODE + * + * @extends pm_node_t + */ +typedef struct pm_lambda_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * LambdaNode#locals + */ + pm_constant_id_list_t locals; + + /** + * LambdaNode#operator_loc + */ + pm_location_t operator_loc; + + /** + * LambdaNode#opening_loc + */ + pm_location_t opening_loc; + + /** + * LambdaNode#closing_loc + */ + pm_location_t closing_loc; + + /** + * LambdaNode#parameters + */ + struct pm_node *parameters; + + /** + * LambdaNode#body + */ + struct pm_node *body; +} pm_lambda_node_t; + +/** + * LocalVariableAndWriteNode + * + * Represents the use of the `&&=` operator for assignment to a local variable. + * + * target &&= value + * ^^^^^^^^^^^^^^^^ + * + * Type: ::PM_LOCAL_VARIABLE_AND_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_local_variable_and_write_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * LocalVariableAndWriteNode#name_loc + */ + pm_location_t name_loc; + + /** + * LocalVariableAndWriteNode#operator_loc + */ + pm_location_t operator_loc; + + /** + * LocalVariableAndWriteNode#value + */ + struct pm_node *value; + + /** + * LocalVariableAndWriteNode#name + */ + pm_constant_id_t name; + + /** + * LocalVariableAndWriteNode#depth + */ + uint32_t depth; +} pm_local_variable_and_write_node_t; + +/** + * LocalVariableOperatorWriteNode + * + * Represents assigning to a local variable using an operator that isn't `=`. + * + * target += value + * ^^^^^^^^^^^^^^^ + * + * Type: ::PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_local_variable_operator_write_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * LocalVariableOperatorWriteNode#name_loc + */ + pm_location_t name_loc; + + /** + * LocalVariableOperatorWriteNode#binary_operator_loc + */ + pm_location_t binary_operator_loc; + + /** + * LocalVariableOperatorWriteNode#value + */ + struct pm_node *value; + + /** + * LocalVariableOperatorWriteNode#name + */ + pm_constant_id_t name; + + /** + * LocalVariableOperatorWriteNode#binary_operator + */ + pm_constant_id_t binary_operator; + + /** + * LocalVariableOperatorWriteNode#depth + */ + uint32_t depth; +} pm_local_variable_operator_write_node_t; + +/** + * LocalVariableOrWriteNode + * + * Represents the use of the `||=` operator for assignment to a local variable. + * + * target ||= value + * ^^^^^^^^^^^^^^^^ + * + * Type: ::PM_LOCAL_VARIABLE_OR_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_local_variable_or_write_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * LocalVariableOrWriteNode#name_loc + */ + pm_location_t name_loc; + + /** + * LocalVariableOrWriteNode#operator_loc + */ + pm_location_t operator_loc; + + /** + * LocalVariableOrWriteNode#value + */ + struct pm_node *value; + + /** + * LocalVariableOrWriteNode#name + */ + pm_constant_id_t name; + + /** + * LocalVariableOrWriteNode#depth + */ + uint32_t depth; +} pm_local_variable_or_write_node_t; + +/** + * LocalVariableReadNode + * + * 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 + * ^^^ + * + * Type: ::PM_LOCAL_VARIABLE_READ_NODE + * + * @extends pm_node_t + */ +typedef struct pm_local_variable_read_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * LocalVariableReadNode#name + * + * 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` + */ + pm_constant_id_t name; + + /** + * LocalVariableReadNode#depth + * + * 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). + */ + uint32_t depth; +} pm_local_variable_read_node_t; + +/** + * LocalVariableTargetNode + * + * Represents writing to a local variable in a context that doesn't have an explicit value. + * + * foo, bar = baz + * ^^^ ^^^ + * + * foo => baz + * ^^^ + * + * Type: ::PM_LOCAL_VARIABLE_TARGET_NODE + * + * @extends pm_node_t + */ +typedef struct pm_local_variable_target_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * LocalVariableTargetNode#name + */ + pm_constant_id_t name; + + /** + * LocalVariableTargetNode#depth + */ + uint32_t depth; +} pm_local_variable_target_node_t; + +/** + * LocalVariableWriteNode + * + * Represents writing to a local variable. + * + * foo = 1 + * ^^^^^^^ + * + * Type: ::PM_LOCAL_VARIABLE_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_local_variable_write_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * LocalVariableWriteNode#name + * + * 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` + */ + pm_constant_id_t name; + + /** + * LocalVariableWriteNode#depth + * + * 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). + */ + uint32_t depth; + + /** + * LocalVariableWriteNode#name_loc + * + * The location of the variable name. + * + * foo = :bar + * ^^^ + */ + pm_location_t name_loc; + + /** + * LocalVariableWriteNode#value + * + * 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 + */ + struct pm_node *value; + + /** + * LocalVariableWriteNode#operator_loc + * + * The location of the `=` operator. + * + * x = :y + * ^ + */ + pm_location_t operator_loc; +} pm_local_variable_write_node_t; + +/** + * MatchLastLineNode + * + * 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 + * ^^^^^^ + * + * Type: ::PM_MATCH_LAST_LINE_NODE + + * Flags (#pm_regular_expression_flags): + * * ::PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE + * * ::PM_REGULAR_EXPRESSION_FLAGS_EXTENDED + * * ::PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE + * * ::PM_REGULAR_EXPRESSION_FLAGS_ONCE + * * ::PM_REGULAR_EXPRESSION_FLAGS_EUC_JP + * * ::PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT + * * ::PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J + * * ::PM_REGULAR_EXPRESSION_FLAGS_UTF_8 + * * ::PM_REGULAR_EXPRESSION_FLAGS_FORCED_UTF8_ENCODING + * * ::PM_REGULAR_EXPRESSION_FLAGS_FORCED_BINARY_ENCODING + * * ::PM_REGULAR_EXPRESSION_FLAGS_FORCED_US_ASCII_ENCODING + * + * @extends pm_node_t + */ +typedef struct pm_match_last_line_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * MatchLastLineNode#opening_loc + */ + pm_location_t opening_loc; + + /** + * MatchLastLineNode#content_loc + */ + pm_location_t content_loc; + + /** + * MatchLastLineNode#closing_loc + */ + pm_location_t closing_loc; + + /** + * MatchLastLineNode#unescaped + */ + pm_string_t unescaped; +} pm_match_last_line_node_t; + +/** + * MatchPredicateNode + * + * Represents the use of the modifier `in` operator. + * + * foo in bar + * ^^^^^^^^^^ + * + * Type: ::PM_MATCH_PREDICATE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_match_predicate_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * MatchPredicateNode#value + */ + struct pm_node *value; + + /** + * MatchPredicateNode#pattern + */ + struct pm_node *pattern; + + /** + * MatchPredicateNode#operator_loc + */ + pm_location_t operator_loc; +} pm_match_predicate_node_t; + +/** + * MatchRequiredNode + * + * Represents the use of the `=>` operator. + * + * foo => bar + * ^^^^^^^^^^ + * + * Type: ::PM_MATCH_REQUIRED_NODE + * + * @extends pm_node_t + */ +typedef struct pm_match_required_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * MatchRequiredNode#value + * + * Represents the left-hand side of the operator. + * + * foo => bar + * ^^^ + */ + struct pm_node *value; + + /** + * MatchRequiredNode#pattern + * + * Represents the right-hand side of the operator. The type of the node depends on the expression. + * + * Anything that looks like a local variable name (including `_`) will result in a `LocalVariableTargetNode`. + * + * foo => a # This is equivalent to writing `a = foo` + * ^ + * + * Using an explicit `Array` or combining expressions with `,` will result in a `ArrayPatternNode`. This can be preceded by a constant. + * + * foo => [a] + * ^^^ + * + * foo => a, b + * ^^^^ + * + * foo => Bar[a, b] + * ^^^^^^^^^ + * + * If the array pattern contains at least two wildcard matches, a `FindPatternNode` is created instead. + * + * foo => *, 1, *a + * ^^^^^ + * + * Using an explicit `Hash` or a constant with square brackets and hash keys in the square brackets will result in a `HashPatternNode`. + * + * foo => { a: 1, b: } + * + * foo => Bar[a: 1, b:] + * + * foo => Bar[**] + * + * To use any variable that needs run time evaluation, pinning is required. This results in a `PinnedVariableNode` + * + * foo => ^a + * ^^ + * + * Similar, any expression can be used with pinning. This results in a `PinnedExpressionNode`. + * + * foo => ^(a + 1) + * + * Anything else will result in the regular node for that expression, for example a `ConstantReadNode`. + * + * foo => CONST + */ + struct pm_node *pattern; + + /** + * MatchRequiredNode#operator_loc + * + * The location of the operator. + * + * foo => bar + * ^^ + */ + pm_location_t operator_loc; +} pm_match_required_node_t; + +/** + * MatchWriteNode + * + * Represents writing local variables using a regular expression match with named capture groups. + * + * /(?bar)/ =~ baz + * ^^^^^^^^^^^^^^^^^^^^ + * + * Type: ::PM_MATCH_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_match_write_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * MatchWriteNode#call + */ + struct pm_call_node *call; + + /** + * MatchWriteNode#targets + */ + struct pm_node_list targets; +} pm_match_write_node_t; + +/** + * MissingNode + * + * Represents a node that is missing from the source and results in a syntax error. + * + * Type: ::PM_MISSING_NODE + * + * @extends pm_node_t + */ +typedef struct pm_missing_node { + /** The embedded base node. */ + pm_node_t base; + +} pm_missing_node_t; + +/** + * ModuleNode + * + * Represents a module declaration involving the `module` keyword. + * + * module Foo end + * ^^^^^^^^^^^^^^ + * + * Type: ::PM_MODULE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_module_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * ModuleNode#locals + */ + pm_constant_id_list_t locals; + + /** + * ModuleNode#module_keyword_loc + */ + pm_location_t module_keyword_loc; + + /** + * ModuleNode#constant_path + */ + struct pm_node *constant_path; + + /** + * ModuleNode#body + */ + struct pm_node *body; + + /** + * ModuleNode#end_keyword_loc + */ + pm_location_t end_keyword_loc; + + /** + * ModuleNode#name + */ + pm_constant_id_t name; +} pm_module_node_t; + +/** + * MultiTargetNode + * + * Represents a multi-target expression. + * + * a, (b, c) = 1, 2, 3 + * ^^^^^^ + * + * This can be a part of `MultiWriteNode` as above, or the target of a `for` loop + * + * for a, b in [[1, 2], [3, 4]] + * ^^^^ + * + * Type: ::PM_MULTI_TARGET_NODE + * + * @extends pm_node_t + */ +typedef struct pm_multi_target_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * MultiTargetNode#lefts + * + * Represents the targets expressions before a splat node. + * + * a, (b, c, *) = 1, 2, 3, 4, 5 + * ^^^^ + * + * The splat node can be absent, in that case all target expressions are in the left field. + * + * a, (b, c) = 1, 2, 3, 4, 5 + * ^^^^ + */ + struct pm_node_list lefts; + + /** + * MultiTargetNode#rest + * + * Represents a splat node in the target expression. + * + * a, (b, *c) = 1, 2, 3, 4 + * ^^ + * + * The variable can be empty, this results in a `SplatNode` with a `nil` expression field. + * + * a, (b, *) = 1, 2, 3, 4 + * ^ + * + * If the `*` is omitted, this field will contain an `ImplicitRestNode` + * + * a, (b,) = 1, 2, 3, 4 + * ^ + */ + struct pm_node *rest; + + /** + * MultiTargetNode#rights + * + * Represents the targets expressions after a splat node. + * + * a, (*, b, c) = 1, 2, 3, 4, 5 + * ^^^^ + */ + struct pm_node_list rights; + + /** + * MultiTargetNode#lparen_loc + * + * The location of the opening parenthesis. + * + * a, (b, c) = 1, 2, 3 + * ^ + */ + pm_location_t lparen_loc; + + /** + * MultiTargetNode#rparen_loc + * + * The location of the closing parenthesis. + * + * a, (b, c) = 1, 2, 3 + * ^ + */ + pm_location_t rparen_loc; +} pm_multi_target_node_t; + +/** + * MultiWriteNode + * + * Represents a write to a multi-target expression. + * + * a, b, c = 1, 2, 3 + * ^^^^^^^^^^^^^^^^^ + * + * Type: ::PM_MULTI_WRITE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_multi_write_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * MultiWriteNode#lefts + * + * Represents the targets expressions before a splat node. + * + * a, b, * = 1, 2, 3, 4, 5 + * ^^^^ + * + * The splat node can be absent, in that case all target expressions are in the left field. + * + * a, b, c = 1, 2, 3, 4, 5 + * ^^^^^^^ + */ + struct pm_node_list lefts; + + /** + * MultiWriteNode#rest + * + * Represents a splat node in the target expression. + * + * a, b, *c = 1, 2, 3, 4 + * ^^ + * + * The variable can be empty, this results in a `SplatNode` with a `nil` expression field. + * + * a, b, * = 1, 2, 3, 4 + * ^ + * + * If the `*` is omitted, this field will contain an `ImplicitRestNode` + * + * a, b, = 1, 2, 3, 4 + * ^ + */ + struct pm_node *rest; + + /** + * MultiWriteNode#rights + * + * Represents the targets expressions after a splat node. + * + * a, *, b, c = 1, 2, 3, 4, 5 + * ^^^^ + */ + struct pm_node_list rights; + + /** + * MultiWriteNode#lparen_loc + * + * The location of the opening parenthesis. + * + * (a, b, c) = 1, 2, 3 + * ^ + */ + pm_location_t lparen_loc; + + /** + * MultiWriteNode#rparen_loc + * + * The location of the closing parenthesis. + * + * (a, b, c) = 1, 2, 3 + * ^ + */ + pm_location_t rparen_loc; + + /** + * MultiWriteNode#operator_loc + * + * The location of the operator. + * + * a, b, c = 1, 2, 3 + * ^ + */ + pm_location_t operator_loc; + + /** + * MultiWriteNode#value + * + * The value to write to the targets. It can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + * + * a, b, c = 1, 2, 3 + * ^^^^^^^ + */ + struct pm_node *value; +} pm_multi_write_node_t; + +/** + * NextNode + * + * Represents the use of the `next` keyword. + * + * next 1 + * ^^^^^^ + * + * Type: ::PM_NEXT_NODE + * + * @extends pm_node_t + */ +typedef struct pm_next_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * NextNode#arguments + */ + struct pm_arguments_node *arguments; + + /** + * NextNode#keyword_loc + */ + pm_location_t keyword_loc; +} pm_next_node_t; + +/** + * NilNode + * + * Represents the use of the `nil` keyword. + * + * nil + * ^^^ + * + * Type: ::PM_NIL_NODE + * + * @extends pm_node_t + */ +typedef struct pm_nil_node { + /** The embedded base node. */ + pm_node_t base; + +} pm_nil_node_t; + +/** + * NoKeywordsParameterNode + * + * Represents the use of `**nil` inside method arguments. + * + * def a(**nil) + * ^^^^^ + * end + * + * Type: ::PM_NO_KEYWORDS_PARAMETER_NODE + * + * @extends pm_node_t + */ +typedef struct pm_no_keywords_parameter_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * NoKeywordsParameterNode#operator_loc + */ + pm_location_t operator_loc; + + /** + * NoKeywordsParameterNode#keyword_loc + */ + pm_location_t keyword_loc; +} pm_no_keywords_parameter_node_t; + +/** + * NumberedParametersNode + * + * Represents an implicit set of parameters through the use of numbered parameters within a block or lambda. + * + * -> { _1 + _2 } + * ^^^^^^^^^^^^^^ + * + * Type: ::PM_NUMBERED_PARAMETERS_NODE + * + * @extends pm_node_t + */ +typedef struct pm_numbered_parameters_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * NumberedParametersNode#maximum + */ + uint8_t maximum; +} pm_numbered_parameters_node_t; + +/** + * NumberedReferenceReadNode + * + * Represents reading a numbered reference to a capture in the previous match. + * + * $1 + * ^^ + * + * Type: ::PM_NUMBERED_REFERENCE_READ_NODE + * + * @extends pm_node_t + */ +typedef struct pm_numbered_reference_read_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * NumberedReferenceReadNode#number + * + * 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` + */ + uint32_t number; +} pm_numbered_reference_read_node_t; + +/** + * OptionalKeywordParameterNode + * + * Represents an optional keyword parameter to a method, block, or lambda definition. + * + * def a(b: 1) + * ^^^^ + * end + * + * Type: ::PM_OPTIONAL_KEYWORD_PARAMETER_NODE + + * Flags (#pm_parameter_flags): + * * ::PM_PARAMETER_FLAGS_REPEATED_PARAMETER + * + * @extends pm_node_t + */ +typedef struct pm_optional_keyword_parameter_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * OptionalKeywordParameterNode#name + */ + pm_constant_id_t name; + + /** + * OptionalKeywordParameterNode#name_loc + */ + pm_location_t name_loc; + + /** + * OptionalKeywordParameterNode#value + */ + struct pm_node *value; +} pm_optional_keyword_parameter_node_t; + +/** + * OptionalParameterNode + * + * Represents an optional parameter to a method, block, or lambda definition. + * + * def a(b = 1) + * ^^^^^ + * end + * + * Type: ::PM_OPTIONAL_PARAMETER_NODE + + * Flags (#pm_parameter_flags): + * * ::PM_PARAMETER_FLAGS_REPEATED_PARAMETER + * + * @extends pm_node_t + */ +typedef struct pm_optional_parameter_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * OptionalParameterNode#name + */ + pm_constant_id_t name; + + /** + * OptionalParameterNode#name_loc + */ + pm_location_t name_loc; + + /** + * OptionalParameterNode#operator_loc + */ + pm_location_t operator_loc; + + /** + * OptionalParameterNode#value + */ + struct pm_node *value; +} pm_optional_parameter_node_t; + +/** + * OrNode + * + * Represents the use of the `||` operator or the `or` keyword. + * + * left or right + * ^^^^^^^^^^^^^ + * + * Type: ::PM_OR_NODE + * + * @extends pm_node_t + */ +typedef struct pm_or_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * OrNode#left + * + * 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 + * ^ + */ + struct pm_node *left; + + /** + * OrNode#right + * + * Represents the right side of the expression. + * + * left || right + * ^^^^^ + * + * 1 or 2 + * ^ + */ + struct pm_node *right; + + /** + * OrNode#operator_loc + * + * The location of the `or` keyword or the `||` operator. + * + * left or right + * ^^ + */ + pm_location_t operator_loc; +} pm_or_node_t; + +/** + * ParametersNode + * + * Represents the list of parameters on a method, block, or lambda definition. + * + * def a(b, c, d) + * ^^^^^^^ + * end + * + * Type: ::PM_PARAMETERS_NODE + * + * @extends pm_node_t + */ +typedef struct pm_parameters_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * ParametersNode#requireds + */ + struct pm_node_list requireds; + + /** + * ParametersNode#optionals + */ + struct pm_node_list optionals; + + /** + * ParametersNode#rest + */ + struct pm_node *rest; + + /** + * ParametersNode#posts + */ + struct pm_node_list posts; + + /** + * ParametersNode#keywords + */ + struct pm_node_list keywords; + + /** + * ParametersNode#keyword_rest + */ + struct pm_node *keyword_rest; + + /** + * ParametersNode#block + */ + struct pm_block_parameter_node *block; +} pm_parameters_node_t; + +/** + * ParenthesesNode + * + * Represents a parenthesized expression + * + * (10 + 34) + * ^^^^^^^^^ + * + * Type: ::PM_PARENTHESES_NODE + + * Flags (#pm_parentheses_node_flags): + * * ::PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS + * + * @extends pm_node_t + */ +typedef struct pm_parentheses_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * ParenthesesNode#body + */ + struct pm_node *body; + + /** + * ParenthesesNode#opening_loc + */ + pm_location_t opening_loc; + + /** + * ParenthesesNode#closing_loc + */ + pm_location_t closing_loc; +} pm_parentheses_node_t; + +/** + * PinnedExpressionNode + * + * Represents the use of the `^` operator for pinning an expression in a pattern matching expression. + * + * foo in ^(bar) + * ^^^^^^ + * + * Type: ::PM_PINNED_EXPRESSION_NODE + * + * @extends pm_node_t + */ +typedef struct pm_pinned_expression_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * PinnedExpressionNode#expression + * + * The expression used in the pinned expression + * + * foo in ^(bar) + * ^^^ + */ + struct pm_node *expression; + + /** + * PinnedExpressionNode#operator_loc + * + * The location of the `^` operator + * + * foo in ^(bar) + * ^ + */ + pm_location_t operator_loc; + + /** + * PinnedExpressionNode#lparen_loc + * + * The location of the opening parenthesis. + * + * foo in ^(bar) + * ^ + */ + pm_location_t lparen_loc; + + /** + * PinnedExpressionNode#rparen_loc + * + * The location of the closing parenthesis. + * + * foo in ^(bar) + * ^ + */ + pm_location_t rparen_loc; +} pm_pinned_expression_node_t; + +/** + * PinnedVariableNode + * + * Represents the use of the `^` operator for pinning a variable in a pattern matching expression. + * + * foo in ^bar + * ^^^^ + * + * Type: ::PM_PINNED_VARIABLE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_pinned_variable_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * PinnedVariableNode#variable + * + * The variable used in the pinned expression + * + * foo in ^bar + * ^^^ + */ + struct pm_node *variable; + + /** + * PinnedVariableNode#operator_loc + * + * The location of the `^` operator + * + * foo in ^bar + * ^ + */ + pm_location_t operator_loc; +} pm_pinned_variable_node_t; + +/** + * PostExecutionNode + * + * Represents the use of the `END` keyword. + * + * END { foo } + * ^^^^^^^^^^^ + * + * Type: ::PM_POST_EXECUTION_NODE + * + * @extends pm_node_t + */ +typedef struct pm_post_execution_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * PostExecutionNode#statements + */ + struct pm_statements_node *statements; + + /** + * PostExecutionNode#keyword_loc + */ + pm_location_t keyword_loc; + + /** + * PostExecutionNode#opening_loc + */ + pm_location_t opening_loc; + + /** + * PostExecutionNode#closing_loc + */ + pm_location_t closing_loc; +} pm_post_execution_node_t; + +/** + * PreExecutionNode + * + * Represents the use of the `BEGIN` keyword. + * + * BEGIN { foo } + * ^^^^^^^^^^^^^ + * + * Type: ::PM_PRE_EXECUTION_NODE + * + * @extends pm_node_t + */ +typedef struct pm_pre_execution_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * PreExecutionNode#statements + */ + struct pm_statements_node *statements; + + /** + * PreExecutionNode#keyword_loc + */ + pm_location_t keyword_loc; + + /** + * PreExecutionNode#opening_loc + */ + pm_location_t opening_loc; + + /** + * PreExecutionNode#closing_loc + */ + pm_location_t closing_loc; +} pm_pre_execution_node_t; + +/** + * ProgramNode + * + * The top level node of any parse tree. + * + * Type: ::PM_PROGRAM_NODE + * + * @extends pm_node_t + */ +typedef struct pm_program_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * ProgramNode#locals + */ + pm_constant_id_list_t locals; + + /** + * ProgramNode#statements + */ + struct pm_statements_node *statements; +} pm_program_node_t; + +/** + * RangeNode + * + * Represents the use of the `..` or `...` operators. + * + * 1..2 + * ^^^^ + * + * c if a =~ /left/ ... b =~ /right/ + * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + * + * Type: ::PM_RANGE_NODE + + * Flags (#pm_range_flags): + * * ::PM_RANGE_FLAGS_EXCLUDE_END + * + * @extends pm_node_t + */ +typedef struct pm_range_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * RangeNode#left + * + * 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 + * ^^^^^ + */ + struct pm_node *left; + + /** + * RangeNode#right + * + * 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. + */ + struct pm_node *right; + + /** + * RangeNode#operator_loc + * + * The location of the `..` or `...` operator. + */ + pm_location_t operator_loc; +} pm_range_node_t; + +/** + * RationalNode + * + * Represents a rational number literal. + * + * 1.0r + * ^^^^ + * + * Type: ::PM_RATIONAL_NODE + + * Flags (#pm_integer_base_flags): + * * ::PM_INTEGER_BASE_FLAGS_BINARY + * * ::PM_INTEGER_BASE_FLAGS_DECIMAL + * * ::PM_INTEGER_BASE_FLAGS_OCTAL + * * ::PM_INTEGER_BASE_FLAGS_HEXADECIMAL + * + * @extends pm_node_t + */ +typedef struct pm_rational_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * RationalNode#numerator + * + * The numerator of the rational number. + * + * 1.5r # numerator 3 + */ + pm_integer_t numerator; + + /** + * RationalNode#denominator + * + * The denominator of the rational number. + * + * 1.5r # denominator 2 + */ + pm_integer_t denominator; +} pm_rational_node_t; + +/** + * RedoNode + * + * Represents the use of the `redo` keyword. + * + * redo + * ^^^^ + * + * Type: ::PM_REDO_NODE + * + * @extends pm_node_t + */ +typedef struct pm_redo_node { + /** The embedded base node. */ + pm_node_t base; + +} pm_redo_node_t; + +/** + * RegularExpressionNode + * + * Represents a regular expression literal with no interpolation. + * + * /foo/i + * ^^^^^^ + * + * Type: ::PM_REGULAR_EXPRESSION_NODE + + * Flags (#pm_regular_expression_flags): + * * ::PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE + * * ::PM_REGULAR_EXPRESSION_FLAGS_EXTENDED + * * ::PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE + * * ::PM_REGULAR_EXPRESSION_FLAGS_ONCE + * * ::PM_REGULAR_EXPRESSION_FLAGS_EUC_JP + * * ::PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT + * * ::PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J + * * ::PM_REGULAR_EXPRESSION_FLAGS_UTF_8 + * * ::PM_REGULAR_EXPRESSION_FLAGS_FORCED_UTF8_ENCODING + * * ::PM_REGULAR_EXPRESSION_FLAGS_FORCED_BINARY_ENCODING + * * ::PM_REGULAR_EXPRESSION_FLAGS_FORCED_US_ASCII_ENCODING + * + * @extends pm_node_t + */ +typedef struct pm_regular_expression_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * RegularExpressionNode#opening_loc + */ + pm_location_t opening_loc; + + /** + * RegularExpressionNode#content_loc + */ + pm_location_t content_loc; + + /** + * RegularExpressionNode#closing_loc + */ + pm_location_t closing_loc; + + /** + * RegularExpressionNode#unescaped + */ + pm_string_t unescaped; +} pm_regular_expression_node_t; + +/** + * RequiredKeywordParameterNode + * + * Represents a required keyword parameter to a method, block, or lambda definition. + * + * def a(b: ) + * ^^ + * end + * + * Type: ::PM_REQUIRED_KEYWORD_PARAMETER_NODE + + * Flags (#pm_parameter_flags): + * * ::PM_PARAMETER_FLAGS_REPEATED_PARAMETER + * + * @extends pm_node_t + */ +typedef struct pm_required_keyword_parameter_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * RequiredKeywordParameterNode#name + */ + pm_constant_id_t name; + + /** + * RequiredKeywordParameterNode#name_loc + */ + pm_location_t name_loc; +} pm_required_keyword_parameter_node_t; + +/** + * RequiredParameterNode + * + * Represents a required parameter to a method, block, or lambda definition. + * + * def a(b) + * ^ + * end + * + * Type: ::PM_REQUIRED_PARAMETER_NODE + + * Flags (#pm_parameter_flags): + * * ::PM_PARAMETER_FLAGS_REPEATED_PARAMETER + * + * @extends pm_node_t + */ +typedef struct pm_required_parameter_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * RequiredParameterNode#name + */ + pm_constant_id_t name; +} pm_required_parameter_node_t; + +/** + * RescueModifierNode + * + * Represents an expression modified with a rescue. + * + * foo rescue nil + * ^^^^^^^^^^^^^^ + * + * Type: ::PM_RESCUE_MODIFIER_NODE + * + * @extends pm_node_t + */ +typedef struct pm_rescue_modifier_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * RescueModifierNode#expression + */ + struct pm_node *expression; + + /** + * RescueModifierNode#keyword_loc + */ + pm_location_t keyword_loc; + + /** + * RescueModifierNode#rescue_expression + */ + struct pm_node *rescue_expression; +} pm_rescue_modifier_node_t; + +/** + * RescueNode + * + * Represents a rescue statement. + * + * begin + * rescue Foo, *splat, Bar => ex + * foo + * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + * end + * + * `Foo, *splat, Bar` are in the `exceptions` field. `ex` is in the `reference` field. + * + * Type: ::PM_RESCUE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_rescue_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * RescueNode#keyword_loc + */ + pm_location_t keyword_loc; + + /** + * RescueNode#exceptions + */ + struct pm_node_list exceptions; + + /** + * RescueNode#operator_loc + */ + pm_location_t operator_loc; + + /** + * RescueNode#reference + */ + struct pm_node *reference; + + /** + * RescueNode#then_keyword_loc + */ + pm_location_t then_keyword_loc; + + /** + * RescueNode#statements + */ + struct pm_statements_node *statements; + + /** + * RescueNode#subsequent + */ + struct pm_rescue_node *subsequent; +} pm_rescue_node_t; + +/** + * RestParameterNode + * + * Represents a rest parameter to a method, block, or lambda definition. + * + * def a(*b) + * ^^ + * end + * + * Type: ::PM_REST_PARAMETER_NODE + + * Flags (#pm_parameter_flags): + * * ::PM_PARAMETER_FLAGS_REPEATED_PARAMETER + * + * @extends pm_node_t + */ +typedef struct pm_rest_parameter_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * RestParameterNode#name + */ + pm_constant_id_t name; + + /** + * RestParameterNode#name_loc + */ + pm_location_t name_loc; + + /** + * RestParameterNode#operator_loc + */ + pm_location_t operator_loc; +} pm_rest_parameter_node_t; + +/** + * RetryNode + * + * Represents the use of the `retry` keyword. + * + * retry + * ^^^^^ + * + * Type: ::PM_RETRY_NODE + * + * @extends pm_node_t + */ +typedef struct pm_retry_node { + /** The embedded base node. */ + pm_node_t base; + +} pm_retry_node_t; + +/** + * ReturnNode + * + * Represents the use of the `return` keyword. + * + * return 1 + * ^^^^^^^^ + * + * Type: ::PM_RETURN_NODE + * + * @extends pm_node_t + */ +typedef struct pm_return_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * ReturnNode#keyword_loc + */ + pm_location_t keyword_loc; + + /** + * ReturnNode#arguments + */ + struct pm_arguments_node *arguments; +} pm_return_node_t; + +/** + * SelfNode + * + * Represents the `self` keyword. + * + * self + * ^^^^ + * + * Type: ::PM_SELF_NODE + * + * @extends pm_node_t + */ +typedef struct pm_self_node { + /** The embedded base node. */ + pm_node_t base; + +} pm_self_node_t; + +/** + * ShareableConstantNode + * + * 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 } + * ^^^^^^^^^^^^ + * + * Type: ::PM_SHAREABLE_CONSTANT_NODE + + * Flags (#pm_shareable_constant_node_flags): + * * ::PM_SHAREABLE_CONSTANT_NODE_FLAGS_LITERAL + * * ::PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_EVERYTHING + * * ::PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_COPY + * + * @extends pm_node_t + */ +typedef struct pm_shareable_constant_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * ShareableConstantNode#write + * + * The constant write that should be modified with the shareability state. + */ + struct pm_node *write; +} pm_shareable_constant_node_t; + +/** + * SingletonClassNode + * + * Represents a singleton class declaration involving the `class` keyword. + * + * class << self end + * ^^^^^^^^^^^^^^^^^ + * + * Type: ::PM_SINGLETON_CLASS_NODE + * + * @extends pm_node_t + */ +typedef struct pm_singleton_class_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * SingletonClassNode#locals + */ + pm_constant_id_list_t locals; + + /** + * SingletonClassNode#class_keyword_loc + */ + pm_location_t class_keyword_loc; + + /** + * SingletonClassNode#operator_loc + */ + pm_location_t operator_loc; + + /** + * SingletonClassNode#expression + */ + struct pm_node *expression; + + /** + * SingletonClassNode#body + */ + struct pm_node *body; + + /** + * SingletonClassNode#end_keyword_loc + */ + pm_location_t end_keyword_loc; +} pm_singleton_class_node_t; + +/** + * SourceEncodingNode + * + * Represents the use of the `__ENCODING__` keyword. + * + * __ENCODING__ + * ^^^^^^^^^^^^ + * + * Type: ::PM_SOURCE_ENCODING_NODE + * + * @extends pm_node_t + */ +typedef struct pm_source_encoding_node { + /** The embedded base node. */ + pm_node_t base; + +} pm_source_encoding_node_t; + +/** + * SourceFileNode + * + * Represents the use of the `__FILE__` keyword. + * + * __FILE__ + * ^^^^^^^^ + * + * Type: ::PM_SOURCE_FILE_NODE + + * Flags (#pm_string_flags): + * * ::PM_STRING_FLAGS_FORCED_UTF8_ENCODING + * * ::PM_STRING_FLAGS_FORCED_BINARY_ENCODING + * * ::PM_STRING_FLAGS_FROZEN + * * ::PM_STRING_FLAGS_MUTABLE + * + * @extends pm_node_t + */ +typedef struct pm_source_file_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * SourceFileNode#filepath + * + * Represents the file path being parsed. This corresponds directly to the `filepath` option given to the various `Prism::parse*` APIs. + */ + pm_string_t filepath; +} pm_source_file_node_t; + +/** + * SourceLineNode + * + * Represents the use of the `__LINE__` keyword. + * + * __LINE__ + * ^^^^^^^^ + * + * Type: ::PM_SOURCE_LINE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_source_line_node { + /** The embedded base node. */ + pm_node_t base; + +} pm_source_line_node_t; + +/** + * SplatNode + * + * Represents the use of the splat operator. + * + * [*a] + * ^^ + * + * Type: ::PM_SPLAT_NODE + * + * @extends pm_node_t + */ +typedef struct pm_splat_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * SplatNode#operator_loc + */ + pm_location_t operator_loc; + + /** + * SplatNode#expression + */ + struct pm_node *expression; +} pm_splat_node_t; + +/** + * StatementsNode + * + * Represents a set of statements contained within some scope. + * + * foo; bar; baz + * ^^^^^^^^^^^^^ + * + * Type: ::PM_STATEMENTS_NODE + * + * @extends pm_node_t + */ +typedef struct pm_statements_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * StatementsNode#body + */ + struct pm_node_list body; +} pm_statements_node_t; + +/** + * StringNode + * + * 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" + * ^^^^ ^^^^ + * + * Type: ::PM_STRING_NODE + + * Flags (#pm_string_flags): + * * ::PM_STRING_FLAGS_FORCED_UTF8_ENCODING + * * ::PM_STRING_FLAGS_FORCED_BINARY_ENCODING + * * ::PM_STRING_FLAGS_FROZEN + * * ::PM_STRING_FLAGS_MUTABLE + * + * @extends pm_node_t + */ +typedef struct pm_string_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * StringNode#opening_loc + */ + pm_location_t opening_loc; + + /** + * StringNode#content_loc + */ + pm_location_t content_loc; + + /** + * StringNode#closing_loc + */ + pm_location_t closing_loc; + + /** + * StringNode#unescaped + */ + pm_string_t unescaped; +} pm_string_node_t; + +/** + * SuperNode + * + * Represents the use of the `super` keyword with parentheses or arguments. + * + * super() + * ^^^^^^^ + * + * super foo, bar + * ^^^^^^^^^^^^^^ + * + * If no arguments are provided (except for a block), it would be a `ForwardingSuperNode` instead. + * + * Type: ::PM_SUPER_NODE + * + * @extends pm_node_t + */ +typedef struct pm_super_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * SuperNode#keyword_loc + */ + pm_location_t keyword_loc; + + /** + * SuperNode#lparen_loc + */ + pm_location_t lparen_loc; + + /** + * SuperNode#arguments + * + * Can be only `nil` when there are empty parentheses, like `super()`. + */ + struct pm_arguments_node *arguments; + + /** + * SuperNode#rparen_loc + */ + pm_location_t rparen_loc; + + /** + * SuperNode#block + */ + struct pm_node *block; +} pm_super_node_t; + +/** + * SymbolNode + * + * Represents a symbol literal or a symbol contained within a `%i` list. + * + * :foo + * ^^^^ + * + * %i[foo] + * ^^^ + * + * Type: ::PM_SYMBOL_NODE + + * Flags (#pm_symbol_flags): + * * ::PM_SYMBOL_FLAGS_FORCED_UTF8_ENCODING + * * ::PM_SYMBOL_FLAGS_FORCED_BINARY_ENCODING + * * ::PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING + * + * @extends pm_node_t + */ +typedef struct pm_symbol_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * SymbolNode#opening_loc + */ + pm_location_t opening_loc; + + /** + * SymbolNode#value_loc + */ + pm_location_t value_loc; + + /** + * SymbolNode#closing_loc + */ + pm_location_t closing_loc; + + /** + * SymbolNode#unescaped + */ + pm_string_t unescaped; +} pm_symbol_node_t; + +/** + * TrueNode + * + * Represents the use of the literal `true` keyword. + * + * true + * ^^^^ + * + * Type: ::PM_TRUE_NODE + * + * @extends pm_node_t + */ +typedef struct pm_true_node { + /** The embedded base node. */ + pm_node_t base; + +} pm_true_node_t; + +/** + * UndefNode + * + * Represents the use of the `undef` keyword. + * + * undef :foo, :bar, :baz + * ^^^^^^^^^^^^^^^^^^^^^^ + * + * Type: ::PM_UNDEF_NODE + * + * @extends pm_node_t + */ +typedef struct pm_undef_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * UndefNode#names + */ + struct pm_node_list names; + + /** + * UndefNode#keyword_loc + */ + pm_location_t keyword_loc; +} pm_undef_node_t; + +/** + * UnlessNode + * + * Represents the use of the `unless` keyword, either in the block form or the modifier form. + * + * bar unless foo + * ^^^^^^^^^^^^^^ + * + * unless foo then bar end + * ^^^^^^^^^^^^^^^^^^^^^^^ + * + * Type: ::PM_UNLESS_NODE + * + * @extends pm_node_t + */ +typedef struct pm_unless_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * UnlessNode#keyword_loc + * + * The location of the `unless` keyword. + * + * unless cond then bar end + * ^^^^^^ + * + * bar unless cond + * ^^^^^^ + */ + pm_location_t keyword_loc; + + /** + * UnlessNode#predicate + * + * 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 + * ^^^^ + */ + struct pm_node *predicate; + + /** + * UnlessNode#then_keyword_loc + * + * The location of the `then` keyword, if present. + * + * unless cond then bar end + * ^^^^ + */ + pm_location_t then_keyword_loc; + + /** + * UnlessNode#statements + * + * 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 + * ^^^ + */ + struct pm_statements_node *statements; + + /** + * UnlessNode#else_clause + * + * The else clause of the unless expression, if present. + * + * unless cond then bar else baz end + * ^^^^^^^^ + */ + struct pm_else_node *else_clause; + + /** + * UnlessNode#end_keyword_loc + * + * The location of the `end` keyword, if present. + * + * unless cond then bar end + * ^^^ + */ + pm_location_t end_keyword_loc; +} pm_unless_node_t; + +/** + * UntilNode + * + * Represents the use of the `until` keyword, either in the block form or the modifier form. + * + * bar until foo + * ^^^^^^^^^^^^^ + * + * until foo do bar end + * ^^^^^^^^^^^^^^^^^^^^ + * + * Type: ::PM_UNTIL_NODE + + * Flags (#pm_loop_flags): + * * ::PM_LOOP_FLAGS_BEGIN_MODIFIER + * + * @extends pm_node_t + */ +typedef struct pm_until_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * UntilNode#keyword_loc + */ + pm_location_t keyword_loc; + + /** + * UntilNode#do_keyword_loc + */ + pm_location_t do_keyword_loc; + + /** + * UntilNode#closing_loc + */ + pm_location_t closing_loc; + + /** + * UntilNode#predicate + */ + struct pm_node *predicate; + + /** + * UntilNode#statements + */ + struct pm_statements_node *statements; +} pm_until_node_t; + +/** + * WhenNode + * + * Represents the use of the `when` keyword within a case statement. + * + * case true + * when true + * ^^^^^^^^^ + * end + * + * Type: ::PM_WHEN_NODE + * + * @extends pm_node_t + */ +typedef struct pm_when_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * WhenNode#keyword_loc + */ + pm_location_t keyword_loc; + + /** + * WhenNode#conditions + */ + struct pm_node_list conditions; + + /** + * WhenNode#then_keyword_loc + */ + pm_location_t then_keyword_loc; + + /** + * WhenNode#statements + */ + struct pm_statements_node *statements; +} pm_when_node_t; + +/** + * WhileNode + * + * Represents the use of the `while` keyword, either in the block form or the modifier form. + * + * bar while foo + * ^^^^^^^^^^^^^ + * + * while foo do bar end + * ^^^^^^^^^^^^^^^^^^^^ + * + * Type: ::PM_WHILE_NODE + + * Flags (#pm_loop_flags): + * * ::PM_LOOP_FLAGS_BEGIN_MODIFIER + * + * @extends pm_node_t + */ +typedef struct pm_while_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * WhileNode#keyword_loc + */ + pm_location_t keyword_loc; + + /** + * WhileNode#do_keyword_loc + */ + pm_location_t do_keyword_loc; + + /** + * WhileNode#closing_loc + */ + pm_location_t closing_loc; + + /** + * WhileNode#predicate + */ + struct pm_node *predicate; + + /** + * WhileNode#statements + */ + struct pm_statements_node *statements; +} pm_while_node_t; + +/** + * XStringNode + * + * Represents an xstring literal with no interpolation. + * + * `foo` + * ^^^^^ + * + * Type: ::PM_X_STRING_NODE + + * Flags (#pm_encoding_flags): + * * ::PM_ENCODING_FLAGS_FORCED_UTF8_ENCODING + * * ::PM_ENCODING_FLAGS_FORCED_BINARY_ENCODING + * + * @extends pm_node_t + */ +typedef struct pm_x_string_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * XStringNode#opening_loc + */ + pm_location_t opening_loc; + + /** + * XStringNode#content_loc + */ + pm_location_t content_loc; + + /** + * XStringNode#closing_loc + */ + pm_location_t closing_loc; + + /** + * XStringNode#unescaped + */ + pm_string_t unescaped; +} pm_x_string_node_t; + +/** + * YieldNode + * + * Represents the use of the `yield` keyword. + * + * yield 1 + * ^^^^^^^ + * + * Type: ::PM_YIELD_NODE + * + * @extends pm_node_t + */ +typedef struct pm_yield_node { + /** The embedded base node. */ + pm_node_t base; + + + /** + * YieldNode#keyword_loc + */ + pm_location_t keyword_loc; + + /** + * YieldNode#lparen_loc + */ + pm_location_t lparen_loc; + + /** + * YieldNode#arguments + */ + struct pm_arguments_node *arguments; + + /** + * YieldNode#rparen_loc + */ + pm_location_t rparen_loc; +} pm_yield_node_t; + +/** + * Flags for arguments nodes. + */ +typedef enum pm_arguments_node_flags { + /** if the arguments contain forwarding */ + PM_ARGUMENTS_NODE_FLAGS_CONTAINS_FORWARDING = 4, + + /** if the arguments contain keywords */ + PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS = 8, + + /** if the arguments contain a keyword splat */ + PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT = 16, + + /** if the arguments contain a splat */ + PM_ARGUMENTS_NODE_FLAGS_CONTAINS_SPLAT = 32, + + /** if the arguments contain multiple splats */ + PM_ARGUMENTS_NODE_FLAGS_CONTAINS_MULTIPLE_SPLATS = 64, + + PM_ARGUMENTS_NODE_FLAGS_LAST, +} pm_arguments_node_flags_t; + +/** + * Flags for array nodes. + */ +typedef enum pm_array_node_flags { + /** if array contains splat nodes */ + PM_ARRAY_NODE_FLAGS_CONTAINS_SPLAT = 4, + + PM_ARRAY_NODE_FLAGS_LAST, +} pm_array_node_flags_t; + +/** + * Flags for call nodes. + */ +typedef enum pm_call_node_flags { + /** &. operator */ + PM_CALL_NODE_FLAGS_SAFE_NAVIGATION = 4, + + /** a call that could have been a local variable */ + PM_CALL_NODE_FLAGS_VARIABLE_CALL = 8, + + /** a call that is an attribute write, so the value being written should be returned */ + PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE = 16, + + /** a call that ignores method visibility */ + PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY = 32, + + PM_CALL_NODE_FLAGS_LAST, +} pm_call_node_flags_t; + +/** + * Flags for nodes that have unescaped content. + */ +typedef enum pm_encoding_flags { + /** internal bytes forced the encoding to UTF-8 */ + PM_ENCODING_FLAGS_FORCED_UTF8_ENCODING = 4, + + /** internal bytes forced the encoding to binary */ + PM_ENCODING_FLAGS_FORCED_BINARY_ENCODING = 8, + + PM_ENCODING_FLAGS_LAST, +} pm_encoding_flags_t; + +/** + * Flags for integer nodes that correspond to the base of the integer. + */ +typedef enum pm_integer_base_flags { + /** 0b prefix */ + PM_INTEGER_BASE_FLAGS_BINARY = 4, + + /** 0d or no prefix */ + PM_INTEGER_BASE_FLAGS_DECIMAL = 8, + + /** 0o or 0 prefix */ + PM_INTEGER_BASE_FLAGS_OCTAL = 16, + + /** 0x prefix */ + PM_INTEGER_BASE_FLAGS_HEXADECIMAL = 32, + + PM_INTEGER_BASE_FLAGS_LAST, +} pm_integer_base_flags_t; + +/** + * Flags for interpolated string nodes that indicated mutability if they are also marked as literals. + */ +typedef enum pm_interpolated_string_node_flags { + /** frozen by virtue of a `frozen_string_literal: true` comment or `--enable-frozen-string-literal`; only for adjacent string literals like `'a' 'b'` */ + PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN = 4, + + /** mutable by virtue of a `frozen_string_literal: false` comment or `--disable-frozen-string-literal`; only for adjacent string literals like `'a' 'b'` */ + PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE = 8, + + PM_INTERPOLATED_STRING_NODE_FLAGS_LAST, +} pm_interpolated_string_node_flags_t; + +/** + * Flags for keyword hash nodes. + */ +typedef enum pm_keyword_hash_node_flags { + /** a keyword hash which only has `AssocNode` elements all with symbol keys, which means the elements can be treated as keyword arguments */ + PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS = 4, + + PM_KEYWORD_HASH_NODE_FLAGS_LAST, +} pm_keyword_hash_node_flags_t; + +/** + * Flags for while and until loop nodes. + */ +typedef enum pm_loop_flags { + /** a loop after a begin statement, so the body is executed first before the condition */ + PM_LOOP_FLAGS_BEGIN_MODIFIER = 4, + + PM_LOOP_FLAGS_LAST, +} pm_loop_flags_t; + +/** + * Flags for parameter nodes. + */ +typedef enum pm_parameter_flags { + /** a parameter name that has been repeated in the method signature */ + PM_PARAMETER_FLAGS_REPEATED_PARAMETER = 4, + + PM_PARAMETER_FLAGS_LAST, +} pm_parameter_flags_t; + +/** + * Flags for parentheses nodes. + */ +typedef enum pm_parentheses_node_flags { + /** parentheses that contain multiple potentially void statements */ + PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS = 4, + + PM_PARENTHESES_NODE_FLAGS_LAST, +} pm_parentheses_node_flags_t; + +/** + * Flags for range and flip-flop nodes. + */ +typedef enum pm_range_flags { + /** ... operator */ + PM_RANGE_FLAGS_EXCLUDE_END = 4, + + PM_RANGE_FLAGS_LAST, +} pm_range_flags_t; + +/** + * Flags for regular expression and match last line nodes. + */ +typedef enum pm_regular_expression_flags { + /** i - ignores the case of characters when matching */ + PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE = 4, + + /** x - ignores whitespace and allows comments in regular expressions */ + PM_REGULAR_EXPRESSION_FLAGS_EXTENDED = 8, + + /** m - allows $ to match the end of lines within strings */ + PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE = 16, + + /** o - only interpolates values into the regular expression once */ + PM_REGULAR_EXPRESSION_FLAGS_ONCE = 32, + + /** e - forces the EUC-JP encoding */ + PM_REGULAR_EXPRESSION_FLAGS_EUC_JP = 64, + + /** n - forces the ASCII-8BIT encoding */ + PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT = 128, + + /** s - forces the Windows-31J encoding */ + PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J = 256, + + /** u - forces the UTF-8 encoding */ + PM_REGULAR_EXPRESSION_FLAGS_UTF_8 = 512, + + /** internal bytes forced the encoding to UTF-8 */ + PM_REGULAR_EXPRESSION_FLAGS_FORCED_UTF8_ENCODING = 1024, + + /** internal bytes forced the encoding to binary */ + PM_REGULAR_EXPRESSION_FLAGS_FORCED_BINARY_ENCODING = 2048, + + /** internal bytes forced the encoding to US-ASCII */ + PM_REGULAR_EXPRESSION_FLAGS_FORCED_US_ASCII_ENCODING = 4096, + + PM_REGULAR_EXPRESSION_FLAGS_LAST, +} pm_regular_expression_flags_t; + +/** + * Flags for shareable constant nodes. + */ +typedef enum pm_shareable_constant_node_flags { + /** constant writes that should be modified with shareable constant value literal */ + PM_SHAREABLE_CONSTANT_NODE_FLAGS_LITERAL = 4, + + /** constant writes that should be modified with shareable constant value experimental everything */ + PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_EVERYTHING = 8, + + /** constant writes that should be modified with shareable constant value experimental copy */ + PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_COPY = 16, + + PM_SHAREABLE_CONSTANT_NODE_FLAGS_LAST, +} pm_shareable_constant_node_flags_t; + +/** + * Flags for string nodes. + */ +typedef enum pm_string_flags { + /** internal bytes forced the encoding to UTF-8 */ + PM_STRING_FLAGS_FORCED_UTF8_ENCODING = 4, + + /** internal bytes forced the encoding to binary */ + PM_STRING_FLAGS_FORCED_BINARY_ENCODING = 8, + + /** frozen by virtue of a `frozen_string_literal: true` comment or `--enable-frozen-string-literal` */ + PM_STRING_FLAGS_FROZEN = 16, + + /** mutable by virtue of a `frozen_string_literal: false` comment or `--disable-frozen-string-literal` */ + PM_STRING_FLAGS_MUTABLE = 32, + + PM_STRING_FLAGS_LAST, +} pm_string_flags_t; + +/** + * Flags for symbol nodes. + */ +typedef enum pm_symbol_flags { + /** internal bytes forced the encoding to UTF-8 */ + PM_SYMBOL_FLAGS_FORCED_UTF8_ENCODING = 4, + + /** internal bytes forced the encoding to binary */ + PM_SYMBOL_FLAGS_FORCED_BINARY_ENCODING = 8, + + /** internal bytes forced the encoding to US-ASCII */ + PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING = 16, + + PM_SYMBOL_FLAGS_LAST, +} pm_symbol_flags_t; + +/** + * 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 0 + +#endif diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/defines.h b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/defines.h new file mode 100644 index 0000000..e31429c --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/defines.h @@ -0,0 +1,260 @@ +/** + * @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 +#include +#include +#include +#include +#include +#include +#include + +/** + * 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 sys/types.h before inttypes.h to work around issue with +// certain versions of GCC and newlib which causes omission of PRIx64 +#include +#include + +/** + * When we are parsing using recursive descent, we want to protect against + * malicious payloads that could attempt to crash our parser. We do this by + * specifying a maximum depth to which we are allowed to recurse. + */ +#ifndef PRISM_DEPTH_MAXIMUM + #define PRISM_DEPTH_MAXIMUM 10000 +#endif + +/** + * 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 +# ifdef _POSIX_MAPPED_FILES +# define PRISM_HAS_MMAP +# endif +#endif + +/** + * If PRISM_HAS_NO_FILESYSTEM is defined, then we want to exclude all filesystem + * related code from the library. All filesystem related code should be guarded + * by PRISM_HAS_FILESYSTEM. + */ +#ifndef PRISM_HAS_NO_FILESYSTEM +# define PRISM_HAS_FILESYSTEM +#endif + +/** + * isinf on POSIX systems it accepts a float, a double, or a long double. + * But mingw didn't provide an isinf macro, only an isinf function that only + * accepts floats, so we need to use _finite instead. + */ +#ifdef __MINGW64__ + #include + #define PRISM_ISINF(x) (!_finite(x)) +#else + #define PRISM_ISINF(x) isinf(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 + +/** + * Support PRISM_LIKELY and PRISM_UNLIKELY to help the compiler optimize its + * branch predication. + */ +#if defined(__GNUC__) || defined(__clang__) + /** The compiler should predicate that this branch will be taken. */ + #define PRISM_LIKELY(x) __builtin_expect(!!(x), 1) + + /** The compiler should predicate that this branch will not be taken. */ + #define PRISM_UNLIKELY(x) __builtin_expect(!!(x), 0) +#else + /** Void because this platform does not support branch prediction hints. */ + #define PRISM_LIKELY(x) (x) + + /** Void because this platform does not support branch prediction hints. */ + #define PRISM_UNLIKELY(x) (x) +#endif + +/** + * We use -Wimplicit-fallthrough to guard potentially unintended fall-through between cases of a switch. + * Use PRISM_FALLTHROUGH to explicitly annotate cases where the fallthrough is intentional. + */ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L // C23 or later + #define PRISM_FALLTHROUGH [[fallthrough]]; +#elif defined(__GNUC__) || defined(__clang__) + #define PRISM_FALLTHROUGH __attribute__((fallthrough)); +#elif defined(_MSC_VER) + #define PRISM_FALLTHROUGH __fallthrough; +#else + #define PRISM_FALLTHROUGH +#endif + +#endif diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/diagnostic.h b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/diagnostic.h new file mode 100644 index 0000000..9bf3ade --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/diagnostic.h @@ -0,0 +1,458 @@ +/* :markup: markdown */ + +/*----------------------------------------------------------------------------*/ +/* This file is generated by the templates/template.rb script and should not */ +/* be modified manually. See */ +/* templates/include/prism/diagnostic.h.erb */ +/* if you are looking to modify the */ +/* template */ +/*----------------------------------------------------------------------------*/ + +/** + * @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 +#include +#include + +/** + * 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. + PM_ERR_ALIAS_ARGUMENT, + PM_ERR_ALIAS_ARGUMENT_NUMBERED_REFERENCE, + PM_ERR_AMPAMPEQ_MULTI_ASSIGN, + PM_ERR_ARGUMENT_AFTER_BLOCK, + PM_ERR_ARGUMENT_AFTER_FORWARDING_ELLIPSES, + PM_ERR_ARGUMENT_BARE_HASH, + PM_ERR_ARGUMENT_BLOCK_FORWARDING, + PM_ERR_ARGUMENT_BLOCK_MULTI, + PM_ERR_ARGUMENT_CONFLICT_AMPERSAND, + PM_ERR_ARGUMENT_CONFLICT_STAR, + PM_ERR_ARGUMENT_CONFLICT_STAR_STAR, + PM_ERR_ARGUMENT_FORMAL_CLASS, + PM_ERR_ARGUMENT_FORMAL_CONSTANT, + PM_ERR_ARGUMENT_FORMAL_GLOBAL, + PM_ERR_ARGUMENT_FORMAL_IVAR, + PM_ERR_ARGUMENT_FORWARDING_UNBOUND, + PM_ERR_ARGUMENT_NO_FORWARDING_AMPERSAND, + PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES, + PM_ERR_ARGUMENT_NO_FORWARDING_STAR, + PM_ERR_ARGUMENT_NO_FORWARDING_STAR_STAR, + PM_ERR_ARGUMENT_SPLAT_AFTER_ASSOC_SPLAT, + PM_ERR_ARGUMENT_SPLAT_AFTER_SPLAT, + PM_ERR_ARGUMENT_TERM_PAREN, + PM_ERR_ARGUMENT_UNEXPECTED_BLOCK, + PM_ERR_ARRAY_ELEMENT, + PM_ERR_ARRAY_EXPRESSION, + PM_ERR_ARRAY_EXPRESSION_AFTER_STAR, + PM_ERR_ARRAY_SEPARATOR, + PM_ERR_ARRAY_TERM, + PM_ERR_BEGIN_LONELY_ELSE, + PM_ERR_BEGIN_TERM, + PM_ERR_BEGIN_UPCASE_BRACE, + PM_ERR_BEGIN_UPCASE_TERM, + PM_ERR_BEGIN_UPCASE_TOPLEVEL, + PM_ERR_BLOCK_PARAM_LOCAL_VARIABLE, + PM_ERR_BLOCK_PARAM_PIPE_TERM, + PM_ERR_BLOCK_TERM_BRACE, + PM_ERR_BLOCK_TERM_END, + PM_ERR_CANNOT_PARSE_EXPRESSION, + PM_ERR_CANNOT_PARSE_STRING_PART, + PM_ERR_CASE_EXPRESSION_AFTER_CASE, + PM_ERR_CASE_EXPRESSION_AFTER_WHEN, + PM_ERR_CASE_MATCH_MISSING_PREDICATE, + PM_ERR_CASE_MISSING_CONDITIONS, + PM_ERR_CASE_TERM, + PM_ERR_CLASS_IN_METHOD, + PM_ERR_CLASS_NAME, + PM_ERR_CLASS_SUPERCLASS, + PM_ERR_CLASS_TERM, + PM_ERR_CLASS_UNEXPECTED_END, + PM_ERR_CLASS_VARIABLE_BARE, + PM_ERR_CONDITIONAL_ELSIF_PREDICATE, + PM_ERR_CONDITIONAL_IF_PREDICATE, + PM_ERR_CONDITIONAL_PREDICATE_TERM, + PM_ERR_CONDITIONAL_TERM, + PM_ERR_CONDITIONAL_TERM_ELSE, + PM_ERR_CONDITIONAL_UNLESS_PREDICATE, + PM_ERR_CONDITIONAL_UNTIL_PREDICATE, + PM_ERR_CONDITIONAL_WHILE_PREDICATE, + PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT, + PM_ERR_DEF_ENDLESS, + PM_ERR_DEF_ENDLESS_PARAMETERS, + PM_ERR_DEF_ENDLESS_SETTER, + PM_ERR_DEF_NAME, + PM_ERR_DEF_PARAMS_TERM, + PM_ERR_DEF_PARAMS_TERM_PAREN, + PM_ERR_DEF_RECEIVER, + PM_ERR_DEF_RECEIVER_TERM, + PM_ERR_DEF_TERM, + PM_ERR_DEFINED_EXPRESSION, + PM_ERR_EMBDOC_TERM, + PM_ERR_EMBEXPR_END, + PM_ERR_EMBVAR_INVALID, + PM_ERR_END_UPCASE_BRACE, + PM_ERR_END_UPCASE_TERM, + PM_ERR_ESCAPE_INVALID_CONTROL, + PM_ERR_ESCAPE_INVALID_CONTROL_REPEAT, + PM_ERR_ESCAPE_INVALID_HEXADECIMAL, + PM_ERR_ESCAPE_INVALID_META, + PM_ERR_ESCAPE_INVALID_META_REPEAT, + PM_ERR_ESCAPE_INVALID_UNICODE, + PM_ERR_ESCAPE_INVALID_UNICODE_CM_FLAGS, + PM_ERR_ESCAPE_INVALID_UNICODE_LIST, + PM_ERR_ESCAPE_INVALID_UNICODE_LITERAL, + PM_ERR_ESCAPE_INVALID_UNICODE_LONG, + PM_ERR_ESCAPE_INVALID_UNICODE_SHORT, + PM_ERR_ESCAPE_INVALID_UNICODE_TERM, + PM_ERR_EXPECT_ARGUMENT, + PM_ERR_EXPECT_EOL_AFTER_STATEMENT, + PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, + PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA, + PM_ERR_EXPECT_EXPRESSION_AFTER_EQUAL, + PM_ERR_EXPECT_EXPRESSION_AFTER_LESS_LESS, + PM_ERR_EXPECT_EXPRESSION_AFTER_LPAREN, + PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, + PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, + PM_ERR_EXPECT_EXPRESSION_AFTER_QUESTION, + PM_ERR_EXPECT_EXPRESSION_AFTER_SPLAT, + PM_ERR_EXPECT_EXPRESSION_AFTER_SPLAT_HASH, + PM_ERR_EXPECT_EXPRESSION_AFTER_STAR, + PM_ERR_EXPECT_FOR_DELIMITER, + PM_ERR_EXPECT_IDENT_REQ_PARAMETER, + PM_ERR_EXPECT_IN_DELIMITER, + PM_ERR_EXPECT_LPAREN_AFTER_NOT_LPAREN, + PM_ERR_EXPECT_LPAREN_AFTER_NOT_OTHER, + PM_ERR_EXPECT_LPAREN_REQ_PARAMETER, + PM_ERR_EXPECT_MESSAGE, + PM_ERR_EXPECT_RBRACKET, + PM_ERR_EXPECT_RPAREN, + PM_ERR_EXPECT_RPAREN_AFTER_MULTI, + PM_ERR_EXPECT_RPAREN_REQ_PARAMETER, + PM_ERR_EXPECT_SINGLETON_CLASS_DELIMITER, + PM_ERR_EXPECT_STRING_CONTENT, + PM_ERR_EXPECT_WHEN_DELIMITER, + PM_ERR_EXPRESSION_BARE_HASH, + PM_ERR_EXPRESSION_NOT_WRITABLE, + PM_ERR_EXPRESSION_NOT_WRITABLE_ENCODING, + PM_ERR_EXPRESSION_NOT_WRITABLE_FALSE, + PM_ERR_EXPRESSION_NOT_WRITABLE_FILE, + PM_ERR_EXPRESSION_NOT_WRITABLE_LINE, + PM_ERR_EXPRESSION_NOT_WRITABLE_NIL, + PM_ERR_EXPRESSION_NOT_WRITABLE_NUMBERED, + PM_ERR_EXPRESSION_NOT_WRITABLE_SELF, + PM_ERR_EXPRESSION_NOT_WRITABLE_TRUE, + PM_ERR_FLOAT_PARSE, + PM_ERR_FOR_COLLECTION, + PM_ERR_FOR_IN, + PM_ERR_FOR_INDEX, + PM_ERR_FOR_TERM, + PM_ERR_GLOBAL_VARIABLE_BARE, + PM_ERR_HASH_EXPRESSION_AFTER_LABEL, + PM_ERR_HASH_KEY, + PM_ERR_HASH_ROCKET, + PM_ERR_HASH_TERM, + PM_ERR_HASH_VALUE, + PM_ERR_HEREDOC_IDENTIFIER, + PM_ERR_HEREDOC_TERM, + PM_ERR_INCOMPLETE_QUESTION_MARK, + PM_ERR_INCOMPLETE_VARIABLE_CLASS, + PM_ERR_INCOMPLETE_VARIABLE_CLASS_3_3, + PM_ERR_INCOMPLETE_VARIABLE_INSTANCE, + PM_ERR_INCOMPLETE_VARIABLE_INSTANCE_3_3, + PM_ERR_INSTANCE_VARIABLE_BARE, + PM_ERR_INVALID_BLOCK_EXIT, + PM_ERR_INVALID_CHARACTER, + PM_ERR_INVALID_COMMA, + PM_ERR_INVALID_ENCODING_MAGIC_COMMENT, + PM_ERR_INVALID_ESCAPE_CHARACTER, + PM_ERR_INVALID_FLOAT_EXPONENT, + PM_ERR_INVALID_LOCAL_VARIABLE_READ, + PM_ERR_INVALID_LOCAL_VARIABLE_WRITE, + PM_ERR_INVALID_MULTIBYTE_CHAR, + PM_ERR_INVALID_MULTIBYTE_CHARACTER, + PM_ERR_INVALID_MULTIBYTE_ESCAPE, + PM_ERR_INVALID_NUMBER_BINARY, + PM_ERR_INVALID_NUMBER_DECIMAL, + PM_ERR_INVALID_NUMBER_FRACTION, + PM_ERR_INVALID_NUMBER_HEXADECIMAL, + PM_ERR_INVALID_NUMBER_OCTAL, + PM_ERR_INVALID_NUMBER_UNDERSCORE_INNER, + PM_ERR_INVALID_NUMBER_UNDERSCORE_TRAILING, + PM_ERR_INVALID_PERCENT, + PM_ERR_INVALID_PERCENT_EOF, + PM_ERR_INVALID_PRINTABLE_CHARACTER, + PM_ERR_INVALID_RETRY_AFTER_ELSE, + PM_ERR_INVALID_RETRY_AFTER_ENSURE, + PM_ERR_INVALID_RETRY_WITHOUT_RESCUE, + PM_ERR_INVALID_SYMBOL, + PM_ERR_INVALID_VARIABLE_GLOBAL, + PM_ERR_INVALID_VARIABLE_GLOBAL_3_3, + PM_ERR_INVALID_YIELD, + PM_ERR_IT_NOT_ALLOWED_NUMBERED, + PM_ERR_IT_NOT_ALLOWED_ORDINARY, + PM_ERR_LAMBDA_OPEN, + PM_ERR_LAMBDA_TERM_BRACE, + PM_ERR_LAMBDA_TERM_END, + PM_ERR_LIST_I_LOWER_ELEMENT, + PM_ERR_LIST_I_LOWER_TERM, + PM_ERR_LIST_I_UPPER_ELEMENT, + PM_ERR_LIST_I_UPPER_TERM, + PM_ERR_LIST_W_LOWER_ELEMENT, + PM_ERR_LIST_W_LOWER_TERM, + PM_ERR_LIST_W_UPPER_ELEMENT, + PM_ERR_LIST_W_UPPER_TERM, + PM_ERR_MALLOC_FAILED, + PM_ERR_MIXED_ENCODING, + PM_ERR_MODULE_IN_METHOD, + PM_ERR_MODULE_NAME, + PM_ERR_MODULE_TERM, + PM_ERR_MULTI_ASSIGN_MULTI_SPLATS, + PM_ERR_MULTI_ASSIGN_UNEXPECTED_REST, + PM_ERR_NESTING_TOO_DEEP, + PM_ERR_NO_LOCAL_VARIABLE, + PM_ERR_NON_ASSOCIATIVE_OPERATOR, + PM_ERR_NOT_EXPRESSION, + PM_ERR_NUMBER_LITERAL_UNDERSCORE, + PM_ERR_NUMBERED_PARAMETER_INNER_BLOCK, + PM_ERR_NUMBERED_PARAMETER_IT, + PM_ERR_NUMBERED_PARAMETER_ORDINARY, + PM_ERR_NUMBERED_PARAMETER_OUTER_BLOCK, + PM_ERR_OPERATOR_MULTI_ASSIGN, + PM_ERR_OPERATOR_WRITE_ARGUMENTS, + PM_ERR_OPERATOR_WRITE_BLOCK, + PM_ERR_PARAMETER_ASSOC_SPLAT_MULTI, + PM_ERR_PARAMETER_BLOCK_MULTI, + PM_ERR_PARAMETER_CIRCULAR, + PM_ERR_PARAMETER_FORWARDING_AFTER_REST, + PM_ERR_PARAMETER_METHOD_NAME, + PM_ERR_PARAMETER_NAME_DUPLICATED, + PM_ERR_PARAMETER_NO_DEFAULT, + PM_ERR_PARAMETER_NO_DEFAULT_KW, + PM_ERR_PARAMETER_NUMBERED_RESERVED, + PM_ERR_PARAMETER_ORDER, + PM_ERR_PARAMETER_SPLAT_MULTI, + PM_ERR_PARAMETER_STAR, + PM_ERR_PARAMETER_UNEXPECTED_FWD, + PM_ERR_PARAMETER_UNEXPECTED_NO_KW, + PM_ERR_PARAMETER_WILD_LOOSE_COMMA, + PM_ERR_PATTERN_ARRAY_MULTIPLE_RESTS, + PM_ERR_PATTERN_CAPTURE_DUPLICATE, + PM_ERR_PATTERN_CAPTURE_IN_ALTERNATIVE, + PM_ERR_PATTERN_EXPRESSION_AFTER_BRACKET, + PM_ERR_PATTERN_EXPRESSION_AFTER_COMMA, + PM_ERR_PATTERN_EXPRESSION_AFTER_HROCKET, + PM_ERR_PATTERN_EXPRESSION_AFTER_IN, + PM_ERR_PATTERN_EXPRESSION_AFTER_KEY, + PM_ERR_PATTERN_EXPRESSION_AFTER_PAREN, + PM_ERR_PATTERN_EXPRESSION_AFTER_PIN, + PM_ERR_PATTERN_EXPRESSION_AFTER_PIPE, + PM_ERR_PATTERN_EXPRESSION_AFTER_RANGE, + PM_ERR_PATTERN_EXPRESSION_AFTER_REST, + PM_ERR_PATTERN_FIND_MISSING_INNER, + PM_ERR_PATTERN_HASH_IMPLICIT, + PM_ERR_PATTERN_HASH_KEY, + PM_ERR_PATTERN_HASH_KEY_DUPLICATE, + PM_ERR_PATTERN_HASH_KEY_INTERPOLATED, + PM_ERR_PATTERN_HASH_KEY_LABEL, + PM_ERR_PATTERN_HASH_KEY_LOCALS, + PM_ERR_PATTERN_IDENT_AFTER_HROCKET, + PM_ERR_PATTERN_LABEL_AFTER_COMMA, + PM_ERR_PATTERN_REST, + PM_ERR_PATTERN_TERM_BRACE, + PM_ERR_PATTERN_TERM_BRACKET, + PM_ERR_PATTERN_TERM_PAREN, + PM_ERR_PIPEPIPEEQ_MULTI_ASSIGN, + PM_ERR_REGEXP_ENCODING_OPTION_MISMATCH, + PM_ERR_REGEXP_INCOMPAT_CHAR_ENCODING, + PM_ERR_REGEXP_INVALID_UNICODE_RANGE, + PM_ERR_REGEXP_NON_ESCAPED_MBC, + PM_ERR_REGEXP_PARSE_ERROR, + PM_ERR_REGEXP_TERM, + PM_ERR_REGEXP_UNKNOWN_OPTIONS, + PM_ERR_REGEXP_UTF8_CHAR_NON_UTF8_REGEXP, + PM_ERR_RESCUE_EXPRESSION, + PM_ERR_RESCUE_MODIFIER_VALUE, + PM_ERR_RESCUE_TERM, + PM_ERR_RESCUE_VARIABLE, + PM_ERR_RETURN_INVALID, + PM_ERR_SCRIPT_NOT_FOUND, + PM_ERR_SINGLETON_FOR_LITERALS, + PM_ERR_STATEMENT_ALIAS, + PM_ERR_STATEMENT_POSTEXE_END, + PM_ERR_STATEMENT_PREEXE_BEGIN, + PM_ERR_STATEMENT_UNDEF, + PM_ERR_STRING_CONCATENATION, + PM_ERR_STRING_INTERPOLATED_TERM, + PM_ERR_STRING_LITERAL_EOF, + PM_ERR_STRING_LITERAL_TERM, + PM_ERR_SYMBOL_INVALID, + PM_ERR_SYMBOL_TERM_DYNAMIC, + PM_ERR_SYMBOL_TERM_INTERPOLATED, + PM_ERR_TERNARY_COLON, + PM_ERR_TERNARY_EXPRESSION_FALSE, + PM_ERR_TERNARY_EXPRESSION_TRUE, + PM_ERR_UNARY_DISALLOWED, + PM_ERR_UNARY_RECEIVER, + PM_ERR_UNDEF_ARGUMENT, + PM_ERR_UNEXPECTED_BLOCK_ARGUMENT, + PM_ERR_UNEXPECTED_INDEX_BLOCK, + PM_ERR_UNEXPECTED_INDEX_KEYWORDS, + PM_ERR_UNEXPECTED_LABEL, + PM_ERR_UNEXPECTED_MULTI_WRITE, + PM_ERR_UNEXPECTED_PARAMETER_DEFAULT_VALUE, + PM_ERR_UNEXPECTED_RANGE_OPERATOR, + PM_ERR_UNEXPECTED_SAFE_NAVIGATION, + PM_ERR_UNEXPECTED_TOKEN_CLOSE_CONTEXT, + PM_ERR_UNEXPECTED_TOKEN_IGNORE, + PM_ERR_UNTIL_TERM, + PM_ERR_VOID_EXPRESSION, + PM_ERR_WHILE_TERM, + PM_ERR_WRITE_TARGET_IN_METHOD, + PM_ERR_WRITE_TARGET_READONLY, + PM_ERR_WRITE_TARGET_UNEXPECTED, + PM_ERR_XSTRING_TERM, + + // These are the warning diagnostics. + PM_WARN_AMBIGUOUS_BINARY_OPERATOR, + PM_WARN_AMBIGUOUS_FIRST_ARGUMENT_MINUS, + PM_WARN_AMBIGUOUS_FIRST_ARGUMENT_PLUS, + PM_WARN_AMBIGUOUS_PREFIX_AMPERSAND, + PM_WARN_AMBIGUOUS_PREFIX_STAR, + PM_WARN_AMBIGUOUS_PREFIX_STAR_STAR, + PM_WARN_AMBIGUOUS_SLASH, + PM_WARN_COMPARISON_AFTER_COMPARISON, + PM_WARN_DOT_DOT_DOT_EOL, + PM_WARN_EQUAL_IN_CONDITIONAL, + PM_WARN_EQUAL_IN_CONDITIONAL_3_3, + PM_WARN_END_IN_METHOD, + PM_WARN_DUPLICATED_HASH_KEY, + PM_WARN_DUPLICATED_WHEN_CLAUSE, + PM_WARN_FLOAT_OUT_OF_RANGE, + PM_WARN_IGNORED_FROZEN_STRING_LITERAL, + PM_WARN_INDENTATION_MISMATCH, + PM_WARN_INTEGER_IN_FLIP_FLOP, + PM_WARN_INVALID_CHARACTER, + PM_WARN_INVALID_MAGIC_COMMENT_VALUE, + PM_WARN_INVALID_NUMBERED_REFERENCE, + PM_WARN_KEYWORD_EOL, + PM_WARN_LITERAL_IN_CONDITION_DEFAULT, + PM_WARN_LITERAL_IN_CONDITION_VERBOSE, + PM_WARN_SHAREABLE_CONSTANT_VALUE_LINE, + PM_WARN_SHEBANG_CARRIAGE_RETURN, + PM_WARN_UNEXPECTED_CARRIAGE_RETURN, + PM_WARN_UNREACHABLE_STATEMENT, + PM_WARN_UNUSED_LOCAL_VARIABLE, + PM_WARN_VOID_STATEMENT, +} 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/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/encoding.h b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/encoding.h new file mode 100644 index 0000000..5f77248 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/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 +#include +#include +#include + +/** + * 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/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/node.h b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/node.h new file mode 100644 index 0000000..e8686a3 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/node.h @@ -0,0 +1,129 @@ +/** + * @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); + +/** + * 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/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/options.h b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/options.h new file mode 100644 index 0000000..c00c7bf --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/options.h @@ -0,0 +1,488 @@ +/** + * @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_char.h" +#include "prism/util/pm_string.h" + +#include +#include +#include + +/** + * 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; + + /** Flags for the set of forwarding parameters in this scope. */ + uint8_t forwarding; +} pm_options_scope_t; + +/** The default value for parameters. */ +static const uint8_t PM_OPTIONS_SCOPE_FORWARDING_NONE = 0x0; + +/** When the scope is fowarding with the * parameter. */ +static const uint8_t PM_OPTIONS_SCOPE_FORWARDING_POSITIONALS = 0x1; + +/** When the scope is fowarding with the ** parameter. */ +static const uint8_t PM_OPTIONS_SCOPE_FORWARDING_KEYWORDS = 0x2; + +/** When the scope is fowarding with the & parameter. */ +static const uint8_t PM_OPTIONS_SCOPE_FORWARDING_BLOCK = 0x4; + +/** When the scope is fowarding with the ... parameter. */ +static const uint8_t PM_OPTIONS_SCOPE_FORWARDING_ALL = 0x8; + +// Forward declaration needed by the callback typedef. +struct pm_options; + +/** + * The callback called when additional switches are found in a shebang comment + * that need to be processed by the runtime. + * + * @param options The options struct that may be updated by this callback. + * Certain fields will be checked for changes, specifically encoding, + * command_line, and frozen_string_literal. + * @param source The source of the shebang comment. + * @param length The length of the source. + * @param shebang_callback_data Any additional data that should be passed along + * to the callback. + */ +typedef void (*pm_options_shebang_callback_t)(struct pm_options *options, const uint8_t *source, size_t length, void *shebang_callback_data); + +/** + * 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 { + /** If an explicit version is not provided, the current version of prism will be used. */ + PM_OPTIONS_VERSION_UNSET = 0, + + /** The vendored version of prism in CRuby 3.3.x. */ + PM_OPTIONS_VERSION_CRUBY_3_3 = 1, + + /** The vendored version of prism in CRuby 3.4.x. */ + PM_OPTIONS_VERSION_CRUBY_3_4 = 2, + + /** The vendored version of prism in CRuby 4.0.x. */ + PM_OPTIONS_VERSION_CRUBY_3_5 = 3, + + /** The vendored version of prism in CRuby 4.0.x. */ + PM_OPTIONS_VERSION_CRUBY_4_0 = 3, + + /** The vendored version of prism in CRuby 4.1.x. */ + PM_OPTIONS_VERSION_CRUBY_4_1 = 4, + + /** The current version of prism. */ + PM_OPTIONS_VERSION_LATEST = PM_OPTIONS_VERSION_CRUBY_4_1 +} pm_options_version_t; + +/** + * The options that can be passed to the parser. + */ +typedef struct pm_options { + /** + * The callback to call when additional switches are found in a shebang + * comment. + */ + pm_options_shebang_callback_t shebang_callback; + + /** + * Any additional data that should be passed along to the shebang callback + * if one was set. + */ + void *shebang_callback_data; + + /** 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; + + /** + * Whether or not the encoding magic comments should be respected. This is a + * niche use-case where you want to parse a file with a specific encoding + * but ignore any encoding magic comments at the top of the file. + */ + bool encoding_locked; + + /** + * When the file being parsed is the main script, the shebang will be + * considered for command-line flags (or for implicit -x). The caller needs + * to pass this information to the parser so that it can behave correctly. + */ + bool main_script; + + /** + * When the file being parsed is considered a "partial" script, jumps will + * not be marked as errors if they are not contained within loops/blocks. + * This is used in the case that you're parsing a script that you know will + * be embedded inside another script later, but you do not have that context + * yet. For example, when parsing an ERB template that will be evaluated + * inside another script. + */ + bool partial_script; + + /** + * Whether or not the parser should freeze the nodes that it creates. This + * makes it possible to have a deeply frozen AST that is safe to share + * between concurrency primitives. + */ + bool freeze; +} 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 shebang callback option on the given options struct. + * + * @param options The options struct to set the shebang callback on. + * @param shebang_callback The shebang callback to set. + * @param shebang_callback_data Any additional data that should be passed along + * to the callback. + * + * \public \memberof pm_options + */ +PRISM_EXPORTED_FUNCTION void pm_options_shebang_callback_set(pm_options_t *options, pm_options_shebang_callback_t shebang_callback, void *shebang_callback_data); + +/** + * 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. + * + * \public \memberof pm_options + */ +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. + * + * \public \memberof pm_options + */ +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. + * + * \public \memberof pm_options + */ +PRISM_EXPORTED_FUNCTION void pm_options_encoding_set(pm_options_t *options, const char *encoding); + +/** + * Set the encoding_locked option on the given options struct. + * + * @param options The options struct to set the encoding_locked value on. + * @param encoding_locked The encoding_locked value to set. + * + * \public \memberof pm_options + */ +PRISM_EXPORTED_FUNCTION void pm_options_encoding_locked_set(pm_options_t *options, bool encoding_locked); + +/** + * 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. + * + * \public \memberof pm_options + */ +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. + * + * \public \memberof pm_options + */ +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. + * + * \public \memberof pm_options + */ +PRISM_EXPORTED_FUNCTION bool pm_options_version_set(pm_options_t *options, const char *version, size_t length); + +/** + * Set the main script option on the given options struct. + * + * @param options The options struct to set the main script value on. + * @param main_script The main script value to set. + * + * \public \memberof pm_options + */ +PRISM_EXPORTED_FUNCTION void pm_options_main_script_set(pm_options_t *options, bool main_script); + +/** + * Set the partial script option on the given options struct. + * + * @param options The options struct to set the partial script value on. + * @param partial_script The partial script value to set. + * + * \public \memberof pm_options + */ +PRISM_EXPORTED_FUNCTION void pm_options_partial_script_set(pm_options_t *options, bool partial_script); + +/** + * Set the freeze option on the given options struct. + * + * @param options The options struct to set the freeze value on. + * @param freeze The freeze value to set. + * + * \public \memberof pm_options + */ +PRISM_EXPORTED_FUNCTION void pm_options_freeze_set(pm_options_t *options, bool freeze); + +/** + * 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. + * + * \public \memberof pm_options + */ +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. + * + * \public \memberof pm_options + */ +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. + * + * \public \memberof pm_options + */ +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. + * + * \public \memberof pm_options + */ +PRISM_EXPORTED_FUNCTION const pm_string_t * pm_options_scope_local_get(const pm_options_scope_t *scope, size_t index); + +/** + * Set the forwarding option on the given scope struct. + * + * @param scope The scope struct to set the forwarding on. + * @param forwarding The forwarding value to set. + * + * \public \memberof pm_options + */ +PRISM_EXPORTED_FUNCTION void pm_options_scope_forwarding_set(pm_options_scope_t *scope, uint8_t forwarding); + +/** + * Free the internal memory associated with the options. + * + * @param options The options struct whose internal memory should be freed. + * + * \public \memberof pm_options + */ +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 | + * | `1` | encoding locked | + * | `1` | main script | + * | `1` | partial script | + * | `1` | freeze | + * | `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 | + * | `1` | the forwarding flags | + * | ... | 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, encoding locked, main script, and partial script + * 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/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/pack.h b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/pack.h new file mode 100644 index 0000000..0b0b4b1 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/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 +#include + +/** 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/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/parser.h b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/parser.h new file mode 100644 index 0000000..95d7aac --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/parser.h @@ -0,0 +1,936 @@ +/** + * @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 + +/** + * 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; + +/** + * All of the information necessary to store to lexing a heredoc. + */ +typedef 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; +} pm_heredoc_lex_mode_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 { + /** + * All of the data necessary to lex a heredoc. + */ + pm_heredoc_lex_mode_t base; + + /** + * 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, + + /** expressions in block parameters `foo do |...| end ` */ + PM_CONTEXT_BLOCK_PARAMETERS, + + /** 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 predicate clause of a loop statement */ + PM_CONTEXT_LOOP_PREDICATE, + + /** 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 multiple target expression */ + PM_CONTEXT_MULTI_TARGET, + + /** 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 = PM_SHAREABLE_CONSTANT_NODE_FLAGS_LITERAL; +static const pm_shareable_constant_value_t PM_SCOPE_SHAREABLE_CONSTANT_EXPERIMENTAL_EVERYTHING = PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_EVERYTHING; +static const pm_shareable_constant_value_t PM_SCOPE_SHAREABLE_CONSTANT_EXPERIMENTAL_COPY = PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_COPY; + +/** + * 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; + +/** The flags about scope parameters that can be set. */ +typedef uint8_t pm_scope_parameters_t; +static const pm_scope_parameters_t PM_SCOPE_PARAMETERS_NONE = 0x0; +static const pm_scope_parameters_t PM_SCOPE_PARAMETERS_FORWARDING_POSITIONALS = 0x1; +static const pm_scope_parameters_t PM_SCOPE_PARAMETERS_FORWARDING_KEYWORDS = 0x2; +static const pm_scope_parameters_t PM_SCOPE_PARAMETERS_FORWARDING_BLOCK = 0x4; +static const pm_scope_parameters_t PM_SCOPE_PARAMETERS_FORWARDING_ALL = 0x8; +static const pm_scope_parameters_t PM_SCOPE_PARAMETERS_IMPLICIT_DISALLOWED = 0x10; +static const pm_scope_parameters_t PM_SCOPE_PARAMETERS_NUMBERED_INNER = 0x20; +static const pm_scope_parameters_t PM_SCOPE_PARAMETERS_NUMBERED_FOUND = 0x40; + +/** + * 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 list of the implicit parameters contained within the block. + * These will be processed after the block is parsed to determine the kind + * of parameters node that should be used and to check if any errors need to + * be added. + */ + pm_node_list_t implicit_parameters; + + /** + * This is a bitfield that indicates the parameters that are being used in + * this scope. It is a combination of the PM_SCOPE_PARAMETERS_* 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 + */ + pm_scope_parameters_t 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; + +/** + * 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 next node identifier that will be assigned. This is a unique + * identifier used to track nodes such that the syntax tree can be dropped + * but the node can be found through another parse. + */ + uint32_t node_id; + + /** 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 are parsing a "partial" script, which is a script that + * will be evaluated in the context of another script, so we should not + * check jumps (next/break/etc.) for validity. + */ + bool partial_script; + + /** 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; + + /** + * This is very specialized behavior for when you want to parse in a context + * that does not respect encoding comments. Its main use case is translating + * into the whitequark/parser AST which re-encodes source files in UTF-8 + * before they are parsed and ignores encoding comments. + */ + bool encoding_locked; + + /** + * 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; + + /** + * By default, Ruby always warns about mismatched indentation. This can be + * toggled with a magic comment. + */ + bool warn_mismatched_indentation; +}; + +#endif diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/prettyprint.h b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/prettyprint.h new file mode 100644 index 0000000..5a52b2b --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/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 + +#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/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/regexp.h b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/regexp.h new file mode 100644 index 0000000..5366b5a --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/regexp.h @@ -0,0 +1,43 @@ +/** + * @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.h" + +#include +#include +#include + +/** + * This callback is called by pm_regexp_parse() when a named capture group is found. + */ +typedef void (*pm_regexp_name_callback_t)(const pm_string_t *name, void *data); + +/** + * This callback is called by pm_regexp_parse() when a parse error is found. + */ +typedef void (*pm_regexp_error_callback_t)(const uint8_t *start, const uint8_t *end, const char *message, void *data); + +/** + * Parse a regular expression. + * + * @param parser The parser that is currently being used. + * @param source The source code to parse. + * @param size The size of the source code. + * @param extended_mode Whether to parse the regular expression in extended mode. + * @param name_callback The optional callback to call when a named capture group is found. + * @param name_data The optional data to pass to the name callback. + * @param error_callback The callback to call when a parse error is found. + * @param error_data The data to pass to the error callback. + */ +PRISM_EXPORTED_FUNCTION void pm_regexp_parse(pm_parser_t *parser, const uint8_t *source, size_t size, bool extended_mode, pm_regexp_name_callback_t name_callback, void *name_data, pm_regexp_error_callback_t error_callback, void *error_data); + +#endif diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/static_literals.h b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/static_literals.h new file mode 100644 index 0000000..bd29761 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/static_literals.h @@ -0,0 +1,121 @@ +/** + * @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 +#include + +/** + * 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. + * @param replace Whether to replace the previous node if one already exists. + * @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, bool replace); + +/** + * 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. + */ +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/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/util/pm_buffer.h b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/util/pm_buffer.h new file mode 100644 index 0000000..cb80f8b --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/util/pm_buffer.h @@ -0,0 +1,236 @@ +/** + * @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 +#include +#include +#include +#include + +/** + * 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. + * + * \public \memberof pm_buffer_t + */ +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. + * + * \public \memberof pm_buffer_t + */ +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. + * + * \public \memberof pm_buffer_t + */ +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); + +/** + * Append a unicode codepoint to the buffer. + * + * @param buffer The buffer to append to. + * @param value The character to append. + * @returns True if the codepoint was valid and appended successfully, false + * otherwise. + */ +bool pm_buffer_append_unicode_codepoint(pm_buffer_t *buffer, uint32_t 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. + * + * \public \memberof pm_buffer_t + */ +PRISM_EXPORTED_FUNCTION void pm_buffer_free(pm_buffer_t *buffer); + +#endif diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/util/pm_char.h b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/util/pm_char.h new file mode 100644 index 0000000..deeafd6 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/util/pm_char.h @@ -0,0 +1,204 @@ +/** + * @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 +#include + +/** + * 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/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/util/pm_constant_pool.h b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/util/pm_constant_pool.h new file mode 100644 index 0000000..6df23f8 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/util/pm_constant_pool.h @@ -0,0 +1,218 @@ +/** + * @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 +#include +#include +#include +#include + +/** + * 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); + +/** + * 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/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/util/pm_integer.h b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/util/pm_integer.h new file mode 100644 index 0000000..304665e --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/util/pm_integer.h @@ -0,0 +1,130 @@ +/** + * @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 +#include +#include +#include + +/** + * A structure represents an arbitrary-sized integer. + */ +typedef struct { + /** + * 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; + + /** + * Embedded value for small integer. This value is set to 0 if the value + * does not fit into uint32_t. + */ + uint32_t value; + + /** + * 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 default decimal base, with no prefix. Leading 0s will be ignored. */ + PM_INTEGER_BASE_DEFAULT, + + /** 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. + */ +void pm_integer_parse(pm_integer_t *integer, pm_integer_base_t base, const uint8_t *start, const uint8_t *end); + +/** + * 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); + +/** + * Reduce a ratio of integers to its simplest form. + * + * If either the numerator or denominator do not fit into a 32-bit integer, then + * this function is a no-op. In the future, we may consider reducing even the + * larger numbers, but for now we're going to keep it simple. + * + * @param numerator The numerator of the ratio. + * @param denominator The denominator of the ratio. + */ +void pm_integers_reduce(pm_integer_t *numerator, pm_integer_t *denominator); + +/** + * 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. + * + * \public \memberof pm_integer_t + */ +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. + * + * \public \memberof pm_integer_t + */ +PRISM_EXPORTED_FUNCTION void pm_integer_free(pm_integer_t *integer); + +#endif diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/util/pm_list.h b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/util/pm_list.h new file mode 100644 index 0000000..f544bb2 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/util/pm_list.h @@ -0,0 +1,103 @@ +/** + * @file pm_list.h + * + * An abstract linked list. + */ +#ifndef PRISM_LIST_H +#define PRISM_LIST_H + +#include "prism/defines.h" + +#include +#include +#include +#include + +/** + * 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. + * + * \public \memberof pm_list_t + */ +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. + * + * \public \memberof pm_list_t + */ +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. + * + * \public \memberof pm_list_t + */ +PRISM_EXPORTED_FUNCTION void pm_list_free(pm_list_t *list); + +#endif diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/util/pm_memchr.h b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/util/pm_memchr.h new file mode 100644 index 0000000..e0671ea --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/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 + +/** + * 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/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/util/pm_newline_list.h b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/util/pm_newline_list.h new file mode 100644 index 0000000..406abe8 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/util/pm_newline_list.h @@ -0,0 +1,113 @@ +/** + * @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 +#include +#include +#include + +/** + * 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 of the given offset. If the offset is not in the list, the + * line of the closest offset less than the given offset is 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 of the given offset. + */ +int32_t pm_newline_list_line(const pm_newline_list_t *list, const uint8_t *cursor, int32_t start_line); + +/** + * 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/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/util/pm_string.h b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/util/pm_string.h new file mode 100644 index 0000000..d8456ff --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/util/pm_string.h @@ -0,0 +1,200 @@ +/** + * @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 +#include +#include +#include +#include +#include + +// The following headers are necessary to read files using demand paging. +#ifdef _WIN32 +#include +#elif defined(_POSIX_MAPPED_FILES) +#include +#include +#include +#elif defined(PRISM_HAS_FILESYSTEM) +#include +#include +#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); + +/** + * Represents the result of calling pm_string_mapped_init or + * pm_string_file_init. We need this additional information because there is + * not a platform-agnostic way to indicate that the file that was attempted to + * be opened was a directory. + */ +typedef enum { + /** Indicates that the string was successfully initialized. */ + PM_STRING_INIT_SUCCESS = 0, + /** + * Indicates a generic error from a string_*_init function, where the type + * of error should be read from `errno` or `GetLastError()`. + */ + PM_STRING_INIT_ERROR_GENERIC = 1, + /** + * Indicates that the file that was attempted to be opened was a directory. + */ + PM_STRING_INIT_ERROR_DIRECTORY = 2 +} pm_string_init_result_t; + +/** + * 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 The success of the read, indicated by the value of the enum. + * + * \public \memberof pm_string_t + */ +PRISM_EXPORTED_FUNCTION pm_string_init_result_t 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 The success of the read, indicated by the value of the enum. + * + * \public \memberof pm_string_t + */ +PRISM_EXPORTED_FUNCTION pm_string_init_result_t pm_string_file_init(pm_string_t *string, const char *filepath); + +/** + * 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. + * + * \public \memberof pm_string_t + */ +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. + * + * \public \memberof pm_string_t + */ +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. + * + * \public \memberof pm_string_t + */ +PRISM_EXPORTED_FUNCTION void pm_string_free(pm_string_t *string); + +#endif diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/util/pm_strncasecmp.h b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/util/pm_strncasecmp.h new file mode 100644 index 0000000..5cb88cb --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/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 +#include +#include + +/** + * 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/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/util/pm_strpbrk.h b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/util/pm_strpbrk.h new file mode 100644 index 0000000..f387bd5 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/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 +#include + +/** + * 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/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/version.h b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/prism/version.h new file mode 100644 index 0000000..b95611f --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/include/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 1 + +/** + * The minor version of the Prism library as an int. + */ +#define PRISM_VERSION_MINOR 9 + +/** + * 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 "1.9.0" + +#endif diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism.rb b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism.rb new file mode 100644 index 0000000..781bd4b --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism.rb @@ -0,0 +1,98 @@ +# frozen_string_literal: true +# :markup: markdown + +# 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 :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 :MutationCompiler, "prism/mutation_compiler" + autoload :Pack, "prism/pack" + autoload :Pattern, "prism/pattern" + autoload :Reflection, "prism/reflection" + autoload :Relocation, "prism/relocation" + autoload :Serialize, "prism/serialize" + autoload :StringQuery, "prism/string_query" + 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 :LexCompat + + # Raised when requested to parse as the currently running Ruby version but Prism has no support for it. + class CurrentVersionError < ArgumentError + # Initialize a new exception for the given ruby version string. + def initialize(version) + message = +"invalid version: Requested to parse as `version: 'current'`; " + segments = + if version.match?(/\A\d+\.\d+.\d+\z/) + version.split(".").map(&:to_i) + end + + if segments && ((segments[0] < 3) || (segments[0] == 3 && segments[1] < 3)) + message << " #{version} is below the minimum supported syntax." + else + message << " #{version} is unknown. Please update the `prism` gem." + end + + super(message) + end + end + + # :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. + # + # For supported options, see Prism::parse. + def self.lex_compat(source, **options) + LexCompat.new(source, **options).result # steep:ignore + end + + # :call-seq: + # Prism::load(source, serialized, freeze) -> ParseResult + # + # Load the serialized AST using the source as a reference into a tree. + def self.load(source, serialized, freeze = false) + Serialize.load_parse(source, serialized, freeze) + end +end + +require_relative "prism/polyfill/byteindex" +require_relative "prism/polyfill/warn" +require_relative "prism/node" +require_relative "prism/node_ext" +require_relative "prism/parse_result" + +# 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"] + # The C extension is the default backend on CRuby. + Prism::BACKEND = :CEXT + + require "prism/prism" +else + # The FFI backend is used on other Ruby implementations. + Prism::BACKEND = :FFI + + require_relative "prism/ffi" +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/compiler.rb b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/compiler.rb new file mode 100644 index 0000000..bedcda6 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/compiler.rb @@ -0,0 +1,801 @@ +# frozen_string_literal: true +# :markup: markdown + +=begin +-- +This file is generated by the templates/template.rb script and should not be +modified manually. See templates/lib/prism/compiler.rb.erb +if you are looking to modify the template +++ +=end + +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.each_child_node.map { |node| node.accept(self) } + end + + # Compile a AliasGlobalVariableNode node + def visit_alias_global_variable_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a AliasMethodNode node + def visit_alias_method_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a AlternationPatternNode node + def visit_alternation_pattern_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a AndNode node + def visit_and_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a ArgumentsNode node + def visit_arguments_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a ArrayNode node + def visit_array_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a ArrayPatternNode node + def visit_array_pattern_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a AssocNode node + def visit_assoc_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a AssocSplatNode node + def visit_assoc_splat_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a BackReferenceReadNode node + def visit_back_reference_read_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a BeginNode node + def visit_begin_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a BlockArgumentNode node + def visit_block_argument_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a BlockLocalVariableNode node + def visit_block_local_variable_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a BlockNode node + def visit_block_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a BlockParameterNode node + def visit_block_parameter_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a BlockParametersNode node + def visit_block_parameters_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a BreakNode node + def visit_break_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a CallAndWriteNode node + def visit_call_and_write_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a CallNode node + def visit_call_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a CallOperatorWriteNode node + def visit_call_operator_write_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a CallOrWriteNode node + def visit_call_or_write_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a CallTargetNode node + def visit_call_target_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a CapturePatternNode node + def visit_capture_pattern_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a CaseMatchNode node + def visit_case_match_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a CaseNode node + def visit_case_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a ClassNode node + def visit_class_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a ClassVariableAndWriteNode node + def visit_class_variable_and_write_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a ClassVariableOperatorWriteNode node + def visit_class_variable_operator_write_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a ClassVariableOrWriteNode node + def visit_class_variable_or_write_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a ClassVariableReadNode node + def visit_class_variable_read_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a ClassVariableTargetNode node + def visit_class_variable_target_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a ClassVariableWriteNode node + def visit_class_variable_write_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a ConstantAndWriteNode node + def visit_constant_and_write_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a ConstantOperatorWriteNode node + def visit_constant_operator_write_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a ConstantOrWriteNode node + def visit_constant_or_write_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a ConstantPathAndWriteNode node + def visit_constant_path_and_write_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a ConstantPathNode node + def visit_constant_path_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a ConstantPathOperatorWriteNode node + def visit_constant_path_operator_write_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a ConstantPathOrWriteNode node + def visit_constant_path_or_write_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a ConstantPathTargetNode node + def visit_constant_path_target_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a ConstantPathWriteNode node + def visit_constant_path_write_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a ConstantReadNode node + def visit_constant_read_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a ConstantTargetNode node + def visit_constant_target_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a ConstantWriteNode node + def visit_constant_write_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a DefNode node + def visit_def_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a DefinedNode node + def visit_defined_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a ElseNode node + def visit_else_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a EmbeddedStatementsNode node + def visit_embedded_statements_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a EmbeddedVariableNode node + def visit_embedded_variable_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a EnsureNode node + def visit_ensure_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a FalseNode node + def visit_false_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a FindPatternNode node + def visit_find_pattern_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a FlipFlopNode node + def visit_flip_flop_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a FloatNode node + def visit_float_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a ForNode node + def visit_for_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a ForwardingArgumentsNode node + def visit_forwarding_arguments_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a ForwardingParameterNode node + def visit_forwarding_parameter_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a ForwardingSuperNode node + def visit_forwarding_super_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a GlobalVariableAndWriteNode node + def visit_global_variable_and_write_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a GlobalVariableOperatorWriteNode node + def visit_global_variable_operator_write_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a GlobalVariableOrWriteNode node + def visit_global_variable_or_write_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a GlobalVariableReadNode node + def visit_global_variable_read_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a GlobalVariableTargetNode node + def visit_global_variable_target_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a GlobalVariableWriteNode node + def visit_global_variable_write_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a HashNode node + def visit_hash_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a HashPatternNode node + def visit_hash_pattern_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a IfNode node + def visit_if_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a ImaginaryNode node + def visit_imaginary_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a ImplicitNode node + def visit_implicit_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a ImplicitRestNode node + def visit_implicit_rest_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a InNode node + def visit_in_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a IndexAndWriteNode node + def visit_index_and_write_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a IndexOperatorWriteNode node + def visit_index_operator_write_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a IndexOrWriteNode node + def visit_index_or_write_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a IndexTargetNode node + def visit_index_target_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a InstanceVariableAndWriteNode node + def visit_instance_variable_and_write_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a InstanceVariableOperatorWriteNode node + def visit_instance_variable_operator_write_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a InstanceVariableOrWriteNode node + def visit_instance_variable_or_write_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a InstanceVariableReadNode node + def visit_instance_variable_read_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a InstanceVariableTargetNode node + def visit_instance_variable_target_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a InstanceVariableWriteNode node + def visit_instance_variable_write_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a IntegerNode node + def visit_integer_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a InterpolatedMatchLastLineNode node + def visit_interpolated_match_last_line_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a InterpolatedRegularExpressionNode node + def visit_interpolated_regular_expression_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a InterpolatedStringNode node + def visit_interpolated_string_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a InterpolatedSymbolNode node + def visit_interpolated_symbol_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a InterpolatedXStringNode node + def visit_interpolated_x_string_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a ItLocalVariableReadNode node + def visit_it_local_variable_read_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a ItParametersNode node + def visit_it_parameters_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a KeywordHashNode node + def visit_keyword_hash_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a KeywordRestParameterNode node + def visit_keyword_rest_parameter_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a LambdaNode node + def visit_lambda_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a LocalVariableAndWriteNode node + def visit_local_variable_and_write_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a LocalVariableOperatorWriteNode node + def visit_local_variable_operator_write_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a LocalVariableOrWriteNode node + def visit_local_variable_or_write_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a LocalVariableReadNode node + def visit_local_variable_read_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a LocalVariableTargetNode node + def visit_local_variable_target_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a LocalVariableWriteNode node + def visit_local_variable_write_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a MatchLastLineNode node + def visit_match_last_line_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a MatchPredicateNode node + def visit_match_predicate_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a MatchRequiredNode node + def visit_match_required_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a MatchWriteNode node + def visit_match_write_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a MissingNode node + def visit_missing_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a ModuleNode node + def visit_module_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a MultiTargetNode node + def visit_multi_target_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a MultiWriteNode node + def visit_multi_write_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a NextNode node + def visit_next_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a NilNode node + def visit_nil_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a NoKeywordsParameterNode node + def visit_no_keywords_parameter_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a NumberedParametersNode node + def visit_numbered_parameters_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a NumberedReferenceReadNode node + def visit_numbered_reference_read_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a OptionalKeywordParameterNode node + def visit_optional_keyword_parameter_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a OptionalParameterNode node + def visit_optional_parameter_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a OrNode node + def visit_or_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a ParametersNode node + def visit_parameters_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a ParenthesesNode node + def visit_parentheses_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a PinnedExpressionNode node + def visit_pinned_expression_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a PinnedVariableNode node + def visit_pinned_variable_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a PostExecutionNode node + def visit_post_execution_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a PreExecutionNode node + def visit_pre_execution_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a ProgramNode node + def visit_program_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a RangeNode node + def visit_range_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a RationalNode node + def visit_rational_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a RedoNode node + def visit_redo_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a RegularExpressionNode node + def visit_regular_expression_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a RequiredKeywordParameterNode node + def visit_required_keyword_parameter_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a RequiredParameterNode node + def visit_required_parameter_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a RescueModifierNode node + def visit_rescue_modifier_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a RescueNode node + def visit_rescue_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a RestParameterNode node + def visit_rest_parameter_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a RetryNode node + def visit_retry_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a ReturnNode node + def visit_return_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a SelfNode node + def visit_self_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a ShareableConstantNode node + def visit_shareable_constant_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a SingletonClassNode node + def visit_singleton_class_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a SourceEncodingNode node + def visit_source_encoding_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a SourceFileNode node + def visit_source_file_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a SourceLineNode node + def visit_source_line_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a SplatNode node + def visit_splat_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a StatementsNode node + def visit_statements_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a StringNode node + def visit_string_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a SuperNode node + def visit_super_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a SymbolNode node + def visit_symbol_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a TrueNode node + def visit_true_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a UndefNode node + def visit_undef_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a UnlessNode node + def visit_unless_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a UntilNode node + def visit_until_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a WhenNode node + def visit_when_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a WhileNode node + def visit_while_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a XStringNode node + def visit_x_string_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + + # Compile a YieldNode node + def visit_yield_node(node) + node.each_child_node.map { |node| node.accept(self) } + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/desugar_compiler.rb b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/desugar_compiler.rb new file mode 100644 index 0000000..5d7d38d --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/desugar_compiler.rb @@ -0,0 +1,392 @@ +# frozen_string_literal: true +# :markup: markdown + +module Prism + class DesugarAndWriteNode # :nodoc: + include DSL + + attr_reader :node, :default_source, :read_class, :write_class, :arguments + + def initialize(node, default_source, read_class, write_class, **arguments) + @node = node + @default_source = default_source + @read_class = read_class + @write_class = write_class + @arguments = arguments + end + + # Desugar `x &&= y` to `x && x = y` + def compile + and_node( + location: node.location, + left: public_send(read_class, location: node.name_loc, **arguments), + right: public_send( + write_class, + location: node.location, + **arguments, + name_loc: node.name_loc, + value: node.value, + operator_loc: node.operator_loc + ), + operator_loc: node.operator_loc + ) + end + end + + class DesugarOrWriteDefinedNode # :nodoc: + include DSL + + attr_reader :node, :default_source, :read_class, :write_class, :arguments + + def initialize(node, default_source, read_class, write_class, **arguments) + @node = node + @default_source = default_source + @read_class = read_class + @write_class = write_class + @arguments = arguments + end + + # Desugar `x ||= y` to `defined?(x) ? x : x = y` + def compile + if_node( + location: node.location, + if_keyword_loc: node.operator_loc, + predicate: defined_node( + location: node.name_loc, + value: public_send(read_class, location: node.name_loc, **arguments), + keyword_loc: node.operator_loc + ), + then_keyword_loc: node.operator_loc, + statements: statements_node( + location: node.location, + body: [public_send(read_class, location: node.name_loc, **arguments)] + ), + subsequent: else_node( + location: node.location, + else_keyword_loc: node.operator_loc, + statements: statements_node( + location: node.location, + body: [ + public_send( + write_class, + location: node.location, + **arguments, + name_loc: node.name_loc, + value: node.value, + operator_loc: node.operator_loc + ) + ] + ), + end_keyword_loc: node.operator_loc + ), + end_keyword_loc: node.operator_loc + ) + end + end + + class DesugarOperatorWriteNode # :nodoc: + include DSL + + attr_reader :node, :default_source, :read_class, :write_class, :arguments + + def initialize(node, default_source, read_class, write_class, **arguments) + @node = node + @default_source = default_source + @read_class = read_class + @write_class = write_class + @arguments = arguments + end + + # Desugar `x += y` to `x = x + y` + def compile + binary_operator_loc = node.binary_operator_loc.chop + + public_send( + write_class, + location: node.location, + **arguments, + name_loc: node.name_loc, + value: call_node( + location: node.location, + receiver: public_send( + read_class, + location: node.name_loc, + **arguments + ), + name: binary_operator_loc.slice.to_sym, + message_loc: binary_operator_loc, + arguments: arguments_node( + location: node.value.location, + arguments: [node.value] + ) + ), + operator_loc: node.binary_operator_loc.copy( + start_offset: node.binary_operator_loc.end_offset - 1, + length: 1 + ) + ) + end + end + + class DesugarOrWriteNode # :nodoc: + include DSL + + attr_reader :node, :default_source, :read_class, :write_class, :arguments + + def initialize(node, default_source, read_class, write_class, **arguments) + @node = node + @default_source = default_source + @read_class = read_class + @write_class = write_class + @arguments = arguments + end + + # Desugar `x ||= y` to `x || x = y` + def compile + or_node( + location: node.location, + left: public_send(read_class, location: node.name_loc, **arguments), + right: public_send( + write_class, + location: node.location, + **arguments, + name_loc: node.name_loc, + value: node.value, + operator_loc: node.operator_loc + ), + operator_loc: node.operator_loc + ) + end + end + + private_constant :DesugarAndWriteNode, :DesugarOrWriteNode, :DesugarOrWriteDefinedNode, :DesugarOperatorWriteNode + + class ClassVariableAndWriteNode + def desugar # :nodoc: + DesugarAndWriteNode.new(self, source, :class_variable_read_node, :class_variable_write_node, name: name).compile + end + end + + class ClassVariableOrWriteNode + def desugar # :nodoc: + DesugarOrWriteDefinedNode.new(self, source, :class_variable_read_node, :class_variable_write_node, name: name).compile + end + end + + class ClassVariableOperatorWriteNode + def desugar # :nodoc: + DesugarOperatorWriteNode.new(self, source, :class_variable_read_node, :class_variable_write_node, name: name).compile + end + end + + class ConstantAndWriteNode + def desugar # :nodoc: + DesugarAndWriteNode.new(self, source, :constant_read_node, :constant_write_node, name: name).compile + end + end + + class ConstantOrWriteNode + def desugar # :nodoc: + DesugarOrWriteDefinedNode.new(self, source, :constant_read_node, :constant_write_node, name: name).compile + end + end + + class ConstantOperatorWriteNode + def desugar # :nodoc: + DesugarOperatorWriteNode.new(self, source, :constant_read_node, :constant_write_node, name: name).compile + end + end + + class GlobalVariableAndWriteNode + def desugar # :nodoc: + DesugarAndWriteNode.new(self, source, :global_variable_read_node, :global_variable_write_node, name: name).compile + end + end + + class GlobalVariableOrWriteNode + def desugar # :nodoc: + DesugarOrWriteDefinedNode.new(self, source, :global_variable_read_node, :global_variable_write_node, name: name).compile + end + end + + class GlobalVariableOperatorWriteNode + def desugar # :nodoc: + DesugarOperatorWriteNode.new(self, source, :global_variable_read_node, :global_variable_write_node, name: name).compile + end + end + + class InstanceVariableAndWriteNode + def desugar # :nodoc: + DesugarAndWriteNode.new(self, source, :instance_variable_read_node, :instance_variable_write_node, name: name).compile + end + end + + class InstanceVariableOrWriteNode + def desugar # :nodoc: + DesugarOrWriteNode.new(self, source, :instance_variable_read_node, :instance_variable_write_node, name: name).compile + end + end + + class InstanceVariableOperatorWriteNode + def desugar # :nodoc: + DesugarOperatorWriteNode.new(self, source, :instance_variable_read_node, :instance_variable_write_node, name: name).compile + end + end + + class LocalVariableAndWriteNode + def desugar # :nodoc: + DesugarAndWriteNode.new(self, source, :local_variable_read_node, :local_variable_write_node, name: name, depth: depth).compile + end + end + + class LocalVariableOrWriteNode + def desugar # :nodoc: + DesugarOrWriteNode.new(self, source, :local_variable_read_node, :local_variable_write_node, name: name, depth: depth).compile + end + end + + class LocalVariableOperatorWriteNode + def desugar # :nodoc: + DesugarOperatorWriteNode.new(self, source, :local_variable_read_node, :local_variable_write_node, name: name, depth: 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/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/dispatcher.rb b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/dispatcher.rb new file mode 100644 index 0000000..e3fb418 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/dispatcher.rb @@ -0,0 +1,2210 @@ +# frozen_string_literal: true +# :markup: markdown + +=begin +-- +This file is generated by the templates/template.rb script and should not be +modified manually. See templates/lib/prism/dispatcher.rb.erb +if you are looking to modify the template +++ +=end + +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 + # + # listener = OctalListener.new + # dispatcher = Prism::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) + register_events(listener, events) + end + + # Register all public methods of a listener that match the pattern + # `on__(enter|leave)`. + # + # def register_public_methods: (Listener) -> void + def register_public_methods(listener) + register_events(listener, listener.public_methods(false).grep(/\Aon_.+_(?:enter|leave)\z/)) + end + + # Register a listener for the given events. + private def register_events(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 + + # Dispatch enter and leave events for AliasGlobalVariableNode nodes and continue + # walking the tree. + def visit_alias_global_variable_node(node) + listeners[:on_alias_global_variable_node_enter]&.each { |listener| listener.on_alias_global_variable_node_enter(node) } + super + listeners[:on_alias_global_variable_node_leave]&.each { |listener| listener.on_alias_global_variable_node_leave(node) } + end + + # Dispatch enter and leave events for AliasMethodNode nodes and continue + # walking the tree. + def visit_alias_method_node(node) + listeners[:on_alias_method_node_enter]&.each { |listener| listener.on_alias_method_node_enter(node) } + super + listeners[:on_alias_method_node_leave]&.each { |listener| listener.on_alias_method_node_leave(node) } + end + + # Dispatch enter and leave events for AlternationPatternNode nodes and continue + # walking the tree. + def visit_alternation_pattern_node(node) + listeners[:on_alternation_pattern_node_enter]&.each { |listener| listener.on_alternation_pattern_node_enter(node) } + super + listeners[:on_alternation_pattern_node_leave]&.each { |listener| listener.on_alternation_pattern_node_leave(node) } + end + + # Dispatch enter and leave events for AndNode nodes and continue + # walking the tree. + def visit_and_node(node) + listeners[:on_and_node_enter]&.each { |listener| listener.on_and_node_enter(node) } + super + listeners[:on_and_node_leave]&.each { |listener| listener.on_and_node_leave(node) } + end + + # Dispatch enter and leave events for ArgumentsNode nodes and continue + # walking the tree. + def visit_arguments_node(node) + listeners[:on_arguments_node_enter]&.each { |listener| listener.on_arguments_node_enter(node) } + super + listeners[:on_arguments_node_leave]&.each { |listener| listener.on_arguments_node_leave(node) } + end + + # Dispatch enter and leave events for ArrayNode nodes and continue + # walking the tree. + def visit_array_node(node) + listeners[:on_array_node_enter]&.each { |listener| listener.on_array_node_enter(node) } + super + listeners[:on_array_node_leave]&.each { |listener| listener.on_array_node_leave(node) } + end + + # Dispatch enter and leave events for ArrayPatternNode nodes and continue + # walking the tree. + def visit_array_pattern_node(node) + listeners[:on_array_pattern_node_enter]&.each { |listener| listener.on_array_pattern_node_enter(node) } + super + listeners[:on_array_pattern_node_leave]&.each { |listener| listener.on_array_pattern_node_leave(node) } + end + + # Dispatch enter and leave events for AssocNode nodes and continue + # walking the tree. + def visit_assoc_node(node) + listeners[:on_assoc_node_enter]&.each { |listener| listener.on_assoc_node_enter(node) } + super + listeners[:on_assoc_node_leave]&.each { |listener| listener.on_assoc_node_leave(node) } + end + + # Dispatch enter and leave events for AssocSplatNode nodes and continue + # walking the tree. + def visit_assoc_splat_node(node) + listeners[:on_assoc_splat_node_enter]&.each { |listener| listener.on_assoc_splat_node_enter(node) } + super + listeners[:on_assoc_splat_node_leave]&.each { |listener| listener.on_assoc_splat_node_leave(node) } + end + + # Dispatch enter and leave events for BackReferenceReadNode nodes and continue + # walking the tree. + def visit_back_reference_read_node(node) + listeners[:on_back_reference_read_node_enter]&.each { |listener| listener.on_back_reference_read_node_enter(node) } + super + listeners[:on_back_reference_read_node_leave]&.each { |listener| listener.on_back_reference_read_node_leave(node) } + end + + # Dispatch enter and leave events for BeginNode nodes and continue + # walking the tree. + def visit_begin_node(node) + listeners[:on_begin_node_enter]&.each { |listener| listener.on_begin_node_enter(node) } + super + listeners[:on_begin_node_leave]&.each { |listener| listener.on_begin_node_leave(node) } + end + + # Dispatch enter and leave events for BlockArgumentNode nodes and continue + # walking the tree. + def visit_block_argument_node(node) + listeners[:on_block_argument_node_enter]&.each { |listener| listener.on_block_argument_node_enter(node) } + super + listeners[:on_block_argument_node_leave]&.each { |listener| listener.on_block_argument_node_leave(node) } + end + + # Dispatch enter and leave events for BlockLocalVariableNode nodes and continue + # walking the tree. + def visit_block_local_variable_node(node) + listeners[:on_block_local_variable_node_enter]&.each { |listener| listener.on_block_local_variable_node_enter(node) } + super + listeners[:on_block_local_variable_node_leave]&.each { |listener| listener.on_block_local_variable_node_leave(node) } + end + + # Dispatch enter and leave events for BlockNode nodes and continue + # walking the tree. + def visit_block_node(node) + listeners[:on_block_node_enter]&.each { |listener| listener.on_block_node_enter(node) } + super + listeners[:on_block_node_leave]&.each { |listener| listener.on_block_node_leave(node) } + end + + # Dispatch enter and leave events for BlockParameterNode nodes and continue + # walking the tree. + def visit_block_parameter_node(node) + listeners[:on_block_parameter_node_enter]&.each { |listener| listener.on_block_parameter_node_enter(node) } + super + listeners[:on_block_parameter_node_leave]&.each { |listener| listener.on_block_parameter_node_leave(node) } + end + + # Dispatch enter and leave events for BlockParametersNode nodes and continue + # walking the tree. + def visit_block_parameters_node(node) + listeners[:on_block_parameters_node_enter]&.each { |listener| listener.on_block_parameters_node_enter(node) } + super + listeners[:on_block_parameters_node_leave]&.each { |listener| listener.on_block_parameters_node_leave(node) } + end + + # Dispatch enter and leave events for BreakNode nodes and continue + # walking the tree. + def visit_break_node(node) + listeners[:on_break_node_enter]&.each { |listener| listener.on_break_node_enter(node) } + super + listeners[:on_break_node_leave]&.each { |listener| listener.on_break_node_leave(node) } + end + + # Dispatch enter and leave events for CallAndWriteNode nodes and continue + # walking the tree. + def visit_call_and_write_node(node) + listeners[:on_call_and_write_node_enter]&.each { |listener| listener.on_call_and_write_node_enter(node) } + super + listeners[:on_call_and_write_node_leave]&.each { |listener| listener.on_call_and_write_node_leave(node) } + end + + # Dispatch enter and leave events for CallNode nodes and continue + # walking the tree. + def visit_call_node(node) + listeners[:on_call_node_enter]&.each { |listener| listener.on_call_node_enter(node) } + super + listeners[:on_call_node_leave]&.each { |listener| listener.on_call_node_leave(node) } + end + + # Dispatch enter and leave events for CallOperatorWriteNode nodes and continue + # walking the tree. + def visit_call_operator_write_node(node) + listeners[:on_call_operator_write_node_enter]&.each { |listener| listener.on_call_operator_write_node_enter(node) } + super + listeners[:on_call_operator_write_node_leave]&.each { |listener| listener.on_call_operator_write_node_leave(node) } + end + + # Dispatch enter and leave events for CallOrWriteNode nodes and continue + # walking the tree. + def visit_call_or_write_node(node) + listeners[:on_call_or_write_node_enter]&.each { |listener| listener.on_call_or_write_node_enter(node) } + super + listeners[:on_call_or_write_node_leave]&.each { |listener| listener.on_call_or_write_node_leave(node) } + end + + # Dispatch enter and leave events for CallTargetNode nodes and continue + # walking the tree. + def visit_call_target_node(node) + listeners[:on_call_target_node_enter]&.each { |listener| listener.on_call_target_node_enter(node) } + super + listeners[:on_call_target_node_leave]&.each { |listener| listener.on_call_target_node_leave(node) } + end + + # Dispatch enter and leave events for CapturePatternNode nodes and continue + # walking the tree. + def visit_capture_pattern_node(node) + listeners[:on_capture_pattern_node_enter]&.each { |listener| listener.on_capture_pattern_node_enter(node) } + super + listeners[:on_capture_pattern_node_leave]&.each { |listener| listener.on_capture_pattern_node_leave(node) } + end + + # Dispatch enter and leave events for CaseMatchNode nodes and continue + # walking the tree. + def visit_case_match_node(node) + listeners[:on_case_match_node_enter]&.each { |listener| listener.on_case_match_node_enter(node) } + super + listeners[:on_case_match_node_leave]&.each { |listener| listener.on_case_match_node_leave(node) } + end + + # Dispatch enter and leave events for CaseNode nodes and continue + # walking the tree. + def visit_case_node(node) + listeners[:on_case_node_enter]&.each { |listener| listener.on_case_node_enter(node) } + super + listeners[:on_case_node_leave]&.each { |listener| listener.on_case_node_leave(node) } + end + + # Dispatch enter and leave events for ClassNode nodes and continue + # walking the tree. + def visit_class_node(node) + listeners[:on_class_node_enter]&.each { |listener| listener.on_class_node_enter(node) } + super + listeners[:on_class_node_leave]&.each { |listener| listener.on_class_node_leave(node) } + end + + # Dispatch enter and leave events for ClassVariableAndWriteNode nodes and continue + # walking the tree. + def visit_class_variable_and_write_node(node) + listeners[:on_class_variable_and_write_node_enter]&.each { |listener| listener.on_class_variable_and_write_node_enter(node) } + super + listeners[:on_class_variable_and_write_node_leave]&.each { |listener| listener.on_class_variable_and_write_node_leave(node) } + end + + # Dispatch enter and leave events for ClassVariableOperatorWriteNode nodes and continue + # walking the tree. + def visit_class_variable_operator_write_node(node) + listeners[:on_class_variable_operator_write_node_enter]&.each { |listener| listener.on_class_variable_operator_write_node_enter(node) } + super + listeners[:on_class_variable_operator_write_node_leave]&.each { |listener| listener.on_class_variable_operator_write_node_leave(node) } + end + + # Dispatch enter and leave events for ClassVariableOrWriteNode nodes and continue + # walking the tree. + def visit_class_variable_or_write_node(node) + listeners[:on_class_variable_or_write_node_enter]&.each { |listener| listener.on_class_variable_or_write_node_enter(node) } + super + listeners[:on_class_variable_or_write_node_leave]&.each { |listener| listener.on_class_variable_or_write_node_leave(node) } + end + + # Dispatch enter and leave events for ClassVariableReadNode nodes and continue + # walking the tree. + def visit_class_variable_read_node(node) + listeners[:on_class_variable_read_node_enter]&.each { |listener| listener.on_class_variable_read_node_enter(node) } + super + listeners[:on_class_variable_read_node_leave]&.each { |listener| listener.on_class_variable_read_node_leave(node) } + end + + # Dispatch enter and leave events for ClassVariableTargetNode nodes and continue + # walking the tree. + def visit_class_variable_target_node(node) + listeners[:on_class_variable_target_node_enter]&.each { |listener| listener.on_class_variable_target_node_enter(node) } + super + listeners[:on_class_variable_target_node_leave]&.each { |listener| listener.on_class_variable_target_node_leave(node) } + end + + # Dispatch enter and leave events for ClassVariableWriteNode nodes and continue + # walking the tree. + def visit_class_variable_write_node(node) + listeners[:on_class_variable_write_node_enter]&.each { |listener| listener.on_class_variable_write_node_enter(node) } + super + listeners[:on_class_variable_write_node_leave]&.each { |listener| listener.on_class_variable_write_node_leave(node) } + end + + # Dispatch enter and leave events for ConstantAndWriteNode nodes and continue + # walking the tree. + def visit_constant_and_write_node(node) + listeners[:on_constant_and_write_node_enter]&.each { |listener| listener.on_constant_and_write_node_enter(node) } + super + listeners[:on_constant_and_write_node_leave]&.each { |listener| listener.on_constant_and_write_node_leave(node) } + end + + # Dispatch enter and leave events for ConstantOperatorWriteNode nodes and continue + # walking the tree. + def visit_constant_operator_write_node(node) + listeners[:on_constant_operator_write_node_enter]&.each { |listener| listener.on_constant_operator_write_node_enter(node) } + super + listeners[:on_constant_operator_write_node_leave]&.each { |listener| listener.on_constant_operator_write_node_leave(node) } + end + + # Dispatch enter and leave events for ConstantOrWriteNode nodes and continue + # walking the tree. + def visit_constant_or_write_node(node) + listeners[:on_constant_or_write_node_enter]&.each { |listener| listener.on_constant_or_write_node_enter(node) } + super + listeners[:on_constant_or_write_node_leave]&.each { |listener| listener.on_constant_or_write_node_leave(node) } + end + + # Dispatch enter and leave events for ConstantPathAndWriteNode nodes and continue + # walking the tree. + def visit_constant_path_and_write_node(node) + listeners[:on_constant_path_and_write_node_enter]&.each { |listener| listener.on_constant_path_and_write_node_enter(node) } + super + listeners[:on_constant_path_and_write_node_leave]&.each { |listener| listener.on_constant_path_and_write_node_leave(node) } + end + + # Dispatch enter and leave events for ConstantPathNode nodes and continue + # walking the tree. + def visit_constant_path_node(node) + listeners[:on_constant_path_node_enter]&.each { |listener| listener.on_constant_path_node_enter(node) } + super + listeners[:on_constant_path_node_leave]&.each { |listener| listener.on_constant_path_node_leave(node) } + end + + # Dispatch enter and leave events for ConstantPathOperatorWriteNode nodes and continue + # walking the tree. + def visit_constant_path_operator_write_node(node) + listeners[:on_constant_path_operator_write_node_enter]&.each { |listener| listener.on_constant_path_operator_write_node_enter(node) } + super + listeners[:on_constant_path_operator_write_node_leave]&.each { |listener| listener.on_constant_path_operator_write_node_leave(node) } + end + + # Dispatch enter and leave events for ConstantPathOrWriteNode nodes and continue + # walking the tree. + def visit_constant_path_or_write_node(node) + listeners[:on_constant_path_or_write_node_enter]&.each { |listener| listener.on_constant_path_or_write_node_enter(node) } + super + listeners[:on_constant_path_or_write_node_leave]&.each { |listener| listener.on_constant_path_or_write_node_leave(node) } + end + + # Dispatch enter and leave events for ConstantPathTargetNode nodes and continue + # walking the tree. + def visit_constant_path_target_node(node) + listeners[:on_constant_path_target_node_enter]&.each { |listener| listener.on_constant_path_target_node_enter(node) } + super + listeners[:on_constant_path_target_node_leave]&.each { |listener| listener.on_constant_path_target_node_leave(node) } + end + + # Dispatch enter and leave events for ConstantPathWriteNode nodes and continue + # walking the tree. + def visit_constant_path_write_node(node) + listeners[:on_constant_path_write_node_enter]&.each { |listener| listener.on_constant_path_write_node_enter(node) } + super + listeners[:on_constant_path_write_node_leave]&.each { |listener| listener.on_constant_path_write_node_leave(node) } + end + + # Dispatch enter and leave events for ConstantReadNode nodes and continue + # walking the tree. + def visit_constant_read_node(node) + listeners[:on_constant_read_node_enter]&.each { |listener| listener.on_constant_read_node_enter(node) } + super + listeners[:on_constant_read_node_leave]&.each { |listener| listener.on_constant_read_node_leave(node) } + end + + # Dispatch enter and leave events for ConstantTargetNode nodes and continue + # walking the tree. + def visit_constant_target_node(node) + listeners[:on_constant_target_node_enter]&.each { |listener| listener.on_constant_target_node_enter(node) } + super + listeners[:on_constant_target_node_leave]&.each { |listener| listener.on_constant_target_node_leave(node) } + end + + # Dispatch enter and leave events for ConstantWriteNode nodes and continue + # walking the tree. + def visit_constant_write_node(node) + listeners[:on_constant_write_node_enter]&.each { |listener| listener.on_constant_write_node_enter(node) } + super + listeners[:on_constant_write_node_leave]&.each { |listener| listener.on_constant_write_node_leave(node) } + end + + # Dispatch enter and leave events for DefNode nodes and continue + # walking the tree. + def visit_def_node(node) + listeners[:on_def_node_enter]&.each { |listener| listener.on_def_node_enter(node) } + super + listeners[:on_def_node_leave]&.each { |listener| listener.on_def_node_leave(node) } + end + + # Dispatch enter and leave events for DefinedNode nodes and continue + # walking the tree. + def visit_defined_node(node) + listeners[:on_defined_node_enter]&.each { |listener| listener.on_defined_node_enter(node) } + super + listeners[:on_defined_node_leave]&.each { |listener| listener.on_defined_node_leave(node) } + end + + # Dispatch enter and leave events for ElseNode nodes and continue + # walking the tree. + def visit_else_node(node) + listeners[:on_else_node_enter]&.each { |listener| listener.on_else_node_enter(node) } + super + listeners[:on_else_node_leave]&.each { |listener| listener.on_else_node_leave(node) } + end + + # Dispatch enter and leave events for EmbeddedStatementsNode nodes and continue + # walking the tree. + def visit_embedded_statements_node(node) + listeners[:on_embedded_statements_node_enter]&.each { |listener| listener.on_embedded_statements_node_enter(node) } + super + listeners[:on_embedded_statements_node_leave]&.each { |listener| listener.on_embedded_statements_node_leave(node) } + end + + # Dispatch enter and leave events for EmbeddedVariableNode nodes and continue + # walking the tree. + def visit_embedded_variable_node(node) + listeners[:on_embedded_variable_node_enter]&.each { |listener| listener.on_embedded_variable_node_enter(node) } + super + listeners[:on_embedded_variable_node_leave]&.each { |listener| listener.on_embedded_variable_node_leave(node) } + end + + # Dispatch enter and leave events for EnsureNode nodes and continue + # walking the tree. + def visit_ensure_node(node) + listeners[:on_ensure_node_enter]&.each { |listener| listener.on_ensure_node_enter(node) } + super + listeners[:on_ensure_node_leave]&.each { |listener| listener.on_ensure_node_leave(node) } + end + + # Dispatch enter and leave events for FalseNode nodes and continue + # walking the tree. + def visit_false_node(node) + listeners[:on_false_node_enter]&.each { |listener| listener.on_false_node_enter(node) } + super + listeners[:on_false_node_leave]&.each { |listener| listener.on_false_node_leave(node) } + end + + # Dispatch enter and leave events for FindPatternNode nodes and continue + # walking the tree. + def visit_find_pattern_node(node) + listeners[:on_find_pattern_node_enter]&.each { |listener| listener.on_find_pattern_node_enter(node) } + super + listeners[:on_find_pattern_node_leave]&.each { |listener| listener.on_find_pattern_node_leave(node) } + end + + # Dispatch enter and leave events for FlipFlopNode nodes and continue + # walking the tree. + def visit_flip_flop_node(node) + listeners[:on_flip_flop_node_enter]&.each { |listener| listener.on_flip_flop_node_enter(node) } + super + listeners[:on_flip_flop_node_leave]&.each { |listener| listener.on_flip_flop_node_leave(node) } + end + + # Dispatch enter and leave events for FloatNode nodes and continue + # walking the tree. + def visit_float_node(node) + listeners[:on_float_node_enter]&.each { |listener| listener.on_float_node_enter(node) } + super + listeners[:on_float_node_leave]&.each { |listener| listener.on_float_node_leave(node) } + end + + # Dispatch enter and leave events for ForNode nodes and continue + # walking the tree. + def visit_for_node(node) + listeners[:on_for_node_enter]&.each { |listener| listener.on_for_node_enter(node) } + super + listeners[:on_for_node_leave]&.each { |listener| listener.on_for_node_leave(node) } + end + + # Dispatch enter and leave events for ForwardingArgumentsNode nodes and continue + # walking the tree. + def visit_forwarding_arguments_node(node) + listeners[:on_forwarding_arguments_node_enter]&.each { |listener| listener.on_forwarding_arguments_node_enter(node) } + super + listeners[:on_forwarding_arguments_node_leave]&.each { |listener| listener.on_forwarding_arguments_node_leave(node) } + end + + # Dispatch enter and leave events for ForwardingParameterNode nodes and continue + # walking the tree. + def visit_forwarding_parameter_node(node) + listeners[:on_forwarding_parameter_node_enter]&.each { |listener| listener.on_forwarding_parameter_node_enter(node) } + super + listeners[:on_forwarding_parameter_node_leave]&.each { |listener| listener.on_forwarding_parameter_node_leave(node) } + end + + # Dispatch enter and leave events for ForwardingSuperNode nodes and continue + # walking the tree. + def visit_forwarding_super_node(node) + listeners[:on_forwarding_super_node_enter]&.each { |listener| listener.on_forwarding_super_node_enter(node) } + super + listeners[:on_forwarding_super_node_leave]&.each { |listener| listener.on_forwarding_super_node_leave(node) } + end + + # Dispatch enter and leave events for GlobalVariableAndWriteNode nodes and continue + # walking the tree. + def visit_global_variable_and_write_node(node) + listeners[:on_global_variable_and_write_node_enter]&.each { |listener| listener.on_global_variable_and_write_node_enter(node) } + super + listeners[:on_global_variable_and_write_node_leave]&.each { |listener| listener.on_global_variable_and_write_node_leave(node) } + end + + # Dispatch enter and leave events for GlobalVariableOperatorWriteNode nodes and continue + # walking the tree. + def visit_global_variable_operator_write_node(node) + listeners[:on_global_variable_operator_write_node_enter]&.each { |listener| listener.on_global_variable_operator_write_node_enter(node) } + super + listeners[:on_global_variable_operator_write_node_leave]&.each { |listener| listener.on_global_variable_operator_write_node_leave(node) } + end + + # Dispatch enter and leave events for GlobalVariableOrWriteNode nodes and continue + # walking the tree. + def visit_global_variable_or_write_node(node) + listeners[:on_global_variable_or_write_node_enter]&.each { |listener| listener.on_global_variable_or_write_node_enter(node) } + super + listeners[:on_global_variable_or_write_node_leave]&.each { |listener| listener.on_global_variable_or_write_node_leave(node) } + end + + # Dispatch enter and leave events for GlobalVariableReadNode nodes and continue + # walking the tree. + def visit_global_variable_read_node(node) + listeners[:on_global_variable_read_node_enter]&.each { |listener| listener.on_global_variable_read_node_enter(node) } + super + listeners[:on_global_variable_read_node_leave]&.each { |listener| listener.on_global_variable_read_node_leave(node) } + end + + # Dispatch enter and leave events for GlobalVariableTargetNode nodes and continue + # walking the tree. + def visit_global_variable_target_node(node) + listeners[:on_global_variable_target_node_enter]&.each { |listener| listener.on_global_variable_target_node_enter(node) } + super + listeners[:on_global_variable_target_node_leave]&.each { |listener| listener.on_global_variable_target_node_leave(node) } + end + + # Dispatch enter and leave events for GlobalVariableWriteNode nodes and continue + # walking the tree. + def visit_global_variable_write_node(node) + listeners[:on_global_variable_write_node_enter]&.each { |listener| listener.on_global_variable_write_node_enter(node) } + super + listeners[:on_global_variable_write_node_leave]&.each { |listener| listener.on_global_variable_write_node_leave(node) } + end + + # Dispatch enter and leave events for HashNode nodes and continue + # walking the tree. + def visit_hash_node(node) + listeners[:on_hash_node_enter]&.each { |listener| listener.on_hash_node_enter(node) } + super + listeners[:on_hash_node_leave]&.each { |listener| listener.on_hash_node_leave(node) } + end + + # Dispatch enter and leave events for HashPatternNode nodes and continue + # walking the tree. + def visit_hash_pattern_node(node) + listeners[:on_hash_pattern_node_enter]&.each { |listener| listener.on_hash_pattern_node_enter(node) } + super + listeners[:on_hash_pattern_node_leave]&.each { |listener| listener.on_hash_pattern_node_leave(node) } + end + + # Dispatch enter and leave events for IfNode nodes and continue + # walking the tree. + def visit_if_node(node) + listeners[:on_if_node_enter]&.each { |listener| listener.on_if_node_enter(node) } + super + listeners[:on_if_node_leave]&.each { |listener| listener.on_if_node_leave(node) } + end + + # Dispatch enter and leave events for ImaginaryNode nodes and continue + # walking the tree. + def visit_imaginary_node(node) + listeners[:on_imaginary_node_enter]&.each { |listener| listener.on_imaginary_node_enter(node) } + super + listeners[:on_imaginary_node_leave]&.each { |listener| listener.on_imaginary_node_leave(node) } + end + + # Dispatch enter and leave events for ImplicitNode nodes and continue + # walking the tree. + def visit_implicit_node(node) + listeners[:on_implicit_node_enter]&.each { |listener| listener.on_implicit_node_enter(node) } + super + listeners[:on_implicit_node_leave]&.each { |listener| listener.on_implicit_node_leave(node) } + end + + # Dispatch enter and leave events for ImplicitRestNode nodes and continue + # walking the tree. + def visit_implicit_rest_node(node) + listeners[:on_implicit_rest_node_enter]&.each { |listener| listener.on_implicit_rest_node_enter(node) } + super + listeners[:on_implicit_rest_node_leave]&.each { |listener| listener.on_implicit_rest_node_leave(node) } + end + + # Dispatch enter and leave events for InNode nodes and continue + # walking the tree. + def visit_in_node(node) + listeners[:on_in_node_enter]&.each { |listener| listener.on_in_node_enter(node) } + super + listeners[:on_in_node_leave]&.each { |listener| listener.on_in_node_leave(node) } + end + + # Dispatch enter and leave events for IndexAndWriteNode nodes and continue + # walking the tree. + def visit_index_and_write_node(node) + listeners[:on_index_and_write_node_enter]&.each { |listener| listener.on_index_and_write_node_enter(node) } + super + listeners[:on_index_and_write_node_leave]&.each { |listener| listener.on_index_and_write_node_leave(node) } + end + + # Dispatch enter and leave events for IndexOperatorWriteNode nodes and continue + # walking the tree. + def visit_index_operator_write_node(node) + listeners[:on_index_operator_write_node_enter]&.each { |listener| listener.on_index_operator_write_node_enter(node) } + super + listeners[:on_index_operator_write_node_leave]&.each { |listener| listener.on_index_operator_write_node_leave(node) } + end + + # Dispatch enter and leave events for IndexOrWriteNode nodes and continue + # walking the tree. + def visit_index_or_write_node(node) + listeners[:on_index_or_write_node_enter]&.each { |listener| listener.on_index_or_write_node_enter(node) } + super + listeners[:on_index_or_write_node_leave]&.each { |listener| listener.on_index_or_write_node_leave(node) } + end + + # Dispatch enter and leave events for IndexTargetNode nodes and continue + # walking the tree. + def visit_index_target_node(node) + listeners[:on_index_target_node_enter]&.each { |listener| listener.on_index_target_node_enter(node) } + super + listeners[:on_index_target_node_leave]&.each { |listener| listener.on_index_target_node_leave(node) } + end + + # Dispatch enter and leave events for InstanceVariableAndWriteNode nodes and continue + # walking the tree. + def visit_instance_variable_and_write_node(node) + listeners[:on_instance_variable_and_write_node_enter]&.each { |listener| listener.on_instance_variable_and_write_node_enter(node) } + super + listeners[:on_instance_variable_and_write_node_leave]&.each { |listener| listener.on_instance_variable_and_write_node_leave(node) } + end + + # Dispatch enter and leave events for InstanceVariableOperatorWriteNode nodes and continue + # walking the tree. + def visit_instance_variable_operator_write_node(node) + listeners[:on_instance_variable_operator_write_node_enter]&.each { |listener| listener.on_instance_variable_operator_write_node_enter(node) } + super + listeners[:on_instance_variable_operator_write_node_leave]&.each { |listener| listener.on_instance_variable_operator_write_node_leave(node) } + end + + # Dispatch enter and leave events for InstanceVariableOrWriteNode nodes and continue + # walking the tree. + def visit_instance_variable_or_write_node(node) + listeners[:on_instance_variable_or_write_node_enter]&.each { |listener| listener.on_instance_variable_or_write_node_enter(node) } + super + listeners[:on_instance_variable_or_write_node_leave]&.each { |listener| listener.on_instance_variable_or_write_node_leave(node) } + end + + # Dispatch enter and leave events for InstanceVariableReadNode nodes and continue + # walking the tree. + def visit_instance_variable_read_node(node) + listeners[:on_instance_variable_read_node_enter]&.each { |listener| listener.on_instance_variable_read_node_enter(node) } + super + listeners[:on_instance_variable_read_node_leave]&.each { |listener| listener.on_instance_variable_read_node_leave(node) } + end + + # Dispatch enter and leave events for InstanceVariableTargetNode nodes and continue + # walking the tree. + def visit_instance_variable_target_node(node) + listeners[:on_instance_variable_target_node_enter]&.each { |listener| listener.on_instance_variable_target_node_enter(node) } + super + listeners[:on_instance_variable_target_node_leave]&.each { |listener| listener.on_instance_variable_target_node_leave(node) } + end + + # Dispatch enter and leave events for InstanceVariableWriteNode nodes and continue + # walking the tree. + def visit_instance_variable_write_node(node) + listeners[:on_instance_variable_write_node_enter]&.each { |listener| listener.on_instance_variable_write_node_enter(node) } + super + listeners[:on_instance_variable_write_node_leave]&.each { |listener| listener.on_instance_variable_write_node_leave(node) } + end + + # Dispatch enter and leave events for IntegerNode nodes and continue + # walking the tree. + def visit_integer_node(node) + listeners[:on_integer_node_enter]&.each { |listener| listener.on_integer_node_enter(node) } + super + listeners[:on_integer_node_leave]&.each { |listener| listener.on_integer_node_leave(node) } + end + + # Dispatch enter and leave events for InterpolatedMatchLastLineNode nodes and continue + # walking the tree. + def visit_interpolated_match_last_line_node(node) + listeners[:on_interpolated_match_last_line_node_enter]&.each { |listener| listener.on_interpolated_match_last_line_node_enter(node) } + super + listeners[:on_interpolated_match_last_line_node_leave]&.each { |listener| listener.on_interpolated_match_last_line_node_leave(node) } + end + + # Dispatch enter and leave events for InterpolatedRegularExpressionNode nodes and continue + # walking the tree. + def visit_interpolated_regular_expression_node(node) + listeners[:on_interpolated_regular_expression_node_enter]&.each { |listener| listener.on_interpolated_regular_expression_node_enter(node) } + super + listeners[:on_interpolated_regular_expression_node_leave]&.each { |listener| listener.on_interpolated_regular_expression_node_leave(node) } + end + + # Dispatch enter and leave events for InterpolatedStringNode nodes and continue + # walking the tree. + def visit_interpolated_string_node(node) + listeners[:on_interpolated_string_node_enter]&.each { |listener| listener.on_interpolated_string_node_enter(node) } + super + listeners[:on_interpolated_string_node_leave]&.each { |listener| listener.on_interpolated_string_node_leave(node) } + end + + # Dispatch enter and leave events for InterpolatedSymbolNode nodes and continue + # walking the tree. + def visit_interpolated_symbol_node(node) + listeners[:on_interpolated_symbol_node_enter]&.each { |listener| listener.on_interpolated_symbol_node_enter(node) } + super + listeners[:on_interpolated_symbol_node_leave]&.each { |listener| listener.on_interpolated_symbol_node_leave(node) } + end + + # Dispatch enter and leave events for InterpolatedXStringNode nodes and continue + # walking the tree. + def visit_interpolated_x_string_node(node) + listeners[:on_interpolated_x_string_node_enter]&.each { |listener| listener.on_interpolated_x_string_node_enter(node) } + super + listeners[:on_interpolated_x_string_node_leave]&.each { |listener| listener.on_interpolated_x_string_node_leave(node) } + end + + # Dispatch enter and leave events for ItLocalVariableReadNode nodes and continue + # walking the tree. + def visit_it_local_variable_read_node(node) + listeners[:on_it_local_variable_read_node_enter]&.each { |listener| listener.on_it_local_variable_read_node_enter(node) } + super + listeners[:on_it_local_variable_read_node_leave]&.each { |listener| listener.on_it_local_variable_read_node_leave(node) } + end + + # Dispatch enter and leave events for ItParametersNode nodes and continue + # walking the tree. + def visit_it_parameters_node(node) + listeners[:on_it_parameters_node_enter]&.each { |listener| listener.on_it_parameters_node_enter(node) } + super + listeners[:on_it_parameters_node_leave]&.each { |listener| listener.on_it_parameters_node_leave(node) } + end + + # Dispatch enter and leave events for KeywordHashNode nodes and continue + # walking the tree. + def visit_keyword_hash_node(node) + listeners[:on_keyword_hash_node_enter]&.each { |listener| listener.on_keyword_hash_node_enter(node) } + super + listeners[:on_keyword_hash_node_leave]&.each { |listener| listener.on_keyword_hash_node_leave(node) } + end + + # Dispatch enter and leave events for KeywordRestParameterNode nodes and continue + # walking the tree. + def visit_keyword_rest_parameter_node(node) + listeners[:on_keyword_rest_parameter_node_enter]&.each { |listener| listener.on_keyword_rest_parameter_node_enter(node) } + super + listeners[:on_keyword_rest_parameter_node_leave]&.each { |listener| listener.on_keyword_rest_parameter_node_leave(node) } + end + + # Dispatch enter and leave events for LambdaNode nodes and continue + # walking the tree. + def visit_lambda_node(node) + listeners[:on_lambda_node_enter]&.each { |listener| listener.on_lambda_node_enter(node) } + super + listeners[:on_lambda_node_leave]&.each { |listener| listener.on_lambda_node_leave(node) } + end + + # Dispatch enter and leave events for LocalVariableAndWriteNode nodes and continue + # walking the tree. + def visit_local_variable_and_write_node(node) + listeners[:on_local_variable_and_write_node_enter]&.each { |listener| listener.on_local_variable_and_write_node_enter(node) } + super + listeners[:on_local_variable_and_write_node_leave]&.each { |listener| listener.on_local_variable_and_write_node_leave(node) } + end + + # Dispatch enter and leave events for LocalVariableOperatorWriteNode nodes and continue + # walking the tree. + def visit_local_variable_operator_write_node(node) + listeners[:on_local_variable_operator_write_node_enter]&.each { |listener| listener.on_local_variable_operator_write_node_enter(node) } + super + listeners[:on_local_variable_operator_write_node_leave]&.each { |listener| listener.on_local_variable_operator_write_node_leave(node) } + end + + # Dispatch enter and leave events for LocalVariableOrWriteNode nodes and continue + # walking the tree. + def visit_local_variable_or_write_node(node) + listeners[:on_local_variable_or_write_node_enter]&.each { |listener| listener.on_local_variable_or_write_node_enter(node) } + super + listeners[:on_local_variable_or_write_node_leave]&.each { |listener| listener.on_local_variable_or_write_node_leave(node) } + end + + # Dispatch enter and leave events for LocalVariableReadNode nodes and continue + # walking the tree. + def visit_local_variable_read_node(node) + listeners[:on_local_variable_read_node_enter]&.each { |listener| listener.on_local_variable_read_node_enter(node) } + super + listeners[:on_local_variable_read_node_leave]&.each { |listener| listener.on_local_variable_read_node_leave(node) } + end + + # Dispatch enter and leave events for LocalVariableTargetNode nodes and continue + # walking the tree. + def visit_local_variable_target_node(node) + listeners[:on_local_variable_target_node_enter]&.each { |listener| listener.on_local_variable_target_node_enter(node) } + super + listeners[:on_local_variable_target_node_leave]&.each { |listener| listener.on_local_variable_target_node_leave(node) } + end + + # Dispatch enter and leave events for LocalVariableWriteNode nodes and continue + # walking the tree. + def visit_local_variable_write_node(node) + listeners[:on_local_variable_write_node_enter]&.each { |listener| listener.on_local_variable_write_node_enter(node) } + super + listeners[:on_local_variable_write_node_leave]&.each { |listener| listener.on_local_variable_write_node_leave(node) } + end + + # Dispatch enter and leave events for MatchLastLineNode nodes and continue + # walking the tree. + def visit_match_last_line_node(node) + listeners[:on_match_last_line_node_enter]&.each { |listener| listener.on_match_last_line_node_enter(node) } + super + listeners[:on_match_last_line_node_leave]&.each { |listener| listener.on_match_last_line_node_leave(node) } + end + + # Dispatch enter and leave events for MatchPredicateNode nodes and continue + # walking the tree. + def visit_match_predicate_node(node) + listeners[:on_match_predicate_node_enter]&.each { |listener| listener.on_match_predicate_node_enter(node) } + super + listeners[:on_match_predicate_node_leave]&.each { |listener| listener.on_match_predicate_node_leave(node) } + end + + # Dispatch enter and leave events for MatchRequiredNode nodes and continue + # walking the tree. + def visit_match_required_node(node) + listeners[:on_match_required_node_enter]&.each { |listener| listener.on_match_required_node_enter(node) } + super + listeners[:on_match_required_node_leave]&.each { |listener| listener.on_match_required_node_leave(node) } + end + + # Dispatch enter and leave events for MatchWriteNode nodes and continue + # walking the tree. + def visit_match_write_node(node) + listeners[:on_match_write_node_enter]&.each { |listener| listener.on_match_write_node_enter(node) } + super + listeners[:on_match_write_node_leave]&.each { |listener| listener.on_match_write_node_leave(node) } + end + + # Dispatch enter and leave events for MissingNode nodes and continue + # walking the tree. + def visit_missing_node(node) + listeners[:on_missing_node_enter]&.each { |listener| listener.on_missing_node_enter(node) } + super + listeners[:on_missing_node_leave]&.each { |listener| listener.on_missing_node_leave(node) } + end + + # Dispatch enter and leave events for ModuleNode nodes and continue + # walking the tree. + def visit_module_node(node) + listeners[:on_module_node_enter]&.each { |listener| listener.on_module_node_enter(node) } + super + listeners[:on_module_node_leave]&.each { |listener| listener.on_module_node_leave(node) } + end + + # Dispatch enter and leave events for MultiTargetNode nodes and continue + # walking the tree. + def visit_multi_target_node(node) + listeners[:on_multi_target_node_enter]&.each { |listener| listener.on_multi_target_node_enter(node) } + super + listeners[:on_multi_target_node_leave]&.each { |listener| listener.on_multi_target_node_leave(node) } + end + + # Dispatch enter and leave events for MultiWriteNode nodes and continue + # walking the tree. + def visit_multi_write_node(node) + listeners[:on_multi_write_node_enter]&.each { |listener| listener.on_multi_write_node_enter(node) } + super + listeners[:on_multi_write_node_leave]&.each { |listener| listener.on_multi_write_node_leave(node) } + end + + # Dispatch enter and leave events for NextNode nodes and continue + # walking the tree. + def visit_next_node(node) + listeners[:on_next_node_enter]&.each { |listener| listener.on_next_node_enter(node) } + super + listeners[:on_next_node_leave]&.each { |listener| listener.on_next_node_leave(node) } + end + + # Dispatch enter and leave events for NilNode nodes and continue + # walking the tree. + def visit_nil_node(node) + listeners[:on_nil_node_enter]&.each { |listener| listener.on_nil_node_enter(node) } + super + listeners[:on_nil_node_leave]&.each { |listener| listener.on_nil_node_leave(node) } + end + + # Dispatch enter and leave events for NoKeywordsParameterNode nodes and continue + # walking the tree. + def visit_no_keywords_parameter_node(node) + listeners[:on_no_keywords_parameter_node_enter]&.each { |listener| listener.on_no_keywords_parameter_node_enter(node) } + super + listeners[:on_no_keywords_parameter_node_leave]&.each { |listener| listener.on_no_keywords_parameter_node_leave(node) } + end + + # Dispatch enter and leave events for NumberedParametersNode nodes and continue + # walking the tree. + def visit_numbered_parameters_node(node) + listeners[:on_numbered_parameters_node_enter]&.each { |listener| listener.on_numbered_parameters_node_enter(node) } + super + listeners[:on_numbered_parameters_node_leave]&.each { |listener| listener.on_numbered_parameters_node_leave(node) } + end + + # Dispatch enter and leave events for NumberedReferenceReadNode nodes and continue + # walking the tree. + def visit_numbered_reference_read_node(node) + listeners[:on_numbered_reference_read_node_enter]&.each { |listener| listener.on_numbered_reference_read_node_enter(node) } + super + listeners[:on_numbered_reference_read_node_leave]&.each { |listener| listener.on_numbered_reference_read_node_leave(node) } + end + + # Dispatch enter and leave events for OptionalKeywordParameterNode nodes and continue + # walking the tree. + def visit_optional_keyword_parameter_node(node) + listeners[:on_optional_keyword_parameter_node_enter]&.each { |listener| listener.on_optional_keyword_parameter_node_enter(node) } + super + listeners[:on_optional_keyword_parameter_node_leave]&.each { |listener| listener.on_optional_keyword_parameter_node_leave(node) } + end + + # Dispatch enter and leave events for OptionalParameterNode nodes and continue + # walking the tree. + def visit_optional_parameter_node(node) + listeners[:on_optional_parameter_node_enter]&.each { |listener| listener.on_optional_parameter_node_enter(node) } + super + listeners[:on_optional_parameter_node_leave]&.each { |listener| listener.on_optional_parameter_node_leave(node) } + end + + # Dispatch enter and leave events for OrNode nodes and continue + # walking the tree. + def visit_or_node(node) + listeners[:on_or_node_enter]&.each { |listener| listener.on_or_node_enter(node) } + super + listeners[:on_or_node_leave]&.each { |listener| listener.on_or_node_leave(node) } + end + + # Dispatch enter and leave events for ParametersNode nodes and continue + # walking the tree. + def visit_parameters_node(node) + listeners[:on_parameters_node_enter]&.each { |listener| listener.on_parameters_node_enter(node) } + super + listeners[:on_parameters_node_leave]&.each { |listener| listener.on_parameters_node_leave(node) } + end + + # Dispatch enter and leave events for ParenthesesNode nodes and continue + # walking the tree. + def visit_parentheses_node(node) + listeners[:on_parentheses_node_enter]&.each { |listener| listener.on_parentheses_node_enter(node) } + super + listeners[:on_parentheses_node_leave]&.each { |listener| listener.on_parentheses_node_leave(node) } + end + + # Dispatch enter and leave events for PinnedExpressionNode nodes and continue + # walking the tree. + def visit_pinned_expression_node(node) + listeners[:on_pinned_expression_node_enter]&.each { |listener| listener.on_pinned_expression_node_enter(node) } + super + listeners[:on_pinned_expression_node_leave]&.each { |listener| listener.on_pinned_expression_node_leave(node) } + end + + # Dispatch enter and leave events for PinnedVariableNode nodes and continue + # walking the tree. + def visit_pinned_variable_node(node) + listeners[:on_pinned_variable_node_enter]&.each { |listener| listener.on_pinned_variable_node_enter(node) } + super + listeners[:on_pinned_variable_node_leave]&.each { |listener| listener.on_pinned_variable_node_leave(node) } + end + + # Dispatch enter and leave events for PostExecutionNode nodes and continue + # walking the tree. + def visit_post_execution_node(node) + listeners[:on_post_execution_node_enter]&.each { |listener| listener.on_post_execution_node_enter(node) } + super + listeners[:on_post_execution_node_leave]&.each { |listener| listener.on_post_execution_node_leave(node) } + end + + # Dispatch enter and leave events for PreExecutionNode nodes and continue + # walking the tree. + def visit_pre_execution_node(node) + listeners[:on_pre_execution_node_enter]&.each { |listener| listener.on_pre_execution_node_enter(node) } + super + listeners[:on_pre_execution_node_leave]&.each { |listener| listener.on_pre_execution_node_leave(node) } + end + + # Dispatch enter and leave events for ProgramNode nodes and continue + # walking the tree. + def visit_program_node(node) + listeners[:on_program_node_enter]&.each { |listener| listener.on_program_node_enter(node) } + super + listeners[:on_program_node_leave]&.each { |listener| listener.on_program_node_leave(node) } + end + + # Dispatch enter and leave events for RangeNode nodes and continue + # walking the tree. + def visit_range_node(node) + listeners[:on_range_node_enter]&.each { |listener| listener.on_range_node_enter(node) } + super + listeners[:on_range_node_leave]&.each { |listener| listener.on_range_node_leave(node) } + end + + # Dispatch enter and leave events for RationalNode nodes and continue + # walking the tree. + def visit_rational_node(node) + listeners[:on_rational_node_enter]&.each { |listener| listener.on_rational_node_enter(node) } + super + listeners[:on_rational_node_leave]&.each { |listener| listener.on_rational_node_leave(node) } + end + + # Dispatch enter and leave events for RedoNode nodes and continue + # walking the tree. + def visit_redo_node(node) + listeners[:on_redo_node_enter]&.each { |listener| listener.on_redo_node_enter(node) } + super + listeners[:on_redo_node_leave]&.each { |listener| listener.on_redo_node_leave(node) } + end + + # Dispatch enter and leave events for RegularExpressionNode nodes and continue + # walking the tree. + def visit_regular_expression_node(node) + listeners[:on_regular_expression_node_enter]&.each { |listener| listener.on_regular_expression_node_enter(node) } + super + listeners[:on_regular_expression_node_leave]&.each { |listener| listener.on_regular_expression_node_leave(node) } + end + + # Dispatch enter and leave events for RequiredKeywordParameterNode nodes and continue + # walking the tree. + def visit_required_keyword_parameter_node(node) + listeners[:on_required_keyword_parameter_node_enter]&.each { |listener| listener.on_required_keyword_parameter_node_enter(node) } + super + listeners[:on_required_keyword_parameter_node_leave]&.each { |listener| listener.on_required_keyword_parameter_node_leave(node) } + end + + # Dispatch enter and leave events for RequiredParameterNode nodes and continue + # walking the tree. + def visit_required_parameter_node(node) + listeners[:on_required_parameter_node_enter]&.each { |listener| listener.on_required_parameter_node_enter(node) } + super + listeners[:on_required_parameter_node_leave]&.each { |listener| listener.on_required_parameter_node_leave(node) } + end + + # Dispatch enter and leave events for RescueModifierNode nodes and continue + # walking the tree. + def visit_rescue_modifier_node(node) + listeners[:on_rescue_modifier_node_enter]&.each { |listener| listener.on_rescue_modifier_node_enter(node) } + super + listeners[:on_rescue_modifier_node_leave]&.each { |listener| listener.on_rescue_modifier_node_leave(node) } + end + + # Dispatch enter and leave events for RescueNode nodes and continue + # walking the tree. + def visit_rescue_node(node) + listeners[:on_rescue_node_enter]&.each { |listener| listener.on_rescue_node_enter(node) } + super + listeners[:on_rescue_node_leave]&.each { |listener| listener.on_rescue_node_leave(node) } + end + + # Dispatch enter and leave events for RestParameterNode nodes and continue + # walking the tree. + def visit_rest_parameter_node(node) + listeners[:on_rest_parameter_node_enter]&.each { |listener| listener.on_rest_parameter_node_enter(node) } + super + listeners[:on_rest_parameter_node_leave]&.each { |listener| listener.on_rest_parameter_node_leave(node) } + end + + # Dispatch enter and leave events for RetryNode nodes and continue + # walking the tree. + def visit_retry_node(node) + listeners[:on_retry_node_enter]&.each { |listener| listener.on_retry_node_enter(node) } + super + listeners[:on_retry_node_leave]&.each { |listener| listener.on_retry_node_leave(node) } + end + + # Dispatch enter and leave events for ReturnNode nodes and continue + # walking the tree. + def visit_return_node(node) + listeners[:on_return_node_enter]&.each { |listener| listener.on_return_node_enter(node) } + super + listeners[:on_return_node_leave]&.each { |listener| listener.on_return_node_leave(node) } + end + + # Dispatch enter and leave events for SelfNode nodes and continue + # walking the tree. + def visit_self_node(node) + listeners[:on_self_node_enter]&.each { |listener| listener.on_self_node_enter(node) } + super + listeners[:on_self_node_leave]&.each { |listener| listener.on_self_node_leave(node) } + end + + # Dispatch enter and leave events for ShareableConstantNode nodes and continue + # walking the tree. + def visit_shareable_constant_node(node) + listeners[:on_shareable_constant_node_enter]&.each { |listener| listener.on_shareable_constant_node_enter(node) } + super + listeners[:on_shareable_constant_node_leave]&.each { |listener| listener.on_shareable_constant_node_leave(node) } + end + + # Dispatch enter and leave events for SingletonClassNode nodes and continue + # walking the tree. + def visit_singleton_class_node(node) + listeners[:on_singleton_class_node_enter]&.each { |listener| listener.on_singleton_class_node_enter(node) } + super + listeners[:on_singleton_class_node_leave]&.each { |listener| listener.on_singleton_class_node_leave(node) } + end + + # Dispatch enter and leave events for SourceEncodingNode nodes and continue + # walking the tree. + def visit_source_encoding_node(node) + listeners[:on_source_encoding_node_enter]&.each { |listener| listener.on_source_encoding_node_enter(node) } + super + listeners[:on_source_encoding_node_leave]&.each { |listener| listener.on_source_encoding_node_leave(node) } + end + + # Dispatch enter and leave events for SourceFileNode nodes and continue + # walking the tree. + def visit_source_file_node(node) + listeners[:on_source_file_node_enter]&.each { |listener| listener.on_source_file_node_enter(node) } + super + listeners[:on_source_file_node_leave]&.each { |listener| listener.on_source_file_node_leave(node) } + end + + # Dispatch enter and leave events for SourceLineNode nodes and continue + # walking the tree. + def visit_source_line_node(node) + listeners[:on_source_line_node_enter]&.each { |listener| listener.on_source_line_node_enter(node) } + super + listeners[:on_source_line_node_leave]&.each { |listener| listener.on_source_line_node_leave(node) } + end + + # Dispatch enter and leave events for SplatNode nodes and continue + # walking the tree. + def visit_splat_node(node) + listeners[:on_splat_node_enter]&.each { |listener| listener.on_splat_node_enter(node) } + super + listeners[:on_splat_node_leave]&.each { |listener| listener.on_splat_node_leave(node) } + end + + # Dispatch enter and leave events for StatementsNode nodes and continue + # walking the tree. + def visit_statements_node(node) + listeners[:on_statements_node_enter]&.each { |listener| listener.on_statements_node_enter(node) } + super + listeners[:on_statements_node_leave]&.each { |listener| listener.on_statements_node_leave(node) } + end + + # Dispatch enter and leave events for StringNode nodes and continue + # walking the tree. + def visit_string_node(node) + listeners[:on_string_node_enter]&.each { |listener| listener.on_string_node_enter(node) } + super + listeners[:on_string_node_leave]&.each { |listener| listener.on_string_node_leave(node) } + end + + # Dispatch enter and leave events for SuperNode nodes and continue + # walking the tree. + def visit_super_node(node) + listeners[:on_super_node_enter]&.each { |listener| listener.on_super_node_enter(node) } + super + listeners[:on_super_node_leave]&.each { |listener| listener.on_super_node_leave(node) } + end + + # Dispatch enter and leave events for SymbolNode nodes and continue + # walking the tree. + def visit_symbol_node(node) + listeners[:on_symbol_node_enter]&.each { |listener| listener.on_symbol_node_enter(node) } + super + listeners[:on_symbol_node_leave]&.each { |listener| listener.on_symbol_node_leave(node) } + end + + # Dispatch enter and leave events for TrueNode nodes and continue + # walking the tree. + def visit_true_node(node) + listeners[:on_true_node_enter]&.each { |listener| listener.on_true_node_enter(node) } + super + listeners[:on_true_node_leave]&.each { |listener| listener.on_true_node_leave(node) } + end + + # Dispatch enter and leave events for UndefNode nodes and continue + # walking the tree. + def visit_undef_node(node) + listeners[:on_undef_node_enter]&.each { |listener| listener.on_undef_node_enter(node) } + super + listeners[:on_undef_node_leave]&.each { |listener| listener.on_undef_node_leave(node) } + end + + # Dispatch enter and leave events for UnlessNode nodes and continue + # walking the tree. + def visit_unless_node(node) + listeners[:on_unless_node_enter]&.each { |listener| listener.on_unless_node_enter(node) } + super + listeners[:on_unless_node_leave]&.each { |listener| listener.on_unless_node_leave(node) } + end + + # Dispatch enter and leave events for UntilNode nodes and continue + # walking the tree. + def visit_until_node(node) + listeners[:on_until_node_enter]&.each { |listener| listener.on_until_node_enter(node) } + super + listeners[:on_until_node_leave]&.each { |listener| listener.on_until_node_leave(node) } + end + + # Dispatch enter and leave events for WhenNode nodes and continue + # walking the tree. + def visit_when_node(node) + listeners[:on_when_node_enter]&.each { |listener| listener.on_when_node_enter(node) } + super + listeners[:on_when_node_leave]&.each { |listener| listener.on_when_node_leave(node) } + end + + # Dispatch enter and leave events for WhileNode nodes and continue + # walking the tree. + def visit_while_node(node) + listeners[:on_while_node_enter]&.each { |listener| listener.on_while_node_enter(node) } + super + listeners[:on_while_node_leave]&.each { |listener| listener.on_while_node_leave(node) } + end + + # Dispatch enter and leave events for XStringNode nodes and continue + # walking the tree. + def visit_x_string_node(node) + listeners[:on_x_string_node_enter]&.each { |listener| listener.on_x_string_node_enter(node) } + super + listeners[:on_x_string_node_leave]&.each { |listener| listener.on_x_string_node_leave(node) } + end + + # Dispatch enter and leave events for YieldNode nodes and continue + # walking the tree. + def visit_yield_node(node) + listeners[:on_yield_node_enter]&.each { |listener| listener.on_yield_node_enter(node) } + super + listeners[:on_yield_node_leave]&.each { |listener| listener.on_yield_node_leave(node) } + end + + class DispatchOnce < Visitor # :nodoc: + attr_reader :listeners + + def initialize(listeners) + @listeners = listeners + end + + # Dispatch enter and leave events for AliasGlobalVariableNode nodes. + def visit_alias_global_variable_node(node) + listeners[:on_alias_global_variable_node_enter]&.each { |listener| listener.on_alias_global_variable_node_enter(node) } + listeners[:on_alias_global_variable_node_leave]&.each { |listener| listener.on_alias_global_variable_node_leave(node) } + end + + # Dispatch enter and leave events for AliasMethodNode nodes. + def visit_alias_method_node(node) + listeners[:on_alias_method_node_enter]&.each { |listener| listener.on_alias_method_node_enter(node) } + listeners[:on_alias_method_node_leave]&.each { |listener| listener.on_alias_method_node_leave(node) } + end + + # Dispatch enter and leave events for AlternationPatternNode nodes. + def visit_alternation_pattern_node(node) + listeners[:on_alternation_pattern_node_enter]&.each { |listener| listener.on_alternation_pattern_node_enter(node) } + listeners[:on_alternation_pattern_node_leave]&.each { |listener| listener.on_alternation_pattern_node_leave(node) } + end + + # Dispatch enter and leave events for AndNode nodes. + def visit_and_node(node) + listeners[:on_and_node_enter]&.each { |listener| listener.on_and_node_enter(node) } + listeners[:on_and_node_leave]&.each { |listener| listener.on_and_node_leave(node) } + end + + # Dispatch enter and leave events for ArgumentsNode nodes. + def visit_arguments_node(node) + listeners[:on_arguments_node_enter]&.each { |listener| listener.on_arguments_node_enter(node) } + listeners[:on_arguments_node_leave]&.each { |listener| listener.on_arguments_node_leave(node) } + end + + # Dispatch enter and leave events for ArrayNode nodes. + def visit_array_node(node) + listeners[:on_array_node_enter]&.each { |listener| listener.on_array_node_enter(node) } + listeners[:on_array_node_leave]&.each { |listener| listener.on_array_node_leave(node) } + end + + # Dispatch enter and leave events for ArrayPatternNode nodes. + def visit_array_pattern_node(node) + listeners[:on_array_pattern_node_enter]&.each { |listener| listener.on_array_pattern_node_enter(node) } + listeners[:on_array_pattern_node_leave]&.each { |listener| listener.on_array_pattern_node_leave(node) } + end + + # Dispatch enter and leave events for AssocNode nodes. + def visit_assoc_node(node) + listeners[:on_assoc_node_enter]&.each { |listener| listener.on_assoc_node_enter(node) } + listeners[:on_assoc_node_leave]&.each { |listener| listener.on_assoc_node_leave(node) } + end + + # Dispatch enter and leave events for AssocSplatNode nodes. + def visit_assoc_splat_node(node) + listeners[:on_assoc_splat_node_enter]&.each { |listener| listener.on_assoc_splat_node_enter(node) } + listeners[:on_assoc_splat_node_leave]&.each { |listener| listener.on_assoc_splat_node_leave(node) } + end + + # Dispatch enter and leave events for BackReferenceReadNode nodes. + def visit_back_reference_read_node(node) + listeners[:on_back_reference_read_node_enter]&.each { |listener| listener.on_back_reference_read_node_enter(node) } + listeners[:on_back_reference_read_node_leave]&.each { |listener| listener.on_back_reference_read_node_leave(node) } + end + + # Dispatch enter and leave events for BeginNode nodes. + def visit_begin_node(node) + listeners[:on_begin_node_enter]&.each { |listener| listener.on_begin_node_enter(node) } + listeners[:on_begin_node_leave]&.each { |listener| listener.on_begin_node_leave(node) } + end + + # Dispatch enter and leave events for BlockArgumentNode nodes. + def visit_block_argument_node(node) + listeners[:on_block_argument_node_enter]&.each { |listener| listener.on_block_argument_node_enter(node) } + listeners[:on_block_argument_node_leave]&.each { |listener| listener.on_block_argument_node_leave(node) } + end + + # Dispatch enter and leave events for BlockLocalVariableNode nodes. + def visit_block_local_variable_node(node) + listeners[:on_block_local_variable_node_enter]&.each { |listener| listener.on_block_local_variable_node_enter(node) } + listeners[:on_block_local_variable_node_leave]&.each { |listener| listener.on_block_local_variable_node_leave(node) } + end + + # Dispatch enter and leave events for BlockNode nodes. + def visit_block_node(node) + listeners[:on_block_node_enter]&.each { |listener| listener.on_block_node_enter(node) } + listeners[:on_block_node_leave]&.each { |listener| listener.on_block_node_leave(node) } + end + + # Dispatch enter and leave events for BlockParameterNode nodes. + def visit_block_parameter_node(node) + listeners[:on_block_parameter_node_enter]&.each { |listener| listener.on_block_parameter_node_enter(node) } + listeners[:on_block_parameter_node_leave]&.each { |listener| listener.on_block_parameter_node_leave(node) } + end + + # Dispatch enter and leave events for BlockParametersNode nodes. + def visit_block_parameters_node(node) + listeners[:on_block_parameters_node_enter]&.each { |listener| listener.on_block_parameters_node_enter(node) } + listeners[:on_block_parameters_node_leave]&.each { |listener| listener.on_block_parameters_node_leave(node) } + end + + # Dispatch enter and leave events for BreakNode nodes. + def visit_break_node(node) + listeners[:on_break_node_enter]&.each { |listener| listener.on_break_node_enter(node) } + listeners[:on_break_node_leave]&.each { |listener| listener.on_break_node_leave(node) } + end + + # Dispatch enter and leave events for CallAndWriteNode nodes. + def visit_call_and_write_node(node) + listeners[:on_call_and_write_node_enter]&.each { |listener| listener.on_call_and_write_node_enter(node) } + listeners[:on_call_and_write_node_leave]&.each { |listener| listener.on_call_and_write_node_leave(node) } + end + + # Dispatch enter and leave events for CallNode nodes. + def visit_call_node(node) + listeners[:on_call_node_enter]&.each { |listener| listener.on_call_node_enter(node) } + listeners[:on_call_node_leave]&.each { |listener| listener.on_call_node_leave(node) } + end + + # Dispatch enter and leave events for CallOperatorWriteNode nodes. + def visit_call_operator_write_node(node) + listeners[:on_call_operator_write_node_enter]&.each { |listener| listener.on_call_operator_write_node_enter(node) } + listeners[:on_call_operator_write_node_leave]&.each { |listener| listener.on_call_operator_write_node_leave(node) } + end + + # Dispatch enter and leave events for CallOrWriteNode nodes. + def visit_call_or_write_node(node) + listeners[:on_call_or_write_node_enter]&.each { |listener| listener.on_call_or_write_node_enter(node) } + listeners[:on_call_or_write_node_leave]&.each { |listener| listener.on_call_or_write_node_leave(node) } + end + + # Dispatch enter and leave events for CallTargetNode nodes. + def visit_call_target_node(node) + listeners[:on_call_target_node_enter]&.each { |listener| listener.on_call_target_node_enter(node) } + listeners[:on_call_target_node_leave]&.each { |listener| listener.on_call_target_node_leave(node) } + end + + # Dispatch enter and leave events for CapturePatternNode nodes. + def visit_capture_pattern_node(node) + listeners[:on_capture_pattern_node_enter]&.each { |listener| listener.on_capture_pattern_node_enter(node) } + listeners[:on_capture_pattern_node_leave]&.each { |listener| listener.on_capture_pattern_node_leave(node) } + end + + # Dispatch enter and leave events for CaseMatchNode nodes. + def visit_case_match_node(node) + listeners[:on_case_match_node_enter]&.each { |listener| listener.on_case_match_node_enter(node) } + listeners[:on_case_match_node_leave]&.each { |listener| listener.on_case_match_node_leave(node) } + end + + # Dispatch enter and leave events for CaseNode nodes. + def visit_case_node(node) + listeners[:on_case_node_enter]&.each { |listener| listener.on_case_node_enter(node) } + listeners[:on_case_node_leave]&.each { |listener| listener.on_case_node_leave(node) } + end + + # Dispatch enter and leave events for ClassNode nodes. + def visit_class_node(node) + listeners[:on_class_node_enter]&.each { |listener| listener.on_class_node_enter(node) } + listeners[:on_class_node_leave]&.each { |listener| listener.on_class_node_leave(node) } + end + + # Dispatch enter and leave events for ClassVariableAndWriteNode nodes. + def visit_class_variable_and_write_node(node) + listeners[:on_class_variable_and_write_node_enter]&.each { |listener| listener.on_class_variable_and_write_node_enter(node) } + listeners[:on_class_variable_and_write_node_leave]&.each { |listener| listener.on_class_variable_and_write_node_leave(node) } + end + + # Dispatch enter and leave events for ClassVariableOperatorWriteNode nodes. + def visit_class_variable_operator_write_node(node) + listeners[:on_class_variable_operator_write_node_enter]&.each { |listener| listener.on_class_variable_operator_write_node_enter(node) } + listeners[:on_class_variable_operator_write_node_leave]&.each { |listener| listener.on_class_variable_operator_write_node_leave(node) } + end + + # Dispatch enter and leave events for ClassVariableOrWriteNode nodes. + def visit_class_variable_or_write_node(node) + listeners[:on_class_variable_or_write_node_enter]&.each { |listener| listener.on_class_variable_or_write_node_enter(node) } + listeners[:on_class_variable_or_write_node_leave]&.each { |listener| listener.on_class_variable_or_write_node_leave(node) } + end + + # Dispatch enter and leave events for ClassVariableReadNode nodes. + def visit_class_variable_read_node(node) + listeners[:on_class_variable_read_node_enter]&.each { |listener| listener.on_class_variable_read_node_enter(node) } + listeners[:on_class_variable_read_node_leave]&.each { |listener| listener.on_class_variable_read_node_leave(node) } + end + + # Dispatch enter and leave events for ClassVariableTargetNode nodes. + def visit_class_variable_target_node(node) + listeners[:on_class_variable_target_node_enter]&.each { |listener| listener.on_class_variable_target_node_enter(node) } + listeners[:on_class_variable_target_node_leave]&.each { |listener| listener.on_class_variable_target_node_leave(node) } + end + + # Dispatch enter and leave events for ClassVariableWriteNode nodes. + def visit_class_variable_write_node(node) + listeners[:on_class_variable_write_node_enter]&.each { |listener| listener.on_class_variable_write_node_enter(node) } + listeners[:on_class_variable_write_node_leave]&.each { |listener| listener.on_class_variable_write_node_leave(node) } + end + + # Dispatch enter and leave events for ConstantAndWriteNode nodes. + def visit_constant_and_write_node(node) + listeners[:on_constant_and_write_node_enter]&.each { |listener| listener.on_constant_and_write_node_enter(node) } + listeners[:on_constant_and_write_node_leave]&.each { |listener| listener.on_constant_and_write_node_leave(node) } + end + + # Dispatch enter and leave events for ConstantOperatorWriteNode nodes. + def visit_constant_operator_write_node(node) + listeners[:on_constant_operator_write_node_enter]&.each { |listener| listener.on_constant_operator_write_node_enter(node) } + listeners[:on_constant_operator_write_node_leave]&.each { |listener| listener.on_constant_operator_write_node_leave(node) } + end + + # Dispatch enter and leave events for ConstantOrWriteNode nodes. + def visit_constant_or_write_node(node) + listeners[:on_constant_or_write_node_enter]&.each { |listener| listener.on_constant_or_write_node_enter(node) } + listeners[:on_constant_or_write_node_leave]&.each { |listener| listener.on_constant_or_write_node_leave(node) } + end + + # Dispatch enter and leave events for ConstantPathAndWriteNode nodes. + def visit_constant_path_and_write_node(node) + listeners[:on_constant_path_and_write_node_enter]&.each { |listener| listener.on_constant_path_and_write_node_enter(node) } + listeners[:on_constant_path_and_write_node_leave]&.each { |listener| listener.on_constant_path_and_write_node_leave(node) } + end + + # Dispatch enter and leave events for ConstantPathNode nodes. + def visit_constant_path_node(node) + listeners[:on_constant_path_node_enter]&.each { |listener| listener.on_constant_path_node_enter(node) } + listeners[:on_constant_path_node_leave]&.each { |listener| listener.on_constant_path_node_leave(node) } + end + + # Dispatch enter and leave events for ConstantPathOperatorWriteNode nodes. + def visit_constant_path_operator_write_node(node) + listeners[:on_constant_path_operator_write_node_enter]&.each { |listener| listener.on_constant_path_operator_write_node_enter(node) } + listeners[:on_constant_path_operator_write_node_leave]&.each { |listener| listener.on_constant_path_operator_write_node_leave(node) } + end + + # Dispatch enter and leave events for ConstantPathOrWriteNode nodes. + def visit_constant_path_or_write_node(node) + listeners[:on_constant_path_or_write_node_enter]&.each { |listener| listener.on_constant_path_or_write_node_enter(node) } + listeners[:on_constant_path_or_write_node_leave]&.each { |listener| listener.on_constant_path_or_write_node_leave(node) } + end + + # Dispatch enter and leave events for ConstantPathTargetNode nodes. + def visit_constant_path_target_node(node) + listeners[:on_constant_path_target_node_enter]&.each { |listener| listener.on_constant_path_target_node_enter(node) } + listeners[:on_constant_path_target_node_leave]&.each { |listener| listener.on_constant_path_target_node_leave(node) } + end + + # Dispatch enter and leave events for ConstantPathWriteNode nodes. + def visit_constant_path_write_node(node) + listeners[:on_constant_path_write_node_enter]&.each { |listener| listener.on_constant_path_write_node_enter(node) } + listeners[:on_constant_path_write_node_leave]&.each { |listener| listener.on_constant_path_write_node_leave(node) } + end + + # Dispatch enter and leave events for ConstantReadNode nodes. + def visit_constant_read_node(node) + listeners[:on_constant_read_node_enter]&.each { |listener| listener.on_constant_read_node_enter(node) } + listeners[:on_constant_read_node_leave]&.each { |listener| listener.on_constant_read_node_leave(node) } + end + + # Dispatch enter and leave events for ConstantTargetNode nodes. + def visit_constant_target_node(node) + listeners[:on_constant_target_node_enter]&.each { |listener| listener.on_constant_target_node_enter(node) } + listeners[:on_constant_target_node_leave]&.each { |listener| listener.on_constant_target_node_leave(node) } + end + + # Dispatch enter and leave events for ConstantWriteNode nodes. + def visit_constant_write_node(node) + listeners[:on_constant_write_node_enter]&.each { |listener| listener.on_constant_write_node_enter(node) } + listeners[:on_constant_write_node_leave]&.each { |listener| listener.on_constant_write_node_leave(node) } + end + + # Dispatch enter and leave events for DefNode nodes. + def visit_def_node(node) + listeners[:on_def_node_enter]&.each { |listener| listener.on_def_node_enter(node) } + listeners[:on_def_node_leave]&.each { |listener| listener.on_def_node_leave(node) } + end + + # Dispatch enter and leave events for DefinedNode nodes. + def visit_defined_node(node) + listeners[:on_defined_node_enter]&.each { |listener| listener.on_defined_node_enter(node) } + listeners[:on_defined_node_leave]&.each { |listener| listener.on_defined_node_leave(node) } + end + + # Dispatch enter and leave events for ElseNode nodes. + def visit_else_node(node) + listeners[:on_else_node_enter]&.each { |listener| listener.on_else_node_enter(node) } + listeners[:on_else_node_leave]&.each { |listener| listener.on_else_node_leave(node) } + end + + # Dispatch enter and leave events for EmbeddedStatementsNode nodes. + def visit_embedded_statements_node(node) + listeners[:on_embedded_statements_node_enter]&.each { |listener| listener.on_embedded_statements_node_enter(node) } + listeners[:on_embedded_statements_node_leave]&.each { |listener| listener.on_embedded_statements_node_leave(node) } + end + + # Dispatch enter and leave events for EmbeddedVariableNode nodes. + def visit_embedded_variable_node(node) + listeners[:on_embedded_variable_node_enter]&.each { |listener| listener.on_embedded_variable_node_enter(node) } + listeners[:on_embedded_variable_node_leave]&.each { |listener| listener.on_embedded_variable_node_leave(node) } + end + + # Dispatch enter and leave events for EnsureNode nodes. + def visit_ensure_node(node) + listeners[:on_ensure_node_enter]&.each { |listener| listener.on_ensure_node_enter(node) } + listeners[:on_ensure_node_leave]&.each { |listener| listener.on_ensure_node_leave(node) } + end + + # Dispatch enter and leave events for FalseNode nodes. + def visit_false_node(node) + listeners[:on_false_node_enter]&.each { |listener| listener.on_false_node_enter(node) } + listeners[:on_false_node_leave]&.each { |listener| listener.on_false_node_leave(node) } + end + + # Dispatch enter and leave events for FindPatternNode nodes. + def visit_find_pattern_node(node) + listeners[:on_find_pattern_node_enter]&.each { |listener| listener.on_find_pattern_node_enter(node) } + listeners[:on_find_pattern_node_leave]&.each { |listener| listener.on_find_pattern_node_leave(node) } + end + + # Dispatch enter and leave events for FlipFlopNode nodes. + def visit_flip_flop_node(node) + listeners[:on_flip_flop_node_enter]&.each { |listener| listener.on_flip_flop_node_enter(node) } + listeners[:on_flip_flop_node_leave]&.each { |listener| listener.on_flip_flop_node_leave(node) } + end + + # Dispatch enter and leave events for FloatNode nodes. + def visit_float_node(node) + listeners[:on_float_node_enter]&.each { |listener| listener.on_float_node_enter(node) } + listeners[:on_float_node_leave]&.each { |listener| listener.on_float_node_leave(node) } + end + + # Dispatch enter and leave events for ForNode nodes. + def visit_for_node(node) + listeners[:on_for_node_enter]&.each { |listener| listener.on_for_node_enter(node) } + listeners[:on_for_node_leave]&.each { |listener| listener.on_for_node_leave(node) } + end + + # Dispatch enter and leave events for ForwardingArgumentsNode nodes. + def visit_forwarding_arguments_node(node) + listeners[:on_forwarding_arguments_node_enter]&.each { |listener| listener.on_forwarding_arguments_node_enter(node) } + listeners[:on_forwarding_arguments_node_leave]&.each { |listener| listener.on_forwarding_arguments_node_leave(node) } + end + + # Dispatch enter and leave events for ForwardingParameterNode nodes. + def visit_forwarding_parameter_node(node) + listeners[:on_forwarding_parameter_node_enter]&.each { |listener| listener.on_forwarding_parameter_node_enter(node) } + listeners[:on_forwarding_parameter_node_leave]&.each { |listener| listener.on_forwarding_parameter_node_leave(node) } + end + + # Dispatch enter and leave events for ForwardingSuperNode nodes. + def visit_forwarding_super_node(node) + listeners[:on_forwarding_super_node_enter]&.each { |listener| listener.on_forwarding_super_node_enter(node) } + listeners[:on_forwarding_super_node_leave]&.each { |listener| listener.on_forwarding_super_node_leave(node) } + end + + # Dispatch enter and leave events for GlobalVariableAndWriteNode nodes. + def visit_global_variable_and_write_node(node) + listeners[:on_global_variable_and_write_node_enter]&.each { |listener| listener.on_global_variable_and_write_node_enter(node) } + listeners[:on_global_variable_and_write_node_leave]&.each { |listener| listener.on_global_variable_and_write_node_leave(node) } + end + + # Dispatch enter and leave events for GlobalVariableOperatorWriteNode nodes. + def visit_global_variable_operator_write_node(node) + listeners[:on_global_variable_operator_write_node_enter]&.each { |listener| listener.on_global_variable_operator_write_node_enter(node) } + listeners[:on_global_variable_operator_write_node_leave]&.each { |listener| listener.on_global_variable_operator_write_node_leave(node) } + end + + # Dispatch enter and leave events for GlobalVariableOrWriteNode nodes. + def visit_global_variable_or_write_node(node) + listeners[:on_global_variable_or_write_node_enter]&.each { |listener| listener.on_global_variable_or_write_node_enter(node) } + listeners[:on_global_variable_or_write_node_leave]&.each { |listener| listener.on_global_variable_or_write_node_leave(node) } + end + + # Dispatch enter and leave events for GlobalVariableReadNode nodes. + def visit_global_variable_read_node(node) + listeners[:on_global_variable_read_node_enter]&.each { |listener| listener.on_global_variable_read_node_enter(node) } + listeners[:on_global_variable_read_node_leave]&.each { |listener| listener.on_global_variable_read_node_leave(node) } + end + + # Dispatch enter and leave events for GlobalVariableTargetNode nodes. + def visit_global_variable_target_node(node) + listeners[:on_global_variable_target_node_enter]&.each { |listener| listener.on_global_variable_target_node_enter(node) } + listeners[:on_global_variable_target_node_leave]&.each { |listener| listener.on_global_variable_target_node_leave(node) } + end + + # Dispatch enter and leave events for GlobalVariableWriteNode nodes. + def visit_global_variable_write_node(node) + listeners[:on_global_variable_write_node_enter]&.each { |listener| listener.on_global_variable_write_node_enter(node) } + listeners[:on_global_variable_write_node_leave]&.each { |listener| listener.on_global_variable_write_node_leave(node) } + end + + # Dispatch enter and leave events for HashNode nodes. + def visit_hash_node(node) + listeners[:on_hash_node_enter]&.each { |listener| listener.on_hash_node_enter(node) } + listeners[:on_hash_node_leave]&.each { |listener| listener.on_hash_node_leave(node) } + end + + # Dispatch enter and leave events for HashPatternNode nodes. + def visit_hash_pattern_node(node) + listeners[:on_hash_pattern_node_enter]&.each { |listener| listener.on_hash_pattern_node_enter(node) } + listeners[:on_hash_pattern_node_leave]&.each { |listener| listener.on_hash_pattern_node_leave(node) } + end + + # Dispatch enter and leave events for IfNode nodes. + def visit_if_node(node) + listeners[:on_if_node_enter]&.each { |listener| listener.on_if_node_enter(node) } + listeners[:on_if_node_leave]&.each { |listener| listener.on_if_node_leave(node) } + end + + # Dispatch enter and leave events for ImaginaryNode nodes. + def visit_imaginary_node(node) + listeners[:on_imaginary_node_enter]&.each { |listener| listener.on_imaginary_node_enter(node) } + listeners[:on_imaginary_node_leave]&.each { |listener| listener.on_imaginary_node_leave(node) } + end + + # Dispatch enter and leave events for ImplicitNode nodes. + def visit_implicit_node(node) + listeners[:on_implicit_node_enter]&.each { |listener| listener.on_implicit_node_enter(node) } + listeners[:on_implicit_node_leave]&.each { |listener| listener.on_implicit_node_leave(node) } + end + + # Dispatch enter and leave events for ImplicitRestNode nodes. + def visit_implicit_rest_node(node) + listeners[:on_implicit_rest_node_enter]&.each { |listener| listener.on_implicit_rest_node_enter(node) } + listeners[:on_implicit_rest_node_leave]&.each { |listener| listener.on_implicit_rest_node_leave(node) } + end + + # Dispatch enter and leave events for InNode nodes. + def visit_in_node(node) + listeners[:on_in_node_enter]&.each { |listener| listener.on_in_node_enter(node) } + listeners[:on_in_node_leave]&.each { |listener| listener.on_in_node_leave(node) } + end + + # Dispatch enter and leave events for IndexAndWriteNode nodes. + def visit_index_and_write_node(node) + listeners[:on_index_and_write_node_enter]&.each { |listener| listener.on_index_and_write_node_enter(node) } + listeners[:on_index_and_write_node_leave]&.each { |listener| listener.on_index_and_write_node_leave(node) } + end + + # Dispatch enter and leave events for IndexOperatorWriteNode nodes. + def visit_index_operator_write_node(node) + listeners[:on_index_operator_write_node_enter]&.each { |listener| listener.on_index_operator_write_node_enter(node) } + listeners[:on_index_operator_write_node_leave]&.each { |listener| listener.on_index_operator_write_node_leave(node) } + end + + # Dispatch enter and leave events for IndexOrWriteNode nodes. + def visit_index_or_write_node(node) + listeners[:on_index_or_write_node_enter]&.each { |listener| listener.on_index_or_write_node_enter(node) } + listeners[:on_index_or_write_node_leave]&.each { |listener| listener.on_index_or_write_node_leave(node) } + end + + # Dispatch enter and leave events for IndexTargetNode nodes. + def visit_index_target_node(node) + listeners[:on_index_target_node_enter]&.each { |listener| listener.on_index_target_node_enter(node) } + listeners[:on_index_target_node_leave]&.each { |listener| listener.on_index_target_node_leave(node) } + end + + # Dispatch enter and leave events for InstanceVariableAndWriteNode nodes. + def visit_instance_variable_and_write_node(node) + listeners[:on_instance_variable_and_write_node_enter]&.each { |listener| listener.on_instance_variable_and_write_node_enter(node) } + listeners[:on_instance_variable_and_write_node_leave]&.each { |listener| listener.on_instance_variable_and_write_node_leave(node) } + end + + # Dispatch enter and leave events for InstanceVariableOperatorWriteNode nodes. + def visit_instance_variable_operator_write_node(node) + listeners[:on_instance_variable_operator_write_node_enter]&.each { |listener| listener.on_instance_variable_operator_write_node_enter(node) } + listeners[:on_instance_variable_operator_write_node_leave]&.each { |listener| listener.on_instance_variable_operator_write_node_leave(node) } + end + + # Dispatch enter and leave events for InstanceVariableOrWriteNode nodes. + def visit_instance_variable_or_write_node(node) + listeners[:on_instance_variable_or_write_node_enter]&.each { |listener| listener.on_instance_variable_or_write_node_enter(node) } + listeners[:on_instance_variable_or_write_node_leave]&.each { |listener| listener.on_instance_variable_or_write_node_leave(node) } + end + + # Dispatch enter and leave events for InstanceVariableReadNode nodes. + def visit_instance_variable_read_node(node) + listeners[:on_instance_variable_read_node_enter]&.each { |listener| listener.on_instance_variable_read_node_enter(node) } + listeners[:on_instance_variable_read_node_leave]&.each { |listener| listener.on_instance_variable_read_node_leave(node) } + end + + # Dispatch enter and leave events for InstanceVariableTargetNode nodes. + def visit_instance_variable_target_node(node) + listeners[:on_instance_variable_target_node_enter]&.each { |listener| listener.on_instance_variable_target_node_enter(node) } + listeners[:on_instance_variable_target_node_leave]&.each { |listener| listener.on_instance_variable_target_node_leave(node) } + end + + # Dispatch enter and leave events for InstanceVariableWriteNode nodes. + def visit_instance_variable_write_node(node) + listeners[:on_instance_variable_write_node_enter]&.each { |listener| listener.on_instance_variable_write_node_enter(node) } + listeners[:on_instance_variable_write_node_leave]&.each { |listener| listener.on_instance_variable_write_node_leave(node) } + end + + # Dispatch enter and leave events for IntegerNode nodes. + def visit_integer_node(node) + listeners[:on_integer_node_enter]&.each { |listener| listener.on_integer_node_enter(node) } + listeners[:on_integer_node_leave]&.each { |listener| listener.on_integer_node_leave(node) } + end + + # Dispatch enter and leave events for InterpolatedMatchLastLineNode nodes. + def visit_interpolated_match_last_line_node(node) + listeners[:on_interpolated_match_last_line_node_enter]&.each { |listener| listener.on_interpolated_match_last_line_node_enter(node) } + listeners[:on_interpolated_match_last_line_node_leave]&.each { |listener| listener.on_interpolated_match_last_line_node_leave(node) } + end + + # Dispatch enter and leave events for InterpolatedRegularExpressionNode nodes. + def visit_interpolated_regular_expression_node(node) + listeners[:on_interpolated_regular_expression_node_enter]&.each { |listener| listener.on_interpolated_regular_expression_node_enter(node) } + listeners[:on_interpolated_regular_expression_node_leave]&.each { |listener| listener.on_interpolated_regular_expression_node_leave(node) } + end + + # Dispatch enter and leave events for InterpolatedStringNode nodes. + def visit_interpolated_string_node(node) + listeners[:on_interpolated_string_node_enter]&.each { |listener| listener.on_interpolated_string_node_enter(node) } + listeners[:on_interpolated_string_node_leave]&.each { |listener| listener.on_interpolated_string_node_leave(node) } + end + + # Dispatch enter and leave events for InterpolatedSymbolNode nodes. + def visit_interpolated_symbol_node(node) + listeners[:on_interpolated_symbol_node_enter]&.each { |listener| listener.on_interpolated_symbol_node_enter(node) } + listeners[:on_interpolated_symbol_node_leave]&.each { |listener| listener.on_interpolated_symbol_node_leave(node) } + end + + # Dispatch enter and leave events for InterpolatedXStringNode nodes. + def visit_interpolated_x_string_node(node) + listeners[:on_interpolated_x_string_node_enter]&.each { |listener| listener.on_interpolated_x_string_node_enter(node) } + listeners[:on_interpolated_x_string_node_leave]&.each { |listener| listener.on_interpolated_x_string_node_leave(node) } + end + + # Dispatch enter and leave events for ItLocalVariableReadNode nodes. + def visit_it_local_variable_read_node(node) + listeners[:on_it_local_variable_read_node_enter]&.each { |listener| listener.on_it_local_variable_read_node_enter(node) } + listeners[:on_it_local_variable_read_node_leave]&.each { |listener| listener.on_it_local_variable_read_node_leave(node) } + end + + # Dispatch enter and leave events for ItParametersNode nodes. + def visit_it_parameters_node(node) + listeners[:on_it_parameters_node_enter]&.each { |listener| listener.on_it_parameters_node_enter(node) } + listeners[:on_it_parameters_node_leave]&.each { |listener| listener.on_it_parameters_node_leave(node) } + end + + # Dispatch enter and leave events for KeywordHashNode nodes. + def visit_keyword_hash_node(node) + listeners[:on_keyword_hash_node_enter]&.each { |listener| listener.on_keyword_hash_node_enter(node) } + listeners[:on_keyword_hash_node_leave]&.each { |listener| listener.on_keyword_hash_node_leave(node) } + end + + # Dispatch enter and leave events for KeywordRestParameterNode nodes. + def visit_keyword_rest_parameter_node(node) + listeners[:on_keyword_rest_parameter_node_enter]&.each { |listener| listener.on_keyword_rest_parameter_node_enter(node) } + listeners[:on_keyword_rest_parameter_node_leave]&.each { |listener| listener.on_keyword_rest_parameter_node_leave(node) } + end + + # Dispatch enter and leave events for LambdaNode nodes. + def visit_lambda_node(node) + listeners[:on_lambda_node_enter]&.each { |listener| listener.on_lambda_node_enter(node) } + listeners[:on_lambda_node_leave]&.each { |listener| listener.on_lambda_node_leave(node) } + end + + # Dispatch enter and leave events for LocalVariableAndWriteNode nodes. + def visit_local_variable_and_write_node(node) + listeners[:on_local_variable_and_write_node_enter]&.each { |listener| listener.on_local_variable_and_write_node_enter(node) } + listeners[:on_local_variable_and_write_node_leave]&.each { |listener| listener.on_local_variable_and_write_node_leave(node) } + end + + # Dispatch enter and leave events for LocalVariableOperatorWriteNode nodes. + def visit_local_variable_operator_write_node(node) + listeners[:on_local_variable_operator_write_node_enter]&.each { |listener| listener.on_local_variable_operator_write_node_enter(node) } + listeners[:on_local_variable_operator_write_node_leave]&.each { |listener| listener.on_local_variable_operator_write_node_leave(node) } + end + + # Dispatch enter and leave events for LocalVariableOrWriteNode nodes. + def visit_local_variable_or_write_node(node) + listeners[:on_local_variable_or_write_node_enter]&.each { |listener| listener.on_local_variable_or_write_node_enter(node) } + listeners[:on_local_variable_or_write_node_leave]&.each { |listener| listener.on_local_variable_or_write_node_leave(node) } + end + + # Dispatch enter and leave events for LocalVariableReadNode nodes. + def visit_local_variable_read_node(node) + listeners[:on_local_variable_read_node_enter]&.each { |listener| listener.on_local_variable_read_node_enter(node) } + listeners[:on_local_variable_read_node_leave]&.each { |listener| listener.on_local_variable_read_node_leave(node) } + end + + # Dispatch enter and leave events for LocalVariableTargetNode nodes. + def visit_local_variable_target_node(node) + listeners[:on_local_variable_target_node_enter]&.each { |listener| listener.on_local_variable_target_node_enter(node) } + listeners[:on_local_variable_target_node_leave]&.each { |listener| listener.on_local_variable_target_node_leave(node) } + end + + # Dispatch enter and leave events for LocalVariableWriteNode nodes. + def visit_local_variable_write_node(node) + listeners[:on_local_variable_write_node_enter]&.each { |listener| listener.on_local_variable_write_node_enter(node) } + listeners[:on_local_variable_write_node_leave]&.each { |listener| listener.on_local_variable_write_node_leave(node) } + end + + # Dispatch enter and leave events for MatchLastLineNode nodes. + def visit_match_last_line_node(node) + listeners[:on_match_last_line_node_enter]&.each { |listener| listener.on_match_last_line_node_enter(node) } + listeners[:on_match_last_line_node_leave]&.each { |listener| listener.on_match_last_line_node_leave(node) } + end + + # Dispatch enter and leave events for MatchPredicateNode nodes. + def visit_match_predicate_node(node) + listeners[:on_match_predicate_node_enter]&.each { |listener| listener.on_match_predicate_node_enter(node) } + listeners[:on_match_predicate_node_leave]&.each { |listener| listener.on_match_predicate_node_leave(node) } + end + + # Dispatch enter and leave events for MatchRequiredNode nodes. + def visit_match_required_node(node) + listeners[:on_match_required_node_enter]&.each { |listener| listener.on_match_required_node_enter(node) } + listeners[:on_match_required_node_leave]&.each { |listener| listener.on_match_required_node_leave(node) } + end + + # Dispatch enter and leave events for MatchWriteNode nodes. + def visit_match_write_node(node) + listeners[:on_match_write_node_enter]&.each { |listener| listener.on_match_write_node_enter(node) } + listeners[:on_match_write_node_leave]&.each { |listener| listener.on_match_write_node_leave(node) } + end + + # Dispatch enter and leave events for MissingNode nodes. + def visit_missing_node(node) + listeners[:on_missing_node_enter]&.each { |listener| listener.on_missing_node_enter(node) } + listeners[:on_missing_node_leave]&.each { |listener| listener.on_missing_node_leave(node) } + end + + # Dispatch enter and leave events for ModuleNode nodes. + def visit_module_node(node) + listeners[:on_module_node_enter]&.each { |listener| listener.on_module_node_enter(node) } + listeners[:on_module_node_leave]&.each { |listener| listener.on_module_node_leave(node) } + end + + # Dispatch enter and leave events for MultiTargetNode nodes. + def visit_multi_target_node(node) + listeners[:on_multi_target_node_enter]&.each { |listener| listener.on_multi_target_node_enter(node) } + listeners[:on_multi_target_node_leave]&.each { |listener| listener.on_multi_target_node_leave(node) } + end + + # Dispatch enter and leave events for MultiWriteNode nodes. + def visit_multi_write_node(node) + listeners[:on_multi_write_node_enter]&.each { |listener| listener.on_multi_write_node_enter(node) } + listeners[:on_multi_write_node_leave]&.each { |listener| listener.on_multi_write_node_leave(node) } + end + + # Dispatch enter and leave events for NextNode nodes. + def visit_next_node(node) + listeners[:on_next_node_enter]&.each { |listener| listener.on_next_node_enter(node) } + listeners[:on_next_node_leave]&.each { |listener| listener.on_next_node_leave(node) } + end + + # Dispatch enter and leave events for NilNode nodes. + def visit_nil_node(node) + listeners[:on_nil_node_enter]&.each { |listener| listener.on_nil_node_enter(node) } + listeners[:on_nil_node_leave]&.each { |listener| listener.on_nil_node_leave(node) } + end + + # Dispatch enter and leave events for NoKeywordsParameterNode nodes. + def visit_no_keywords_parameter_node(node) + listeners[:on_no_keywords_parameter_node_enter]&.each { |listener| listener.on_no_keywords_parameter_node_enter(node) } + listeners[:on_no_keywords_parameter_node_leave]&.each { |listener| listener.on_no_keywords_parameter_node_leave(node) } + end + + # Dispatch enter and leave events for NumberedParametersNode nodes. + def visit_numbered_parameters_node(node) + listeners[:on_numbered_parameters_node_enter]&.each { |listener| listener.on_numbered_parameters_node_enter(node) } + listeners[:on_numbered_parameters_node_leave]&.each { |listener| listener.on_numbered_parameters_node_leave(node) } + end + + # Dispatch enter and leave events for NumberedReferenceReadNode nodes. + def visit_numbered_reference_read_node(node) + listeners[:on_numbered_reference_read_node_enter]&.each { |listener| listener.on_numbered_reference_read_node_enter(node) } + listeners[:on_numbered_reference_read_node_leave]&.each { |listener| listener.on_numbered_reference_read_node_leave(node) } + end + + # Dispatch enter and leave events for OptionalKeywordParameterNode nodes. + def visit_optional_keyword_parameter_node(node) + listeners[:on_optional_keyword_parameter_node_enter]&.each { |listener| listener.on_optional_keyword_parameter_node_enter(node) } + listeners[:on_optional_keyword_parameter_node_leave]&.each { |listener| listener.on_optional_keyword_parameter_node_leave(node) } + end + + # Dispatch enter and leave events for OptionalParameterNode nodes. + def visit_optional_parameter_node(node) + listeners[:on_optional_parameter_node_enter]&.each { |listener| listener.on_optional_parameter_node_enter(node) } + listeners[:on_optional_parameter_node_leave]&.each { |listener| listener.on_optional_parameter_node_leave(node) } + end + + # Dispatch enter and leave events for OrNode nodes. + def visit_or_node(node) + listeners[:on_or_node_enter]&.each { |listener| listener.on_or_node_enter(node) } + listeners[:on_or_node_leave]&.each { |listener| listener.on_or_node_leave(node) } + end + + # Dispatch enter and leave events for ParametersNode nodes. + def visit_parameters_node(node) + listeners[:on_parameters_node_enter]&.each { |listener| listener.on_parameters_node_enter(node) } + listeners[:on_parameters_node_leave]&.each { |listener| listener.on_parameters_node_leave(node) } + end + + # Dispatch enter and leave events for ParenthesesNode nodes. + def visit_parentheses_node(node) + listeners[:on_parentheses_node_enter]&.each { |listener| listener.on_parentheses_node_enter(node) } + listeners[:on_parentheses_node_leave]&.each { |listener| listener.on_parentheses_node_leave(node) } + end + + # Dispatch enter and leave events for PinnedExpressionNode nodes. + def visit_pinned_expression_node(node) + listeners[:on_pinned_expression_node_enter]&.each { |listener| listener.on_pinned_expression_node_enter(node) } + listeners[:on_pinned_expression_node_leave]&.each { |listener| listener.on_pinned_expression_node_leave(node) } + end + + # Dispatch enter and leave events for PinnedVariableNode nodes. + def visit_pinned_variable_node(node) + listeners[:on_pinned_variable_node_enter]&.each { |listener| listener.on_pinned_variable_node_enter(node) } + listeners[:on_pinned_variable_node_leave]&.each { |listener| listener.on_pinned_variable_node_leave(node) } + end + + # Dispatch enter and leave events for PostExecutionNode nodes. + def visit_post_execution_node(node) + listeners[:on_post_execution_node_enter]&.each { |listener| listener.on_post_execution_node_enter(node) } + listeners[:on_post_execution_node_leave]&.each { |listener| listener.on_post_execution_node_leave(node) } + end + + # Dispatch enter and leave events for PreExecutionNode nodes. + def visit_pre_execution_node(node) + listeners[:on_pre_execution_node_enter]&.each { |listener| listener.on_pre_execution_node_enter(node) } + listeners[:on_pre_execution_node_leave]&.each { |listener| listener.on_pre_execution_node_leave(node) } + end + + # Dispatch enter and leave events for ProgramNode nodes. + def visit_program_node(node) + listeners[:on_program_node_enter]&.each { |listener| listener.on_program_node_enter(node) } + listeners[:on_program_node_leave]&.each { |listener| listener.on_program_node_leave(node) } + end + + # Dispatch enter and leave events for RangeNode nodes. + def visit_range_node(node) + listeners[:on_range_node_enter]&.each { |listener| listener.on_range_node_enter(node) } + listeners[:on_range_node_leave]&.each { |listener| listener.on_range_node_leave(node) } + end + + # Dispatch enter and leave events for RationalNode nodes. + def visit_rational_node(node) + listeners[:on_rational_node_enter]&.each { |listener| listener.on_rational_node_enter(node) } + listeners[:on_rational_node_leave]&.each { |listener| listener.on_rational_node_leave(node) } + end + + # Dispatch enter and leave events for RedoNode nodes. + def visit_redo_node(node) + listeners[:on_redo_node_enter]&.each { |listener| listener.on_redo_node_enter(node) } + listeners[:on_redo_node_leave]&.each { |listener| listener.on_redo_node_leave(node) } + end + + # Dispatch enter and leave events for RegularExpressionNode nodes. + def visit_regular_expression_node(node) + listeners[:on_regular_expression_node_enter]&.each { |listener| listener.on_regular_expression_node_enter(node) } + listeners[:on_regular_expression_node_leave]&.each { |listener| listener.on_regular_expression_node_leave(node) } + end + + # Dispatch enter and leave events for RequiredKeywordParameterNode nodes. + def visit_required_keyword_parameter_node(node) + listeners[:on_required_keyword_parameter_node_enter]&.each { |listener| listener.on_required_keyword_parameter_node_enter(node) } + listeners[:on_required_keyword_parameter_node_leave]&.each { |listener| listener.on_required_keyword_parameter_node_leave(node) } + end + + # Dispatch enter and leave events for RequiredParameterNode nodes. + def visit_required_parameter_node(node) + listeners[:on_required_parameter_node_enter]&.each { |listener| listener.on_required_parameter_node_enter(node) } + listeners[:on_required_parameter_node_leave]&.each { |listener| listener.on_required_parameter_node_leave(node) } + end + + # Dispatch enter and leave events for RescueModifierNode nodes. + def visit_rescue_modifier_node(node) + listeners[:on_rescue_modifier_node_enter]&.each { |listener| listener.on_rescue_modifier_node_enter(node) } + listeners[:on_rescue_modifier_node_leave]&.each { |listener| listener.on_rescue_modifier_node_leave(node) } + end + + # Dispatch enter and leave events for RescueNode nodes. + def visit_rescue_node(node) + listeners[:on_rescue_node_enter]&.each { |listener| listener.on_rescue_node_enter(node) } + listeners[:on_rescue_node_leave]&.each { |listener| listener.on_rescue_node_leave(node) } + end + + # Dispatch enter and leave events for RestParameterNode nodes. + def visit_rest_parameter_node(node) + listeners[:on_rest_parameter_node_enter]&.each { |listener| listener.on_rest_parameter_node_enter(node) } + listeners[:on_rest_parameter_node_leave]&.each { |listener| listener.on_rest_parameter_node_leave(node) } + end + + # Dispatch enter and leave events for RetryNode nodes. + def visit_retry_node(node) + listeners[:on_retry_node_enter]&.each { |listener| listener.on_retry_node_enter(node) } + listeners[:on_retry_node_leave]&.each { |listener| listener.on_retry_node_leave(node) } + end + + # Dispatch enter and leave events for ReturnNode nodes. + def visit_return_node(node) + listeners[:on_return_node_enter]&.each { |listener| listener.on_return_node_enter(node) } + listeners[:on_return_node_leave]&.each { |listener| listener.on_return_node_leave(node) } + end + + # Dispatch enter and leave events for SelfNode nodes. + def visit_self_node(node) + listeners[:on_self_node_enter]&.each { |listener| listener.on_self_node_enter(node) } + listeners[:on_self_node_leave]&.each { |listener| listener.on_self_node_leave(node) } + end + + # Dispatch enter and leave events for ShareableConstantNode nodes. + def visit_shareable_constant_node(node) + listeners[:on_shareable_constant_node_enter]&.each { |listener| listener.on_shareable_constant_node_enter(node) } + listeners[:on_shareable_constant_node_leave]&.each { |listener| listener.on_shareable_constant_node_leave(node) } + end + + # Dispatch enter and leave events for SingletonClassNode nodes. + def visit_singleton_class_node(node) + listeners[:on_singleton_class_node_enter]&.each { |listener| listener.on_singleton_class_node_enter(node) } + listeners[:on_singleton_class_node_leave]&.each { |listener| listener.on_singleton_class_node_leave(node) } + end + + # Dispatch enter and leave events for SourceEncodingNode nodes. + def visit_source_encoding_node(node) + listeners[:on_source_encoding_node_enter]&.each { |listener| listener.on_source_encoding_node_enter(node) } + listeners[:on_source_encoding_node_leave]&.each { |listener| listener.on_source_encoding_node_leave(node) } + end + + # Dispatch enter and leave events for SourceFileNode nodes. + def visit_source_file_node(node) + listeners[:on_source_file_node_enter]&.each { |listener| listener.on_source_file_node_enter(node) } + listeners[:on_source_file_node_leave]&.each { |listener| listener.on_source_file_node_leave(node) } + end + + # Dispatch enter and leave events for SourceLineNode nodes. + def visit_source_line_node(node) + listeners[:on_source_line_node_enter]&.each { |listener| listener.on_source_line_node_enter(node) } + listeners[:on_source_line_node_leave]&.each { |listener| listener.on_source_line_node_leave(node) } + end + + # Dispatch enter and leave events for SplatNode nodes. + def visit_splat_node(node) + listeners[:on_splat_node_enter]&.each { |listener| listener.on_splat_node_enter(node) } + listeners[:on_splat_node_leave]&.each { |listener| listener.on_splat_node_leave(node) } + end + + # Dispatch enter and leave events for StatementsNode nodes. + def visit_statements_node(node) + listeners[:on_statements_node_enter]&.each { |listener| listener.on_statements_node_enter(node) } + listeners[:on_statements_node_leave]&.each { |listener| listener.on_statements_node_leave(node) } + end + + # Dispatch enter and leave events for StringNode nodes. + def visit_string_node(node) + listeners[:on_string_node_enter]&.each { |listener| listener.on_string_node_enter(node) } + listeners[:on_string_node_leave]&.each { |listener| listener.on_string_node_leave(node) } + end + + # Dispatch enter and leave events for SuperNode nodes. + def visit_super_node(node) + listeners[:on_super_node_enter]&.each { |listener| listener.on_super_node_enter(node) } + listeners[:on_super_node_leave]&.each { |listener| listener.on_super_node_leave(node) } + end + + # Dispatch enter and leave events for SymbolNode nodes. + def visit_symbol_node(node) + listeners[:on_symbol_node_enter]&.each { |listener| listener.on_symbol_node_enter(node) } + listeners[:on_symbol_node_leave]&.each { |listener| listener.on_symbol_node_leave(node) } + end + + # Dispatch enter and leave events for TrueNode nodes. + def visit_true_node(node) + listeners[:on_true_node_enter]&.each { |listener| listener.on_true_node_enter(node) } + listeners[:on_true_node_leave]&.each { |listener| listener.on_true_node_leave(node) } + end + + # Dispatch enter and leave events for UndefNode nodes. + def visit_undef_node(node) + listeners[:on_undef_node_enter]&.each { |listener| listener.on_undef_node_enter(node) } + listeners[:on_undef_node_leave]&.each { |listener| listener.on_undef_node_leave(node) } + end + + # Dispatch enter and leave events for UnlessNode nodes. + def visit_unless_node(node) + listeners[:on_unless_node_enter]&.each { |listener| listener.on_unless_node_enter(node) } + listeners[:on_unless_node_leave]&.each { |listener| listener.on_unless_node_leave(node) } + end + + # Dispatch enter and leave events for UntilNode nodes. + def visit_until_node(node) + listeners[:on_until_node_enter]&.each { |listener| listener.on_until_node_enter(node) } + listeners[:on_until_node_leave]&.each { |listener| listener.on_until_node_leave(node) } + end + + # Dispatch enter and leave events for WhenNode nodes. + def visit_when_node(node) + listeners[:on_when_node_enter]&.each { |listener| listener.on_when_node_enter(node) } + listeners[:on_when_node_leave]&.each { |listener| listener.on_when_node_leave(node) } + end + + # Dispatch enter and leave events for WhileNode nodes. + def visit_while_node(node) + listeners[:on_while_node_enter]&.each { |listener| listener.on_while_node_enter(node) } + listeners[:on_while_node_leave]&.each { |listener| listener.on_while_node_leave(node) } + end + + # Dispatch enter and leave events for XStringNode nodes. + def visit_x_string_node(node) + listeners[:on_x_string_node_enter]&.each { |listener| listener.on_x_string_node_enter(node) } + listeners[:on_x_string_node_leave]&.each { |listener| listener.on_x_string_node_leave(node) } + end + + # Dispatch enter and leave events for YieldNode nodes. + def visit_yield_node(node) + listeners[:on_yield_node_enter]&.each { |listener| listener.on_yield_node_enter(node) } + listeners[:on_yield_node_leave]&.each { |listener| listener.on_yield_node_leave(node) } + end + end + + private_constant :DispatchOnce + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/dot_visitor.rb b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/dot_visitor.rb new file mode 100644 index 0000000..f715716 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/dot_visitor.rb @@ -0,0 +1,4767 @@ +# frozen_string_literal: true +# :markup: markdown + +=begin +-- +This file is generated by the templates/template.rb script and should not be +modified manually. See templates/lib/prism/dot_visitor.rb.erb +if you are looking to modify the template +++ +=end + +require "cgi/escape" +require "cgi/util" unless defined?(CGI::EscapeExt) + +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 + "#{name}" + else + "#{name}#{CGI.escapeHTML(value || raise)}" + 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 + + + DOT + + if fields.any? + "#{dot} #{fields.map(&:to_dot).join("\n ")}\n
#{name}
" + else + "#{dot}" + 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 + + # Visit a AliasGlobalVariableNode node. + def visit_alias_global_variable_node(node) + table = Table.new("AliasGlobalVariableNode") + id = node_id(node) + + # new_name + table.field("new_name", port: true) + digraph.edge("#{id}:new_name -> #{node_id(node.new_name)};") + + # old_name + table.field("old_name", port: true) + digraph.edge("#{id}:old_name -> #{node_id(node.old_name)};") + + # keyword_loc + table.field("keyword_loc", location_inspect(node.keyword_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a AliasMethodNode node. + def visit_alias_method_node(node) + table = Table.new("AliasMethodNode") + id = node_id(node) + + # new_name + table.field("new_name", port: true) + digraph.edge("#{id}:new_name -> #{node_id(node.new_name)};") + + # old_name + table.field("old_name", port: true) + digraph.edge("#{id}:old_name -> #{node_id(node.old_name)};") + + # keyword_loc + table.field("keyword_loc", location_inspect(node.keyword_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a AlternationPatternNode node. + def visit_alternation_pattern_node(node) + table = Table.new("AlternationPatternNode") + id = node_id(node) + + # left + table.field("left", port: true) + digraph.edge("#{id}:left -> #{node_id(node.left)};") + + # right + table.field("right", port: true) + digraph.edge("#{id}:right -> #{node_id(node.right)};") + + # operator_loc + table.field("operator_loc", location_inspect(node.operator_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a AndNode node. + def visit_and_node(node) + table = Table.new("AndNode") + id = node_id(node) + + # left + table.field("left", port: true) + digraph.edge("#{id}:left -> #{node_id(node.left)};") + + # right + table.field("right", port: true) + digraph.edge("#{id}:right -> #{node_id(node.right)};") + + # operator_loc + table.field("operator_loc", location_inspect(node.operator_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a ArgumentsNode node. + def visit_arguments_node(node) + table = Table.new("ArgumentsNode") + id = node_id(node) + + # flags + table.field("flags", arguments_node_flags_inspect(node)) + + # arguments + if node.arguments.any? + table.field("arguments", port: true) + + waypoint = "#{id}_arguments" + digraph.waypoint("#{waypoint};") + + digraph.edge("#{id}:arguments -> #{waypoint};") + node.arguments.each { |child| digraph.edge("#{waypoint} -> #{node_id(child)};") } + else + table.field("arguments", "[]") + end + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a ArrayNode node. + def visit_array_node(node) + table = Table.new("ArrayNode") + id = node_id(node) + + # flags + table.field("flags", array_node_flags_inspect(node)) + + # elements + if node.elements.any? + table.field("elements", port: true) + + waypoint = "#{id}_elements" + digraph.waypoint("#{waypoint};") + + digraph.edge("#{id}:elements -> #{waypoint};") + node.elements.each { |child| digraph.edge("#{waypoint} -> #{node_id(child)};") } + else + table.field("elements", "[]") + end + + # opening_loc + unless (opening_loc = node.opening_loc).nil? + table.field("opening_loc", location_inspect(opening_loc)) + end + + # closing_loc + unless (closing_loc = node.closing_loc).nil? + table.field("closing_loc", location_inspect(closing_loc)) + end + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a ArrayPatternNode node. + def visit_array_pattern_node(node) + table = Table.new("ArrayPatternNode") + id = node_id(node) + + # constant + unless (constant = node.constant).nil? + table.field("constant", port: true) + digraph.edge("#{id}:constant -> #{node_id(constant)};") + end + + # requireds + if node.requireds.any? + table.field("requireds", port: true) + + waypoint = "#{id}_requireds" + digraph.waypoint("#{waypoint};") + + digraph.edge("#{id}:requireds -> #{waypoint};") + node.requireds.each { |child| digraph.edge("#{waypoint} -> #{node_id(child)};") } + else + table.field("requireds", "[]") + end + + # rest + unless (rest = node.rest).nil? + table.field("rest", port: true) + digraph.edge("#{id}:rest -> #{node_id(rest)};") + end + + # posts + if node.posts.any? + table.field("posts", port: true) + + waypoint = "#{id}_posts" + digraph.waypoint("#{waypoint};") + + digraph.edge("#{id}:posts -> #{waypoint};") + node.posts.each { |child| digraph.edge("#{waypoint} -> #{node_id(child)};") } + else + table.field("posts", "[]") + end + + # opening_loc + unless (opening_loc = node.opening_loc).nil? + table.field("opening_loc", location_inspect(opening_loc)) + end + + # closing_loc + unless (closing_loc = node.closing_loc).nil? + table.field("closing_loc", location_inspect(closing_loc)) + end + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a AssocNode node. + def visit_assoc_node(node) + table = Table.new("AssocNode") + id = node_id(node) + + # key + table.field("key", port: true) + digraph.edge("#{id}:key -> #{node_id(node.key)};") + + # value + table.field("value", port: true) + digraph.edge("#{id}:value -> #{node_id(node.value)};") + + # operator_loc + unless (operator_loc = node.operator_loc).nil? + table.field("operator_loc", location_inspect(operator_loc)) + end + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a AssocSplatNode node. + def visit_assoc_splat_node(node) + table = Table.new("AssocSplatNode") + id = node_id(node) + + # value + unless (value = node.value).nil? + table.field("value", port: true) + digraph.edge("#{id}:value -> #{node_id(value)};") + end + + # operator_loc + table.field("operator_loc", location_inspect(node.operator_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a BackReferenceReadNode node. + def visit_back_reference_read_node(node) + table = Table.new("BackReferenceReadNode") + id = node_id(node) + + # name + table.field("name", node.name.inspect) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a BeginNode node. + def visit_begin_node(node) + table = Table.new("BeginNode") + id = node_id(node) + + # begin_keyword_loc + unless (begin_keyword_loc = node.begin_keyword_loc).nil? + table.field("begin_keyword_loc", location_inspect(begin_keyword_loc)) + end + + # statements + unless (statements = node.statements).nil? + table.field("statements", port: true) + digraph.edge("#{id}:statements -> #{node_id(statements)};") + end + + # rescue_clause + unless (rescue_clause = node.rescue_clause).nil? + table.field("rescue_clause", port: true) + digraph.edge("#{id}:rescue_clause -> #{node_id(rescue_clause)};") + end + + # else_clause + unless (else_clause = node.else_clause).nil? + table.field("else_clause", port: true) + digraph.edge("#{id}:else_clause -> #{node_id(else_clause)};") + end + + # ensure_clause + unless (ensure_clause = node.ensure_clause).nil? + table.field("ensure_clause", port: true) + digraph.edge("#{id}:ensure_clause -> #{node_id(ensure_clause)};") + end + + # end_keyword_loc + unless (end_keyword_loc = node.end_keyword_loc).nil? + table.field("end_keyword_loc", location_inspect(end_keyword_loc)) + end + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a BlockArgumentNode node. + def visit_block_argument_node(node) + table = Table.new("BlockArgumentNode") + id = node_id(node) + + # expression + unless (expression = node.expression).nil? + table.field("expression", port: true) + digraph.edge("#{id}:expression -> #{node_id(expression)};") + end + + # operator_loc + table.field("operator_loc", location_inspect(node.operator_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a BlockLocalVariableNode node. + def visit_block_local_variable_node(node) + table = Table.new("BlockLocalVariableNode") + id = node_id(node) + + # flags + table.field("flags", parameter_flags_inspect(node)) + + # name + table.field("name", node.name.inspect) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a BlockNode node. + def visit_block_node(node) + table = Table.new("BlockNode") + id = node_id(node) + + # locals + table.field("locals", node.locals.inspect) + + # parameters + unless (parameters = node.parameters).nil? + table.field("parameters", port: true) + digraph.edge("#{id}:parameters -> #{node_id(parameters)};") + end + + # body + unless (body = node.body).nil? + table.field("body", port: true) + digraph.edge("#{id}:body -> #{node_id(body)};") + end + + # opening_loc + table.field("opening_loc", location_inspect(node.opening_loc)) + + # closing_loc + table.field("closing_loc", location_inspect(node.closing_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a BlockParameterNode node. + def visit_block_parameter_node(node) + table = Table.new("BlockParameterNode") + id = node_id(node) + + # flags + table.field("flags", parameter_flags_inspect(node)) + + # name + table.field("name", node.name.inspect) + + # name_loc + unless (name_loc = node.name_loc).nil? + table.field("name_loc", location_inspect(name_loc)) + end + + # operator_loc + table.field("operator_loc", location_inspect(node.operator_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a BlockParametersNode node. + def visit_block_parameters_node(node) + table = Table.new("BlockParametersNode") + id = node_id(node) + + # parameters + unless (parameters = node.parameters).nil? + table.field("parameters", port: true) + digraph.edge("#{id}:parameters -> #{node_id(parameters)};") + end + + # locals + if node.locals.any? + table.field("locals", port: true) + + waypoint = "#{id}_locals" + digraph.waypoint("#{waypoint};") + + digraph.edge("#{id}:locals -> #{waypoint};") + node.locals.each { |child| digraph.edge("#{waypoint} -> #{node_id(child)};") } + else + table.field("locals", "[]") + end + + # opening_loc + unless (opening_loc = node.opening_loc).nil? + table.field("opening_loc", location_inspect(opening_loc)) + end + + # closing_loc + unless (closing_loc = node.closing_loc).nil? + table.field("closing_loc", location_inspect(closing_loc)) + end + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a BreakNode node. + def visit_break_node(node) + table = Table.new("BreakNode") + id = node_id(node) + + # arguments + unless (arguments = node.arguments).nil? + table.field("arguments", port: true) + digraph.edge("#{id}:arguments -> #{node_id(arguments)};") + end + + # keyword_loc + table.field("keyword_loc", location_inspect(node.keyword_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a CallAndWriteNode node. + def visit_call_and_write_node(node) + table = Table.new("CallAndWriteNode") + id = node_id(node) + + # flags + table.field("flags", call_node_flags_inspect(node)) + + # receiver + unless (receiver = node.receiver).nil? + table.field("receiver", port: true) + digraph.edge("#{id}:receiver -> #{node_id(receiver)};") + end + + # call_operator_loc + unless (call_operator_loc = node.call_operator_loc).nil? + table.field("call_operator_loc", location_inspect(call_operator_loc)) + end + + # message_loc + unless (message_loc = node.message_loc).nil? + table.field("message_loc", location_inspect(message_loc)) + end + + # read_name + table.field("read_name", node.read_name.inspect) + + # write_name + table.field("write_name", node.write_name.inspect) + + # operator_loc + table.field("operator_loc", location_inspect(node.operator_loc)) + + # value + table.field("value", port: true) + digraph.edge("#{id}:value -> #{node_id(node.value)};") + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a CallNode node. + def visit_call_node(node) + table = Table.new("CallNode") + id = node_id(node) + + # flags + table.field("flags", call_node_flags_inspect(node)) + + # receiver + unless (receiver = node.receiver).nil? + table.field("receiver", port: true) + digraph.edge("#{id}:receiver -> #{node_id(receiver)};") + end + + # call_operator_loc + unless (call_operator_loc = node.call_operator_loc).nil? + table.field("call_operator_loc", location_inspect(call_operator_loc)) + end + + # name + table.field("name", node.name.inspect) + + # message_loc + unless (message_loc = node.message_loc).nil? + table.field("message_loc", location_inspect(message_loc)) + end + + # opening_loc + unless (opening_loc = node.opening_loc).nil? + table.field("opening_loc", location_inspect(opening_loc)) + end + + # arguments + unless (arguments = node.arguments).nil? + table.field("arguments", port: true) + digraph.edge("#{id}:arguments -> #{node_id(arguments)};") + end + + # closing_loc + unless (closing_loc = node.closing_loc).nil? + table.field("closing_loc", location_inspect(closing_loc)) + end + + # equal_loc + unless (equal_loc = node.equal_loc).nil? + table.field("equal_loc", location_inspect(equal_loc)) + end + + # block + unless (block = node.block).nil? + table.field("block", port: true) + digraph.edge("#{id}:block -> #{node_id(block)};") + end + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a CallOperatorWriteNode node. + def visit_call_operator_write_node(node) + table = Table.new("CallOperatorWriteNode") + id = node_id(node) + + # flags + table.field("flags", call_node_flags_inspect(node)) + + # receiver + unless (receiver = node.receiver).nil? + table.field("receiver", port: true) + digraph.edge("#{id}:receiver -> #{node_id(receiver)};") + end + + # call_operator_loc + unless (call_operator_loc = node.call_operator_loc).nil? + table.field("call_operator_loc", location_inspect(call_operator_loc)) + end + + # message_loc + unless (message_loc = node.message_loc).nil? + table.field("message_loc", location_inspect(message_loc)) + end + + # read_name + table.field("read_name", node.read_name.inspect) + + # write_name + table.field("write_name", node.write_name.inspect) + + # binary_operator + table.field("binary_operator", node.binary_operator.inspect) + + # binary_operator_loc + table.field("binary_operator_loc", location_inspect(node.binary_operator_loc)) + + # value + table.field("value", port: true) + digraph.edge("#{id}:value -> #{node_id(node.value)};") + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a CallOrWriteNode node. + def visit_call_or_write_node(node) + table = Table.new("CallOrWriteNode") + id = node_id(node) + + # flags + table.field("flags", call_node_flags_inspect(node)) + + # receiver + unless (receiver = node.receiver).nil? + table.field("receiver", port: true) + digraph.edge("#{id}:receiver -> #{node_id(receiver)};") + end + + # call_operator_loc + unless (call_operator_loc = node.call_operator_loc).nil? + table.field("call_operator_loc", location_inspect(call_operator_loc)) + end + + # message_loc + unless (message_loc = node.message_loc).nil? + table.field("message_loc", location_inspect(message_loc)) + end + + # read_name + table.field("read_name", node.read_name.inspect) + + # write_name + table.field("write_name", node.write_name.inspect) + + # operator_loc + table.field("operator_loc", location_inspect(node.operator_loc)) + + # value + table.field("value", port: true) + digraph.edge("#{id}:value -> #{node_id(node.value)};") + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a CallTargetNode node. + def visit_call_target_node(node) + table = Table.new("CallTargetNode") + id = node_id(node) + + # flags + table.field("flags", call_node_flags_inspect(node)) + + # receiver + table.field("receiver", port: true) + digraph.edge("#{id}:receiver -> #{node_id(node.receiver)};") + + # call_operator_loc + table.field("call_operator_loc", location_inspect(node.call_operator_loc)) + + # name + table.field("name", node.name.inspect) + + # message_loc + table.field("message_loc", location_inspect(node.message_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a CapturePatternNode node. + def visit_capture_pattern_node(node) + table = Table.new("CapturePatternNode") + id = node_id(node) + + # value + table.field("value", port: true) + digraph.edge("#{id}:value -> #{node_id(node.value)};") + + # target + table.field("target", port: true) + digraph.edge("#{id}:target -> #{node_id(node.target)};") + + # operator_loc + table.field("operator_loc", location_inspect(node.operator_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a CaseMatchNode node. + def visit_case_match_node(node) + table = Table.new("CaseMatchNode") + id = node_id(node) + + # predicate + unless (predicate = node.predicate).nil? + table.field("predicate", port: true) + digraph.edge("#{id}:predicate -> #{node_id(predicate)};") + end + + # conditions + if node.conditions.any? + table.field("conditions", port: true) + + waypoint = "#{id}_conditions" + digraph.waypoint("#{waypoint};") + + digraph.edge("#{id}:conditions -> #{waypoint};") + node.conditions.each { |child| digraph.edge("#{waypoint} -> #{node_id(child)};") } + else + table.field("conditions", "[]") + end + + # else_clause + unless (else_clause = node.else_clause).nil? + table.field("else_clause", port: true) + digraph.edge("#{id}:else_clause -> #{node_id(else_clause)};") + end + + # case_keyword_loc + table.field("case_keyword_loc", location_inspect(node.case_keyword_loc)) + + # end_keyword_loc + table.field("end_keyword_loc", location_inspect(node.end_keyword_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a CaseNode node. + def visit_case_node(node) + table = Table.new("CaseNode") + id = node_id(node) + + # predicate + unless (predicate = node.predicate).nil? + table.field("predicate", port: true) + digraph.edge("#{id}:predicate -> #{node_id(predicate)};") + end + + # conditions + if node.conditions.any? + table.field("conditions", port: true) + + waypoint = "#{id}_conditions" + digraph.waypoint("#{waypoint};") + + digraph.edge("#{id}:conditions -> #{waypoint};") + node.conditions.each { |child| digraph.edge("#{waypoint} -> #{node_id(child)};") } + else + table.field("conditions", "[]") + end + + # else_clause + unless (else_clause = node.else_clause).nil? + table.field("else_clause", port: true) + digraph.edge("#{id}:else_clause -> #{node_id(else_clause)};") + end + + # case_keyword_loc + table.field("case_keyword_loc", location_inspect(node.case_keyword_loc)) + + # end_keyword_loc + table.field("end_keyword_loc", location_inspect(node.end_keyword_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a ClassNode node. + def visit_class_node(node) + table = Table.new("ClassNode") + id = node_id(node) + + # locals + table.field("locals", node.locals.inspect) + + # class_keyword_loc + table.field("class_keyword_loc", location_inspect(node.class_keyword_loc)) + + # constant_path + table.field("constant_path", port: true) + digraph.edge("#{id}:constant_path -> #{node_id(node.constant_path)};") + + # inheritance_operator_loc + unless (inheritance_operator_loc = node.inheritance_operator_loc).nil? + table.field("inheritance_operator_loc", location_inspect(inheritance_operator_loc)) + end + + # superclass + unless (superclass = node.superclass).nil? + table.field("superclass", port: true) + digraph.edge("#{id}:superclass -> #{node_id(superclass)};") + end + + # body + unless (body = node.body).nil? + table.field("body", port: true) + digraph.edge("#{id}:body -> #{node_id(body)};") + end + + # end_keyword_loc + table.field("end_keyword_loc", location_inspect(node.end_keyword_loc)) + + # name + table.field("name", node.name.inspect) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a ClassVariableAndWriteNode node. + def visit_class_variable_and_write_node(node) + table = Table.new("ClassVariableAndWriteNode") + id = node_id(node) + + # name + table.field("name", node.name.inspect) + + # name_loc + table.field("name_loc", location_inspect(node.name_loc)) + + # operator_loc + table.field("operator_loc", location_inspect(node.operator_loc)) + + # value + table.field("value", port: true) + digraph.edge("#{id}:value -> #{node_id(node.value)};") + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a ClassVariableOperatorWriteNode node. + def visit_class_variable_operator_write_node(node) + table = Table.new("ClassVariableOperatorWriteNode") + id = node_id(node) + + # name + table.field("name", node.name.inspect) + + # name_loc + table.field("name_loc", location_inspect(node.name_loc)) + + # binary_operator_loc + table.field("binary_operator_loc", location_inspect(node.binary_operator_loc)) + + # value + table.field("value", port: true) + digraph.edge("#{id}:value -> #{node_id(node.value)};") + + # binary_operator + table.field("binary_operator", node.binary_operator.inspect) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a ClassVariableOrWriteNode node. + def visit_class_variable_or_write_node(node) + table = Table.new("ClassVariableOrWriteNode") + id = node_id(node) + + # name + table.field("name", node.name.inspect) + + # name_loc + table.field("name_loc", location_inspect(node.name_loc)) + + # operator_loc + table.field("operator_loc", location_inspect(node.operator_loc)) + + # value + table.field("value", port: true) + digraph.edge("#{id}:value -> #{node_id(node.value)};") + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a ClassVariableReadNode node. + def visit_class_variable_read_node(node) + table = Table.new("ClassVariableReadNode") + id = node_id(node) + + # name + table.field("name", node.name.inspect) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a ClassVariableTargetNode node. + def visit_class_variable_target_node(node) + table = Table.new("ClassVariableTargetNode") + id = node_id(node) + + # name + table.field("name", node.name.inspect) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a ClassVariableWriteNode node. + def visit_class_variable_write_node(node) + table = Table.new("ClassVariableWriteNode") + id = node_id(node) + + # name + table.field("name", node.name.inspect) + + # name_loc + table.field("name_loc", location_inspect(node.name_loc)) + + # value + table.field("value", port: true) + digraph.edge("#{id}:value -> #{node_id(node.value)};") + + # operator_loc + table.field("operator_loc", location_inspect(node.operator_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a ConstantAndWriteNode node. + def visit_constant_and_write_node(node) + table = Table.new("ConstantAndWriteNode") + id = node_id(node) + + # name + table.field("name", node.name.inspect) + + # name_loc + table.field("name_loc", location_inspect(node.name_loc)) + + # operator_loc + table.field("operator_loc", location_inspect(node.operator_loc)) + + # value + table.field("value", port: true) + digraph.edge("#{id}:value -> #{node_id(node.value)};") + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a ConstantOperatorWriteNode node. + def visit_constant_operator_write_node(node) + table = Table.new("ConstantOperatorWriteNode") + id = node_id(node) + + # name + table.field("name", node.name.inspect) + + # name_loc + table.field("name_loc", location_inspect(node.name_loc)) + + # binary_operator_loc + table.field("binary_operator_loc", location_inspect(node.binary_operator_loc)) + + # value + table.field("value", port: true) + digraph.edge("#{id}:value -> #{node_id(node.value)};") + + # binary_operator + table.field("binary_operator", node.binary_operator.inspect) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a ConstantOrWriteNode node. + def visit_constant_or_write_node(node) + table = Table.new("ConstantOrWriteNode") + id = node_id(node) + + # name + table.field("name", node.name.inspect) + + # name_loc + table.field("name_loc", location_inspect(node.name_loc)) + + # operator_loc + table.field("operator_loc", location_inspect(node.operator_loc)) + + # value + table.field("value", port: true) + digraph.edge("#{id}:value -> #{node_id(node.value)};") + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a ConstantPathAndWriteNode node. + def visit_constant_path_and_write_node(node) + table = Table.new("ConstantPathAndWriteNode") + id = node_id(node) + + # target + table.field("target", port: true) + digraph.edge("#{id}:target -> #{node_id(node.target)};") + + # operator_loc + table.field("operator_loc", location_inspect(node.operator_loc)) + + # value + table.field("value", port: true) + digraph.edge("#{id}:value -> #{node_id(node.value)};") + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a ConstantPathNode node. + def visit_constant_path_node(node) + table = Table.new("ConstantPathNode") + id = node_id(node) + + # parent + unless (parent = node.parent).nil? + table.field("parent", port: true) + digraph.edge("#{id}:parent -> #{node_id(parent)};") + end + + # name + table.field("name", node.name.inspect) + + # delimiter_loc + table.field("delimiter_loc", location_inspect(node.delimiter_loc)) + + # name_loc + table.field("name_loc", location_inspect(node.name_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a ConstantPathOperatorWriteNode node. + def visit_constant_path_operator_write_node(node) + table = Table.new("ConstantPathOperatorWriteNode") + id = node_id(node) + + # target + table.field("target", port: true) + digraph.edge("#{id}:target -> #{node_id(node.target)};") + + # binary_operator_loc + table.field("binary_operator_loc", location_inspect(node.binary_operator_loc)) + + # value + table.field("value", port: true) + digraph.edge("#{id}:value -> #{node_id(node.value)};") + + # binary_operator + table.field("binary_operator", node.binary_operator.inspect) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a ConstantPathOrWriteNode node. + def visit_constant_path_or_write_node(node) + table = Table.new("ConstantPathOrWriteNode") + id = node_id(node) + + # target + table.field("target", port: true) + digraph.edge("#{id}:target -> #{node_id(node.target)};") + + # operator_loc + table.field("operator_loc", location_inspect(node.operator_loc)) + + # value + table.field("value", port: true) + digraph.edge("#{id}:value -> #{node_id(node.value)};") + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a ConstantPathTargetNode node. + def visit_constant_path_target_node(node) + table = Table.new("ConstantPathTargetNode") + id = node_id(node) + + # parent + unless (parent = node.parent).nil? + table.field("parent", port: true) + digraph.edge("#{id}:parent -> #{node_id(parent)};") + end + + # name + table.field("name", node.name.inspect) + + # delimiter_loc + table.field("delimiter_loc", location_inspect(node.delimiter_loc)) + + # name_loc + table.field("name_loc", location_inspect(node.name_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a ConstantPathWriteNode node. + def visit_constant_path_write_node(node) + table = Table.new("ConstantPathWriteNode") + id = node_id(node) + + # target + table.field("target", port: true) + digraph.edge("#{id}:target -> #{node_id(node.target)};") + + # operator_loc + table.field("operator_loc", location_inspect(node.operator_loc)) + + # value + table.field("value", port: true) + digraph.edge("#{id}:value -> #{node_id(node.value)};") + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a ConstantReadNode node. + def visit_constant_read_node(node) + table = Table.new("ConstantReadNode") + id = node_id(node) + + # name + table.field("name", node.name.inspect) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a ConstantTargetNode node. + def visit_constant_target_node(node) + table = Table.new("ConstantTargetNode") + id = node_id(node) + + # name + table.field("name", node.name.inspect) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a ConstantWriteNode node. + def visit_constant_write_node(node) + table = Table.new("ConstantWriteNode") + id = node_id(node) + + # name + table.field("name", node.name.inspect) + + # name_loc + table.field("name_loc", location_inspect(node.name_loc)) + + # value + table.field("value", port: true) + digraph.edge("#{id}:value -> #{node_id(node.value)};") + + # operator_loc + table.field("operator_loc", location_inspect(node.operator_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a DefNode node. + def visit_def_node(node) + table = Table.new("DefNode") + id = node_id(node) + + # name + table.field("name", node.name.inspect) + + # name_loc + table.field("name_loc", location_inspect(node.name_loc)) + + # receiver + unless (receiver = node.receiver).nil? + table.field("receiver", port: true) + digraph.edge("#{id}:receiver -> #{node_id(receiver)};") + end + + # parameters + unless (parameters = node.parameters).nil? + table.field("parameters", port: true) + digraph.edge("#{id}:parameters -> #{node_id(parameters)};") + end + + # body + unless (body = node.body).nil? + table.field("body", port: true) + digraph.edge("#{id}:body -> #{node_id(body)};") + end + + # locals + table.field("locals", node.locals.inspect) + + # def_keyword_loc + table.field("def_keyword_loc", location_inspect(node.def_keyword_loc)) + + # operator_loc + unless (operator_loc = node.operator_loc).nil? + table.field("operator_loc", location_inspect(operator_loc)) + end + + # lparen_loc + unless (lparen_loc = node.lparen_loc).nil? + table.field("lparen_loc", location_inspect(lparen_loc)) + end + + # rparen_loc + unless (rparen_loc = node.rparen_loc).nil? + table.field("rparen_loc", location_inspect(rparen_loc)) + end + + # equal_loc + unless (equal_loc = node.equal_loc).nil? + table.field("equal_loc", location_inspect(equal_loc)) + end + + # end_keyword_loc + unless (end_keyword_loc = node.end_keyword_loc).nil? + table.field("end_keyword_loc", location_inspect(end_keyword_loc)) + end + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a DefinedNode node. + def visit_defined_node(node) + table = Table.new("DefinedNode") + id = node_id(node) + + # lparen_loc + unless (lparen_loc = node.lparen_loc).nil? + table.field("lparen_loc", location_inspect(lparen_loc)) + end + + # value + table.field("value", port: true) + digraph.edge("#{id}:value -> #{node_id(node.value)};") + + # rparen_loc + unless (rparen_loc = node.rparen_loc).nil? + table.field("rparen_loc", location_inspect(rparen_loc)) + end + + # keyword_loc + table.field("keyword_loc", location_inspect(node.keyword_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a ElseNode node. + def visit_else_node(node) + table = Table.new("ElseNode") + id = node_id(node) + + # else_keyword_loc + table.field("else_keyword_loc", location_inspect(node.else_keyword_loc)) + + # statements + unless (statements = node.statements).nil? + table.field("statements", port: true) + digraph.edge("#{id}:statements -> #{node_id(statements)};") + end + + # end_keyword_loc + unless (end_keyword_loc = node.end_keyword_loc).nil? + table.field("end_keyword_loc", location_inspect(end_keyword_loc)) + end + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a EmbeddedStatementsNode node. + def visit_embedded_statements_node(node) + table = Table.new("EmbeddedStatementsNode") + id = node_id(node) + + # opening_loc + table.field("opening_loc", location_inspect(node.opening_loc)) + + # statements + unless (statements = node.statements).nil? + table.field("statements", port: true) + digraph.edge("#{id}:statements -> #{node_id(statements)};") + end + + # closing_loc + table.field("closing_loc", location_inspect(node.closing_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a EmbeddedVariableNode node. + def visit_embedded_variable_node(node) + table = Table.new("EmbeddedVariableNode") + id = node_id(node) + + # operator_loc + table.field("operator_loc", location_inspect(node.operator_loc)) + + # variable + table.field("variable", port: true) + digraph.edge("#{id}:variable -> #{node_id(node.variable)};") + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a EnsureNode node. + def visit_ensure_node(node) + table = Table.new("EnsureNode") + id = node_id(node) + + # ensure_keyword_loc + table.field("ensure_keyword_loc", location_inspect(node.ensure_keyword_loc)) + + # statements + unless (statements = node.statements).nil? + table.field("statements", port: true) + digraph.edge("#{id}:statements -> #{node_id(statements)};") + end + + # end_keyword_loc + table.field("end_keyword_loc", location_inspect(node.end_keyword_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a FalseNode node. + def visit_false_node(node) + table = Table.new("FalseNode") + id = node_id(node) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a FindPatternNode node. + def visit_find_pattern_node(node) + table = Table.new("FindPatternNode") + id = node_id(node) + + # constant + unless (constant = node.constant).nil? + table.field("constant", port: true) + digraph.edge("#{id}:constant -> #{node_id(constant)};") + end + + # left + table.field("left", port: true) + digraph.edge("#{id}:left -> #{node_id(node.left)};") + + # requireds + if node.requireds.any? + table.field("requireds", port: true) + + waypoint = "#{id}_requireds" + digraph.waypoint("#{waypoint};") + + digraph.edge("#{id}:requireds -> #{waypoint};") + node.requireds.each { |child| digraph.edge("#{waypoint} -> #{node_id(child)};") } + else + table.field("requireds", "[]") + end + + # right + table.field("right", port: true) + digraph.edge("#{id}:right -> #{node_id(node.right)};") + + # opening_loc + unless (opening_loc = node.opening_loc).nil? + table.field("opening_loc", location_inspect(opening_loc)) + end + + # closing_loc + unless (closing_loc = node.closing_loc).nil? + table.field("closing_loc", location_inspect(closing_loc)) + end + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a FlipFlopNode node. + def visit_flip_flop_node(node) + table = Table.new("FlipFlopNode") + id = node_id(node) + + # flags + table.field("flags", range_flags_inspect(node)) + + # left + unless (left = node.left).nil? + table.field("left", port: true) + digraph.edge("#{id}:left -> #{node_id(left)};") + end + + # right + unless (right = node.right).nil? + table.field("right", port: true) + digraph.edge("#{id}:right -> #{node_id(right)};") + end + + # operator_loc + table.field("operator_loc", location_inspect(node.operator_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a FloatNode node. + def visit_float_node(node) + table = Table.new("FloatNode") + id = node_id(node) + + # value + table.field("value", node.value.inspect) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a ForNode node. + def visit_for_node(node) + table = Table.new("ForNode") + id = node_id(node) + + # index + table.field("index", port: true) + digraph.edge("#{id}:index -> #{node_id(node.index)};") + + # collection + table.field("collection", port: true) + digraph.edge("#{id}:collection -> #{node_id(node.collection)};") + + # statements + unless (statements = node.statements).nil? + table.field("statements", port: true) + digraph.edge("#{id}:statements -> #{node_id(statements)};") + end + + # for_keyword_loc + table.field("for_keyword_loc", location_inspect(node.for_keyword_loc)) + + # in_keyword_loc + table.field("in_keyword_loc", location_inspect(node.in_keyword_loc)) + + # do_keyword_loc + unless (do_keyword_loc = node.do_keyword_loc).nil? + table.field("do_keyword_loc", location_inspect(do_keyword_loc)) + end + + # end_keyword_loc + table.field("end_keyword_loc", location_inspect(node.end_keyword_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a ForwardingArgumentsNode node. + def visit_forwarding_arguments_node(node) + table = Table.new("ForwardingArgumentsNode") + id = node_id(node) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a ForwardingParameterNode node. + def visit_forwarding_parameter_node(node) + table = Table.new("ForwardingParameterNode") + id = node_id(node) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a ForwardingSuperNode node. + def visit_forwarding_super_node(node) + table = Table.new("ForwardingSuperNode") + id = node_id(node) + + # block + unless (block = node.block).nil? + table.field("block", port: true) + digraph.edge("#{id}:block -> #{node_id(block)};") + end + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a GlobalVariableAndWriteNode node. + def visit_global_variable_and_write_node(node) + table = Table.new("GlobalVariableAndWriteNode") + id = node_id(node) + + # name + table.field("name", node.name.inspect) + + # name_loc + table.field("name_loc", location_inspect(node.name_loc)) + + # operator_loc + table.field("operator_loc", location_inspect(node.operator_loc)) + + # value + table.field("value", port: true) + digraph.edge("#{id}:value -> #{node_id(node.value)};") + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a GlobalVariableOperatorWriteNode node. + def visit_global_variable_operator_write_node(node) + table = Table.new("GlobalVariableOperatorWriteNode") + id = node_id(node) + + # name + table.field("name", node.name.inspect) + + # name_loc + table.field("name_loc", location_inspect(node.name_loc)) + + # binary_operator_loc + table.field("binary_operator_loc", location_inspect(node.binary_operator_loc)) + + # value + table.field("value", port: true) + digraph.edge("#{id}:value -> #{node_id(node.value)};") + + # binary_operator + table.field("binary_operator", node.binary_operator.inspect) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a GlobalVariableOrWriteNode node. + def visit_global_variable_or_write_node(node) + table = Table.new("GlobalVariableOrWriteNode") + id = node_id(node) + + # name + table.field("name", node.name.inspect) + + # name_loc + table.field("name_loc", location_inspect(node.name_loc)) + + # operator_loc + table.field("operator_loc", location_inspect(node.operator_loc)) + + # value + table.field("value", port: true) + digraph.edge("#{id}:value -> #{node_id(node.value)};") + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a GlobalVariableReadNode node. + def visit_global_variable_read_node(node) + table = Table.new("GlobalVariableReadNode") + id = node_id(node) + + # name + table.field("name", node.name.inspect) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a GlobalVariableTargetNode node. + def visit_global_variable_target_node(node) + table = Table.new("GlobalVariableTargetNode") + id = node_id(node) + + # name + table.field("name", node.name.inspect) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a GlobalVariableWriteNode node. + def visit_global_variable_write_node(node) + table = Table.new("GlobalVariableWriteNode") + id = node_id(node) + + # name + table.field("name", node.name.inspect) + + # name_loc + table.field("name_loc", location_inspect(node.name_loc)) + + # value + table.field("value", port: true) + digraph.edge("#{id}:value -> #{node_id(node.value)};") + + # operator_loc + table.field("operator_loc", location_inspect(node.operator_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a HashNode node. + def visit_hash_node(node) + table = Table.new("HashNode") + id = node_id(node) + + # opening_loc + table.field("opening_loc", location_inspect(node.opening_loc)) + + # elements + if node.elements.any? + table.field("elements", port: true) + + waypoint = "#{id}_elements" + digraph.waypoint("#{waypoint};") + + digraph.edge("#{id}:elements -> #{waypoint};") + node.elements.each { |child| digraph.edge("#{waypoint} -> #{node_id(child)};") } + else + table.field("elements", "[]") + end + + # closing_loc + table.field("closing_loc", location_inspect(node.closing_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a HashPatternNode node. + def visit_hash_pattern_node(node) + table = Table.new("HashPatternNode") + id = node_id(node) + + # constant + unless (constant = node.constant).nil? + table.field("constant", port: true) + digraph.edge("#{id}:constant -> #{node_id(constant)};") + end + + # elements + if node.elements.any? + table.field("elements", port: true) + + waypoint = "#{id}_elements" + digraph.waypoint("#{waypoint};") + + digraph.edge("#{id}:elements -> #{waypoint};") + node.elements.each { |child| digraph.edge("#{waypoint} -> #{node_id(child)};") } + else + table.field("elements", "[]") + end + + # rest + unless (rest = node.rest).nil? + table.field("rest", port: true) + digraph.edge("#{id}:rest -> #{node_id(rest)};") + end + + # opening_loc + unless (opening_loc = node.opening_loc).nil? + table.field("opening_loc", location_inspect(opening_loc)) + end + + # closing_loc + unless (closing_loc = node.closing_loc).nil? + table.field("closing_loc", location_inspect(closing_loc)) + end + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a IfNode node. + def visit_if_node(node) + table = Table.new("IfNode") + id = node_id(node) + + # if_keyword_loc + unless (if_keyword_loc = node.if_keyword_loc).nil? + table.field("if_keyword_loc", location_inspect(if_keyword_loc)) + end + + # predicate + table.field("predicate", port: true) + digraph.edge("#{id}:predicate -> #{node_id(node.predicate)};") + + # then_keyword_loc + unless (then_keyword_loc = node.then_keyword_loc).nil? + table.field("then_keyword_loc", location_inspect(then_keyword_loc)) + end + + # statements + unless (statements = node.statements).nil? + table.field("statements", port: true) + digraph.edge("#{id}:statements -> #{node_id(statements)};") + end + + # subsequent + unless (subsequent = node.subsequent).nil? + table.field("subsequent", port: true) + digraph.edge("#{id}:subsequent -> #{node_id(subsequent)};") + end + + # end_keyword_loc + unless (end_keyword_loc = node.end_keyword_loc).nil? + table.field("end_keyword_loc", location_inspect(end_keyword_loc)) + end + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a ImaginaryNode node. + def visit_imaginary_node(node) + table = Table.new("ImaginaryNode") + id = node_id(node) + + # numeric + table.field("numeric", port: true) + digraph.edge("#{id}:numeric -> #{node_id(node.numeric)};") + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a ImplicitNode node. + def visit_implicit_node(node) + table = Table.new("ImplicitNode") + id = node_id(node) + + # value + table.field("value", port: true) + digraph.edge("#{id}:value -> #{node_id(node.value)};") + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a ImplicitRestNode node. + def visit_implicit_rest_node(node) + table = Table.new("ImplicitRestNode") + id = node_id(node) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a InNode node. + def visit_in_node(node) + table = Table.new("InNode") + id = node_id(node) + + # pattern + table.field("pattern", port: true) + digraph.edge("#{id}:pattern -> #{node_id(node.pattern)};") + + # statements + unless (statements = node.statements).nil? + table.field("statements", port: true) + digraph.edge("#{id}:statements -> #{node_id(statements)};") + end + + # in_loc + table.field("in_loc", location_inspect(node.in_loc)) + + # then_loc + unless (then_loc = node.then_loc).nil? + table.field("then_loc", location_inspect(then_loc)) + end + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a IndexAndWriteNode node. + def visit_index_and_write_node(node) + table = Table.new("IndexAndWriteNode") + id = node_id(node) + + # flags + table.field("flags", call_node_flags_inspect(node)) + + # receiver + unless (receiver = node.receiver).nil? + table.field("receiver", port: true) + digraph.edge("#{id}:receiver -> #{node_id(receiver)};") + end + + # call_operator_loc + unless (call_operator_loc = node.call_operator_loc).nil? + table.field("call_operator_loc", location_inspect(call_operator_loc)) + end + + # opening_loc + table.field("opening_loc", location_inspect(node.opening_loc)) + + # arguments + unless (arguments = node.arguments).nil? + table.field("arguments", port: true) + digraph.edge("#{id}:arguments -> #{node_id(arguments)};") + end + + # closing_loc + table.field("closing_loc", location_inspect(node.closing_loc)) + + # block + unless (block = node.block).nil? + table.field("block", port: true) + digraph.edge("#{id}:block -> #{node_id(block)};") + end + + # operator_loc + table.field("operator_loc", location_inspect(node.operator_loc)) + + # value + table.field("value", port: true) + digraph.edge("#{id}:value -> #{node_id(node.value)};") + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a IndexOperatorWriteNode node. + def visit_index_operator_write_node(node) + table = Table.new("IndexOperatorWriteNode") + id = node_id(node) + + # flags + table.field("flags", call_node_flags_inspect(node)) + + # receiver + unless (receiver = node.receiver).nil? + table.field("receiver", port: true) + digraph.edge("#{id}:receiver -> #{node_id(receiver)};") + end + + # call_operator_loc + unless (call_operator_loc = node.call_operator_loc).nil? + table.field("call_operator_loc", location_inspect(call_operator_loc)) + end + + # opening_loc + table.field("opening_loc", location_inspect(node.opening_loc)) + + # arguments + unless (arguments = node.arguments).nil? + table.field("arguments", port: true) + digraph.edge("#{id}:arguments -> #{node_id(arguments)};") + end + + # closing_loc + table.field("closing_loc", location_inspect(node.closing_loc)) + + # block + unless (block = node.block).nil? + table.field("block", port: true) + digraph.edge("#{id}:block -> #{node_id(block)};") + end + + # binary_operator + table.field("binary_operator", node.binary_operator.inspect) + + # binary_operator_loc + table.field("binary_operator_loc", location_inspect(node.binary_operator_loc)) + + # value + table.field("value", port: true) + digraph.edge("#{id}:value -> #{node_id(node.value)};") + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a IndexOrWriteNode node. + def visit_index_or_write_node(node) + table = Table.new("IndexOrWriteNode") + id = node_id(node) + + # flags + table.field("flags", call_node_flags_inspect(node)) + + # receiver + unless (receiver = node.receiver).nil? + table.field("receiver", port: true) + digraph.edge("#{id}:receiver -> #{node_id(receiver)};") + end + + # call_operator_loc + unless (call_operator_loc = node.call_operator_loc).nil? + table.field("call_operator_loc", location_inspect(call_operator_loc)) + end + + # opening_loc + table.field("opening_loc", location_inspect(node.opening_loc)) + + # arguments + unless (arguments = node.arguments).nil? + table.field("arguments", port: true) + digraph.edge("#{id}:arguments -> #{node_id(arguments)};") + end + + # closing_loc + table.field("closing_loc", location_inspect(node.closing_loc)) + + # block + unless (block = node.block).nil? + table.field("block", port: true) + digraph.edge("#{id}:block -> #{node_id(block)};") + end + + # operator_loc + table.field("operator_loc", location_inspect(node.operator_loc)) + + # value + table.field("value", port: true) + digraph.edge("#{id}:value -> #{node_id(node.value)};") + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a IndexTargetNode node. + def visit_index_target_node(node) + table = Table.new("IndexTargetNode") + id = node_id(node) + + # flags + table.field("flags", call_node_flags_inspect(node)) + + # receiver + table.field("receiver", port: true) + digraph.edge("#{id}:receiver -> #{node_id(node.receiver)};") + + # opening_loc + table.field("opening_loc", location_inspect(node.opening_loc)) + + # arguments + unless (arguments = node.arguments).nil? + table.field("arguments", port: true) + digraph.edge("#{id}:arguments -> #{node_id(arguments)};") + end + + # closing_loc + table.field("closing_loc", location_inspect(node.closing_loc)) + + # block + unless (block = node.block).nil? + table.field("block", port: true) + digraph.edge("#{id}:block -> #{node_id(block)};") + end + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a InstanceVariableAndWriteNode node. + def visit_instance_variable_and_write_node(node) + table = Table.new("InstanceVariableAndWriteNode") + id = node_id(node) + + # name + table.field("name", node.name.inspect) + + # name_loc + table.field("name_loc", location_inspect(node.name_loc)) + + # operator_loc + table.field("operator_loc", location_inspect(node.operator_loc)) + + # value + table.field("value", port: true) + digraph.edge("#{id}:value -> #{node_id(node.value)};") + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a InstanceVariableOperatorWriteNode node. + def visit_instance_variable_operator_write_node(node) + table = Table.new("InstanceVariableOperatorWriteNode") + id = node_id(node) + + # name + table.field("name", node.name.inspect) + + # name_loc + table.field("name_loc", location_inspect(node.name_loc)) + + # binary_operator_loc + table.field("binary_operator_loc", location_inspect(node.binary_operator_loc)) + + # value + table.field("value", port: true) + digraph.edge("#{id}:value -> #{node_id(node.value)};") + + # binary_operator + table.field("binary_operator", node.binary_operator.inspect) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a InstanceVariableOrWriteNode node. + def visit_instance_variable_or_write_node(node) + table = Table.new("InstanceVariableOrWriteNode") + id = node_id(node) + + # name + table.field("name", node.name.inspect) + + # name_loc + table.field("name_loc", location_inspect(node.name_loc)) + + # operator_loc + table.field("operator_loc", location_inspect(node.operator_loc)) + + # value + table.field("value", port: true) + digraph.edge("#{id}:value -> #{node_id(node.value)};") + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a InstanceVariableReadNode node. + def visit_instance_variable_read_node(node) + table = Table.new("InstanceVariableReadNode") + id = node_id(node) + + # name + table.field("name", node.name.inspect) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a InstanceVariableTargetNode node. + def visit_instance_variable_target_node(node) + table = Table.new("InstanceVariableTargetNode") + id = node_id(node) + + # name + table.field("name", node.name.inspect) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a InstanceVariableWriteNode node. + def visit_instance_variable_write_node(node) + table = Table.new("InstanceVariableWriteNode") + id = node_id(node) + + # name + table.field("name", node.name.inspect) + + # name_loc + table.field("name_loc", location_inspect(node.name_loc)) + + # value + table.field("value", port: true) + digraph.edge("#{id}:value -> #{node_id(node.value)};") + + # operator_loc + table.field("operator_loc", location_inspect(node.operator_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a IntegerNode node. + def visit_integer_node(node) + table = Table.new("IntegerNode") + id = node_id(node) + + # flags + table.field("flags", integer_base_flags_inspect(node)) + + # value + table.field("value", node.value.inspect) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a InterpolatedMatchLastLineNode node. + def visit_interpolated_match_last_line_node(node) + table = Table.new("InterpolatedMatchLastLineNode") + id = node_id(node) + + # flags + table.field("flags", regular_expression_flags_inspect(node)) + + # opening_loc + table.field("opening_loc", location_inspect(node.opening_loc)) + + # parts + if node.parts.any? + table.field("parts", port: true) + + waypoint = "#{id}_parts" + digraph.waypoint("#{waypoint};") + + digraph.edge("#{id}:parts -> #{waypoint};") + node.parts.each { |child| digraph.edge("#{waypoint} -> #{node_id(child)};") } + else + table.field("parts", "[]") + end + + # closing_loc + table.field("closing_loc", location_inspect(node.closing_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a InterpolatedRegularExpressionNode node. + def visit_interpolated_regular_expression_node(node) + table = Table.new("InterpolatedRegularExpressionNode") + id = node_id(node) + + # flags + table.field("flags", regular_expression_flags_inspect(node)) + + # opening_loc + table.field("opening_loc", location_inspect(node.opening_loc)) + + # parts + if node.parts.any? + table.field("parts", port: true) + + waypoint = "#{id}_parts" + digraph.waypoint("#{waypoint};") + + digraph.edge("#{id}:parts -> #{waypoint};") + node.parts.each { |child| digraph.edge("#{waypoint} -> #{node_id(child)};") } + else + table.field("parts", "[]") + end + + # closing_loc + table.field("closing_loc", location_inspect(node.closing_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a InterpolatedStringNode node. + def visit_interpolated_string_node(node) + table = Table.new("InterpolatedStringNode") + id = node_id(node) + + # flags + table.field("flags", interpolated_string_node_flags_inspect(node)) + + # opening_loc + unless (opening_loc = node.opening_loc).nil? + table.field("opening_loc", location_inspect(opening_loc)) + end + + # parts + if node.parts.any? + table.field("parts", port: true) + + waypoint = "#{id}_parts" + digraph.waypoint("#{waypoint};") + + digraph.edge("#{id}:parts -> #{waypoint};") + node.parts.each { |child| digraph.edge("#{waypoint} -> #{node_id(child)};") } + else + table.field("parts", "[]") + end + + # closing_loc + unless (closing_loc = node.closing_loc).nil? + table.field("closing_loc", location_inspect(closing_loc)) + end + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a InterpolatedSymbolNode node. + def visit_interpolated_symbol_node(node) + table = Table.new("InterpolatedSymbolNode") + id = node_id(node) + + # opening_loc + unless (opening_loc = node.opening_loc).nil? + table.field("opening_loc", location_inspect(opening_loc)) + end + + # parts + if node.parts.any? + table.field("parts", port: true) + + waypoint = "#{id}_parts" + digraph.waypoint("#{waypoint};") + + digraph.edge("#{id}:parts -> #{waypoint};") + node.parts.each { |child| digraph.edge("#{waypoint} -> #{node_id(child)};") } + else + table.field("parts", "[]") + end + + # closing_loc + unless (closing_loc = node.closing_loc).nil? + table.field("closing_loc", location_inspect(closing_loc)) + end + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a InterpolatedXStringNode node. + def visit_interpolated_x_string_node(node) + table = Table.new("InterpolatedXStringNode") + id = node_id(node) + + # opening_loc + table.field("opening_loc", location_inspect(node.opening_loc)) + + # parts + if node.parts.any? + table.field("parts", port: true) + + waypoint = "#{id}_parts" + digraph.waypoint("#{waypoint};") + + digraph.edge("#{id}:parts -> #{waypoint};") + node.parts.each { |child| digraph.edge("#{waypoint} -> #{node_id(child)};") } + else + table.field("parts", "[]") + end + + # closing_loc + table.field("closing_loc", location_inspect(node.closing_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a ItLocalVariableReadNode node. + def visit_it_local_variable_read_node(node) + table = Table.new("ItLocalVariableReadNode") + id = node_id(node) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a ItParametersNode node. + def visit_it_parameters_node(node) + table = Table.new("ItParametersNode") + id = node_id(node) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a KeywordHashNode node. + def visit_keyword_hash_node(node) + table = Table.new("KeywordHashNode") + id = node_id(node) + + # flags + table.field("flags", keyword_hash_node_flags_inspect(node)) + + # elements + if node.elements.any? + table.field("elements", port: true) + + waypoint = "#{id}_elements" + digraph.waypoint("#{waypoint};") + + digraph.edge("#{id}:elements -> #{waypoint};") + node.elements.each { |child| digraph.edge("#{waypoint} -> #{node_id(child)};") } + else + table.field("elements", "[]") + end + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a KeywordRestParameterNode node. + def visit_keyword_rest_parameter_node(node) + table = Table.new("KeywordRestParameterNode") + id = node_id(node) + + # flags + table.field("flags", parameter_flags_inspect(node)) + + # name + table.field("name", node.name.inspect) + + # name_loc + unless (name_loc = node.name_loc).nil? + table.field("name_loc", location_inspect(name_loc)) + end + + # operator_loc + table.field("operator_loc", location_inspect(node.operator_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a LambdaNode node. + def visit_lambda_node(node) + table = Table.new("LambdaNode") + id = node_id(node) + + # locals + table.field("locals", node.locals.inspect) + + # operator_loc + table.field("operator_loc", location_inspect(node.operator_loc)) + + # opening_loc + table.field("opening_loc", location_inspect(node.opening_loc)) + + # closing_loc + table.field("closing_loc", location_inspect(node.closing_loc)) + + # parameters + unless (parameters = node.parameters).nil? + table.field("parameters", port: true) + digraph.edge("#{id}:parameters -> #{node_id(parameters)};") + end + + # body + unless (body = node.body).nil? + table.field("body", port: true) + digraph.edge("#{id}:body -> #{node_id(body)};") + end + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a LocalVariableAndWriteNode node. + def visit_local_variable_and_write_node(node) + table = Table.new("LocalVariableAndWriteNode") + id = node_id(node) + + # name_loc + table.field("name_loc", location_inspect(node.name_loc)) + + # operator_loc + table.field("operator_loc", location_inspect(node.operator_loc)) + + # value + table.field("value", port: true) + digraph.edge("#{id}:value -> #{node_id(node.value)};") + + # name + table.field("name", node.name.inspect) + + # depth + table.field("depth", node.depth.inspect) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a LocalVariableOperatorWriteNode node. + def visit_local_variable_operator_write_node(node) + table = Table.new("LocalVariableOperatorWriteNode") + id = node_id(node) + + # name_loc + table.field("name_loc", location_inspect(node.name_loc)) + + # binary_operator_loc + table.field("binary_operator_loc", location_inspect(node.binary_operator_loc)) + + # value + table.field("value", port: true) + digraph.edge("#{id}:value -> #{node_id(node.value)};") + + # name + table.field("name", node.name.inspect) + + # binary_operator + table.field("binary_operator", node.binary_operator.inspect) + + # depth + table.field("depth", node.depth.inspect) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a LocalVariableOrWriteNode node. + def visit_local_variable_or_write_node(node) + table = Table.new("LocalVariableOrWriteNode") + id = node_id(node) + + # name_loc + table.field("name_loc", location_inspect(node.name_loc)) + + # operator_loc + table.field("operator_loc", location_inspect(node.operator_loc)) + + # value + table.field("value", port: true) + digraph.edge("#{id}:value -> #{node_id(node.value)};") + + # name + table.field("name", node.name.inspect) + + # depth + table.field("depth", node.depth.inspect) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a LocalVariableReadNode node. + def visit_local_variable_read_node(node) + table = Table.new("LocalVariableReadNode") + id = node_id(node) + + # name + table.field("name", node.name.inspect) + + # depth + table.field("depth", node.depth.inspect) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a LocalVariableTargetNode node. + def visit_local_variable_target_node(node) + table = Table.new("LocalVariableTargetNode") + id = node_id(node) + + # name + table.field("name", node.name.inspect) + + # depth + table.field("depth", node.depth.inspect) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a LocalVariableWriteNode node. + def visit_local_variable_write_node(node) + table = Table.new("LocalVariableWriteNode") + id = node_id(node) + + # name + table.field("name", node.name.inspect) + + # depth + table.field("depth", node.depth.inspect) + + # name_loc + table.field("name_loc", location_inspect(node.name_loc)) + + # value + table.field("value", port: true) + digraph.edge("#{id}:value -> #{node_id(node.value)};") + + # operator_loc + table.field("operator_loc", location_inspect(node.operator_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a MatchLastLineNode node. + def visit_match_last_line_node(node) + table = Table.new("MatchLastLineNode") + id = node_id(node) + + # flags + table.field("flags", regular_expression_flags_inspect(node)) + + # opening_loc + table.field("opening_loc", location_inspect(node.opening_loc)) + + # content_loc + table.field("content_loc", location_inspect(node.content_loc)) + + # closing_loc + table.field("closing_loc", location_inspect(node.closing_loc)) + + # unescaped + table.field("unescaped", node.unescaped.inspect) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a MatchPredicateNode node. + def visit_match_predicate_node(node) + table = Table.new("MatchPredicateNode") + id = node_id(node) + + # value + table.field("value", port: true) + digraph.edge("#{id}:value -> #{node_id(node.value)};") + + # pattern + table.field("pattern", port: true) + digraph.edge("#{id}:pattern -> #{node_id(node.pattern)};") + + # operator_loc + table.field("operator_loc", location_inspect(node.operator_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a MatchRequiredNode node. + def visit_match_required_node(node) + table = Table.new("MatchRequiredNode") + id = node_id(node) + + # value + table.field("value", port: true) + digraph.edge("#{id}:value -> #{node_id(node.value)};") + + # pattern + table.field("pattern", port: true) + digraph.edge("#{id}:pattern -> #{node_id(node.pattern)};") + + # operator_loc + table.field("operator_loc", location_inspect(node.operator_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a MatchWriteNode node. + def visit_match_write_node(node) + table = Table.new("MatchWriteNode") + id = node_id(node) + + # call + table.field("call", port: true) + digraph.edge("#{id}:call -> #{node_id(node.call)};") + + # targets + if node.targets.any? + table.field("targets", port: true) + + waypoint = "#{id}_targets" + digraph.waypoint("#{waypoint};") + + digraph.edge("#{id}:targets -> #{waypoint};") + node.targets.each { |child| digraph.edge("#{waypoint} -> #{node_id(child)};") } + else + table.field("targets", "[]") + end + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a MissingNode node. + def visit_missing_node(node) + table = Table.new("MissingNode") + id = node_id(node) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a ModuleNode node. + def visit_module_node(node) + table = Table.new("ModuleNode") + id = node_id(node) + + # locals + table.field("locals", node.locals.inspect) + + # module_keyword_loc + table.field("module_keyword_loc", location_inspect(node.module_keyword_loc)) + + # constant_path + table.field("constant_path", port: true) + digraph.edge("#{id}:constant_path -> #{node_id(node.constant_path)};") + + # body + unless (body = node.body).nil? + table.field("body", port: true) + digraph.edge("#{id}:body -> #{node_id(body)};") + end + + # end_keyword_loc + table.field("end_keyword_loc", location_inspect(node.end_keyword_loc)) + + # name + table.field("name", node.name.inspect) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a MultiTargetNode node. + def visit_multi_target_node(node) + table = Table.new("MultiTargetNode") + id = node_id(node) + + # lefts + if node.lefts.any? + table.field("lefts", port: true) + + waypoint = "#{id}_lefts" + digraph.waypoint("#{waypoint};") + + digraph.edge("#{id}:lefts -> #{waypoint};") + node.lefts.each { |child| digraph.edge("#{waypoint} -> #{node_id(child)};") } + else + table.field("lefts", "[]") + end + + # rest + unless (rest = node.rest).nil? + table.field("rest", port: true) + digraph.edge("#{id}:rest -> #{node_id(rest)};") + end + + # rights + if node.rights.any? + table.field("rights", port: true) + + waypoint = "#{id}_rights" + digraph.waypoint("#{waypoint};") + + digraph.edge("#{id}:rights -> #{waypoint};") + node.rights.each { |child| digraph.edge("#{waypoint} -> #{node_id(child)};") } + else + table.field("rights", "[]") + end + + # lparen_loc + unless (lparen_loc = node.lparen_loc).nil? + table.field("lparen_loc", location_inspect(lparen_loc)) + end + + # rparen_loc + unless (rparen_loc = node.rparen_loc).nil? + table.field("rparen_loc", location_inspect(rparen_loc)) + end + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a MultiWriteNode node. + def visit_multi_write_node(node) + table = Table.new("MultiWriteNode") + id = node_id(node) + + # lefts + if node.lefts.any? + table.field("lefts", port: true) + + waypoint = "#{id}_lefts" + digraph.waypoint("#{waypoint};") + + digraph.edge("#{id}:lefts -> #{waypoint};") + node.lefts.each { |child| digraph.edge("#{waypoint} -> #{node_id(child)};") } + else + table.field("lefts", "[]") + end + + # rest + unless (rest = node.rest).nil? + table.field("rest", port: true) + digraph.edge("#{id}:rest -> #{node_id(rest)};") + end + + # rights + if node.rights.any? + table.field("rights", port: true) + + waypoint = "#{id}_rights" + digraph.waypoint("#{waypoint};") + + digraph.edge("#{id}:rights -> #{waypoint};") + node.rights.each { |child| digraph.edge("#{waypoint} -> #{node_id(child)};") } + else + table.field("rights", "[]") + end + + # lparen_loc + unless (lparen_loc = node.lparen_loc).nil? + table.field("lparen_loc", location_inspect(lparen_loc)) + end + + # rparen_loc + unless (rparen_loc = node.rparen_loc).nil? + table.field("rparen_loc", location_inspect(rparen_loc)) + end + + # operator_loc + table.field("operator_loc", location_inspect(node.operator_loc)) + + # value + table.field("value", port: true) + digraph.edge("#{id}:value -> #{node_id(node.value)};") + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a NextNode node. + def visit_next_node(node) + table = Table.new("NextNode") + id = node_id(node) + + # arguments + unless (arguments = node.arguments).nil? + table.field("arguments", port: true) + digraph.edge("#{id}:arguments -> #{node_id(arguments)};") + end + + # keyword_loc + table.field("keyword_loc", location_inspect(node.keyword_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a NilNode node. + def visit_nil_node(node) + table = Table.new("NilNode") + id = node_id(node) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a NoKeywordsParameterNode node. + def visit_no_keywords_parameter_node(node) + table = Table.new("NoKeywordsParameterNode") + id = node_id(node) + + # operator_loc + table.field("operator_loc", location_inspect(node.operator_loc)) + + # keyword_loc + table.field("keyword_loc", location_inspect(node.keyword_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a NumberedParametersNode node. + def visit_numbered_parameters_node(node) + table = Table.new("NumberedParametersNode") + id = node_id(node) + + # maximum + table.field("maximum", node.maximum.inspect) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a NumberedReferenceReadNode node. + def visit_numbered_reference_read_node(node) + table = Table.new("NumberedReferenceReadNode") + id = node_id(node) + + # number + table.field("number", node.number.inspect) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a OptionalKeywordParameterNode node. + def visit_optional_keyword_parameter_node(node) + table = Table.new("OptionalKeywordParameterNode") + id = node_id(node) + + # flags + table.field("flags", parameter_flags_inspect(node)) + + # name + table.field("name", node.name.inspect) + + # name_loc + table.field("name_loc", location_inspect(node.name_loc)) + + # value + table.field("value", port: true) + digraph.edge("#{id}:value -> #{node_id(node.value)};") + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a OptionalParameterNode node. + def visit_optional_parameter_node(node) + table = Table.new("OptionalParameterNode") + id = node_id(node) + + # flags + table.field("flags", parameter_flags_inspect(node)) + + # name + table.field("name", node.name.inspect) + + # name_loc + table.field("name_loc", location_inspect(node.name_loc)) + + # operator_loc + table.field("operator_loc", location_inspect(node.operator_loc)) + + # value + table.field("value", port: true) + digraph.edge("#{id}:value -> #{node_id(node.value)};") + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a OrNode node. + def visit_or_node(node) + table = Table.new("OrNode") + id = node_id(node) + + # left + table.field("left", port: true) + digraph.edge("#{id}:left -> #{node_id(node.left)};") + + # right + table.field("right", port: true) + digraph.edge("#{id}:right -> #{node_id(node.right)};") + + # operator_loc + table.field("operator_loc", location_inspect(node.operator_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a ParametersNode node. + def visit_parameters_node(node) + table = Table.new("ParametersNode") + id = node_id(node) + + # requireds + if node.requireds.any? + table.field("requireds", port: true) + + waypoint = "#{id}_requireds" + digraph.waypoint("#{waypoint};") + + digraph.edge("#{id}:requireds -> #{waypoint};") + node.requireds.each { |child| digraph.edge("#{waypoint} -> #{node_id(child)};") } + else + table.field("requireds", "[]") + end + + # optionals + if node.optionals.any? + table.field("optionals", port: true) + + waypoint = "#{id}_optionals" + digraph.waypoint("#{waypoint};") + + digraph.edge("#{id}:optionals -> #{waypoint};") + node.optionals.each { |child| digraph.edge("#{waypoint} -> #{node_id(child)};") } + else + table.field("optionals", "[]") + end + + # rest + unless (rest = node.rest).nil? + table.field("rest", port: true) + digraph.edge("#{id}:rest -> #{node_id(rest)};") + end + + # posts + if node.posts.any? + table.field("posts", port: true) + + waypoint = "#{id}_posts" + digraph.waypoint("#{waypoint};") + + digraph.edge("#{id}:posts -> #{waypoint};") + node.posts.each { |child| digraph.edge("#{waypoint} -> #{node_id(child)};") } + else + table.field("posts", "[]") + end + + # keywords + if node.keywords.any? + table.field("keywords", port: true) + + waypoint = "#{id}_keywords" + digraph.waypoint("#{waypoint};") + + digraph.edge("#{id}:keywords -> #{waypoint};") + node.keywords.each { |child| digraph.edge("#{waypoint} -> #{node_id(child)};") } + else + table.field("keywords", "[]") + end + + # keyword_rest + unless (keyword_rest = node.keyword_rest).nil? + table.field("keyword_rest", port: true) + digraph.edge("#{id}:keyword_rest -> #{node_id(keyword_rest)};") + end + + # block + unless (block = node.block).nil? + table.field("block", port: true) + digraph.edge("#{id}:block -> #{node_id(block)};") + end + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a ParenthesesNode node. + def visit_parentheses_node(node) + table = Table.new("ParenthesesNode") + id = node_id(node) + + # flags + table.field("flags", parentheses_node_flags_inspect(node)) + + # body + unless (body = node.body).nil? + table.field("body", port: true) + digraph.edge("#{id}:body -> #{node_id(body)};") + end + + # opening_loc + table.field("opening_loc", location_inspect(node.opening_loc)) + + # closing_loc + table.field("closing_loc", location_inspect(node.closing_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a PinnedExpressionNode node. + def visit_pinned_expression_node(node) + table = Table.new("PinnedExpressionNode") + id = node_id(node) + + # expression + table.field("expression", port: true) + digraph.edge("#{id}:expression -> #{node_id(node.expression)};") + + # operator_loc + table.field("operator_loc", location_inspect(node.operator_loc)) + + # lparen_loc + table.field("lparen_loc", location_inspect(node.lparen_loc)) + + # rparen_loc + table.field("rparen_loc", location_inspect(node.rparen_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a PinnedVariableNode node. + def visit_pinned_variable_node(node) + table = Table.new("PinnedVariableNode") + id = node_id(node) + + # variable + table.field("variable", port: true) + digraph.edge("#{id}:variable -> #{node_id(node.variable)};") + + # operator_loc + table.field("operator_loc", location_inspect(node.operator_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a PostExecutionNode node. + def visit_post_execution_node(node) + table = Table.new("PostExecutionNode") + id = node_id(node) + + # statements + unless (statements = node.statements).nil? + table.field("statements", port: true) + digraph.edge("#{id}:statements -> #{node_id(statements)};") + end + + # keyword_loc + table.field("keyword_loc", location_inspect(node.keyword_loc)) + + # opening_loc + table.field("opening_loc", location_inspect(node.opening_loc)) + + # closing_loc + table.field("closing_loc", location_inspect(node.closing_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a PreExecutionNode node. + def visit_pre_execution_node(node) + table = Table.new("PreExecutionNode") + id = node_id(node) + + # statements + unless (statements = node.statements).nil? + table.field("statements", port: true) + digraph.edge("#{id}:statements -> #{node_id(statements)};") + end + + # keyword_loc + table.field("keyword_loc", location_inspect(node.keyword_loc)) + + # opening_loc + table.field("opening_loc", location_inspect(node.opening_loc)) + + # closing_loc + table.field("closing_loc", location_inspect(node.closing_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a ProgramNode node. + def visit_program_node(node) + table = Table.new("ProgramNode") + id = node_id(node) + + # locals + table.field("locals", node.locals.inspect) + + # statements + table.field("statements", port: true) + digraph.edge("#{id}:statements -> #{node_id(node.statements)};") + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a RangeNode node. + def visit_range_node(node) + table = Table.new("RangeNode") + id = node_id(node) + + # flags + table.field("flags", range_flags_inspect(node)) + + # left + unless (left = node.left).nil? + table.field("left", port: true) + digraph.edge("#{id}:left -> #{node_id(left)};") + end + + # right + unless (right = node.right).nil? + table.field("right", port: true) + digraph.edge("#{id}:right -> #{node_id(right)};") + end + + # operator_loc + table.field("operator_loc", location_inspect(node.operator_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a RationalNode node. + def visit_rational_node(node) + table = Table.new("RationalNode") + id = node_id(node) + + # flags + table.field("flags", integer_base_flags_inspect(node)) + + # numerator + table.field("numerator", node.numerator.inspect) + + # denominator + table.field("denominator", node.denominator.inspect) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a RedoNode node. + def visit_redo_node(node) + table = Table.new("RedoNode") + id = node_id(node) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a RegularExpressionNode node. + def visit_regular_expression_node(node) + table = Table.new("RegularExpressionNode") + id = node_id(node) + + # flags + table.field("flags", regular_expression_flags_inspect(node)) + + # opening_loc + table.field("opening_loc", location_inspect(node.opening_loc)) + + # content_loc + table.field("content_loc", location_inspect(node.content_loc)) + + # closing_loc + table.field("closing_loc", location_inspect(node.closing_loc)) + + # unescaped + table.field("unescaped", node.unescaped.inspect) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a RequiredKeywordParameterNode node. + def visit_required_keyword_parameter_node(node) + table = Table.new("RequiredKeywordParameterNode") + id = node_id(node) + + # flags + table.field("flags", parameter_flags_inspect(node)) + + # name + table.field("name", node.name.inspect) + + # name_loc + table.field("name_loc", location_inspect(node.name_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a RequiredParameterNode node. + def visit_required_parameter_node(node) + table = Table.new("RequiredParameterNode") + id = node_id(node) + + # flags + table.field("flags", parameter_flags_inspect(node)) + + # name + table.field("name", node.name.inspect) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a RescueModifierNode node. + def visit_rescue_modifier_node(node) + table = Table.new("RescueModifierNode") + id = node_id(node) + + # expression + table.field("expression", port: true) + digraph.edge("#{id}:expression -> #{node_id(node.expression)};") + + # keyword_loc + table.field("keyword_loc", location_inspect(node.keyword_loc)) + + # rescue_expression + table.field("rescue_expression", port: true) + digraph.edge("#{id}:rescue_expression -> #{node_id(node.rescue_expression)};") + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a RescueNode node. + def visit_rescue_node(node) + table = Table.new("RescueNode") + id = node_id(node) + + # keyword_loc + table.field("keyword_loc", location_inspect(node.keyword_loc)) + + # exceptions + if node.exceptions.any? + table.field("exceptions", port: true) + + waypoint = "#{id}_exceptions" + digraph.waypoint("#{waypoint};") + + digraph.edge("#{id}:exceptions -> #{waypoint};") + node.exceptions.each { |child| digraph.edge("#{waypoint} -> #{node_id(child)};") } + else + table.field("exceptions", "[]") + end + + # operator_loc + unless (operator_loc = node.operator_loc).nil? + table.field("operator_loc", location_inspect(operator_loc)) + end + + # reference + unless (reference = node.reference).nil? + table.field("reference", port: true) + digraph.edge("#{id}:reference -> #{node_id(reference)};") + end + + # then_keyword_loc + unless (then_keyword_loc = node.then_keyword_loc).nil? + table.field("then_keyword_loc", location_inspect(then_keyword_loc)) + end + + # statements + unless (statements = node.statements).nil? + table.field("statements", port: true) + digraph.edge("#{id}:statements -> #{node_id(statements)};") + end + + # subsequent + unless (subsequent = node.subsequent).nil? + table.field("subsequent", port: true) + digraph.edge("#{id}:subsequent -> #{node_id(subsequent)};") + end + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a RestParameterNode node. + def visit_rest_parameter_node(node) + table = Table.new("RestParameterNode") + id = node_id(node) + + # flags + table.field("flags", parameter_flags_inspect(node)) + + # name + table.field("name", node.name.inspect) + + # name_loc + unless (name_loc = node.name_loc).nil? + table.field("name_loc", location_inspect(name_loc)) + end + + # operator_loc + table.field("operator_loc", location_inspect(node.operator_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a RetryNode node. + def visit_retry_node(node) + table = Table.new("RetryNode") + id = node_id(node) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a ReturnNode node. + def visit_return_node(node) + table = Table.new("ReturnNode") + id = node_id(node) + + # keyword_loc + table.field("keyword_loc", location_inspect(node.keyword_loc)) + + # arguments + unless (arguments = node.arguments).nil? + table.field("arguments", port: true) + digraph.edge("#{id}:arguments -> #{node_id(arguments)};") + end + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a SelfNode node. + def visit_self_node(node) + table = Table.new("SelfNode") + id = node_id(node) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a ShareableConstantNode node. + def visit_shareable_constant_node(node) + table = Table.new("ShareableConstantNode") + id = node_id(node) + + # flags + table.field("flags", shareable_constant_node_flags_inspect(node)) + + # write + table.field("write", port: true) + digraph.edge("#{id}:write -> #{node_id(node.write)};") + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a SingletonClassNode node. + def visit_singleton_class_node(node) + table = Table.new("SingletonClassNode") + id = node_id(node) + + # locals + table.field("locals", node.locals.inspect) + + # class_keyword_loc + table.field("class_keyword_loc", location_inspect(node.class_keyword_loc)) + + # operator_loc + table.field("operator_loc", location_inspect(node.operator_loc)) + + # expression + table.field("expression", port: true) + digraph.edge("#{id}:expression -> #{node_id(node.expression)};") + + # body + unless (body = node.body).nil? + table.field("body", port: true) + digraph.edge("#{id}:body -> #{node_id(body)};") + end + + # end_keyword_loc + table.field("end_keyword_loc", location_inspect(node.end_keyword_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a SourceEncodingNode node. + def visit_source_encoding_node(node) + table = Table.new("SourceEncodingNode") + id = node_id(node) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a SourceFileNode node. + def visit_source_file_node(node) + table = Table.new("SourceFileNode") + id = node_id(node) + + # flags + table.field("flags", string_flags_inspect(node)) + + # filepath + table.field("filepath", node.filepath.inspect) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a SourceLineNode node. + def visit_source_line_node(node) + table = Table.new("SourceLineNode") + id = node_id(node) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a SplatNode node. + def visit_splat_node(node) + table = Table.new("SplatNode") + id = node_id(node) + + # operator_loc + table.field("operator_loc", location_inspect(node.operator_loc)) + + # expression + unless (expression = node.expression).nil? + table.field("expression", port: true) + digraph.edge("#{id}:expression -> #{node_id(expression)};") + end + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a StatementsNode node. + def visit_statements_node(node) + table = Table.new("StatementsNode") + id = node_id(node) + + # body + if node.body.any? + table.field("body", port: true) + + waypoint = "#{id}_body" + digraph.waypoint("#{waypoint};") + + digraph.edge("#{id}:body -> #{waypoint};") + node.body.each { |child| digraph.edge("#{waypoint} -> #{node_id(child)};") } + else + table.field("body", "[]") + end + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a StringNode node. + def visit_string_node(node) + table = Table.new("StringNode") + id = node_id(node) + + # flags + table.field("flags", string_flags_inspect(node)) + + # opening_loc + unless (opening_loc = node.opening_loc).nil? + table.field("opening_loc", location_inspect(opening_loc)) + end + + # content_loc + table.field("content_loc", location_inspect(node.content_loc)) + + # closing_loc + unless (closing_loc = node.closing_loc).nil? + table.field("closing_loc", location_inspect(closing_loc)) + end + + # unescaped + table.field("unescaped", node.unescaped.inspect) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a SuperNode node. + def visit_super_node(node) + table = Table.new("SuperNode") + id = node_id(node) + + # keyword_loc + table.field("keyword_loc", location_inspect(node.keyword_loc)) + + # lparen_loc + unless (lparen_loc = node.lparen_loc).nil? + table.field("lparen_loc", location_inspect(lparen_loc)) + end + + # arguments + unless (arguments = node.arguments).nil? + table.field("arguments", port: true) + digraph.edge("#{id}:arguments -> #{node_id(arguments)};") + end + + # rparen_loc + unless (rparen_loc = node.rparen_loc).nil? + table.field("rparen_loc", location_inspect(rparen_loc)) + end + + # block + unless (block = node.block).nil? + table.field("block", port: true) + digraph.edge("#{id}:block -> #{node_id(block)};") + end + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a SymbolNode node. + def visit_symbol_node(node) + table = Table.new("SymbolNode") + id = node_id(node) + + # flags + table.field("flags", symbol_flags_inspect(node)) + + # opening_loc + unless (opening_loc = node.opening_loc).nil? + table.field("opening_loc", location_inspect(opening_loc)) + end + + # value_loc + unless (value_loc = node.value_loc).nil? + table.field("value_loc", location_inspect(value_loc)) + end + + # closing_loc + unless (closing_loc = node.closing_loc).nil? + table.field("closing_loc", location_inspect(closing_loc)) + end + + # unescaped + table.field("unescaped", node.unescaped.inspect) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a TrueNode node. + def visit_true_node(node) + table = Table.new("TrueNode") + id = node_id(node) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a UndefNode node. + def visit_undef_node(node) + table = Table.new("UndefNode") + id = node_id(node) + + # names + if node.names.any? + table.field("names", port: true) + + waypoint = "#{id}_names" + digraph.waypoint("#{waypoint};") + + digraph.edge("#{id}:names -> #{waypoint};") + node.names.each { |child| digraph.edge("#{waypoint} -> #{node_id(child)};") } + else + table.field("names", "[]") + end + + # keyword_loc + table.field("keyword_loc", location_inspect(node.keyword_loc)) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a UnlessNode node. + def visit_unless_node(node) + table = Table.new("UnlessNode") + id = node_id(node) + + # keyword_loc + table.field("keyword_loc", location_inspect(node.keyword_loc)) + + # predicate + table.field("predicate", port: true) + digraph.edge("#{id}:predicate -> #{node_id(node.predicate)};") + + # then_keyword_loc + unless (then_keyword_loc = node.then_keyword_loc).nil? + table.field("then_keyword_loc", location_inspect(then_keyword_loc)) + end + + # statements + unless (statements = node.statements).nil? + table.field("statements", port: true) + digraph.edge("#{id}:statements -> #{node_id(statements)};") + end + + # else_clause + unless (else_clause = node.else_clause).nil? + table.field("else_clause", port: true) + digraph.edge("#{id}:else_clause -> #{node_id(else_clause)};") + end + + # end_keyword_loc + unless (end_keyword_loc = node.end_keyword_loc).nil? + table.field("end_keyword_loc", location_inspect(end_keyword_loc)) + end + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a UntilNode node. + def visit_until_node(node) + table = Table.new("UntilNode") + id = node_id(node) + + # flags + table.field("flags", loop_flags_inspect(node)) + + # keyword_loc + table.field("keyword_loc", location_inspect(node.keyword_loc)) + + # do_keyword_loc + unless (do_keyword_loc = node.do_keyword_loc).nil? + table.field("do_keyword_loc", location_inspect(do_keyword_loc)) + end + + # closing_loc + unless (closing_loc = node.closing_loc).nil? + table.field("closing_loc", location_inspect(closing_loc)) + end + + # predicate + table.field("predicate", port: true) + digraph.edge("#{id}:predicate -> #{node_id(node.predicate)};") + + # statements + unless (statements = node.statements).nil? + table.field("statements", port: true) + digraph.edge("#{id}:statements -> #{node_id(statements)};") + end + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a WhenNode node. + def visit_when_node(node) + table = Table.new("WhenNode") + id = node_id(node) + + # keyword_loc + table.field("keyword_loc", location_inspect(node.keyword_loc)) + + # conditions + if node.conditions.any? + table.field("conditions", port: true) + + waypoint = "#{id}_conditions" + digraph.waypoint("#{waypoint};") + + digraph.edge("#{id}:conditions -> #{waypoint};") + node.conditions.each { |child| digraph.edge("#{waypoint} -> #{node_id(child)};") } + else + table.field("conditions", "[]") + end + + # then_keyword_loc + unless (then_keyword_loc = node.then_keyword_loc).nil? + table.field("then_keyword_loc", location_inspect(then_keyword_loc)) + end + + # statements + unless (statements = node.statements).nil? + table.field("statements", port: true) + digraph.edge("#{id}:statements -> #{node_id(statements)};") + end + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a WhileNode node. + def visit_while_node(node) + table = Table.new("WhileNode") + id = node_id(node) + + # flags + table.field("flags", loop_flags_inspect(node)) + + # keyword_loc + table.field("keyword_loc", location_inspect(node.keyword_loc)) + + # do_keyword_loc + unless (do_keyword_loc = node.do_keyword_loc).nil? + table.field("do_keyword_loc", location_inspect(do_keyword_loc)) + end + + # closing_loc + unless (closing_loc = node.closing_loc).nil? + table.field("closing_loc", location_inspect(closing_loc)) + end + + # predicate + table.field("predicate", port: true) + digraph.edge("#{id}:predicate -> #{node_id(node.predicate)};") + + # statements + unless (statements = node.statements).nil? + table.field("statements", port: true) + digraph.edge("#{id}:statements -> #{node_id(statements)};") + end + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a XStringNode node. + def visit_x_string_node(node) + table = Table.new("XStringNode") + id = node_id(node) + + # flags + table.field("flags", encoding_flags_inspect(node)) + + # opening_loc + table.field("opening_loc", location_inspect(node.opening_loc)) + + # content_loc + table.field("content_loc", location_inspect(node.content_loc)) + + # closing_loc + table.field("closing_loc", location_inspect(node.closing_loc)) + + # unescaped + table.field("unescaped", node.unescaped.inspect) + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + end + + # Visit a YieldNode node. + def visit_yield_node(node) + table = Table.new("YieldNode") + id = node_id(node) + + # keyword_loc + table.field("keyword_loc", location_inspect(node.keyword_loc)) + + # lparen_loc + unless (lparen_loc = node.lparen_loc).nil? + table.field("lparen_loc", location_inspect(lparen_loc)) + end + + # arguments + unless (arguments = node.arguments).nil? + table.field("arguments", port: true) + digraph.edge("#{id}:arguments -> #{node_id(arguments)};") + end + + # rparen_loc + unless (rparen_loc = node.rparen_loc).nil? + table.field("rparen_loc", location_inspect(rparen_loc)) + end + + digraph.nodes << <<~DOT + #{id} [ + label=<#{table.to_dot.gsub(/\n/, "\n ")}> + ]; + DOT + + super + 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 + + # Inspect a node that has arguments_node_flags flags to display the flags as a + # comma-separated list. + def arguments_node_flags_inspect(node) + flags = [] #: Array[String] + flags << "contains_forwarding" if node.contains_forwarding? + flags << "contains_keywords" if node.contains_keywords? + flags << "contains_keyword_splat" if node.contains_keyword_splat? + flags << "contains_splat" if node.contains_splat? + flags << "contains_multiple_splats" if node.contains_multiple_splats? + flags.join(", ") + end + + # Inspect a node that has array_node_flags flags to display the flags as a + # comma-separated list. + def array_node_flags_inspect(node) + flags = [] #: Array[String] + flags << "contains_splat" if node.contains_splat? + flags.join(", ") + end + + # Inspect a node that has call_node_flags flags to display the flags as a + # comma-separated list. + def call_node_flags_inspect(node) + flags = [] #: Array[String] + flags << "safe_navigation" if node.safe_navigation? + flags << "variable_call" if node.variable_call? + flags << "attribute_write" if node.attribute_write? + flags << "ignore_visibility" if node.ignore_visibility? + flags.join(", ") + end + + # Inspect a node that has encoding_flags flags to display the flags as a + # comma-separated list. + def encoding_flags_inspect(node) + flags = [] #: Array[String] + flags << "forced_utf8_encoding" if node.forced_utf8_encoding? + flags << "forced_binary_encoding" if node.forced_binary_encoding? + flags.join(", ") + end + + # Inspect a node that has integer_base_flags flags to display the flags as a + # comma-separated list. + def integer_base_flags_inspect(node) + flags = [] #: Array[String] + flags << "binary" if node.binary? + flags << "decimal" if node.decimal? + flags << "octal" if node.octal? + flags << "hexadecimal" if node.hexadecimal? + flags.join(", ") + end + + # Inspect a node that has interpolated_string_node_flags flags to display the flags as a + # comma-separated list. + def interpolated_string_node_flags_inspect(node) + flags = [] #: Array[String] + flags << "frozen" if node.frozen? + flags << "mutable" if node.mutable? + flags.join(", ") + end + + # Inspect a node that has keyword_hash_node_flags flags to display the flags as a + # comma-separated list. + def keyword_hash_node_flags_inspect(node) + flags = [] #: Array[String] + flags << "symbol_keys" if node.symbol_keys? + flags.join(", ") + end + + # Inspect a node that has loop_flags flags to display the flags as a + # comma-separated list. + def loop_flags_inspect(node) + flags = [] #: Array[String] + flags << "begin_modifier" if node.begin_modifier? + flags.join(", ") + end + + # Inspect a node that has parameter_flags flags to display the flags as a + # comma-separated list. + def parameter_flags_inspect(node) + flags = [] #: Array[String] + flags << "repeated_parameter" if node.repeated_parameter? + flags.join(", ") + end + + # Inspect a node that has parentheses_node_flags flags to display the flags as a + # comma-separated list. + def parentheses_node_flags_inspect(node) + flags = [] #: Array[String] + flags << "multiple_statements" if node.multiple_statements? + flags.join(", ") + end + + # Inspect a node that has range_flags flags to display the flags as a + # comma-separated list. + def range_flags_inspect(node) + flags = [] #: Array[String] + flags << "exclude_end" if node.exclude_end? + flags.join(", ") + end + + # Inspect a node that has regular_expression_flags flags to display the flags as a + # comma-separated list. + def regular_expression_flags_inspect(node) + flags = [] #: Array[String] + flags << "ignore_case" if node.ignore_case? + flags << "extended" if node.extended? + flags << "multi_line" if node.multi_line? + flags << "once" if node.once? + flags << "euc_jp" if node.euc_jp? + flags << "ascii_8bit" if node.ascii_8bit? + flags << "windows_31j" if node.windows_31j? + flags << "utf_8" if node.utf_8? + flags << "forced_utf8_encoding" if node.forced_utf8_encoding? + flags << "forced_binary_encoding" if node.forced_binary_encoding? + flags << "forced_us_ascii_encoding" if node.forced_us_ascii_encoding? + flags.join(", ") + end + + # Inspect a node that has shareable_constant_node_flags flags to display the flags as a + # comma-separated list. + def shareable_constant_node_flags_inspect(node) + flags = [] #: Array[String] + flags << "literal" if node.literal? + flags << "experimental_everything" if node.experimental_everything? + flags << "experimental_copy" if node.experimental_copy? + flags.join(", ") + end + + # Inspect a node that has string_flags flags to display the flags as a + # comma-separated list. + def string_flags_inspect(node) + flags = [] #: Array[String] + flags << "forced_utf8_encoding" if node.forced_utf8_encoding? + flags << "forced_binary_encoding" if node.forced_binary_encoding? + flags << "frozen" if node.frozen? + flags << "mutable" if node.mutable? + flags.join(", ") + end + + # Inspect a node that has symbol_flags flags to display the flags as a + # comma-separated list. + def symbol_flags_inspect(node) + flags = [] #: Array[String] + flags << "forced_utf8_encoding" if node.forced_utf8_encoding? + flags << "forced_binary_encoding" if node.forced_binary_encoding? + flags << "forced_us_ascii_encoding" if node.forced_us_ascii_encoding? + flags.join(", ") + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/dsl.rb b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/dsl.rb new file mode 100644 index 0000000..b7f9908 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/dsl.rb @@ -0,0 +1,1003 @@ +# frozen_string_literal: true +# :markup: markdown + +=begin +-- +This file is generated by the templates/template.rb script and should not be +modified manually. See templates/lib/prism/dsl.rb.erb +if you are looking to modify the template +++ +=end + +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.for("[1]") + # + # Prism::ArrayNode.new( + # source, + # 0, + # Prism::Location.new(source, 0, 3), + # 0, + # [ + # Prism::IntegerNode.new( + # source, + # 0, + # Prism::Location.new(source, 1, 1), + # Prism::IntegerBaseFlags::DECIMAL, + # 1 + # ) + # ], + # Prism::Location.new(source, 0, 1), + # Prism::Location.new(source, 2, 1) + # ) + # + # you could instead write: + # + # class Builder + # include Prism::DSL + # + # attr_reader :default_source + # + # def initialize + # @default_source = source("[1]") + # end + # + # def build + # array_node( + # location: location(start_offset: 0, length: 3), + # elements: [ + # integer_node( + # location: location(start_offset: 1, length: 1), + # flags: integer_base_flag(:decimal), + # value: 1 + # ) + # ], + # opening_loc: location(start_offset: 0, length: 1), + # closing_loc: location(start_offset: 2, length: 1) + # ) + # end + # end + # + # This is mostly helpful in the context of generating trees programmatically. + module DSL + # Provide all of these methods as module methods as well, to allow for + # building nodes like Prism::DSL.nil_node. + extend self + + # Create a new Source object. + def source(string) + Source.for(string) + end + + # Create a new Location object. + def location(source: default_source, start_offset: 0, length: 0) + Location.new(source, start_offset, length) + end + + # Create a new AliasGlobalVariableNode node. + def alias_global_variable_node(source: default_source, node_id: 0, location: default_location, flags: 0, new_name: global_variable_read_node(source: source), old_name: global_variable_read_node(source: source), keyword_loc: location) + AliasGlobalVariableNode.new(source, node_id, location, flags, new_name, old_name, keyword_loc) + end + + # Create a new AliasMethodNode node. + def alias_method_node(source: default_source, node_id: 0, location: default_location, flags: 0, new_name: symbol_node(source: source), old_name: symbol_node(source: source), keyword_loc: location) + AliasMethodNode.new(source, node_id, location, flags, new_name, old_name, keyword_loc) + end + + # Create a new AlternationPatternNode node. + def alternation_pattern_node(source: default_source, node_id: 0, location: default_location, flags: 0, left: default_node(source, location), right: default_node(source, location), operator_loc: location) + AlternationPatternNode.new(source, node_id, location, flags, left, right, operator_loc) + end + + # Create a new AndNode node. + def and_node(source: default_source, node_id: 0, location: default_location, flags: 0, left: default_node(source, location), right: default_node(source, location), operator_loc: location) + AndNode.new(source, node_id, location, flags, left, right, operator_loc) + end + + # Create a new ArgumentsNode node. + def arguments_node(source: default_source, node_id: 0, location: default_location, flags: 0, arguments: []) + ArgumentsNode.new(source, node_id, location, flags, arguments) + end + + # Create a new ArrayNode node. + def array_node(source: default_source, node_id: 0, location: default_location, flags: 0, elements: [], opening_loc: nil, closing_loc: nil) + ArrayNode.new(source, node_id, location, flags, elements, opening_loc, closing_loc) + end + + # Create a new ArrayPatternNode node. + def array_pattern_node(source: default_source, node_id: 0, location: default_location, flags: 0, constant: nil, requireds: [], rest: nil, posts: [], opening_loc: nil, closing_loc: nil) + ArrayPatternNode.new(source, node_id, location, flags, constant, requireds, rest, posts, opening_loc, closing_loc) + end + + # Create a new AssocNode node. + def assoc_node(source: default_source, node_id: 0, location: default_location, flags: 0, key: default_node(source, location), value: default_node(source, location), operator_loc: nil) + AssocNode.new(source, node_id, location, flags, key, value, operator_loc) + end + + # Create a new AssocSplatNode node. + def assoc_splat_node(source: default_source, node_id: 0, location: default_location, flags: 0, value: nil, operator_loc: location) + AssocSplatNode.new(source, node_id, location, flags, value, operator_loc) + end + + # Create a new BackReferenceReadNode node. + def back_reference_read_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"") + BackReferenceReadNode.new(source, node_id, location, flags, name) + end + + # Create a new BeginNode node. + def begin_node(source: default_source, node_id: 0, location: default_location, flags: 0, begin_keyword_loc: nil, statements: nil, rescue_clause: nil, else_clause: nil, ensure_clause: nil, end_keyword_loc: nil) + BeginNode.new(source, node_id, location, flags, begin_keyword_loc, statements, rescue_clause, else_clause, ensure_clause, end_keyword_loc) + end + + # Create a new BlockArgumentNode node. + def block_argument_node(source: default_source, node_id: 0, location: default_location, flags: 0, expression: nil, operator_loc: location) + BlockArgumentNode.new(source, node_id, location, flags, expression, operator_loc) + end + + # Create a new BlockLocalVariableNode node. + def block_local_variable_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"") + BlockLocalVariableNode.new(source, node_id, location, flags, name) + end + + # Create a new BlockNode node. + def block_node(source: default_source, node_id: 0, location: default_location, flags: 0, locals: [], parameters: nil, body: nil, opening_loc: location, closing_loc: location) + BlockNode.new(source, node_id, location, flags, locals, parameters, body, opening_loc, closing_loc) + end + + # Create a new BlockParameterNode node. + def block_parameter_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: nil, name_loc: nil, operator_loc: location) + BlockParameterNode.new(source, node_id, location, flags, name, name_loc, operator_loc) + end + + # Create a new BlockParametersNode node. + def block_parameters_node(source: default_source, node_id: 0, location: default_location, flags: 0, parameters: nil, locals: [], opening_loc: nil, closing_loc: nil) + BlockParametersNode.new(source, node_id, location, flags, parameters, locals, opening_loc, closing_loc) + end + + # Create a new BreakNode node. + def break_node(source: default_source, node_id: 0, location: default_location, flags: 0, arguments: nil, keyword_loc: location) + BreakNode.new(source, node_id, location, flags, arguments, keyword_loc) + end + + # Create a new CallAndWriteNode node. + def call_and_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, receiver: nil, call_operator_loc: nil, message_loc: nil, read_name: :"", write_name: :"", operator_loc: location, value: default_node(source, location)) + CallAndWriteNode.new(source, node_id, location, flags, receiver, call_operator_loc, message_loc, read_name, write_name, operator_loc, value) + end + + # Create a new CallNode node. + def call_node(source: default_source, node_id: 0, location: default_location, flags: 0, receiver: nil, call_operator_loc: nil, name: :"", message_loc: nil, opening_loc: nil, arguments: nil, closing_loc: nil, equal_loc: nil, block: nil) + CallNode.new(source, node_id, location, flags, receiver, call_operator_loc, name, message_loc, opening_loc, arguments, closing_loc, equal_loc, block) + end + + # Create a new CallOperatorWriteNode node. + def call_operator_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, receiver: nil, call_operator_loc: nil, message_loc: nil, read_name: :"", write_name: :"", binary_operator: :"", binary_operator_loc: location, value: default_node(source, location)) + CallOperatorWriteNode.new(source, node_id, location, flags, receiver, call_operator_loc, message_loc, read_name, write_name, binary_operator, binary_operator_loc, value) + end + + # Create a new CallOrWriteNode node. + def call_or_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, receiver: nil, call_operator_loc: nil, message_loc: nil, read_name: :"", write_name: :"", operator_loc: location, value: default_node(source, location)) + CallOrWriteNode.new(source, node_id, location, flags, receiver, call_operator_loc, message_loc, read_name, write_name, operator_loc, value) + end + + # Create a new CallTargetNode node. + def call_target_node(source: default_source, node_id: 0, location: default_location, flags: 0, receiver: default_node(source, location), call_operator_loc: location, name: :"", message_loc: location) + CallTargetNode.new(source, node_id, location, flags, receiver, call_operator_loc, name, message_loc) + end + + # Create a new CapturePatternNode node. + def capture_pattern_node(source: default_source, node_id: 0, location: default_location, flags: 0, value: default_node(source, location), target: local_variable_target_node(source: source), operator_loc: location) + CapturePatternNode.new(source, node_id, location, flags, value, target, operator_loc) + end + + # Create a new CaseMatchNode node. + def case_match_node(source: default_source, node_id: 0, location: default_location, flags: 0, predicate: nil, conditions: [], else_clause: nil, case_keyword_loc: location, end_keyword_loc: location) + CaseMatchNode.new(source, node_id, location, flags, predicate, conditions, else_clause, case_keyword_loc, end_keyword_loc) + end + + # Create a new CaseNode node. + def case_node(source: default_source, node_id: 0, location: default_location, flags: 0, predicate: nil, conditions: [], else_clause: nil, case_keyword_loc: location, end_keyword_loc: location) + CaseNode.new(source, node_id, location, flags, predicate, conditions, else_clause, case_keyword_loc, end_keyword_loc) + end + + # Create a new ClassNode node. + def class_node(source: default_source, node_id: 0, location: default_location, flags: 0, locals: [], class_keyword_loc: location, constant_path: constant_read_node(source: source), inheritance_operator_loc: nil, superclass: nil, body: nil, end_keyword_loc: location, name: :"") + ClassNode.new(source, node_id, location, flags, locals, class_keyword_loc, constant_path, inheritance_operator_loc, superclass, body, end_keyword_loc, name) + end + + # Create a new ClassVariableAndWriteNode node. + def class_variable_and_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", name_loc: location, operator_loc: location, value: default_node(source, location)) + ClassVariableAndWriteNode.new(source, node_id, location, flags, name, name_loc, operator_loc, value) + end + + # Create a new ClassVariableOperatorWriteNode node. + def class_variable_operator_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", name_loc: location, binary_operator_loc: location, value: default_node(source, location), binary_operator: :"") + ClassVariableOperatorWriteNode.new(source, node_id, location, flags, name, name_loc, binary_operator_loc, value, binary_operator) + end + + # Create a new ClassVariableOrWriteNode node. + def class_variable_or_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", name_loc: location, operator_loc: location, value: default_node(source, location)) + ClassVariableOrWriteNode.new(source, node_id, location, flags, name, name_loc, operator_loc, value) + end + + # Create a new ClassVariableReadNode node. + def class_variable_read_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"") + ClassVariableReadNode.new(source, node_id, location, flags, name) + end + + # Create a new ClassVariableTargetNode node. + def class_variable_target_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"") + ClassVariableTargetNode.new(source, node_id, location, flags, name) + end + + # Create a new ClassVariableWriteNode node. + def class_variable_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", name_loc: location, value: default_node(source, location), operator_loc: location) + ClassVariableWriteNode.new(source, node_id, location, flags, name, name_loc, value, operator_loc) + end + + # Create a new ConstantAndWriteNode node. + def constant_and_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", name_loc: location, operator_loc: location, value: default_node(source, location)) + ConstantAndWriteNode.new(source, node_id, location, flags, name, name_loc, operator_loc, value) + end + + # Create a new ConstantOperatorWriteNode node. + def constant_operator_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", name_loc: location, binary_operator_loc: location, value: default_node(source, location), binary_operator: :"") + ConstantOperatorWriteNode.new(source, node_id, location, flags, name, name_loc, binary_operator_loc, value, binary_operator) + end + + # Create a new ConstantOrWriteNode node. + def constant_or_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", name_loc: location, operator_loc: location, value: default_node(source, location)) + ConstantOrWriteNode.new(source, node_id, location, flags, name, name_loc, operator_loc, value) + end + + # Create a new ConstantPathAndWriteNode node. + def constant_path_and_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, target: constant_path_node(source: source), operator_loc: location, value: default_node(source, location)) + ConstantPathAndWriteNode.new(source, node_id, location, flags, target, operator_loc, value) + end + + # Create a new ConstantPathNode node. + def constant_path_node(source: default_source, node_id: 0, location: default_location, flags: 0, parent: nil, name: nil, delimiter_loc: location, name_loc: location) + ConstantPathNode.new(source, node_id, location, flags, parent, name, delimiter_loc, name_loc) + end + + # Create a new ConstantPathOperatorWriteNode node. + def constant_path_operator_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, target: constant_path_node(source: source), binary_operator_loc: location, value: default_node(source, location), binary_operator: :"") + ConstantPathOperatorWriteNode.new(source, node_id, location, flags, target, binary_operator_loc, value, binary_operator) + end + + # Create a new ConstantPathOrWriteNode node. + def constant_path_or_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, target: constant_path_node(source: source), operator_loc: location, value: default_node(source, location)) + ConstantPathOrWriteNode.new(source, node_id, location, flags, target, operator_loc, value) + end + + # Create a new ConstantPathTargetNode node. + def constant_path_target_node(source: default_source, node_id: 0, location: default_location, flags: 0, parent: nil, name: nil, delimiter_loc: location, name_loc: location) + ConstantPathTargetNode.new(source, node_id, location, flags, parent, name, delimiter_loc, name_loc) + end + + # Create a new ConstantPathWriteNode node. + def constant_path_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, target: constant_path_node(source: source), operator_loc: location, value: default_node(source, location)) + ConstantPathWriteNode.new(source, node_id, location, flags, target, operator_loc, value) + end + + # Create a new ConstantReadNode node. + def constant_read_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"") + ConstantReadNode.new(source, node_id, location, flags, name) + end + + # Create a new ConstantTargetNode node. + def constant_target_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"") + ConstantTargetNode.new(source, node_id, location, flags, name) + end + + # Create a new ConstantWriteNode node. + def constant_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", name_loc: location, value: default_node(source, location), operator_loc: location) + ConstantWriteNode.new(source, node_id, location, flags, name, name_loc, value, operator_loc) + end + + # Create a new DefNode node. + def def_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", name_loc: location, receiver: nil, parameters: nil, body: nil, locals: [], def_keyword_loc: location, operator_loc: nil, lparen_loc: nil, rparen_loc: nil, equal_loc: nil, end_keyword_loc: nil) + DefNode.new(source, node_id, location, flags, name, name_loc, receiver, parameters, body, locals, def_keyword_loc, operator_loc, lparen_loc, rparen_loc, equal_loc, end_keyword_loc) + end + + # Create a new DefinedNode node. + def defined_node(source: default_source, node_id: 0, location: default_location, flags: 0, lparen_loc: nil, value: default_node(source, location), rparen_loc: nil, keyword_loc: location) + DefinedNode.new(source, node_id, location, flags, lparen_loc, value, rparen_loc, keyword_loc) + end + + # Create a new ElseNode node. + def else_node(source: default_source, node_id: 0, location: default_location, flags: 0, else_keyword_loc: location, statements: nil, end_keyword_loc: nil) + ElseNode.new(source, node_id, location, flags, else_keyword_loc, statements, end_keyword_loc) + end + + # Create a new EmbeddedStatementsNode node. + def embedded_statements_node(source: default_source, node_id: 0, location: default_location, flags: 0, opening_loc: location, statements: nil, closing_loc: location) + EmbeddedStatementsNode.new(source, node_id, location, flags, opening_loc, statements, closing_loc) + end + + # Create a new EmbeddedVariableNode node. + def embedded_variable_node(source: default_source, node_id: 0, location: default_location, flags: 0, operator_loc: location, variable: instance_variable_read_node(source: source)) + EmbeddedVariableNode.new(source, node_id, location, flags, operator_loc, variable) + end + + # Create a new EnsureNode node. + def ensure_node(source: default_source, node_id: 0, location: default_location, flags: 0, ensure_keyword_loc: location, statements: nil, end_keyword_loc: location) + EnsureNode.new(source, node_id, location, flags, ensure_keyword_loc, statements, end_keyword_loc) + end + + # Create a new FalseNode node. + def false_node(source: default_source, node_id: 0, location: default_location, flags: 0) + FalseNode.new(source, node_id, location, flags) + end + + # Create a new FindPatternNode node. + def find_pattern_node(source: default_source, node_id: 0, location: default_location, flags: 0, constant: nil, left: splat_node(source: source), requireds: [], right: splat_node(source: source), opening_loc: nil, closing_loc: nil) + FindPatternNode.new(source, node_id, location, flags, constant, left, requireds, right, opening_loc, closing_loc) + end + + # Create a new FlipFlopNode node. + def flip_flop_node(source: default_source, node_id: 0, location: default_location, flags: 0, left: nil, right: nil, operator_loc: location) + FlipFlopNode.new(source, node_id, location, flags, left, right, operator_loc) + end + + # Create a new FloatNode node. + def float_node(source: default_source, node_id: 0, location: default_location, flags: 0, value: 0.0) + FloatNode.new(source, node_id, location, flags, value) + end + + # Create a new ForNode node. + def for_node(source: default_source, node_id: 0, location: default_location, flags: 0, index: local_variable_target_node(source: source), collection: default_node(source, location), statements: nil, for_keyword_loc: location, in_keyword_loc: location, do_keyword_loc: nil, end_keyword_loc: location) + ForNode.new(source, node_id, location, flags, index, collection, statements, for_keyword_loc, in_keyword_loc, do_keyword_loc, end_keyword_loc) + end + + # Create a new ForwardingArgumentsNode node. + def forwarding_arguments_node(source: default_source, node_id: 0, location: default_location, flags: 0) + ForwardingArgumentsNode.new(source, node_id, location, flags) + end + + # Create a new ForwardingParameterNode node. + def forwarding_parameter_node(source: default_source, node_id: 0, location: default_location, flags: 0) + ForwardingParameterNode.new(source, node_id, location, flags) + end + + # Create a new ForwardingSuperNode node. + def forwarding_super_node(source: default_source, node_id: 0, location: default_location, flags: 0, block: nil) + ForwardingSuperNode.new(source, node_id, location, flags, block) + end + + # Create a new GlobalVariableAndWriteNode node. + def global_variable_and_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", name_loc: location, operator_loc: location, value: default_node(source, location)) + GlobalVariableAndWriteNode.new(source, node_id, location, flags, name, name_loc, operator_loc, value) + end + + # Create a new GlobalVariableOperatorWriteNode node. + def global_variable_operator_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", name_loc: location, binary_operator_loc: location, value: default_node(source, location), binary_operator: :"") + GlobalVariableOperatorWriteNode.new(source, node_id, location, flags, name, name_loc, binary_operator_loc, value, binary_operator) + end + + # Create a new GlobalVariableOrWriteNode node. + def global_variable_or_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", name_loc: location, operator_loc: location, value: default_node(source, location)) + GlobalVariableOrWriteNode.new(source, node_id, location, flags, name, name_loc, operator_loc, value) + end + + # Create a new GlobalVariableReadNode node. + def global_variable_read_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"") + GlobalVariableReadNode.new(source, node_id, location, flags, name) + end + + # Create a new GlobalVariableTargetNode node. + def global_variable_target_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"") + GlobalVariableTargetNode.new(source, node_id, location, flags, name) + end + + # Create a new GlobalVariableWriteNode node. + def global_variable_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", name_loc: location, value: default_node(source, location), operator_loc: location) + GlobalVariableWriteNode.new(source, node_id, location, flags, name, name_loc, value, operator_loc) + end + + # Create a new HashNode node. + def hash_node(source: default_source, node_id: 0, location: default_location, flags: 0, opening_loc: location, elements: [], closing_loc: location) + HashNode.new(source, node_id, location, flags, opening_loc, elements, closing_loc) + end + + # Create a new HashPatternNode node. + def hash_pattern_node(source: default_source, node_id: 0, location: default_location, flags: 0, constant: nil, elements: [], rest: nil, opening_loc: nil, closing_loc: nil) + HashPatternNode.new(source, node_id, location, flags, constant, elements, rest, opening_loc, closing_loc) + end + + # Create a new IfNode node. + def if_node(source: default_source, node_id: 0, location: default_location, flags: 0, if_keyword_loc: nil, predicate: default_node(source, location), then_keyword_loc: nil, statements: nil, subsequent: nil, end_keyword_loc: nil) + IfNode.new(source, node_id, location, flags, if_keyword_loc, predicate, then_keyword_loc, statements, subsequent, end_keyword_loc) + end + + # Create a new ImaginaryNode node. + def imaginary_node(source: default_source, node_id: 0, location: default_location, flags: 0, numeric: float_node(source: source)) + ImaginaryNode.new(source, node_id, location, flags, numeric) + end + + # Create a new ImplicitNode node. + def implicit_node(source: default_source, node_id: 0, location: default_location, flags: 0, value: local_variable_read_node(source: source)) + ImplicitNode.new(source, node_id, location, flags, value) + end + + # Create a new ImplicitRestNode node. + def implicit_rest_node(source: default_source, node_id: 0, location: default_location, flags: 0) + ImplicitRestNode.new(source, node_id, location, flags) + end + + # Create a new InNode node. + def in_node(source: default_source, node_id: 0, location: default_location, flags: 0, pattern: default_node(source, location), statements: nil, in_loc: location, then_loc: nil) + InNode.new(source, node_id, location, flags, pattern, statements, in_loc, then_loc) + end + + # Create a new IndexAndWriteNode node. + def index_and_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, receiver: nil, call_operator_loc: nil, opening_loc: location, arguments: nil, closing_loc: location, block: nil, operator_loc: location, value: default_node(source, location)) + IndexAndWriteNode.new(source, node_id, location, flags, receiver, call_operator_loc, opening_loc, arguments, closing_loc, block, operator_loc, value) + end + + # Create a new IndexOperatorWriteNode node. + def index_operator_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, receiver: nil, call_operator_loc: nil, opening_loc: location, arguments: nil, closing_loc: location, block: nil, binary_operator: :"", binary_operator_loc: location, value: default_node(source, location)) + IndexOperatorWriteNode.new(source, node_id, location, flags, receiver, call_operator_loc, opening_loc, arguments, closing_loc, block, binary_operator, binary_operator_loc, value) + end + + # Create a new IndexOrWriteNode node. + def index_or_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, receiver: nil, call_operator_loc: nil, opening_loc: location, arguments: nil, closing_loc: location, block: nil, operator_loc: location, value: default_node(source, location)) + IndexOrWriteNode.new(source, node_id, location, flags, receiver, call_operator_loc, opening_loc, arguments, closing_loc, block, operator_loc, value) + end + + # Create a new IndexTargetNode node. + def index_target_node(source: default_source, node_id: 0, location: default_location, flags: 0, receiver: default_node(source, location), opening_loc: location, arguments: nil, closing_loc: location, block: nil) + IndexTargetNode.new(source, node_id, location, flags, receiver, opening_loc, arguments, closing_loc, block) + end + + # Create a new InstanceVariableAndWriteNode node. + def instance_variable_and_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", name_loc: location, operator_loc: location, value: default_node(source, location)) + InstanceVariableAndWriteNode.new(source, node_id, location, flags, name, name_loc, operator_loc, value) + end + + # Create a new InstanceVariableOperatorWriteNode node. + def instance_variable_operator_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", name_loc: location, binary_operator_loc: location, value: default_node(source, location), binary_operator: :"") + InstanceVariableOperatorWriteNode.new(source, node_id, location, flags, name, name_loc, binary_operator_loc, value, binary_operator) + end + + # Create a new InstanceVariableOrWriteNode node. + def instance_variable_or_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", name_loc: location, operator_loc: location, value: default_node(source, location)) + InstanceVariableOrWriteNode.new(source, node_id, location, flags, name, name_loc, operator_loc, value) + end + + # Create a new InstanceVariableReadNode node. + def instance_variable_read_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"") + InstanceVariableReadNode.new(source, node_id, location, flags, name) + end + + # Create a new InstanceVariableTargetNode node. + def instance_variable_target_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"") + InstanceVariableTargetNode.new(source, node_id, location, flags, name) + end + + # Create a new InstanceVariableWriteNode node. + def instance_variable_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", name_loc: location, value: default_node(source, location), operator_loc: location) + InstanceVariableWriteNode.new(source, node_id, location, flags, name, name_loc, value, operator_loc) + end + + # Create a new IntegerNode node. + def integer_node(source: default_source, node_id: 0, location: default_location, flags: 0, value: 0) + IntegerNode.new(source, node_id, location, flags, value) + end + + # Create a new InterpolatedMatchLastLineNode node. + def interpolated_match_last_line_node(source: default_source, node_id: 0, location: default_location, flags: 0, opening_loc: location, parts: [], closing_loc: location) + InterpolatedMatchLastLineNode.new(source, node_id, location, flags, opening_loc, parts, closing_loc) + end + + # Create a new InterpolatedRegularExpressionNode node. + def interpolated_regular_expression_node(source: default_source, node_id: 0, location: default_location, flags: 0, opening_loc: location, parts: [], closing_loc: location) + InterpolatedRegularExpressionNode.new(source, node_id, location, flags, opening_loc, parts, closing_loc) + end + + # Create a new InterpolatedStringNode node. + def interpolated_string_node(source: default_source, node_id: 0, location: default_location, flags: 0, opening_loc: nil, parts: [], closing_loc: nil) + InterpolatedStringNode.new(source, node_id, location, flags, opening_loc, parts, closing_loc) + end + + # Create a new InterpolatedSymbolNode node. + def interpolated_symbol_node(source: default_source, node_id: 0, location: default_location, flags: 0, opening_loc: nil, parts: [], closing_loc: nil) + InterpolatedSymbolNode.new(source, node_id, location, flags, opening_loc, parts, closing_loc) + end + + # Create a new InterpolatedXStringNode node. + def interpolated_x_string_node(source: default_source, node_id: 0, location: default_location, flags: 0, opening_loc: location, parts: [], closing_loc: location) + InterpolatedXStringNode.new(source, node_id, location, flags, opening_loc, parts, closing_loc) + end + + # Create a new ItLocalVariableReadNode node. + def it_local_variable_read_node(source: default_source, node_id: 0, location: default_location, flags: 0) + ItLocalVariableReadNode.new(source, node_id, location, flags) + end + + # Create a new ItParametersNode node. + def it_parameters_node(source: default_source, node_id: 0, location: default_location, flags: 0) + ItParametersNode.new(source, node_id, location, flags) + end + + # Create a new KeywordHashNode node. + def keyword_hash_node(source: default_source, node_id: 0, location: default_location, flags: 0, elements: []) + KeywordHashNode.new(source, node_id, location, flags, elements) + end + + # Create a new KeywordRestParameterNode node. + def keyword_rest_parameter_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: nil, name_loc: nil, operator_loc: location) + KeywordRestParameterNode.new(source, node_id, location, flags, name, name_loc, operator_loc) + end + + # Create a new LambdaNode node. + def lambda_node(source: default_source, node_id: 0, location: default_location, flags: 0, locals: [], operator_loc: location, opening_loc: location, closing_loc: location, parameters: nil, body: nil) + LambdaNode.new(source, node_id, location, flags, locals, operator_loc, opening_loc, closing_loc, parameters, body) + end + + # Create a new LocalVariableAndWriteNode node. + def local_variable_and_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, name_loc: location, operator_loc: location, value: default_node(source, location), name: :"", depth: 0) + LocalVariableAndWriteNode.new(source, node_id, location, flags, name_loc, operator_loc, value, name, depth) + end + + # Create a new LocalVariableOperatorWriteNode node. + def local_variable_operator_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, name_loc: location, binary_operator_loc: location, value: default_node(source, location), name: :"", binary_operator: :"", depth: 0) + LocalVariableOperatorWriteNode.new(source, node_id, location, flags, name_loc, binary_operator_loc, value, name, binary_operator, depth) + end + + # Create a new LocalVariableOrWriteNode node. + def local_variable_or_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, name_loc: location, operator_loc: location, value: default_node(source, location), name: :"", depth: 0) + LocalVariableOrWriteNode.new(source, node_id, location, flags, name_loc, operator_loc, value, name, depth) + end + + # Create a new LocalVariableReadNode node. + def local_variable_read_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", depth: 0) + LocalVariableReadNode.new(source, node_id, location, flags, name, depth) + end + + # Create a new LocalVariableTargetNode node. + def local_variable_target_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", depth: 0) + LocalVariableTargetNode.new(source, node_id, location, flags, name, depth) + end + + # Create a new LocalVariableWriteNode node. + def local_variable_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", depth: 0, name_loc: location, value: default_node(source, location), operator_loc: location) + LocalVariableWriteNode.new(source, node_id, location, flags, name, depth, name_loc, value, operator_loc) + end + + # Create a new MatchLastLineNode node. + def match_last_line_node(source: default_source, node_id: 0, location: default_location, flags: 0, opening_loc: location, content_loc: location, closing_loc: location, unescaped: "") + MatchLastLineNode.new(source, node_id, location, flags, opening_loc, content_loc, closing_loc, unescaped) + end + + # Create a new MatchPredicateNode node. + def match_predicate_node(source: default_source, node_id: 0, location: default_location, flags: 0, value: default_node(source, location), pattern: default_node(source, location), operator_loc: location) + MatchPredicateNode.new(source, node_id, location, flags, value, pattern, operator_loc) + end + + # Create a new MatchRequiredNode node. + def match_required_node(source: default_source, node_id: 0, location: default_location, flags: 0, value: default_node(source, location), pattern: default_node(source, location), operator_loc: location) + MatchRequiredNode.new(source, node_id, location, flags, value, pattern, operator_loc) + end + + # Create a new MatchWriteNode node. + def match_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, call: call_node(source: source), targets: []) + MatchWriteNode.new(source, node_id, location, flags, call, targets) + end + + # Create a new MissingNode node. + def missing_node(source: default_source, node_id: 0, location: default_location, flags: 0) + MissingNode.new(source, node_id, location, flags) + end + + # Create a new ModuleNode node. + def module_node(source: default_source, node_id: 0, location: default_location, flags: 0, locals: [], module_keyword_loc: location, constant_path: constant_read_node(source: source), body: nil, end_keyword_loc: location, name: :"") + ModuleNode.new(source, node_id, location, flags, locals, module_keyword_loc, constant_path, body, end_keyword_loc, name) + end + + # Create a new MultiTargetNode node. + def multi_target_node(source: default_source, node_id: 0, location: default_location, flags: 0, lefts: [], rest: nil, rights: [], lparen_loc: nil, rparen_loc: nil) + MultiTargetNode.new(source, node_id, location, flags, lefts, rest, rights, lparen_loc, rparen_loc) + end + + # Create a new MultiWriteNode node. + def multi_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, lefts: [], rest: nil, rights: [], lparen_loc: nil, rparen_loc: nil, operator_loc: location, value: default_node(source, location)) + MultiWriteNode.new(source, node_id, location, flags, lefts, rest, rights, lparen_loc, rparen_loc, operator_loc, value) + end + + # Create a new NextNode node. + def next_node(source: default_source, node_id: 0, location: default_location, flags: 0, arguments: nil, keyword_loc: location) + NextNode.new(source, node_id, location, flags, arguments, keyword_loc) + end + + # Create a new NilNode node. + def nil_node(source: default_source, node_id: 0, location: default_location, flags: 0) + NilNode.new(source, node_id, location, flags) + end + + # Create a new NoKeywordsParameterNode node. + def no_keywords_parameter_node(source: default_source, node_id: 0, location: default_location, flags: 0, operator_loc: location, keyword_loc: location) + NoKeywordsParameterNode.new(source, node_id, location, flags, operator_loc, keyword_loc) + end + + # Create a new NumberedParametersNode node. + def numbered_parameters_node(source: default_source, node_id: 0, location: default_location, flags: 0, maximum: 0) + NumberedParametersNode.new(source, node_id, location, flags, maximum) + end + + # Create a new NumberedReferenceReadNode node. + def numbered_reference_read_node(source: default_source, node_id: 0, location: default_location, flags: 0, number: 0) + NumberedReferenceReadNode.new(source, node_id, location, flags, number) + end + + # Create a new OptionalKeywordParameterNode node. + def optional_keyword_parameter_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", name_loc: location, value: default_node(source, location)) + OptionalKeywordParameterNode.new(source, node_id, location, flags, name, name_loc, value) + end + + # Create a new OptionalParameterNode node. + def optional_parameter_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", name_loc: location, operator_loc: location, value: default_node(source, location)) + OptionalParameterNode.new(source, node_id, location, flags, name, name_loc, operator_loc, value) + end + + # Create a new OrNode node. + def or_node(source: default_source, node_id: 0, location: default_location, flags: 0, left: default_node(source, location), right: default_node(source, location), operator_loc: location) + OrNode.new(source, node_id, location, flags, left, right, operator_loc) + end + + # Create a new ParametersNode node. + def parameters_node(source: default_source, node_id: 0, location: default_location, flags: 0, requireds: [], optionals: [], rest: nil, posts: [], keywords: [], keyword_rest: nil, block: nil) + ParametersNode.new(source, node_id, location, flags, requireds, optionals, rest, posts, keywords, keyword_rest, block) + end + + # Create a new ParenthesesNode node. + def parentheses_node(source: default_source, node_id: 0, location: default_location, flags: 0, body: nil, opening_loc: location, closing_loc: location) + ParenthesesNode.new(source, node_id, location, flags, body, opening_loc, closing_loc) + end + + # Create a new PinnedExpressionNode node. + def pinned_expression_node(source: default_source, node_id: 0, location: default_location, flags: 0, expression: default_node(source, location), operator_loc: location, lparen_loc: location, rparen_loc: location) + PinnedExpressionNode.new(source, node_id, location, flags, expression, operator_loc, lparen_loc, rparen_loc) + end + + # Create a new PinnedVariableNode node. + def pinned_variable_node(source: default_source, node_id: 0, location: default_location, flags: 0, variable: local_variable_read_node(source: source), operator_loc: location) + PinnedVariableNode.new(source, node_id, location, flags, variable, operator_loc) + end + + # Create a new PostExecutionNode node. + def post_execution_node(source: default_source, node_id: 0, location: default_location, flags: 0, statements: nil, keyword_loc: location, opening_loc: location, closing_loc: location) + PostExecutionNode.new(source, node_id, location, flags, statements, keyword_loc, opening_loc, closing_loc) + end + + # Create a new PreExecutionNode node. + def pre_execution_node(source: default_source, node_id: 0, location: default_location, flags: 0, statements: nil, keyword_loc: location, opening_loc: location, closing_loc: location) + PreExecutionNode.new(source, node_id, location, flags, statements, keyword_loc, opening_loc, closing_loc) + end + + # Create a new ProgramNode node. + def program_node(source: default_source, node_id: 0, location: default_location, flags: 0, locals: [], statements: statements_node(source: source)) + ProgramNode.new(source, node_id, location, flags, locals, statements) + end + + # Create a new RangeNode node. + def range_node(source: default_source, node_id: 0, location: default_location, flags: 0, left: nil, right: nil, operator_loc: location) + RangeNode.new(source, node_id, location, flags, left, right, operator_loc) + end + + # Create a new RationalNode node. + def rational_node(source: default_source, node_id: 0, location: default_location, flags: 0, numerator: 0, denominator: 0) + RationalNode.new(source, node_id, location, flags, numerator, denominator) + end + + # Create a new RedoNode node. + def redo_node(source: default_source, node_id: 0, location: default_location, flags: 0) + RedoNode.new(source, node_id, location, flags) + end + + # Create a new RegularExpressionNode node. + def regular_expression_node(source: default_source, node_id: 0, location: default_location, flags: 0, opening_loc: location, content_loc: location, closing_loc: location, unescaped: "") + RegularExpressionNode.new(source, node_id, location, flags, opening_loc, content_loc, closing_loc, unescaped) + end + + # Create a new RequiredKeywordParameterNode node. + def required_keyword_parameter_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", name_loc: location) + RequiredKeywordParameterNode.new(source, node_id, location, flags, name, name_loc) + end + + # Create a new RequiredParameterNode node. + def required_parameter_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"") + RequiredParameterNode.new(source, node_id, location, flags, name) + end + + # Create a new RescueModifierNode node. + def rescue_modifier_node(source: default_source, node_id: 0, location: default_location, flags: 0, expression: default_node(source, location), keyword_loc: location, rescue_expression: default_node(source, location)) + RescueModifierNode.new(source, node_id, location, flags, expression, keyword_loc, rescue_expression) + end + + # Create a new RescueNode node. + def rescue_node(source: default_source, node_id: 0, location: default_location, flags: 0, keyword_loc: location, exceptions: [], operator_loc: nil, reference: nil, then_keyword_loc: nil, statements: nil, subsequent: nil) + RescueNode.new(source, node_id, location, flags, keyword_loc, exceptions, operator_loc, reference, then_keyword_loc, statements, subsequent) + end + + # Create a new RestParameterNode node. + def rest_parameter_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: nil, name_loc: nil, operator_loc: location) + RestParameterNode.new(source, node_id, location, flags, name, name_loc, operator_loc) + end + + # Create a new RetryNode node. + def retry_node(source: default_source, node_id: 0, location: default_location, flags: 0) + RetryNode.new(source, node_id, location, flags) + end + + # Create a new ReturnNode node. + def return_node(source: default_source, node_id: 0, location: default_location, flags: 0, keyword_loc: location, arguments: nil) + ReturnNode.new(source, node_id, location, flags, keyword_loc, arguments) + end + + # Create a new SelfNode node. + def self_node(source: default_source, node_id: 0, location: default_location, flags: 0) + SelfNode.new(source, node_id, location, flags) + end + + # Create a new ShareableConstantNode node. + def shareable_constant_node(source: default_source, node_id: 0, location: default_location, flags: 0, write: constant_write_node(source: source)) + ShareableConstantNode.new(source, node_id, location, flags, write) + end + + # Create a new SingletonClassNode node. + def singleton_class_node(source: default_source, node_id: 0, location: default_location, flags: 0, locals: [], class_keyword_loc: location, operator_loc: location, expression: default_node(source, location), body: nil, end_keyword_loc: location) + SingletonClassNode.new(source, node_id, location, flags, locals, class_keyword_loc, operator_loc, expression, body, end_keyword_loc) + end + + # Create a new SourceEncodingNode node. + def source_encoding_node(source: default_source, node_id: 0, location: default_location, flags: 0) + SourceEncodingNode.new(source, node_id, location, flags) + end + + # Create a new SourceFileNode node. + def source_file_node(source: default_source, node_id: 0, location: default_location, flags: 0, filepath: "") + SourceFileNode.new(source, node_id, location, flags, filepath) + end + + # Create a new SourceLineNode node. + def source_line_node(source: default_source, node_id: 0, location: default_location, flags: 0) + SourceLineNode.new(source, node_id, location, flags) + end + + # Create a new SplatNode node. + def splat_node(source: default_source, node_id: 0, location: default_location, flags: 0, operator_loc: location, expression: nil) + SplatNode.new(source, node_id, location, flags, operator_loc, expression) + end + + # Create a new StatementsNode node. + def statements_node(source: default_source, node_id: 0, location: default_location, flags: 0, body: []) + StatementsNode.new(source, node_id, location, flags, body) + end + + # Create a new StringNode node. + def string_node(source: default_source, node_id: 0, location: default_location, flags: 0, opening_loc: nil, content_loc: location, closing_loc: nil, unescaped: "") + StringNode.new(source, node_id, location, flags, opening_loc, content_loc, closing_loc, unescaped) + end + + # Create a new SuperNode node. + def super_node(source: default_source, node_id: 0, location: default_location, flags: 0, keyword_loc: location, lparen_loc: nil, arguments: nil, rparen_loc: nil, block: nil) + SuperNode.new(source, node_id, location, flags, keyword_loc, lparen_loc, arguments, rparen_loc, block) + end + + # Create a new SymbolNode node. + def symbol_node(source: default_source, node_id: 0, location: default_location, flags: 0, opening_loc: nil, value_loc: nil, closing_loc: nil, unescaped: "") + SymbolNode.new(source, node_id, location, flags, opening_loc, value_loc, closing_loc, unescaped) + end + + # Create a new TrueNode node. + def true_node(source: default_source, node_id: 0, location: default_location, flags: 0) + TrueNode.new(source, node_id, location, flags) + end + + # Create a new UndefNode node. + def undef_node(source: default_source, node_id: 0, location: default_location, flags: 0, names: [], keyword_loc: location) + UndefNode.new(source, node_id, location, flags, names, keyword_loc) + end + + # Create a new UnlessNode node. + def unless_node(source: default_source, node_id: 0, location: default_location, flags: 0, keyword_loc: location, predicate: default_node(source, location), then_keyword_loc: nil, statements: nil, else_clause: nil, end_keyword_loc: nil) + UnlessNode.new(source, node_id, location, flags, keyword_loc, predicate, then_keyword_loc, statements, else_clause, end_keyword_loc) + end + + # Create a new UntilNode node. + def until_node(source: default_source, node_id: 0, location: default_location, flags: 0, keyword_loc: location, do_keyword_loc: nil, closing_loc: nil, predicate: default_node(source, location), statements: nil) + UntilNode.new(source, node_id, location, flags, keyword_loc, do_keyword_loc, closing_loc, predicate, statements) + end + + # Create a new WhenNode node. + def when_node(source: default_source, node_id: 0, location: default_location, flags: 0, keyword_loc: location, conditions: [], then_keyword_loc: nil, statements: nil) + WhenNode.new(source, node_id, location, flags, keyword_loc, conditions, then_keyword_loc, statements) + end + + # Create a new WhileNode node. + def while_node(source: default_source, node_id: 0, location: default_location, flags: 0, keyword_loc: location, do_keyword_loc: nil, closing_loc: nil, predicate: default_node(source, location), statements: nil) + WhileNode.new(source, node_id, location, flags, keyword_loc, do_keyword_loc, closing_loc, predicate, statements) + end + + # Create a new XStringNode node. + def x_string_node(source: default_source, node_id: 0, location: default_location, flags: 0, opening_loc: location, content_loc: location, closing_loc: location, unescaped: "") + XStringNode.new(source, node_id, location, flags, opening_loc, content_loc, closing_loc, unescaped) + end + + # Create a new YieldNode node. + def yield_node(source: default_source, node_id: 0, location: default_location, flags: 0, keyword_loc: location, lparen_loc: nil, arguments: nil, rparen_loc: nil) + YieldNode.new(source, node_id, location, flags, keyword_loc, lparen_loc, arguments, rparen_loc) + end + + # Retrieve the value of one of the ArgumentsNodeFlags flags. + def arguments_node_flag(name) + case name + when :contains_forwarding then ArgumentsNodeFlags::CONTAINS_FORWARDING + when :contains_keywords then ArgumentsNodeFlags::CONTAINS_KEYWORDS + when :contains_keyword_splat then ArgumentsNodeFlags::CONTAINS_KEYWORD_SPLAT + when :contains_splat then ArgumentsNodeFlags::CONTAINS_SPLAT + when :contains_multiple_splats then ArgumentsNodeFlags::CONTAINS_MULTIPLE_SPLATS + else Kernel.raise ArgumentError, "invalid ArgumentsNodeFlags flag: #{name.inspect}" + end + end + + # Retrieve the value of one of the ArrayNodeFlags flags. + def array_node_flag(name) + case name + when :contains_splat then ArrayNodeFlags::CONTAINS_SPLAT + else Kernel.raise ArgumentError, "invalid ArrayNodeFlags flag: #{name.inspect}" + end + end + + # Retrieve the value of one of the CallNodeFlags flags. + def call_node_flag(name) + case name + when :safe_navigation then CallNodeFlags::SAFE_NAVIGATION + when :variable_call then CallNodeFlags::VARIABLE_CALL + when :attribute_write then CallNodeFlags::ATTRIBUTE_WRITE + when :ignore_visibility then CallNodeFlags::IGNORE_VISIBILITY + else Kernel.raise ArgumentError, "invalid CallNodeFlags flag: #{name.inspect}" + end + end + + # Retrieve the value of one of the EncodingFlags flags. + def encoding_flag(name) + case name + when :forced_utf8_encoding then EncodingFlags::FORCED_UTF8_ENCODING + when :forced_binary_encoding then EncodingFlags::FORCED_BINARY_ENCODING + else Kernel.raise ArgumentError, "invalid EncodingFlags flag: #{name.inspect}" + end + end + + # Retrieve the value of one of the IntegerBaseFlags flags. + def integer_base_flag(name) + case name + when :binary then IntegerBaseFlags::BINARY + when :decimal then IntegerBaseFlags::DECIMAL + when :octal then IntegerBaseFlags::OCTAL + when :hexadecimal then IntegerBaseFlags::HEXADECIMAL + else Kernel.raise ArgumentError, "invalid IntegerBaseFlags flag: #{name.inspect}" + end + end + + # Retrieve the value of one of the InterpolatedStringNodeFlags flags. + def interpolated_string_node_flag(name) + case name + when :frozen then InterpolatedStringNodeFlags::FROZEN + when :mutable then InterpolatedStringNodeFlags::MUTABLE + else Kernel.raise ArgumentError, "invalid InterpolatedStringNodeFlags flag: #{name.inspect}" + end + end + + # Retrieve the value of one of the KeywordHashNodeFlags flags. + def keyword_hash_node_flag(name) + case name + when :symbol_keys then KeywordHashNodeFlags::SYMBOL_KEYS + else Kernel.raise ArgumentError, "invalid KeywordHashNodeFlags flag: #{name.inspect}" + end + end + + # Retrieve the value of one of the LoopFlags flags. + def loop_flag(name) + case name + when :begin_modifier then LoopFlags::BEGIN_MODIFIER + else Kernel.raise ArgumentError, "invalid LoopFlags flag: #{name.inspect}" + end + end + + # Retrieve the value of one of the ParameterFlags flags. + def parameter_flag(name) + case name + when :repeated_parameter then ParameterFlags::REPEATED_PARAMETER + else Kernel.raise ArgumentError, "invalid ParameterFlags flag: #{name.inspect}" + end + end + + # Retrieve the value of one of the ParenthesesNodeFlags flags. + def parentheses_node_flag(name) + case name + when :multiple_statements then ParenthesesNodeFlags::MULTIPLE_STATEMENTS + else Kernel.raise ArgumentError, "invalid ParenthesesNodeFlags flag: #{name.inspect}" + end + end + + # Retrieve the value of one of the RangeFlags flags. + def range_flag(name) + case name + when :exclude_end then RangeFlags::EXCLUDE_END + else Kernel.raise ArgumentError, "invalid RangeFlags flag: #{name.inspect}" + end + end + + # Retrieve the value of one of the RegularExpressionFlags flags. + def regular_expression_flag(name) + case name + when :ignore_case then RegularExpressionFlags::IGNORE_CASE + when :extended then RegularExpressionFlags::EXTENDED + when :multi_line then RegularExpressionFlags::MULTI_LINE + when :once then RegularExpressionFlags::ONCE + when :euc_jp then RegularExpressionFlags::EUC_JP + when :ascii_8bit then RegularExpressionFlags::ASCII_8BIT + when :windows_31j then RegularExpressionFlags::WINDOWS_31J + when :utf_8 then RegularExpressionFlags::UTF_8 + when :forced_utf8_encoding then RegularExpressionFlags::FORCED_UTF8_ENCODING + when :forced_binary_encoding then RegularExpressionFlags::FORCED_BINARY_ENCODING + when :forced_us_ascii_encoding then RegularExpressionFlags::FORCED_US_ASCII_ENCODING + else Kernel.raise ArgumentError, "invalid RegularExpressionFlags flag: #{name.inspect}" + end + end + + # Retrieve the value of one of the ShareableConstantNodeFlags flags. + def shareable_constant_node_flag(name) + case name + when :literal then ShareableConstantNodeFlags::LITERAL + when :experimental_everything then ShareableConstantNodeFlags::EXPERIMENTAL_EVERYTHING + when :experimental_copy then ShareableConstantNodeFlags::EXPERIMENTAL_COPY + else Kernel.raise ArgumentError, "invalid ShareableConstantNodeFlags flag: #{name.inspect}" + end + end + + # Retrieve the value of one of the StringFlags flags. + def string_flag(name) + case name + when :forced_utf8_encoding then StringFlags::FORCED_UTF8_ENCODING + when :forced_binary_encoding then StringFlags::FORCED_BINARY_ENCODING + when :frozen then StringFlags::FROZEN + when :mutable then StringFlags::MUTABLE + else Kernel.raise ArgumentError, "invalid StringFlags flag: #{name.inspect}" + end + end + + # Retrieve the value of one of the SymbolFlags flags. + def symbol_flag(name) + case name + when :forced_utf8_encoding then SymbolFlags::FORCED_UTF8_ENCODING + when :forced_binary_encoding then SymbolFlags::FORCED_BINARY_ENCODING + when :forced_us_ascii_encoding then SymbolFlags::FORCED_US_ASCII_ENCODING + else Kernel.raise ArgumentError, "invalid SymbolFlags flag: #{name.inspect}" + end + end + + private + + # The default source object that gets attached to nodes and locations if no + # source is specified. + def default_source + Source.for("") + end + + # The default location object that gets attached to nodes if no location is + # specified, which uses the given source. + def default_location + Location.new(default_source, 0, 0) + end + + # The default node that gets attached to nodes if no node is specified for a + # required node field. + def default_node(source, location) + MissingNode.new(source, -1, location, 0) + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/ffi.rb b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/ffi.rb new file mode 100644 index 0000000..d4c9d60 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/ffi.rb @@ -0,0 +1,578 @@ +# frozen_string_literal: true +# :markup: markdown +# 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" + +# We want to eagerly load this file if there are Ractors so that it does not get +# autoloaded from within a non-main Ractor. +require "prism/serialize" if defined?(Ractor) + +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. + libprism_in_build = File.expand_path("../../build/libprism.#{RbConfig::CONFIG["SOEXT"]}", __dir__) + libprism_in_libdir = "#{RbConfig::CONFIG["libdir"]}/prism/libprism.#{RbConfig::CONFIG["SOEXT"]}" + + if File.exist?(libprism_in_build) + INCLUDE_DIR = File.expand_path("../../include", __dir__) + ffi_lib libprism_in_build + else + INCLUDE_DIR = "#{RbConfig::CONFIG["libdir"]}/prism/include" + ffi_lib libprism_in_libdir + end + + # 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("#{INCLUDE_DIR}/#{header}") 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 (?.+) (?\w+)\((?.+)\);$/ =~ 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 + callback :pm_parse_stream_feof_t, [:pointer], :int + enum :pm_string_init_result_t, %i[PM_STRING_INIT_SUCCESS PM_STRING_INIT_ERROR_GENERIC PM_STRING_INIT_ERROR_DIRECTORY] + enum :pm_string_query_t, [:PM_STRING_QUERY_ERROR, -1, :PM_STRING_QUERY_FALSE, :PM_STRING_QUERY_TRUE] + + 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_string_query_local", + "pm_string_query_constant", + "pm_string_query_method_name", + [:pm_parse_stream_fgets_t, :pm_parse_stream_feof_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 + + PLATFORM_EXPECTS_UTF8 = + RbConfig::CONFIG["host_os"].match?(/bccwin|cygwin|djgpp|mingw|mswin|wince|darwin/i) + + 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) + + # On Windows and Mac, it's expected that filepaths will be encoded in + # UTF-8. If they are not, we need to convert them to UTF-8 before + # passing them into pm_string_mapped_init. + if PLATFORM_EXPECTS_UTF8 && (encoding = filepath.encoding) != Encoding::ASCII_8BIT && encoding != Encoding::UTF_8 + filepath = filepath.encode(Encoding::UTF_8) + end + + FFI::MemoryPointer.new(SIZEOF) do |pm_string| + case (result = LibRubyParser.pm_string_mapped_init(pm_string, filepath)) + when :PM_STRING_INIT_SUCCESS + pointer = LibRubyParser.pm_string_source(pm_string) + length = LibRubyParser.pm_string_length(pm_string) + return yield new(pointer, length, false) + when :PM_STRING_INIT_ERROR_GENERIC + raise SystemCallError.new(filepath, FFI.errno) + when :PM_STRING_INIT_ERROR_DIRECTORY + raise Errno::EISDIR.new(filepath) + else + raise "Unknown error initializing pm_string_t: #{result.inspect}" + 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.freeze + + class << self + # Mirror the Prism.dump API by using the serialization API. + def dump(source, **options) + LibRubyParser::PrismString.with_string(source) { |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 + } + + eof_callback = -> (_) { stream.eof? } + + # 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, eof_callback, dump_options(options)) + Prism.load(source, buffer.read, options.fetch(:freeze, false)) + 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 + + # Mirror the Prism.profile API by using the serialization API. + def profile(source, **options) + LibRubyParser::PrismString.with_string(source) do |string| + LibRubyParser::PrismBuffer.with do |buffer| + LibRubyParser.pm_serialize_parse(buffer.pointer, string.pointer, string.length, dump_options(options)) + nil + end + end + end + + # Mirror the Prism.profile_file API by using the serialization API. + def profile_file(filepath, **options) + LibRubyParser::PrismString.with_file(filepath) do |string| + LibRubyParser::PrismBuffer.with do |buffer| + options[:filepath] = filepath + LibRubyParser.pm_serialize_parse(buffer.pointer, string.pointer, string.length, dump_options(options)) + nil + end + end + 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)) + + dumped = buffer.read + dumped.freeze if options.fetch(:freeze, false) + + dumped + end + end + + def lex_common(string, code, options) # :nodoc: + LibRubyParser::PrismBuffer.with do |buffer| + LibRubyParser.pm_serialize_lex(buffer.pointer, string.pointer, string.length, dump_options(options)) + Serialize.load_lex(code, buffer.read, options.fetch(:freeze, false)) + end + end + + def parse_common(string, code, options) # :nodoc: + serialized = dump_common(string, options) + Serialize.load_parse(code, serialized, options.fetch(:freeze, false)) + 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)) + Serialize.load_parse_comments(code, buffer.read, options.fetch(:freeze, false)) + 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)) + Serialize.load_parse_lex(code, buffer.read, options.fetch(:freeze, false)) + 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 + + # Return the value that should be dumped for the version option. + def dump_options_version(version) + current = version == "current" + + case current ? RUBY_VERSION : version + when nil, "latest" + 0 # Handled in pm_parser_init + when /\A3\.3(\.\d+)?\z/ + 1 + when /\A3\.4(\.\d+)?\z/ + 2 + when /\A3\.5(\.\d+)?\z/, /\A4\.0(\.\d+)?\z/ + 3 + when /\A4\.1(\.\d+)?\z/ + 4 + else + if current + raise CurrentVersionError, RUBY_VERSION + else + raise ArgumentError, "invalid version: #{version}" + 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.is_a?(Encoding) ? encoding.name : encoding + 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 << dump_options_version(options[:version]) + + template << "C" + values << (options[:encoding] == false ? 1 : 0) + + template << "C" + values << (options.fetch(:main_script, false) ? 1 : 0) + + template << "C" + values << (options.fetch(:partial_script, false) ? 1 : 0) + + template << "C" + values << (options.fetch(:freeze, false) ? 1 : 0) + + template << "L" + if (scopes = options[:scopes]) + values << scopes.length + + scopes.each do |scope| + locals = nil + forwarding = 0 + + case scope + when Array + locals = scope + when Scope + locals = scope.locals + + scope.forwarding.each do |forward| + case forward + when :* then forwarding |= 0x1 + when :** then forwarding |= 0x2 + when :& then forwarding |= 0x4 + when :"..." then forwarding |= 0x8 + else raise ArgumentError, "invalid forwarding value: #{forward}" + end + end + else + raise TypeError, "wrong argument type #{scope.class.inspect} (expected Array or Prism::Scope)" + end + + template << "L" + values << locals.length + + template << "C" + values << forwarding + + locals.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 + + # Here we are going to patch StringQuery to put in the class-level methods so + # that it can maintain a consistent interface + class StringQuery + class << self + # Mirrors the C extension's StringQuery::local? method. + def local?(string) + query(LibRubyParser.pm_string_query_local(string, string.bytesize, string.encoding.name)) + end + + # Mirrors the C extension's StringQuery::constant? method. + def constant?(string) + query(LibRubyParser.pm_string_query_constant(string, string.bytesize, string.encoding.name)) + end + + # Mirrors the C extension's StringQuery::method_name? method. + def method_name?(string) + query(LibRubyParser.pm_string_query_method_name(string, string.bytesize, string.encoding.name)) + end + + private + + # Parse the enum result and return an appropriate boolean. + def query(result) + case result + when :PM_STRING_QUERY_ERROR + raise ArgumentError, "Invalid or non ascii-compatible encoding" + when :PM_STRING_QUERY_FALSE + false + when :PM_STRING_QUERY_TRUE + true + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/inspect_visitor.rb b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/inspect_visitor.rb new file mode 100644 index 0000000..f4548f0 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/inspect_visitor.rb @@ -0,0 +1,2393 @@ +# frozen_string_literal: true +# :markup: markdown + +=begin +-- +This file is generated by the templates/template.rb script and should not be +modified manually. See templates/lib/prism/inspect_visitor.rb.erb +if you are looking to modify the template +++ +=end + +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 + + # Inspect a AliasGlobalVariableNode node. + def visit_alias_global_variable_node(node) + commands << [inspect_node("AliasGlobalVariableNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── new_name:\n", indent] + commands << [node.new_name, "#{indent}│ "] + commands << ["├── old_name:\n", indent] + commands << [node.old_name, "#{indent}│ "] + commands << ["└── keyword_loc: #{inspect_location(node.keyword_loc)}\n", indent] + end + + # Inspect a AliasMethodNode node. + def visit_alias_method_node(node) + commands << [inspect_node("AliasMethodNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── new_name:\n", indent] + commands << [node.new_name, "#{indent}│ "] + commands << ["├── old_name:\n", indent] + commands << [node.old_name, "#{indent}│ "] + commands << ["└── keyword_loc: #{inspect_location(node.keyword_loc)}\n", indent] + end + + # Inspect a AlternationPatternNode node. + def visit_alternation_pattern_node(node) + commands << [inspect_node("AlternationPatternNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── left:\n", indent] + commands << [node.left, "#{indent}│ "] + commands << ["├── right:\n", indent] + commands << [node.right, "#{indent}│ "] + commands << ["└── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + end + + # Inspect a AndNode node. + def visit_and_node(node) + commands << [inspect_node("AndNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── left:\n", indent] + commands << [node.left, "#{indent}│ "] + commands << ["├── right:\n", indent] + commands << [node.right, "#{indent}│ "] + commands << ["└── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + end + + # Inspect a ArgumentsNode node. + def visit_arguments_node(node) + commands << [inspect_node("ArgumentsNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ("contains_forwarding" if node.contains_forwarding?), ("contains_keywords" if node.contains_keywords?), ("contains_keyword_splat" if node.contains_keyword_splat?), ("contains_splat" if node.contains_splat?), ("contains_multiple_splats" if node.contains_multiple_splats?)].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["└── arguments: (length: #{(arguments = node.arguments).length})\n", indent] + if arguments.any? + arguments[0...-1].each do |child| + commands << [Replace.new("#{indent} ├── "), indent] + commands << [child, "#{indent} │ "] + end + commands << [Replace.new("#{indent} └── "), indent] + commands << [arguments[-1], "#{indent} "] + end + end + + # Inspect a ArrayNode node. + def visit_array_node(node) + commands << [inspect_node("ArrayNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ("contains_splat" if node.contains_splat?)].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── elements: (length: #{(elements = node.elements).length})\n", indent] + if elements.any? + elements[0...-1].each do |child| + commands << [Replace.new("#{indent}│ ├── "), indent] + commands << [child, "#{indent}│ │ "] + end + commands << [Replace.new("#{indent}│ └── "), indent] + commands << [elements[-1], "#{indent}│ "] + end + commands << ["├── opening_loc: #{inspect_location(node.opening_loc)}\n", indent] + commands << ["└── closing_loc: #{inspect_location(node.closing_loc)}\n", indent] + end + + # Inspect a ArrayPatternNode node. + def visit_array_pattern_node(node) + commands << [inspect_node("ArrayPatternNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + if (constant = node.constant).nil? + commands << ["├── constant: ∅\n", indent] + else + commands << ["├── constant:\n", indent] + commands << [constant, "#{indent}│ "] + end + commands << ["├── requireds: (length: #{(requireds = node.requireds).length})\n", indent] + if requireds.any? + requireds[0...-1].each do |child| + commands << [Replace.new("#{indent}│ ├── "), indent] + commands << [child, "#{indent}│ │ "] + end + commands << [Replace.new("#{indent}│ └── "), indent] + commands << [requireds[-1], "#{indent}│ "] + end + if (rest = node.rest).nil? + commands << ["├── rest: ∅\n", indent] + else + commands << ["├── rest:\n", indent] + commands << [rest, "#{indent}│ "] + end + commands << ["├── posts: (length: #{(posts = node.posts).length})\n", indent] + if posts.any? + posts[0...-1].each do |child| + commands << [Replace.new("#{indent}│ ├── "), indent] + commands << [child, "#{indent}│ │ "] + end + commands << [Replace.new("#{indent}│ └── "), indent] + commands << [posts[-1], "#{indent}│ "] + end + commands << ["├── opening_loc: #{inspect_location(node.opening_loc)}\n", indent] + commands << ["└── closing_loc: #{inspect_location(node.closing_loc)}\n", indent] + end + + # Inspect a AssocNode node. + def visit_assoc_node(node) + commands << [inspect_node("AssocNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── key:\n", indent] + commands << [node.key, "#{indent}│ "] + commands << ["├── value:\n", indent] + commands << [node.value, "#{indent}│ "] + commands << ["└── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + end + + # Inspect a AssocSplatNode node. + def visit_assoc_splat_node(node) + commands << [inspect_node("AssocSplatNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + if (value = node.value).nil? + commands << ["├── value: ∅\n", indent] + else + commands << ["├── value:\n", indent] + commands << [value, "#{indent}│ "] + end + commands << ["└── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + end + + # Inspect a BackReferenceReadNode node. + def visit_back_reference_read_node(node) + commands << [inspect_node("BackReferenceReadNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["└── name: #{node.name.inspect}\n", indent] + end + + # Inspect a BeginNode node. + def visit_begin_node(node) + commands << [inspect_node("BeginNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── begin_keyword_loc: #{inspect_location(node.begin_keyword_loc)}\n", indent] + if (statements = node.statements).nil? + commands << ["├── statements: ∅\n", indent] + else + commands << ["├── statements:\n", indent] + commands << [statements, "#{indent}│ "] + end + if (rescue_clause = node.rescue_clause).nil? + commands << ["├── rescue_clause: ∅\n", indent] + else + commands << ["├── rescue_clause:\n", indent] + commands << [rescue_clause, "#{indent}│ "] + end + if (else_clause = node.else_clause).nil? + commands << ["├── else_clause: ∅\n", indent] + else + commands << ["├── else_clause:\n", indent] + commands << [else_clause, "#{indent}│ "] + end + if (ensure_clause = node.ensure_clause).nil? + commands << ["├── ensure_clause: ∅\n", indent] + else + commands << ["├── ensure_clause:\n", indent] + commands << [ensure_clause, "#{indent}│ "] + end + commands << ["└── end_keyword_loc: #{inspect_location(node.end_keyword_loc)}\n", indent] + end + + # Inspect a BlockArgumentNode node. + def visit_block_argument_node(node) + commands << [inspect_node("BlockArgumentNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + if (expression = node.expression).nil? + commands << ["├── expression: ∅\n", indent] + else + commands << ["├── expression:\n", indent] + commands << [expression, "#{indent}│ "] + end + commands << ["└── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + end + + # Inspect a BlockLocalVariableNode node. + def visit_block_local_variable_node(node) + commands << [inspect_node("BlockLocalVariableNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ("repeated_parameter" if node.repeated_parameter?)].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["└── name: #{node.name.inspect}\n", indent] + end + + # Inspect a BlockNode node. + def visit_block_node(node) + commands << [inspect_node("BlockNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── locals: #{node.locals.inspect}\n", indent] + if (parameters = node.parameters).nil? + commands << ["├── parameters: ∅\n", indent] + else + commands << ["├── parameters:\n", indent] + commands << [parameters, "#{indent}│ "] + end + if (body = node.body).nil? + commands << ["├── body: ∅\n", indent] + else + commands << ["├── body:\n", indent] + commands << [body, "#{indent}│ "] + end + commands << ["├── opening_loc: #{inspect_location(node.opening_loc)}\n", indent] + commands << ["└── closing_loc: #{inspect_location(node.closing_loc)}\n", indent] + end + + # Inspect a BlockParameterNode node. + def visit_block_parameter_node(node) + commands << [inspect_node("BlockParameterNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ("repeated_parameter" if node.repeated_parameter?)].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + if (name = node.name).nil? + commands << ["├── name: ∅\n", indent] + else + commands << ["├── name: #{name.inspect}\n", indent] + end + commands << ["├── name_loc: #{inspect_location(node.name_loc)}\n", indent] + commands << ["└── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + end + + # Inspect a BlockParametersNode node. + def visit_block_parameters_node(node) + commands << [inspect_node("BlockParametersNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + if (parameters = node.parameters).nil? + commands << ["├── parameters: ∅\n", indent] + else + commands << ["├── parameters:\n", indent] + commands << [parameters, "#{indent}│ "] + end + commands << ["├── locals: (length: #{(locals = node.locals).length})\n", indent] + if locals.any? + locals[0...-1].each do |child| + commands << [Replace.new("#{indent}│ ├── "), indent] + commands << [child, "#{indent}│ │ "] + end + commands << [Replace.new("#{indent}│ └── "), indent] + commands << [locals[-1], "#{indent}│ "] + end + commands << ["├── opening_loc: #{inspect_location(node.opening_loc)}\n", indent] + commands << ["└── closing_loc: #{inspect_location(node.closing_loc)}\n", indent] + end + + # Inspect a BreakNode node. + def visit_break_node(node) + commands << [inspect_node("BreakNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + if (arguments = node.arguments).nil? + commands << ["├── arguments: ∅\n", indent] + else + commands << ["├── arguments:\n", indent] + commands << [arguments, "#{indent}│ "] + end + commands << ["└── keyword_loc: #{inspect_location(node.keyword_loc)}\n", indent] + end + + # Inspect a CallAndWriteNode node. + def visit_call_and_write_node(node) + commands << [inspect_node("CallAndWriteNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ("safe_navigation" if node.safe_navigation?), ("variable_call" if node.variable_call?), ("attribute_write" if node.attribute_write?), ("ignore_visibility" if node.ignore_visibility?)].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + if (receiver = node.receiver).nil? + commands << ["├── receiver: ∅\n", indent] + else + commands << ["├── receiver:\n", indent] + commands << [receiver, "#{indent}│ "] + end + commands << ["├── call_operator_loc: #{inspect_location(node.call_operator_loc)}\n", indent] + commands << ["├── message_loc: #{inspect_location(node.message_loc)}\n", indent] + commands << ["├── read_name: #{node.read_name.inspect}\n", indent] + commands << ["├── write_name: #{node.write_name.inspect}\n", indent] + commands << ["├── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + commands << ["└── value:\n", indent] + commands << [node.value, "#{indent} "] + end + + # Inspect a CallNode node. + def visit_call_node(node) + commands << [inspect_node("CallNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ("safe_navigation" if node.safe_navigation?), ("variable_call" if node.variable_call?), ("attribute_write" if node.attribute_write?), ("ignore_visibility" if node.ignore_visibility?)].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + if (receiver = node.receiver).nil? + commands << ["├── receiver: ∅\n", indent] + else + commands << ["├── receiver:\n", indent] + commands << [receiver, "#{indent}│ "] + end + commands << ["├── call_operator_loc: #{inspect_location(node.call_operator_loc)}\n", indent] + commands << ["├── name: #{node.name.inspect}\n", indent] + commands << ["├── message_loc: #{inspect_location(node.message_loc)}\n", indent] + commands << ["├── opening_loc: #{inspect_location(node.opening_loc)}\n", indent] + if (arguments = node.arguments).nil? + commands << ["├── arguments: ∅\n", indent] + else + commands << ["├── arguments:\n", indent] + commands << [arguments, "#{indent}│ "] + end + commands << ["├── closing_loc: #{inspect_location(node.closing_loc)}\n", indent] + commands << ["├── equal_loc: #{inspect_location(node.equal_loc)}\n", indent] + if (block = node.block).nil? + commands << ["└── block: ∅\n", indent] + else + commands << ["└── block:\n", indent] + commands << [block, "#{indent} "] + end + end + + # Inspect a CallOperatorWriteNode node. + def visit_call_operator_write_node(node) + commands << [inspect_node("CallOperatorWriteNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ("safe_navigation" if node.safe_navigation?), ("variable_call" if node.variable_call?), ("attribute_write" if node.attribute_write?), ("ignore_visibility" if node.ignore_visibility?)].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + if (receiver = node.receiver).nil? + commands << ["├── receiver: ∅\n", indent] + else + commands << ["├── receiver:\n", indent] + commands << [receiver, "#{indent}│ "] + end + commands << ["├── call_operator_loc: #{inspect_location(node.call_operator_loc)}\n", indent] + commands << ["├── message_loc: #{inspect_location(node.message_loc)}\n", indent] + commands << ["├── read_name: #{node.read_name.inspect}\n", indent] + commands << ["├── write_name: #{node.write_name.inspect}\n", indent] + commands << ["├── binary_operator: #{node.binary_operator.inspect}\n", indent] + commands << ["├── binary_operator_loc: #{inspect_location(node.binary_operator_loc)}\n", indent] + commands << ["└── value:\n", indent] + commands << [node.value, "#{indent} "] + end + + # Inspect a CallOrWriteNode node. + def visit_call_or_write_node(node) + commands << [inspect_node("CallOrWriteNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ("safe_navigation" if node.safe_navigation?), ("variable_call" if node.variable_call?), ("attribute_write" if node.attribute_write?), ("ignore_visibility" if node.ignore_visibility?)].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + if (receiver = node.receiver).nil? + commands << ["├── receiver: ∅\n", indent] + else + commands << ["├── receiver:\n", indent] + commands << [receiver, "#{indent}│ "] + end + commands << ["├── call_operator_loc: #{inspect_location(node.call_operator_loc)}\n", indent] + commands << ["├── message_loc: #{inspect_location(node.message_loc)}\n", indent] + commands << ["├── read_name: #{node.read_name.inspect}\n", indent] + commands << ["├── write_name: #{node.write_name.inspect}\n", indent] + commands << ["├── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + commands << ["└── value:\n", indent] + commands << [node.value, "#{indent} "] + end + + # Inspect a CallTargetNode node. + def visit_call_target_node(node) + commands << [inspect_node("CallTargetNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ("safe_navigation" if node.safe_navigation?), ("variable_call" if node.variable_call?), ("attribute_write" if node.attribute_write?), ("ignore_visibility" if node.ignore_visibility?)].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── receiver:\n", indent] + commands << [node.receiver, "#{indent}│ "] + commands << ["├── call_operator_loc: #{inspect_location(node.call_operator_loc)}\n", indent] + commands << ["├── name: #{node.name.inspect}\n", indent] + commands << ["└── message_loc: #{inspect_location(node.message_loc)}\n", indent] + end + + # Inspect a CapturePatternNode node. + def visit_capture_pattern_node(node) + commands << [inspect_node("CapturePatternNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── value:\n", indent] + commands << [node.value, "#{indent}│ "] + commands << ["├── target:\n", indent] + commands << [node.target, "#{indent}│ "] + commands << ["└── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + end + + # Inspect a CaseMatchNode node. + def visit_case_match_node(node) + commands << [inspect_node("CaseMatchNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + if (predicate = node.predicate).nil? + commands << ["├── predicate: ∅\n", indent] + else + commands << ["├── predicate:\n", indent] + commands << [predicate, "#{indent}│ "] + end + commands << ["├── conditions: (length: #{(conditions = node.conditions).length})\n", indent] + if conditions.any? + conditions[0...-1].each do |child| + commands << [Replace.new("#{indent}│ ├── "), indent] + commands << [child, "#{indent}│ │ "] + end + commands << [Replace.new("#{indent}│ └── "), indent] + commands << [conditions[-1], "#{indent}│ "] + end + if (else_clause = node.else_clause).nil? + commands << ["├── else_clause: ∅\n", indent] + else + commands << ["├── else_clause:\n", indent] + commands << [else_clause, "#{indent}│ "] + end + commands << ["├── case_keyword_loc: #{inspect_location(node.case_keyword_loc)}\n", indent] + commands << ["└── end_keyword_loc: #{inspect_location(node.end_keyword_loc)}\n", indent] + end + + # Inspect a CaseNode node. + def visit_case_node(node) + commands << [inspect_node("CaseNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + if (predicate = node.predicate).nil? + commands << ["├── predicate: ∅\n", indent] + else + commands << ["├── predicate:\n", indent] + commands << [predicate, "#{indent}│ "] + end + commands << ["├── conditions: (length: #{(conditions = node.conditions).length})\n", indent] + if conditions.any? + conditions[0...-1].each do |child| + commands << [Replace.new("#{indent}│ ├── "), indent] + commands << [child, "#{indent}│ │ "] + end + commands << [Replace.new("#{indent}│ └── "), indent] + commands << [conditions[-1], "#{indent}│ "] + end + if (else_clause = node.else_clause).nil? + commands << ["├── else_clause: ∅\n", indent] + else + commands << ["├── else_clause:\n", indent] + commands << [else_clause, "#{indent}│ "] + end + commands << ["├── case_keyword_loc: #{inspect_location(node.case_keyword_loc)}\n", indent] + commands << ["└── end_keyword_loc: #{inspect_location(node.end_keyword_loc)}\n", indent] + end + + # Inspect a ClassNode node. + def visit_class_node(node) + commands << [inspect_node("ClassNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── locals: #{node.locals.inspect}\n", indent] + commands << ["├── class_keyword_loc: #{inspect_location(node.class_keyword_loc)}\n", indent] + commands << ["├── constant_path:\n", indent] + commands << [node.constant_path, "#{indent}│ "] + commands << ["├── inheritance_operator_loc: #{inspect_location(node.inheritance_operator_loc)}\n", indent] + if (superclass = node.superclass).nil? + commands << ["├── superclass: ∅\n", indent] + else + commands << ["├── superclass:\n", indent] + commands << [superclass, "#{indent}│ "] + end + if (body = node.body).nil? + commands << ["├── body: ∅\n", indent] + else + commands << ["├── body:\n", indent] + commands << [body, "#{indent}│ "] + end + commands << ["├── end_keyword_loc: #{inspect_location(node.end_keyword_loc)}\n", indent] + commands << ["└── name: #{node.name.inspect}\n", indent] + end + + # Inspect a ClassVariableAndWriteNode node. + def visit_class_variable_and_write_node(node) + commands << [inspect_node("ClassVariableAndWriteNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── name: #{node.name.inspect}\n", indent] + commands << ["├── name_loc: #{inspect_location(node.name_loc)}\n", indent] + commands << ["├── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + commands << ["└── value:\n", indent] + commands << [node.value, "#{indent} "] + end + + # Inspect a ClassVariableOperatorWriteNode node. + def visit_class_variable_operator_write_node(node) + commands << [inspect_node("ClassVariableOperatorWriteNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── name: #{node.name.inspect}\n", indent] + commands << ["├── name_loc: #{inspect_location(node.name_loc)}\n", indent] + commands << ["├── binary_operator_loc: #{inspect_location(node.binary_operator_loc)}\n", indent] + commands << ["├── value:\n", indent] + commands << [node.value, "#{indent}│ "] + commands << ["└── binary_operator: #{node.binary_operator.inspect}\n", indent] + end + + # Inspect a ClassVariableOrWriteNode node. + def visit_class_variable_or_write_node(node) + commands << [inspect_node("ClassVariableOrWriteNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── name: #{node.name.inspect}\n", indent] + commands << ["├── name_loc: #{inspect_location(node.name_loc)}\n", indent] + commands << ["├── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + commands << ["└── value:\n", indent] + commands << [node.value, "#{indent} "] + end + + # Inspect a ClassVariableReadNode node. + def visit_class_variable_read_node(node) + commands << [inspect_node("ClassVariableReadNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["└── name: #{node.name.inspect}\n", indent] + end + + # Inspect a ClassVariableTargetNode node. + def visit_class_variable_target_node(node) + commands << [inspect_node("ClassVariableTargetNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["└── name: #{node.name.inspect}\n", indent] + end + + # Inspect a ClassVariableWriteNode node. + def visit_class_variable_write_node(node) + commands << [inspect_node("ClassVariableWriteNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── name: #{node.name.inspect}\n", indent] + commands << ["├── name_loc: #{inspect_location(node.name_loc)}\n", indent] + commands << ["├── value:\n", indent] + commands << [node.value, "#{indent}│ "] + commands << ["└── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + end + + # Inspect a ConstantAndWriteNode node. + def visit_constant_and_write_node(node) + commands << [inspect_node("ConstantAndWriteNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── name: #{node.name.inspect}\n", indent] + commands << ["├── name_loc: #{inspect_location(node.name_loc)}\n", indent] + commands << ["├── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + commands << ["└── value:\n", indent] + commands << [node.value, "#{indent} "] + end + + # Inspect a ConstantOperatorWriteNode node. + def visit_constant_operator_write_node(node) + commands << [inspect_node("ConstantOperatorWriteNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── name: #{node.name.inspect}\n", indent] + commands << ["├── name_loc: #{inspect_location(node.name_loc)}\n", indent] + commands << ["├── binary_operator_loc: #{inspect_location(node.binary_operator_loc)}\n", indent] + commands << ["├── value:\n", indent] + commands << [node.value, "#{indent}│ "] + commands << ["└── binary_operator: #{node.binary_operator.inspect}\n", indent] + end + + # Inspect a ConstantOrWriteNode node. + def visit_constant_or_write_node(node) + commands << [inspect_node("ConstantOrWriteNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── name: #{node.name.inspect}\n", indent] + commands << ["├── name_loc: #{inspect_location(node.name_loc)}\n", indent] + commands << ["├── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + commands << ["└── value:\n", indent] + commands << [node.value, "#{indent} "] + end + + # Inspect a ConstantPathAndWriteNode node. + def visit_constant_path_and_write_node(node) + commands << [inspect_node("ConstantPathAndWriteNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── target:\n", indent] + commands << [node.target, "#{indent}│ "] + commands << ["├── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + commands << ["└── value:\n", indent] + commands << [node.value, "#{indent} "] + end + + # Inspect a ConstantPathNode node. + def visit_constant_path_node(node) + commands << [inspect_node("ConstantPathNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + if (parent = node.parent).nil? + commands << ["├── parent: ∅\n", indent] + else + commands << ["├── parent:\n", indent] + commands << [parent, "#{indent}│ "] + end + if (name = node.name).nil? + commands << ["├── name: ∅\n", indent] + else + commands << ["├── name: #{name.inspect}\n", indent] + end + commands << ["├── delimiter_loc: #{inspect_location(node.delimiter_loc)}\n", indent] + commands << ["└── name_loc: #{inspect_location(node.name_loc)}\n", indent] + end + + # Inspect a ConstantPathOperatorWriteNode node. + def visit_constant_path_operator_write_node(node) + commands << [inspect_node("ConstantPathOperatorWriteNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── target:\n", indent] + commands << [node.target, "#{indent}│ "] + commands << ["├── binary_operator_loc: #{inspect_location(node.binary_operator_loc)}\n", indent] + commands << ["├── value:\n", indent] + commands << [node.value, "#{indent}│ "] + commands << ["└── binary_operator: #{node.binary_operator.inspect}\n", indent] + end + + # Inspect a ConstantPathOrWriteNode node. + def visit_constant_path_or_write_node(node) + commands << [inspect_node("ConstantPathOrWriteNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── target:\n", indent] + commands << [node.target, "#{indent}│ "] + commands << ["├── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + commands << ["└── value:\n", indent] + commands << [node.value, "#{indent} "] + end + + # Inspect a ConstantPathTargetNode node. + def visit_constant_path_target_node(node) + commands << [inspect_node("ConstantPathTargetNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + if (parent = node.parent).nil? + commands << ["├── parent: ∅\n", indent] + else + commands << ["├── parent:\n", indent] + commands << [parent, "#{indent}│ "] + end + if (name = node.name).nil? + commands << ["├── name: ∅\n", indent] + else + commands << ["├── name: #{name.inspect}\n", indent] + end + commands << ["├── delimiter_loc: #{inspect_location(node.delimiter_loc)}\n", indent] + commands << ["└── name_loc: #{inspect_location(node.name_loc)}\n", indent] + end + + # Inspect a ConstantPathWriteNode node. + def visit_constant_path_write_node(node) + commands << [inspect_node("ConstantPathWriteNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── target:\n", indent] + commands << [node.target, "#{indent}│ "] + commands << ["├── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + commands << ["└── value:\n", indent] + commands << [node.value, "#{indent} "] + end + + # Inspect a ConstantReadNode node. + def visit_constant_read_node(node) + commands << [inspect_node("ConstantReadNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["└── name: #{node.name.inspect}\n", indent] + end + + # Inspect a ConstantTargetNode node. + def visit_constant_target_node(node) + commands << [inspect_node("ConstantTargetNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["└── name: #{node.name.inspect}\n", indent] + end + + # Inspect a ConstantWriteNode node. + def visit_constant_write_node(node) + commands << [inspect_node("ConstantWriteNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── name: #{node.name.inspect}\n", indent] + commands << ["├── name_loc: #{inspect_location(node.name_loc)}\n", indent] + commands << ["├── value:\n", indent] + commands << [node.value, "#{indent}│ "] + commands << ["└── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + end + + # Inspect a DefNode node. + def visit_def_node(node) + commands << [inspect_node("DefNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── name: #{node.name.inspect}\n", indent] + commands << ["├── name_loc: #{inspect_location(node.name_loc)}\n", indent] + if (receiver = node.receiver).nil? + commands << ["├── receiver: ∅\n", indent] + else + commands << ["├── receiver:\n", indent] + commands << [receiver, "#{indent}│ "] + end + if (parameters = node.parameters).nil? + commands << ["├── parameters: ∅\n", indent] + else + commands << ["├── parameters:\n", indent] + commands << [parameters, "#{indent}│ "] + end + if (body = node.body).nil? + commands << ["├── body: ∅\n", indent] + else + commands << ["├── body:\n", indent] + commands << [body, "#{indent}│ "] + end + commands << ["├── locals: #{node.locals.inspect}\n", indent] + commands << ["├── def_keyword_loc: #{inspect_location(node.def_keyword_loc)}\n", indent] + commands << ["├── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + commands << ["├── lparen_loc: #{inspect_location(node.lparen_loc)}\n", indent] + commands << ["├── rparen_loc: #{inspect_location(node.rparen_loc)}\n", indent] + commands << ["├── equal_loc: #{inspect_location(node.equal_loc)}\n", indent] + commands << ["└── end_keyword_loc: #{inspect_location(node.end_keyword_loc)}\n", indent] + end + + # Inspect a DefinedNode node. + def visit_defined_node(node) + commands << [inspect_node("DefinedNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── lparen_loc: #{inspect_location(node.lparen_loc)}\n", indent] + commands << ["├── value:\n", indent] + commands << [node.value, "#{indent}│ "] + commands << ["├── rparen_loc: #{inspect_location(node.rparen_loc)}\n", indent] + commands << ["└── keyword_loc: #{inspect_location(node.keyword_loc)}\n", indent] + end + + # Inspect a ElseNode node. + def visit_else_node(node) + commands << [inspect_node("ElseNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── else_keyword_loc: #{inspect_location(node.else_keyword_loc)}\n", indent] + if (statements = node.statements).nil? + commands << ["├── statements: ∅\n", indent] + else + commands << ["├── statements:\n", indent] + commands << [statements, "#{indent}│ "] + end + commands << ["└── end_keyword_loc: #{inspect_location(node.end_keyword_loc)}\n", indent] + end + + # Inspect a EmbeddedStatementsNode node. + def visit_embedded_statements_node(node) + commands << [inspect_node("EmbeddedStatementsNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── opening_loc: #{inspect_location(node.opening_loc)}\n", indent] + if (statements = node.statements).nil? + commands << ["├── statements: ∅\n", indent] + else + commands << ["├── statements:\n", indent] + commands << [statements, "#{indent}│ "] + end + commands << ["└── closing_loc: #{inspect_location(node.closing_loc)}\n", indent] + end + + # Inspect a EmbeddedVariableNode node. + def visit_embedded_variable_node(node) + commands << [inspect_node("EmbeddedVariableNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + commands << ["└── variable:\n", indent] + commands << [node.variable, "#{indent} "] + end + + # Inspect a EnsureNode node. + def visit_ensure_node(node) + commands << [inspect_node("EnsureNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── ensure_keyword_loc: #{inspect_location(node.ensure_keyword_loc)}\n", indent] + if (statements = node.statements).nil? + commands << ["├── statements: ∅\n", indent] + else + commands << ["├── statements:\n", indent] + commands << [statements, "#{indent}│ "] + end + commands << ["└── end_keyword_loc: #{inspect_location(node.end_keyword_loc)}\n", indent] + end + + # Inspect a FalseNode node. + def visit_false_node(node) + commands << [inspect_node("FalseNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["└── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + end + + # Inspect a FindPatternNode node. + def visit_find_pattern_node(node) + commands << [inspect_node("FindPatternNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + if (constant = node.constant).nil? + commands << ["├── constant: ∅\n", indent] + else + commands << ["├── constant:\n", indent] + commands << [constant, "#{indent}│ "] + end + commands << ["├── left:\n", indent] + commands << [node.left, "#{indent}│ "] + commands << ["├── requireds: (length: #{(requireds = node.requireds).length})\n", indent] + if requireds.any? + requireds[0...-1].each do |child| + commands << [Replace.new("#{indent}│ ├── "), indent] + commands << [child, "#{indent}│ │ "] + end + commands << [Replace.new("#{indent}│ └── "), indent] + commands << [requireds[-1], "#{indent}│ "] + end + commands << ["├── right:\n", indent] + commands << [node.right, "#{indent}│ "] + commands << ["├── opening_loc: #{inspect_location(node.opening_loc)}\n", indent] + commands << ["└── closing_loc: #{inspect_location(node.closing_loc)}\n", indent] + end + + # Inspect a FlipFlopNode node. + def visit_flip_flop_node(node) + commands << [inspect_node("FlipFlopNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ("exclude_end" if node.exclude_end?)].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + if (left = node.left).nil? + commands << ["├── left: ∅\n", indent] + else + commands << ["├── left:\n", indent] + commands << [left, "#{indent}│ "] + end + if (right = node.right).nil? + commands << ["├── right: ∅\n", indent] + else + commands << ["├── right:\n", indent] + commands << [right, "#{indent}│ "] + end + commands << ["└── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + end + + # Inspect a FloatNode node. + def visit_float_node(node) + commands << [inspect_node("FloatNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["└── value: #{node.value.inspect}\n", indent] + end + + # Inspect a ForNode node. + def visit_for_node(node) + commands << [inspect_node("ForNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── index:\n", indent] + commands << [node.index, "#{indent}│ "] + commands << ["├── collection:\n", indent] + commands << [node.collection, "#{indent}│ "] + if (statements = node.statements).nil? + commands << ["├── statements: ∅\n", indent] + else + commands << ["├── statements:\n", indent] + commands << [statements, "#{indent}│ "] + end + commands << ["├── for_keyword_loc: #{inspect_location(node.for_keyword_loc)}\n", indent] + commands << ["├── in_keyword_loc: #{inspect_location(node.in_keyword_loc)}\n", indent] + commands << ["├── do_keyword_loc: #{inspect_location(node.do_keyword_loc)}\n", indent] + commands << ["└── end_keyword_loc: #{inspect_location(node.end_keyword_loc)}\n", indent] + end + + # Inspect a ForwardingArgumentsNode node. + def visit_forwarding_arguments_node(node) + commands << [inspect_node("ForwardingArgumentsNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["└── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + end + + # Inspect a ForwardingParameterNode node. + def visit_forwarding_parameter_node(node) + commands << [inspect_node("ForwardingParameterNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["└── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + end + + # Inspect a ForwardingSuperNode node. + def visit_forwarding_super_node(node) + commands << [inspect_node("ForwardingSuperNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + if (block = node.block).nil? + commands << ["└── block: ∅\n", indent] + else + commands << ["└── block:\n", indent] + commands << [block, "#{indent} "] + end + end + + # Inspect a GlobalVariableAndWriteNode node. + def visit_global_variable_and_write_node(node) + commands << [inspect_node("GlobalVariableAndWriteNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── name: #{node.name.inspect}\n", indent] + commands << ["├── name_loc: #{inspect_location(node.name_loc)}\n", indent] + commands << ["├── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + commands << ["└── value:\n", indent] + commands << [node.value, "#{indent} "] + end + + # Inspect a GlobalVariableOperatorWriteNode node. + def visit_global_variable_operator_write_node(node) + commands << [inspect_node("GlobalVariableOperatorWriteNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── name: #{node.name.inspect}\n", indent] + commands << ["├── name_loc: #{inspect_location(node.name_loc)}\n", indent] + commands << ["├── binary_operator_loc: #{inspect_location(node.binary_operator_loc)}\n", indent] + commands << ["├── value:\n", indent] + commands << [node.value, "#{indent}│ "] + commands << ["└── binary_operator: #{node.binary_operator.inspect}\n", indent] + end + + # Inspect a GlobalVariableOrWriteNode node. + def visit_global_variable_or_write_node(node) + commands << [inspect_node("GlobalVariableOrWriteNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── name: #{node.name.inspect}\n", indent] + commands << ["├── name_loc: #{inspect_location(node.name_loc)}\n", indent] + commands << ["├── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + commands << ["└── value:\n", indent] + commands << [node.value, "#{indent} "] + end + + # Inspect a GlobalVariableReadNode node. + def visit_global_variable_read_node(node) + commands << [inspect_node("GlobalVariableReadNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["└── name: #{node.name.inspect}\n", indent] + end + + # Inspect a GlobalVariableTargetNode node. + def visit_global_variable_target_node(node) + commands << [inspect_node("GlobalVariableTargetNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["└── name: #{node.name.inspect}\n", indent] + end + + # Inspect a GlobalVariableWriteNode node. + def visit_global_variable_write_node(node) + commands << [inspect_node("GlobalVariableWriteNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── name: #{node.name.inspect}\n", indent] + commands << ["├── name_loc: #{inspect_location(node.name_loc)}\n", indent] + commands << ["├── value:\n", indent] + commands << [node.value, "#{indent}│ "] + commands << ["└── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + end + + # Inspect a HashNode node. + def visit_hash_node(node) + commands << [inspect_node("HashNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── opening_loc: #{inspect_location(node.opening_loc)}\n", indent] + commands << ["├── elements: (length: #{(elements = node.elements).length})\n", indent] + if elements.any? + elements[0...-1].each do |child| + commands << [Replace.new("#{indent}│ ├── "), indent] + commands << [child, "#{indent}│ │ "] + end + commands << [Replace.new("#{indent}│ └── "), indent] + commands << [elements[-1], "#{indent}│ "] + end + commands << ["└── closing_loc: #{inspect_location(node.closing_loc)}\n", indent] + end + + # Inspect a HashPatternNode node. + def visit_hash_pattern_node(node) + commands << [inspect_node("HashPatternNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + if (constant = node.constant).nil? + commands << ["├── constant: ∅\n", indent] + else + commands << ["├── constant:\n", indent] + commands << [constant, "#{indent}│ "] + end + commands << ["├── elements: (length: #{(elements = node.elements).length})\n", indent] + if elements.any? + elements[0...-1].each do |child| + commands << [Replace.new("#{indent}│ ├── "), indent] + commands << [child, "#{indent}│ │ "] + end + commands << [Replace.new("#{indent}│ └── "), indent] + commands << [elements[-1], "#{indent}│ "] + end + if (rest = node.rest).nil? + commands << ["├── rest: ∅\n", indent] + else + commands << ["├── rest:\n", indent] + commands << [rest, "#{indent}│ "] + end + commands << ["├── opening_loc: #{inspect_location(node.opening_loc)}\n", indent] + commands << ["└── closing_loc: #{inspect_location(node.closing_loc)}\n", indent] + end + + # Inspect a IfNode node. + def visit_if_node(node) + commands << [inspect_node("IfNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── if_keyword_loc: #{inspect_location(node.if_keyword_loc)}\n", indent] + commands << ["├── predicate:\n", indent] + commands << [node.predicate, "#{indent}│ "] + commands << ["├── then_keyword_loc: #{inspect_location(node.then_keyword_loc)}\n", indent] + if (statements = node.statements).nil? + commands << ["├── statements: ∅\n", indent] + else + commands << ["├── statements:\n", indent] + commands << [statements, "#{indent}│ "] + end + if (subsequent = node.subsequent).nil? + commands << ["├── subsequent: ∅\n", indent] + else + commands << ["├── subsequent:\n", indent] + commands << [subsequent, "#{indent}│ "] + end + commands << ["└── end_keyword_loc: #{inspect_location(node.end_keyword_loc)}\n", indent] + end + + # Inspect a ImaginaryNode node. + def visit_imaginary_node(node) + commands << [inspect_node("ImaginaryNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["└── numeric:\n", indent] + commands << [node.numeric, "#{indent} "] + end + + # Inspect a ImplicitNode node. + def visit_implicit_node(node) + commands << [inspect_node("ImplicitNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["└── value:\n", indent] + commands << [node.value, "#{indent} "] + end + + # Inspect a ImplicitRestNode node. + def visit_implicit_rest_node(node) + commands << [inspect_node("ImplicitRestNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["└── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + end + + # Inspect a InNode node. + def visit_in_node(node) + commands << [inspect_node("InNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── pattern:\n", indent] + commands << [node.pattern, "#{indent}│ "] + if (statements = node.statements).nil? + commands << ["├── statements: ∅\n", indent] + else + commands << ["├── statements:\n", indent] + commands << [statements, "#{indent}│ "] + end + commands << ["├── in_loc: #{inspect_location(node.in_loc)}\n", indent] + commands << ["└── then_loc: #{inspect_location(node.then_loc)}\n", indent] + end + + # Inspect a IndexAndWriteNode node. + def visit_index_and_write_node(node) + commands << [inspect_node("IndexAndWriteNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ("safe_navigation" if node.safe_navigation?), ("variable_call" if node.variable_call?), ("attribute_write" if node.attribute_write?), ("ignore_visibility" if node.ignore_visibility?)].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + if (receiver = node.receiver).nil? + commands << ["├── receiver: ∅\n", indent] + else + commands << ["├── receiver:\n", indent] + commands << [receiver, "#{indent}│ "] + end + commands << ["├── call_operator_loc: #{inspect_location(node.call_operator_loc)}\n", indent] + commands << ["├── opening_loc: #{inspect_location(node.opening_loc)}\n", indent] + if (arguments = node.arguments).nil? + commands << ["├── arguments: ∅\n", indent] + else + commands << ["├── arguments:\n", indent] + commands << [arguments, "#{indent}│ "] + end + commands << ["├── closing_loc: #{inspect_location(node.closing_loc)}\n", indent] + if (block = node.block).nil? + commands << ["├── block: ∅\n", indent] + else + commands << ["├── block:\n", indent] + commands << [block, "#{indent}│ "] + end + commands << ["├── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + commands << ["└── value:\n", indent] + commands << [node.value, "#{indent} "] + end + + # Inspect a IndexOperatorWriteNode node. + def visit_index_operator_write_node(node) + commands << [inspect_node("IndexOperatorWriteNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ("safe_navigation" if node.safe_navigation?), ("variable_call" if node.variable_call?), ("attribute_write" if node.attribute_write?), ("ignore_visibility" if node.ignore_visibility?)].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + if (receiver = node.receiver).nil? + commands << ["├── receiver: ∅\n", indent] + else + commands << ["├── receiver:\n", indent] + commands << [receiver, "#{indent}│ "] + end + commands << ["├── call_operator_loc: #{inspect_location(node.call_operator_loc)}\n", indent] + commands << ["├── opening_loc: #{inspect_location(node.opening_loc)}\n", indent] + if (arguments = node.arguments).nil? + commands << ["├── arguments: ∅\n", indent] + else + commands << ["├── arguments:\n", indent] + commands << [arguments, "#{indent}│ "] + end + commands << ["├── closing_loc: #{inspect_location(node.closing_loc)}\n", indent] + if (block = node.block).nil? + commands << ["├── block: ∅\n", indent] + else + commands << ["├── block:\n", indent] + commands << [block, "#{indent}│ "] + end + commands << ["├── binary_operator: #{node.binary_operator.inspect}\n", indent] + commands << ["├── binary_operator_loc: #{inspect_location(node.binary_operator_loc)}\n", indent] + commands << ["└── value:\n", indent] + commands << [node.value, "#{indent} "] + end + + # Inspect a IndexOrWriteNode node. + def visit_index_or_write_node(node) + commands << [inspect_node("IndexOrWriteNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ("safe_navigation" if node.safe_navigation?), ("variable_call" if node.variable_call?), ("attribute_write" if node.attribute_write?), ("ignore_visibility" if node.ignore_visibility?)].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + if (receiver = node.receiver).nil? + commands << ["├── receiver: ∅\n", indent] + else + commands << ["├── receiver:\n", indent] + commands << [receiver, "#{indent}│ "] + end + commands << ["├── call_operator_loc: #{inspect_location(node.call_operator_loc)}\n", indent] + commands << ["├── opening_loc: #{inspect_location(node.opening_loc)}\n", indent] + if (arguments = node.arguments).nil? + commands << ["├── arguments: ∅\n", indent] + else + commands << ["├── arguments:\n", indent] + commands << [arguments, "#{indent}│ "] + end + commands << ["├── closing_loc: #{inspect_location(node.closing_loc)}\n", indent] + if (block = node.block).nil? + commands << ["├── block: ∅\n", indent] + else + commands << ["├── block:\n", indent] + commands << [block, "#{indent}│ "] + end + commands << ["├── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + commands << ["└── value:\n", indent] + commands << [node.value, "#{indent} "] + end + + # Inspect a IndexTargetNode node. + def visit_index_target_node(node) + commands << [inspect_node("IndexTargetNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ("safe_navigation" if node.safe_navigation?), ("variable_call" if node.variable_call?), ("attribute_write" if node.attribute_write?), ("ignore_visibility" if node.ignore_visibility?)].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── receiver:\n", indent] + commands << [node.receiver, "#{indent}│ "] + commands << ["├── opening_loc: #{inspect_location(node.opening_loc)}\n", indent] + if (arguments = node.arguments).nil? + commands << ["├── arguments: ∅\n", indent] + else + commands << ["├── arguments:\n", indent] + commands << [arguments, "#{indent}│ "] + end + commands << ["├── closing_loc: #{inspect_location(node.closing_loc)}\n", indent] + if (block = node.block).nil? + commands << ["└── block: ∅\n", indent] + else + commands << ["└── block:\n", indent] + commands << [block, "#{indent} "] + end + end + + # Inspect a InstanceVariableAndWriteNode node. + def visit_instance_variable_and_write_node(node) + commands << [inspect_node("InstanceVariableAndWriteNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── name: #{node.name.inspect}\n", indent] + commands << ["├── name_loc: #{inspect_location(node.name_loc)}\n", indent] + commands << ["├── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + commands << ["└── value:\n", indent] + commands << [node.value, "#{indent} "] + end + + # Inspect a InstanceVariableOperatorWriteNode node. + def visit_instance_variable_operator_write_node(node) + commands << [inspect_node("InstanceVariableOperatorWriteNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── name: #{node.name.inspect}\n", indent] + commands << ["├── name_loc: #{inspect_location(node.name_loc)}\n", indent] + commands << ["├── binary_operator_loc: #{inspect_location(node.binary_operator_loc)}\n", indent] + commands << ["├── value:\n", indent] + commands << [node.value, "#{indent}│ "] + commands << ["└── binary_operator: #{node.binary_operator.inspect}\n", indent] + end + + # Inspect a InstanceVariableOrWriteNode node. + def visit_instance_variable_or_write_node(node) + commands << [inspect_node("InstanceVariableOrWriteNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── name: #{node.name.inspect}\n", indent] + commands << ["├── name_loc: #{inspect_location(node.name_loc)}\n", indent] + commands << ["├── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + commands << ["└── value:\n", indent] + commands << [node.value, "#{indent} "] + end + + # Inspect a InstanceVariableReadNode node. + def visit_instance_variable_read_node(node) + commands << [inspect_node("InstanceVariableReadNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["└── name: #{node.name.inspect}\n", indent] + end + + # Inspect a InstanceVariableTargetNode node. + def visit_instance_variable_target_node(node) + commands << [inspect_node("InstanceVariableTargetNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["└── name: #{node.name.inspect}\n", indent] + end + + # Inspect a InstanceVariableWriteNode node. + def visit_instance_variable_write_node(node) + commands << [inspect_node("InstanceVariableWriteNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── name: #{node.name.inspect}\n", indent] + commands << ["├── name_loc: #{inspect_location(node.name_loc)}\n", indent] + commands << ["├── value:\n", indent] + commands << [node.value, "#{indent}│ "] + commands << ["└── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + end + + # Inspect a IntegerNode node. + def visit_integer_node(node) + commands << [inspect_node("IntegerNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ("binary" if node.binary?), ("decimal" if node.decimal?), ("octal" if node.octal?), ("hexadecimal" if node.hexadecimal?)].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["└── value: #{node.value.inspect}\n", indent] + end + + # Inspect a InterpolatedMatchLastLineNode node. + def visit_interpolated_match_last_line_node(node) + commands << [inspect_node("InterpolatedMatchLastLineNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ("ignore_case" if node.ignore_case?), ("extended" if node.extended?), ("multi_line" if node.multi_line?), ("once" if node.once?), ("euc_jp" if node.euc_jp?), ("ascii_8bit" if node.ascii_8bit?), ("windows_31j" if node.windows_31j?), ("utf_8" if node.utf_8?), ("forced_utf8_encoding" if node.forced_utf8_encoding?), ("forced_binary_encoding" if node.forced_binary_encoding?), ("forced_us_ascii_encoding" if node.forced_us_ascii_encoding?)].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── opening_loc: #{inspect_location(node.opening_loc)}\n", indent] + commands << ["├── parts: (length: #{(parts = node.parts).length})\n", indent] + if parts.any? + parts[0...-1].each do |child| + commands << [Replace.new("#{indent}│ ├── "), indent] + commands << [child, "#{indent}│ │ "] + end + commands << [Replace.new("#{indent}│ └── "), indent] + commands << [parts[-1], "#{indent}│ "] + end + commands << ["└── closing_loc: #{inspect_location(node.closing_loc)}\n", indent] + end + + # Inspect a InterpolatedRegularExpressionNode node. + def visit_interpolated_regular_expression_node(node) + commands << [inspect_node("InterpolatedRegularExpressionNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ("ignore_case" if node.ignore_case?), ("extended" if node.extended?), ("multi_line" if node.multi_line?), ("once" if node.once?), ("euc_jp" if node.euc_jp?), ("ascii_8bit" if node.ascii_8bit?), ("windows_31j" if node.windows_31j?), ("utf_8" if node.utf_8?), ("forced_utf8_encoding" if node.forced_utf8_encoding?), ("forced_binary_encoding" if node.forced_binary_encoding?), ("forced_us_ascii_encoding" if node.forced_us_ascii_encoding?)].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── opening_loc: #{inspect_location(node.opening_loc)}\n", indent] + commands << ["├── parts: (length: #{(parts = node.parts).length})\n", indent] + if parts.any? + parts[0...-1].each do |child| + commands << [Replace.new("#{indent}│ ├── "), indent] + commands << [child, "#{indent}│ │ "] + end + commands << [Replace.new("#{indent}│ └── "), indent] + commands << [parts[-1], "#{indent}│ "] + end + commands << ["└── closing_loc: #{inspect_location(node.closing_loc)}\n", indent] + end + + # Inspect a InterpolatedStringNode node. + def visit_interpolated_string_node(node) + commands << [inspect_node("InterpolatedStringNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ("frozen" if node.frozen?), ("mutable" if node.mutable?)].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── opening_loc: #{inspect_location(node.opening_loc)}\n", indent] + commands << ["├── parts: (length: #{(parts = node.parts).length})\n", indent] + if parts.any? + parts[0...-1].each do |child| + commands << [Replace.new("#{indent}│ ├── "), indent] + commands << [child, "#{indent}│ │ "] + end + commands << [Replace.new("#{indent}│ └── "), indent] + commands << [parts[-1], "#{indent}│ "] + end + commands << ["└── closing_loc: #{inspect_location(node.closing_loc)}\n", indent] + end + + # Inspect a InterpolatedSymbolNode node. + def visit_interpolated_symbol_node(node) + commands << [inspect_node("InterpolatedSymbolNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── opening_loc: #{inspect_location(node.opening_loc)}\n", indent] + commands << ["├── parts: (length: #{(parts = node.parts).length})\n", indent] + if parts.any? + parts[0...-1].each do |child| + commands << [Replace.new("#{indent}│ ├── "), indent] + commands << [child, "#{indent}│ │ "] + end + commands << [Replace.new("#{indent}│ └── "), indent] + commands << [parts[-1], "#{indent}│ "] + end + commands << ["└── closing_loc: #{inspect_location(node.closing_loc)}\n", indent] + end + + # Inspect a InterpolatedXStringNode node. + def visit_interpolated_x_string_node(node) + commands << [inspect_node("InterpolatedXStringNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── opening_loc: #{inspect_location(node.opening_loc)}\n", indent] + commands << ["├── parts: (length: #{(parts = node.parts).length})\n", indent] + if parts.any? + parts[0...-1].each do |child| + commands << [Replace.new("#{indent}│ ├── "), indent] + commands << [child, "#{indent}│ │ "] + end + commands << [Replace.new("#{indent}│ └── "), indent] + commands << [parts[-1], "#{indent}│ "] + end + commands << ["└── closing_loc: #{inspect_location(node.closing_loc)}\n", indent] + end + + # Inspect a ItLocalVariableReadNode node. + def visit_it_local_variable_read_node(node) + commands << [inspect_node("ItLocalVariableReadNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["└── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + end + + # Inspect a ItParametersNode node. + def visit_it_parameters_node(node) + commands << [inspect_node("ItParametersNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["└── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + end + + # Inspect a KeywordHashNode node. + def visit_keyword_hash_node(node) + commands << [inspect_node("KeywordHashNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ("symbol_keys" if node.symbol_keys?)].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["└── elements: (length: #{(elements = node.elements).length})\n", indent] + if elements.any? + elements[0...-1].each do |child| + commands << [Replace.new("#{indent} ├── "), indent] + commands << [child, "#{indent} │ "] + end + commands << [Replace.new("#{indent} └── "), indent] + commands << [elements[-1], "#{indent} "] + end + end + + # Inspect a KeywordRestParameterNode node. + def visit_keyword_rest_parameter_node(node) + commands << [inspect_node("KeywordRestParameterNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ("repeated_parameter" if node.repeated_parameter?)].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + if (name = node.name).nil? + commands << ["├── name: ∅\n", indent] + else + commands << ["├── name: #{name.inspect}\n", indent] + end + commands << ["├── name_loc: #{inspect_location(node.name_loc)}\n", indent] + commands << ["└── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + end + + # Inspect a LambdaNode node. + def visit_lambda_node(node) + commands << [inspect_node("LambdaNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── locals: #{node.locals.inspect}\n", indent] + commands << ["├── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + commands << ["├── opening_loc: #{inspect_location(node.opening_loc)}\n", indent] + commands << ["├── closing_loc: #{inspect_location(node.closing_loc)}\n", indent] + if (parameters = node.parameters).nil? + commands << ["├── parameters: ∅\n", indent] + else + commands << ["├── parameters:\n", indent] + commands << [parameters, "#{indent}│ "] + end + if (body = node.body).nil? + commands << ["└── body: ∅\n", indent] + else + commands << ["└── body:\n", indent] + commands << [body, "#{indent} "] + end + end + + # Inspect a LocalVariableAndWriteNode node. + def visit_local_variable_and_write_node(node) + commands << [inspect_node("LocalVariableAndWriteNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── name_loc: #{inspect_location(node.name_loc)}\n", indent] + commands << ["├── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + commands << ["├── value:\n", indent] + commands << [node.value, "#{indent}│ "] + commands << ["├── name: #{node.name.inspect}\n", indent] + commands << ["└── depth: #{node.depth.inspect}\n", indent] + end + + # Inspect a LocalVariableOperatorWriteNode node. + def visit_local_variable_operator_write_node(node) + commands << [inspect_node("LocalVariableOperatorWriteNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── name_loc: #{inspect_location(node.name_loc)}\n", indent] + commands << ["├── binary_operator_loc: #{inspect_location(node.binary_operator_loc)}\n", indent] + commands << ["├── value:\n", indent] + commands << [node.value, "#{indent}│ "] + commands << ["├── name: #{node.name.inspect}\n", indent] + commands << ["├── binary_operator: #{node.binary_operator.inspect}\n", indent] + commands << ["└── depth: #{node.depth.inspect}\n", indent] + end + + # Inspect a LocalVariableOrWriteNode node. + def visit_local_variable_or_write_node(node) + commands << [inspect_node("LocalVariableOrWriteNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── name_loc: #{inspect_location(node.name_loc)}\n", indent] + commands << ["├── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + commands << ["├── value:\n", indent] + commands << [node.value, "#{indent}│ "] + commands << ["├── name: #{node.name.inspect}\n", indent] + commands << ["└── depth: #{node.depth.inspect}\n", indent] + end + + # Inspect a LocalVariableReadNode node. + def visit_local_variable_read_node(node) + commands << [inspect_node("LocalVariableReadNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── name: #{node.name.inspect}\n", indent] + commands << ["└── depth: #{node.depth.inspect}\n", indent] + end + + # Inspect a LocalVariableTargetNode node. + def visit_local_variable_target_node(node) + commands << [inspect_node("LocalVariableTargetNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── name: #{node.name.inspect}\n", indent] + commands << ["└── depth: #{node.depth.inspect}\n", indent] + end + + # Inspect a LocalVariableWriteNode node. + def visit_local_variable_write_node(node) + commands << [inspect_node("LocalVariableWriteNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── name: #{node.name.inspect}\n", indent] + commands << ["├── depth: #{node.depth.inspect}\n", indent] + commands << ["├── name_loc: #{inspect_location(node.name_loc)}\n", indent] + commands << ["├── value:\n", indent] + commands << [node.value, "#{indent}│ "] + commands << ["└── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + end + + # Inspect a MatchLastLineNode node. + def visit_match_last_line_node(node) + commands << [inspect_node("MatchLastLineNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ("ignore_case" if node.ignore_case?), ("extended" if node.extended?), ("multi_line" if node.multi_line?), ("once" if node.once?), ("euc_jp" if node.euc_jp?), ("ascii_8bit" if node.ascii_8bit?), ("windows_31j" if node.windows_31j?), ("utf_8" if node.utf_8?), ("forced_utf8_encoding" if node.forced_utf8_encoding?), ("forced_binary_encoding" if node.forced_binary_encoding?), ("forced_us_ascii_encoding" if node.forced_us_ascii_encoding?)].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── opening_loc: #{inspect_location(node.opening_loc)}\n", indent] + commands << ["├── content_loc: #{inspect_location(node.content_loc)}\n", indent] + commands << ["├── closing_loc: #{inspect_location(node.closing_loc)}\n", indent] + commands << ["└── unescaped: #{node.unescaped.inspect}\n", indent] + end + + # Inspect a MatchPredicateNode node. + def visit_match_predicate_node(node) + commands << [inspect_node("MatchPredicateNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── value:\n", indent] + commands << [node.value, "#{indent}│ "] + commands << ["├── pattern:\n", indent] + commands << [node.pattern, "#{indent}│ "] + commands << ["└── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + end + + # Inspect a MatchRequiredNode node. + def visit_match_required_node(node) + commands << [inspect_node("MatchRequiredNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── value:\n", indent] + commands << [node.value, "#{indent}│ "] + commands << ["├── pattern:\n", indent] + commands << [node.pattern, "#{indent}│ "] + commands << ["└── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + end + + # Inspect a MatchWriteNode node. + def visit_match_write_node(node) + commands << [inspect_node("MatchWriteNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── call:\n", indent] + commands << [node.call, "#{indent}│ "] + commands << ["└── targets: (length: #{(targets = node.targets).length})\n", indent] + if targets.any? + targets[0...-1].each do |child| + commands << [Replace.new("#{indent} ├── "), indent] + commands << [child, "#{indent} │ "] + end + commands << [Replace.new("#{indent} └── "), indent] + commands << [targets[-1], "#{indent} "] + end + end + + # Inspect a MissingNode node. + def visit_missing_node(node) + commands << [inspect_node("MissingNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["└── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + end + + # Inspect a ModuleNode node. + def visit_module_node(node) + commands << [inspect_node("ModuleNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── locals: #{node.locals.inspect}\n", indent] + commands << ["├── module_keyword_loc: #{inspect_location(node.module_keyword_loc)}\n", indent] + commands << ["├── constant_path:\n", indent] + commands << [node.constant_path, "#{indent}│ "] + if (body = node.body).nil? + commands << ["├── body: ∅\n", indent] + else + commands << ["├── body:\n", indent] + commands << [body, "#{indent}│ "] + end + commands << ["├── end_keyword_loc: #{inspect_location(node.end_keyword_loc)}\n", indent] + commands << ["└── name: #{node.name.inspect}\n", indent] + end + + # Inspect a MultiTargetNode node. + def visit_multi_target_node(node) + commands << [inspect_node("MultiTargetNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── lefts: (length: #{(lefts = node.lefts).length})\n", indent] + if lefts.any? + lefts[0...-1].each do |child| + commands << [Replace.new("#{indent}│ ├── "), indent] + commands << [child, "#{indent}│ │ "] + end + commands << [Replace.new("#{indent}│ └── "), indent] + commands << [lefts[-1], "#{indent}│ "] + end + if (rest = node.rest).nil? + commands << ["├── rest: ∅\n", indent] + else + commands << ["├── rest:\n", indent] + commands << [rest, "#{indent}│ "] + end + commands << ["├── rights: (length: #{(rights = node.rights).length})\n", indent] + if rights.any? + rights[0...-1].each do |child| + commands << [Replace.new("#{indent}│ ├── "), indent] + commands << [child, "#{indent}│ │ "] + end + commands << [Replace.new("#{indent}│ └── "), indent] + commands << [rights[-1], "#{indent}│ "] + end + commands << ["├── lparen_loc: #{inspect_location(node.lparen_loc)}\n", indent] + commands << ["└── rparen_loc: #{inspect_location(node.rparen_loc)}\n", indent] + end + + # Inspect a MultiWriteNode node. + def visit_multi_write_node(node) + commands << [inspect_node("MultiWriteNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── lefts: (length: #{(lefts = node.lefts).length})\n", indent] + if lefts.any? + lefts[0...-1].each do |child| + commands << [Replace.new("#{indent}│ ├── "), indent] + commands << [child, "#{indent}│ │ "] + end + commands << [Replace.new("#{indent}│ └── "), indent] + commands << [lefts[-1], "#{indent}│ "] + end + if (rest = node.rest).nil? + commands << ["├── rest: ∅\n", indent] + else + commands << ["├── rest:\n", indent] + commands << [rest, "#{indent}│ "] + end + commands << ["├── rights: (length: #{(rights = node.rights).length})\n", indent] + if rights.any? + rights[0...-1].each do |child| + commands << [Replace.new("#{indent}│ ├── "), indent] + commands << [child, "#{indent}│ │ "] + end + commands << [Replace.new("#{indent}│ └── "), indent] + commands << [rights[-1], "#{indent}│ "] + end + commands << ["├── lparen_loc: #{inspect_location(node.lparen_loc)}\n", indent] + commands << ["├── rparen_loc: #{inspect_location(node.rparen_loc)}\n", indent] + commands << ["├── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + commands << ["└── value:\n", indent] + commands << [node.value, "#{indent} "] + end + + # Inspect a NextNode node. + def visit_next_node(node) + commands << [inspect_node("NextNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + if (arguments = node.arguments).nil? + commands << ["├── arguments: ∅\n", indent] + else + commands << ["├── arguments:\n", indent] + commands << [arguments, "#{indent}│ "] + end + commands << ["└── keyword_loc: #{inspect_location(node.keyword_loc)}\n", indent] + end + + # Inspect a NilNode node. + def visit_nil_node(node) + commands << [inspect_node("NilNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["└── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + end + + # Inspect a NoKeywordsParameterNode node. + def visit_no_keywords_parameter_node(node) + commands << [inspect_node("NoKeywordsParameterNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + commands << ["└── keyword_loc: #{inspect_location(node.keyword_loc)}\n", indent] + end + + # Inspect a NumberedParametersNode node. + def visit_numbered_parameters_node(node) + commands << [inspect_node("NumberedParametersNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["└── maximum: #{node.maximum.inspect}\n", indent] + end + + # Inspect a NumberedReferenceReadNode node. + def visit_numbered_reference_read_node(node) + commands << [inspect_node("NumberedReferenceReadNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["└── number: #{node.number.inspect}\n", indent] + end + + # Inspect a OptionalKeywordParameterNode node. + def visit_optional_keyword_parameter_node(node) + commands << [inspect_node("OptionalKeywordParameterNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ("repeated_parameter" if node.repeated_parameter?)].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── name: #{node.name.inspect}\n", indent] + commands << ["├── name_loc: #{inspect_location(node.name_loc)}\n", indent] + commands << ["└── value:\n", indent] + commands << [node.value, "#{indent} "] + end + + # Inspect a OptionalParameterNode node. + def visit_optional_parameter_node(node) + commands << [inspect_node("OptionalParameterNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ("repeated_parameter" if node.repeated_parameter?)].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── name: #{node.name.inspect}\n", indent] + commands << ["├── name_loc: #{inspect_location(node.name_loc)}\n", indent] + commands << ["├── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + commands << ["└── value:\n", indent] + commands << [node.value, "#{indent} "] + end + + # Inspect a OrNode node. + def visit_or_node(node) + commands << [inspect_node("OrNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── left:\n", indent] + commands << [node.left, "#{indent}│ "] + commands << ["├── right:\n", indent] + commands << [node.right, "#{indent}│ "] + commands << ["└── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + end + + # Inspect a ParametersNode node. + def visit_parameters_node(node) + commands << [inspect_node("ParametersNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── requireds: (length: #{(requireds = node.requireds).length})\n", indent] + if requireds.any? + requireds[0...-1].each do |child| + commands << [Replace.new("#{indent}│ ├── "), indent] + commands << [child, "#{indent}│ │ "] + end + commands << [Replace.new("#{indent}│ └── "), indent] + commands << [requireds[-1], "#{indent}│ "] + end + commands << ["├── optionals: (length: #{(optionals = node.optionals).length})\n", indent] + if optionals.any? + optionals[0...-1].each do |child| + commands << [Replace.new("#{indent}│ ├── "), indent] + commands << [child, "#{indent}│ │ "] + end + commands << [Replace.new("#{indent}│ └── "), indent] + commands << [optionals[-1], "#{indent}│ "] + end + if (rest = node.rest).nil? + commands << ["├── rest: ∅\n", indent] + else + commands << ["├── rest:\n", indent] + commands << [rest, "#{indent}│ "] + end + commands << ["├── posts: (length: #{(posts = node.posts).length})\n", indent] + if posts.any? + posts[0...-1].each do |child| + commands << [Replace.new("#{indent}│ ├── "), indent] + commands << [child, "#{indent}│ │ "] + end + commands << [Replace.new("#{indent}│ └── "), indent] + commands << [posts[-1], "#{indent}│ "] + end + commands << ["├── keywords: (length: #{(keywords = node.keywords).length})\n", indent] + if keywords.any? + keywords[0...-1].each do |child| + commands << [Replace.new("#{indent}│ ├── "), indent] + commands << [child, "#{indent}│ │ "] + end + commands << [Replace.new("#{indent}│ └── "), indent] + commands << [keywords[-1], "#{indent}│ "] + end + if (keyword_rest = node.keyword_rest).nil? + commands << ["├── keyword_rest: ∅\n", indent] + else + commands << ["├── keyword_rest:\n", indent] + commands << [keyword_rest, "#{indent}│ "] + end + if (block = node.block).nil? + commands << ["└── block: ∅\n", indent] + else + commands << ["└── block:\n", indent] + commands << [block, "#{indent} "] + end + end + + # Inspect a ParenthesesNode node. + def visit_parentheses_node(node) + commands << [inspect_node("ParenthesesNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ("multiple_statements" if node.multiple_statements?)].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + if (body = node.body).nil? + commands << ["├── body: ∅\n", indent] + else + commands << ["├── body:\n", indent] + commands << [body, "#{indent}│ "] + end + commands << ["├── opening_loc: #{inspect_location(node.opening_loc)}\n", indent] + commands << ["└── closing_loc: #{inspect_location(node.closing_loc)}\n", indent] + end + + # Inspect a PinnedExpressionNode node. + def visit_pinned_expression_node(node) + commands << [inspect_node("PinnedExpressionNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── expression:\n", indent] + commands << [node.expression, "#{indent}│ "] + commands << ["├── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + commands << ["├── lparen_loc: #{inspect_location(node.lparen_loc)}\n", indent] + commands << ["└── rparen_loc: #{inspect_location(node.rparen_loc)}\n", indent] + end + + # Inspect a PinnedVariableNode node. + def visit_pinned_variable_node(node) + commands << [inspect_node("PinnedVariableNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── variable:\n", indent] + commands << [node.variable, "#{indent}│ "] + commands << ["└── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + end + + # Inspect a PostExecutionNode node. + def visit_post_execution_node(node) + commands << [inspect_node("PostExecutionNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + if (statements = node.statements).nil? + commands << ["├── statements: ∅\n", indent] + else + commands << ["├── statements:\n", indent] + commands << [statements, "#{indent}│ "] + end + commands << ["├── keyword_loc: #{inspect_location(node.keyword_loc)}\n", indent] + commands << ["├── opening_loc: #{inspect_location(node.opening_loc)}\n", indent] + commands << ["└── closing_loc: #{inspect_location(node.closing_loc)}\n", indent] + end + + # Inspect a PreExecutionNode node. + def visit_pre_execution_node(node) + commands << [inspect_node("PreExecutionNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + if (statements = node.statements).nil? + commands << ["├── statements: ∅\n", indent] + else + commands << ["├── statements:\n", indent] + commands << [statements, "#{indent}│ "] + end + commands << ["├── keyword_loc: #{inspect_location(node.keyword_loc)}\n", indent] + commands << ["├── opening_loc: #{inspect_location(node.opening_loc)}\n", indent] + commands << ["└── closing_loc: #{inspect_location(node.closing_loc)}\n", indent] + end + + # Inspect a ProgramNode node. + def visit_program_node(node) + commands << [inspect_node("ProgramNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── locals: #{node.locals.inspect}\n", indent] + commands << ["└── statements:\n", indent] + commands << [node.statements, "#{indent} "] + end + + # Inspect a RangeNode node. + def visit_range_node(node) + commands << [inspect_node("RangeNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ("exclude_end" if node.exclude_end?)].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + if (left = node.left).nil? + commands << ["├── left: ∅\n", indent] + else + commands << ["├── left:\n", indent] + commands << [left, "#{indent}│ "] + end + if (right = node.right).nil? + commands << ["├── right: ∅\n", indent] + else + commands << ["├── right:\n", indent] + commands << [right, "#{indent}│ "] + end + commands << ["└── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + end + + # Inspect a RationalNode node. + def visit_rational_node(node) + commands << [inspect_node("RationalNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ("binary" if node.binary?), ("decimal" if node.decimal?), ("octal" if node.octal?), ("hexadecimal" if node.hexadecimal?)].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── numerator: #{node.numerator.inspect}\n", indent] + commands << ["└── denominator: #{node.denominator.inspect}\n", indent] + end + + # Inspect a RedoNode node. + def visit_redo_node(node) + commands << [inspect_node("RedoNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["└── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + end + + # Inspect a RegularExpressionNode node. + def visit_regular_expression_node(node) + commands << [inspect_node("RegularExpressionNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ("ignore_case" if node.ignore_case?), ("extended" if node.extended?), ("multi_line" if node.multi_line?), ("once" if node.once?), ("euc_jp" if node.euc_jp?), ("ascii_8bit" if node.ascii_8bit?), ("windows_31j" if node.windows_31j?), ("utf_8" if node.utf_8?), ("forced_utf8_encoding" if node.forced_utf8_encoding?), ("forced_binary_encoding" if node.forced_binary_encoding?), ("forced_us_ascii_encoding" if node.forced_us_ascii_encoding?)].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── opening_loc: #{inspect_location(node.opening_loc)}\n", indent] + commands << ["├── content_loc: #{inspect_location(node.content_loc)}\n", indent] + commands << ["├── closing_loc: #{inspect_location(node.closing_loc)}\n", indent] + commands << ["└── unescaped: #{node.unescaped.inspect}\n", indent] + end + + # Inspect a RequiredKeywordParameterNode node. + def visit_required_keyword_parameter_node(node) + commands << [inspect_node("RequiredKeywordParameterNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ("repeated_parameter" if node.repeated_parameter?)].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── name: #{node.name.inspect}\n", indent] + commands << ["└── name_loc: #{inspect_location(node.name_loc)}\n", indent] + end + + # Inspect a RequiredParameterNode node. + def visit_required_parameter_node(node) + commands << [inspect_node("RequiredParameterNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ("repeated_parameter" if node.repeated_parameter?)].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["└── name: #{node.name.inspect}\n", indent] + end + + # Inspect a RescueModifierNode node. + def visit_rescue_modifier_node(node) + commands << [inspect_node("RescueModifierNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── expression:\n", indent] + commands << [node.expression, "#{indent}│ "] + commands << ["├── keyword_loc: #{inspect_location(node.keyword_loc)}\n", indent] + commands << ["└── rescue_expression:\n", indent] + commands << [node.rescue_expression, "#{indent} "] + end + + # Inspect a RescueNode node. + def visit_rescue_node(node) + commands << [inspect_node("RescueNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── keyword_loc: #{inspect_location(node.keyword_loc)}\n", indent] + commands << ["├── exceptions: (length: #{(exceptions = node.exceptions).length})\n", indent] + if exceptions.any? + exceptions[0...-1].each do |child| + commands << [Replace.new("#{indent}│ ├── "), indent] + commands << [child, "#{indent}│ │ "] + end + commands << [Replace.new("#{indent}│ └── "), indent] + commands << [exceptions[-1], "#{indent}│ "] + end + commands << ["├── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + if (reference = node.reference).nil? + commands << ["├── reference: ∅\n", indent] + else + commands << ["├── reference:\n", indent] + commands << [reference, "#{indent}│ "] + end + commands << ["├── then_keyword_loc: #{inspect_location(node.then_keyword_loc)}\n", indent] + if (statements = node.statements).nil? + commands << ["├── statements: ∅\n", indent] + else + commands << ["├── statements:\n", indent] + commands << [statements, "#{indent}│ "] + end + if (subsequent = node.subsequent).nil? + commands << ["└── subsequent: ∅\n", indent] + else + commands << ["└── subsequent:\n", indent] + commands << [subsequent, "#{indent} "] + end + end + + # Inspect a RestParameterNode node. + def visit_rest_parameter_node(node) + commands << [inspect_node("RestParameterNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ("repeated_parameter" if node.repeated_parameter?)].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + if (name = node.name).nil? + commands << ["├── name: ∅\n", indent] + else + commands << ["├── name: #{name.inspect}\n", indent] + end + commands << ["├── name_loc: #{inspect_location(node.name_loc)}\n", indent] + commands << ["└── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + end + + # Inspect a RetryNode node. + def visit_retry_node(node) + commands << [inspect_node("RetryNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["└── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + end + + # Inspect a ReturnNode node. + def visit_return_node(node) + commands << [inspect_node("ReturnNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── keyword_loc: #{inspect_location(node.keyword_loc)}\n", indent] + if (arguments = node.arguments).nil? + commands << ["└── arguments: ∅\n", indent] + else + commands << ["└── arguments:\n", indent] + commands << [arguments, "#{indent} "] + end + end + + # Inspect a SelfNode node. + def visit_self_node(node) + commands << [inspect_node("SelfNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["└── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + end + + # Inspect a ShareableConstantNode node. + def visit_shareable_constant_node(node) + commands << [inspect_node("ShareableConstantNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ("literal" if node.literal?), ("experimental_everything" if node.experimental_everything?), ("experimental_copy" if node.experimental_copy?)].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["└── write:\n", indent] + commands << [node.write, "#{indent} "] + end + + # Inspect a SingletonClassNode node. + def visit_singleton_class_node(node) + commands << [inspect_node("SingletonClassNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── locals: #{node.locals.inspect}\n", indent] + commands << ["├── class_keyword_loc: #{inspect_location(node.class_keyword_loc)}\n", indent] + commands << ["├── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + commands << ["├── expression:\n", indent] + commands << [node.expression, "#{indent}│ "] + if (body = node.body).nil? + commands << ["├── body: ∅\n", indent] + else + commands << ["├── body:\n", indent] + commands << [body, "#{indent}│ "] + end + commands << ["└── end_keyword_loc: #{inspect_location(node.end_keyword_loc)}\n", indent] + end + + # Inspect a SourceEncodingNode node. + def visit_source_encoding_node(node) + commands << [inspect_node("SourceEncodingNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["└── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + end + + # Inspect a SourceFileNode node. + def visit_source_file_node(node) + commands << [inspect_node("SourceFileNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ("forced_utf8_encoding" if node.forced_utf8_encoding?), ("forced_binary_encoding" if node.forced_binary_encoding?), ("frozen" if node.frozen?), ("mutable" if node.mutable?)].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["└── filepath: #{node.filepath.inspect}\n", indent] + end + + # Inspect a SourceLineNode node. + def visit_source_line_node(node) + commands << [inspect_node("SourceLineNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["└── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + end + + # Inspect a SplatNode node. + def visit_splat_node(node) + commands << [inspect_node("SplatNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── operator_loc: #{inspect_location(node.operator_loc)}\n", indent] + if (expression = node.expression).nil? + commands << ["└── expression: ∅\n", indent] + else + commands << ["└── expression:\n", indent] + commands << [expression, "#{indent} "] + end + end + + # Inspect a StatementsNode node. + def visit_statements_node(node) + commands << [inspect_node("StatementsNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["└── body: (length: #{(body = node.body).length})\n", indent] + if body.any? + body[0...-1].each do |child| + commands << [Replace.new("#{indent} ├── "), indent] + commands << [child, "#{indent} │ "] + end + commands << [Replace.new("#{indent} └── "), indent] + commands << [body[-1], "#{indent} "] + end + end + + # Inspect a StringNode node. + def visit_string_node(node) + commands << [inspect_node("StringNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ("forced_utf8_encoding" if node.forced_utf8_encoding?), ("forced_binary_encoding" if node.forced_binary_encoding?), ("frozen" if node.frozen?), ("mutable" if node.mutable?)].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── opening_loc: #{inspect_location(node.opening_loc)}\n", indent] + commands << ["├── content_loc: #{inspect_location(node.content_loc)}\n", indent] + commands << ["├── closing_loc: #{inspect_location(node.closing_loc)}\n", indent] + commands << ["└── unescaped: #{node.unescaped.inspect}\n", indent] + end + + # Inspect a SuperNode node. + def visit_super_node(node) + commands << [inspect_node("SuperNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── keyword_loc: #{inspect_location(node.keyword_loc)}\n", indent] + commands << ["├── lparen_loc: #{inspect_location(node.lparen_loc)}\n", indent] + if (arguments = node.arguments).nil? + commands << ["├── arguments: ∅\n", indent] + else + commands << ["├── arguments:\n", indent] + commands << [arguments, "#{indent}│ "] + end + commands << ["├── rparen_loc: #{inspect_location(node.rparen_loc)}\n", indent] + if (block = node.block).nil? + commands << ["└── block: ∅\n", indent] + else + commands << ["└── block:\n", indent] + commands << [block, "#{indent} "] + end + end + + # Inspect a SymbolNode node. + def visit_symbol_node(node) + commands << [inspect_node("SymbolNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ("forced_utf8_encoding" if node.forced_utf8_encoding?), ("forced_binary_encoding" if node.forced_binary_encoding?), ("forced_us_ascii_encoding" if node.forced_us_ascii_encoding?)].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── opening_loc: #{inspect_location(node.opening_loc)}\n", indent] + commands << ["├── value_loc: #{inspect_location(node.value_loc)}\n", indent] + commands << ["├── closing_loc: #{inspect_location(node.closing_loc)}\n", indent] + commands << ["└── unescaped: #{node.unescaped.inspect}\n", indent] + end + + # Inspect a TrueNode node. + def visit_true_node(node) + commands << [inspect_node("TrueNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["└── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + end + + # Inspect a UndefNode node. + def visit_undef_node(node) + commands << [inspect_node("UndefNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── names: (length: #{(names = node.names).length})\n", indent] + if names.any? + names[0...-1].each do |child| + commands << [Replace.new("#{indent}│ ├── "), indent] + commands << [child, "#{indent}│ │ "] + end + commands << [Replace.new("#{indent}│ └── "), indent] + commands << [names[-1], "#{indent}│ "] + end + commands << ["└── keyword_loc: #{inspect_location(node.keyword_loc)}\n", indent] + end + + # Inspect a UnlessNode node. + def visit_unless_node(node) + commands << [inspect_node("UnlessNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── keyword_loc: #{inspect_location(node.keyword_loc)}\n", indent] + commands << ["├── predicate:\n", indent] + commands << [node.predicate, "#{indent}│ "] + commands << ["├── then_keyword_loc: #{inspect_location(node.then_keyword_loc)}\n", indent] + if (statements = node.statements).nil? + commands << ["├── statements: ∅\n", indent] + else + commands << ["├── statements:\n", indent] + commands << [statements, "#{indent}│ "] + end + if (else_clause = node.else_clause).nil? + commands << ["├── else_clause: ∅\n", indent] + else + commands << ["├── else_clause:\n", indent] + commands << [else_clause, "#{indent}│ "] + end + commands << ["└── end_keyword_loc: #{inspect_location(node.end_keyword_loc)}\n", indent] + end + + # Inspect a UntilNode node. + def visit_until_node(node) + commands << [inspect_node("UntilNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ("begin_modifier" if node.begin_modifier?)].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── keyword_loc: #{inspect_location(node.keyword_loc)}\n", indent] + commands << ["├── do_keyword_loc: #{inspect_location(node.do_keyword_loc)}\n", indent] + commands << ["├── closing_loc: #{inspect_location(node.closing_loc)}\n", indent] + commands << ["├── predicate:\n", indent] + commands << [node.predicate, "#{indent}│ "] + if (statements = node.statements).nil? + commands << ["└── statements: ∅\n", indent] + else + commands << ["└── statements:\n", indent] + commands << [statements, "#{indent} "] + end + end + + # Inspect a WhenNode node. + def visit_when_node(node) + commands << [inspect_node("WhenNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── keyword_loc: #{inspect_location(node.keyword_loc)}\n", indent] + commands << ["├── conditions: (length: #{(conditions = node.conditions).length})\n", indent] + if conditions.any? + conditions[0...-1].each do |child| + commands << [Replace.new("#{indent}│ ├── "), indent] + commands << [child, "#{indent}│ │ "] + end + commands << [Replace.new("#{indent}│ └── "), indent] + commands << [conditions[-1], "#{indent}│ "] + end + commands << ["├── then_keyword_loc: #{inspect_location(node.then_keyword_loc)}\n", indent] + if (statements = node.statements).nil? + commands << ["└── statements: ∅\n", indent] + else + commands << ["└── statements:\n", indent] + commands << [statements, "#{indent} "] + end + end + + # Inspect a WhileNode node. + def visit_while_node(node) + commands << [inspect_node("WhileNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ("begin_modifier" if node.begin_modifier?)].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── keyword_loc: #{inspect_location(node.keyword_loc)}\n", indent] + commands << ["├── do_keyword_loc: #{inspect_location(node.do_keyword_loc)}\n", indent] + commands << ["├── closing_loc: #{inspect_location(node.closing_loc)}\n", indent] + commands << ["├── predicate:\n", indent] + commands << [node.predicate, "#{indent}│ "] + if (statements = node.statements).nil? + commands << ["└── statements: ∅\n", indent] + else + commands << ["└── statements:\n", indent] + commands << [statements, "#{indent} "] + end + end + + # Inspect a XStringNode node. + def visit_x_string_node(node) + commands << [inspect_node("XStringNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ("forced_utf8_encoding" if node.forced_utf8_encoding?), ("forced_binary_encoding" if node.forced_binary_encoding?)].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── opening_loc: #{inspect_location(node.opening_loc)}\n", indent] + commands << ["├── content_loc: #{inspect_location(node.content_loc)}\n", indent] + commands << ["├── closing_loc: #{inspect_location(node.closing_loc)}\n", indent] + commands << ["└── unescaped: #{node.unescaped.inspect}\n", indent] + end + + # Inspect a YieldNode node. + def visit_yield_node(node) + commands << [inspect_node("YieldNode", node), indent] + flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact + commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent] + commands << ["├── keyword_loc: #{inspect_location(node.keyword_loc)}\n", indent] + commands << ["├── lparen_loc: #{inspect_location(node.lparen_loc)}\n", indent] + if (arguments = node.arguments).nil? + commands << ["├── arguments: ∅\n", indent] + else + commands << ["├── arguments:\n", indent] + commands << [arguments, "#{indent}│ "] + end + commands << ["└── rparen_loc: #{inspect_location(node.rparen_loc)}\n", indent] + end + + private + + # Compose a header for the given node. + def inspect_node(name, node) + location = node.location + "@ #{name} (location: (#{location.start_line},#{location.start_column})-(#{location.end_line},#{location.end_column}))\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/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/lex_compat.rb b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/lex_compat.rb new file mode 100644 index 0000000..4960230 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/lex_compat.rb @@ -0,0 +1,911 @@ +# frozen_string_literal: true +# :markup: markdown + +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 < BasicObject + # Create a new token object with the given ripper-compatible array. + def initialize(array) + @array = array + end + + # The location of the token in the source. + def location + @array[0] + end + + # The type of the token. + def event + @array[1] + end + + # The slice of the source that this token represents. + def value + @array[2] + end + + # The state of the lexer when this token was produced. + def state + @array[3] + end + + # We want to pretend that this is just an Array. + def ==(other) # :nodoc: + @array == other + end + + def respond_to_missing?(name, include_private = false) # :nodoc: + @array.respond_to?(name, include_private) + end + + def method_missing(name, ...) # :nodoc: + @array.send(name, ...) + end + end + + # Tokens where state should be ignored + # used for :on_sp, :on_comment, :on_heredoc_end, :on_embexpr_end + class IgnoreStateToken < Token + def ==(other) # :nodoc: + self[0...-1] == other[0...-1] + 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.match?(/\\\r?\n\z/) + 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 + + # 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. + BOM_FLUSHED = RUBY_VERSION >= "3.3.0" + private_constant :BOM_FLUSHED + + attr_reader :options + + def initialize(code, **options) + @code = code + @options = options + end + + def result + tokens = [] #: Array[LexCompat::Token] + + state = :default + heredoc_stack = [[]] #: Array[Array[Heredoc::PlainHeredoc | Heredoc::DashHeredoc | Heredoc::DedentingHeredoc]] + + result = Prism.lex(@code, **options) + source = result.source + result_value = result.value + previous_state = nil #: State? + last_heredoc_end = nil #: Integer? + eof_token = nil + + bom = source.slice(0, 3) == "\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 = Translation::Ripper::Lexer::State.cached(lex_state) + + token = + case event + when :on___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. + value = value[0..value.index("\n")] + Token.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_embexpr_end + IgnoreStateToken.new([[lineno, column], event, value, lex_state]) + when :on_words_sep + # Ripper emits one token each per line. + value.each_line.with_index do |line, index| + if index > 0 + lineno += 1 + column = 0 + end + tokens << Token.new([[lineno, column], event, line, lex_state]) + end + tokens.pop + 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 + + Translation::Ripper::Lexer::State.cached(result_value[current_index][1]) + else + previous_state + end + + Token.new([[lineno, column], event, value, lex_state]) + when :on_eof + eof_token = token + 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: < 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 because Ripper.lex sorts. + # Manually implemented instead of `sort_by!(&:location)` for performance. + tokens.sort_by! do |token| + line, column = token.location + source.byte_offset(line, column) + end + + # Add :on_sp tokens + tokens = add_on_sp_tokens(tokens, source, result.data_loc, bom, eof_token) + + Result.new(tokens, result.comments, result.magic_comments, result.data_loc, result.errors, result.warnings, source) + end + + def add_on_sp_tokens(tokens, source, data_loc, bom, eof_token) + new_tokens = [] + + prev_token_state = Translation::Ripper::Lexer::State.cached(Translation::Ripper::EXPR_BEG) + prev_token_end = bom ? 3 : 0 + + tokens.each do |token| + line, column = token.location + start_offset = source.byte_offset(line, column) + + # Ripper reports columns on line 1 without counting the BOM, so we + # adjust to get the real offset + start_offset += 3 if line == 1 && bom + + if start_offset > prev_token_end + sp_value = source.slice(prev_token_end, start_offset - prev_token_end) + sp_line = source.line(prev_token_end) + sp_column = source.column(prev_token_end) + # Ripper reports columns on line 1 without counting the BOM + sp_column -= 3 if sp_line == 1 && bom + continuation_index = sp_value.byteindex("\\") + + # ripper emits up to three :on_sp tokens when line continuations are used + if continuation_index + next_whitespace_index = continuation_index + 1 + next_whitespace_index += 1 if sp_value.byteslice(next_whitespace_index) == "\r" + next_whitespace_index += 1 + first_whitespace = sp_value[0...continuation_index] + continuation = sp_value[continuation_index...next_whitespace_index] + second_whitespace = sp_value[next_whitespace_index..] + + new_tokens << IgnoreStateToken.new([ + [sp_line, sp_column], + :on_sp, + first_whitespace, + prev_token_state + ]) unless first_whitespace.empty? + + new_tokens << IgnoreStateToken.new([ + [sp_line, sp_column + continuation_index], + :on_sp, + continuation, + prev_token_state + ]) + + new_tokens << IgnoreStateToken.new([ + [sp_line + 1, 0], + :on_sp, + second_whitespace, + prev_token_state + ]) unless second_whitespace.empty? + else + new_tokens << IgnoreStateToken.new([ + [sp_line, sp_column], + :on_sp, + sp_value, + prev_token_state + ]) + end + end + + new_tokens << token + prev_token_state = token.state + prev_token_end = start_offset + token.value.bytesize + end + + unless data_loc # no trailing :on_sp with __END__ as it is always preceded by :on_nl + end_offset = eof_token.location.end_offset + if prev_token_end < end_offset + new_tokens << IgnoreStateToken.new([ + [source.line(prev_token_end), source.column(prev_token_end)], + :on_sp, + source.slice(prev_token_end, end_offset - prev_token_end), + prev_token_state + ]) + end + end + + new_tokens + end + end + + private_constant :LexCompat +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/mutation_compiler.rb b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/mutation_compiler.rb new file mode 100644 index 0000000..b7b435b --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/mutation_compiler.rb @@ -0,0 +1,772 @@ +# frozen_string_literal: true +# :markup: markdown + +=begin +-- +This file is generated by the templates/template.rb script and should not be +modified manually. See templates/lib/prism/mutation_compiler.rb.erb +if you are looking to modify the template +++ +=end + +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 + # Copy a AliasGlobalVariableNode node + def visit_alias_global_variable_node(node) + node.copy(new_name: visit(node.new_name), old_name: visit(node.old_name)) + end + + # Copy a AliasMethodNode node + def visit_alias_method_node(node) + node.copy(new_name: visit(node.new_name), old_name: visit(node.old_name)) + end + + # Copy a AlternationPatternNode node + def visit_alternation_pattern_node(node) + node.copy(left: visit(node.left), right: visit(node.right)) + end + + # Copy a AndNode node + def visit_and_node(node) + node.copy(left: visit(node.left), right: visit(node.right)) + end + + # Copy a ArgumentsNode node + def visit_arguments_node(node) + node.copy(arguments: visit_all(node.arguments)) + end + + # Copy a ArrayNode node + def visit_array_node(node) + node.copy(elements: visit_all(node.elements)) + end + + # Copy a ArrayPatternNode node + def visit_array_pattern_node(node) + node.copy(constant: visit(node.constant), requireds: visit_all(node.requireds), rest: visit(node.rest), posts: visit_all(node.posts)) + end + + # Copy a AssocNode node + def visit_assoc_node(node) + node.copy(key: visit(node.key), value: visit(node.value)) + end + + # Copy a AssocSplatNode node + def visit_assoc_splat_node(node) + node.copy(value: visit(node.value)) + end + + # Copy a BackReferenceReadNode node + def visit_back_reference_read_node(node) + node.copy + end + + # Copy a BeginNode node + def visit_begin_node(node) + node.copy(statements: visit(node.statements), rescue_clause: visit(node.rescue_clause), else_clause: visit(node.else_clause), ensure_clause: visit(node.ensure_clause)) + end + + # Copy a BlockArgumentNode node + def visit_block_argument_node(node) + node.copy(expression: visit(node.expression)) + end + + # Copy a BlockLocalVariableNode node + def visit_block_local_variable_node(node) + node.copy + end + + # Copy a BlockNode node + def visit_block_node(node) + node.copy(parameters: visit(node.parameters), body: visit(node.body)) + end + + # Copy a BlockParameterNode node + def visit_block_parameter_node(node) + node.copy + end + + # Copy a BlockParametersNode node + def visit_block_parameters_node(node) + node.copy(parameters: visit(node.parameters), locals: visit_all(node.locals)) + end + + # Copy a BreakNode node + def visit_break_node(node) + node.copy(arguments: visit(node.arguments)) + end + + # Copy a CallAndWriteNode node + def visit_call_and_write_node(node) + node.copy(receiver: visit(node.receiver), value: visit(node.value)) + end + + # Copy a CallNode node + def visit_call_node(node) + node.copy(receiver: visit(node.receiver), arguments: visit(node.arguments), block: visit(node.block)) + end + + # Copy a CallOperatorWriteNode node + def visit_call_operator_write_node(node) + node.copy(receiver: visit(node.receiver), value: visit(node.value)) + end + + # Copy a CallOrWriteNode node + def visit_call_or_write_node(node) + node.copy(receiver: visit(node.receiver), value: visit(node.value)) + end + + # Copy a CallTargetNode node + def visit_call_target_node(node) + node.copy(receiver: visit(node.receiver)) + end + + # Copy a CapturePatternNode node + def visit_capture_pattern_node(node) + node.copy(value: visit(node.value), target: visit(node.target)) + end + + # Copy a CaseMatchNode node + def visit_case_match_node(node) + node.copy(predicate: visit(node.predicate), conditions: visit_all(node.conditions), else_clause: visit(node.else_clause)) + end + + # Copy a CaseNode node + def visit_case_node(node) + node.copy(predicate: visit(node.predicate), conditions: visit_all(node.conditions), else_clause: visit(node.else_clause)) + end + + # Copy a ClassNode node + def visit_class_node(node) + node.copy(constant_path: visit(node.constant_path), superclass: visit(node.superclass), body: visit(node.body)) + end + + # Copy a ClassVariableAndWriteNode node + def visit_class_variable_and_write_node(node) + node.copy(value: visit(node.value)) + end + + # Copy a ClassVariableOperatorWriteNode node + def visit_class_variable_operator_write_node(node) + node.copy(value: visit(node.value)) + end + + # Copy a ClassVariableOrWriteNode node + def visit_class_variable_or_write_node(node) + node.copy(value: visit(node.value)) + end + + # Copy a ClassVariableReadNode node + def visit_class_variable_read_node(node) + node.copy + end + + # Copy a ClassVariableTargetNode node + def visit_class_variable_target_node(node) + node.copy + end + + # Copy a ClassVariableWriteNode node + def visit_class_variable_write_node(node) + node.copy(value: visit(node.value)) + end + + # Copy a ConstantAndWriteNode node + def visit_constant_and_write_node(node) + node.copy(value: visit(node.value)) + end + + # Copy a ConstantOperatorWriteNode node + def visit_constant_operator_write_node(node) + node.copy(value: visit(node.value)) + end + + # Copy a ConstantOrWriteNode node + def visit_constant_or_write_node(node) + node.copy(value: visit(node.value)) + end + + # Copy a ConstantPathAndWriteNode node + def visit_constant_path_and_write_node(node) + node.copy(target: visit(node.target), value: visit(node.value)) + end + + # Copy a ConstantPathNode node + def visit_constant_path_node(node) + node.copy(parent: visit(node.parent)) + end + + # Copy a ConstantPathOperatorWriteNode node + def visit_constant_path_operator_write_node(node) + node.copy(target: visit(node.target), value: visit(node.value)) + end + + # Copy a ConstantPathOrWriteNode node + def visit_constant_path_or_write_node(node) + node.copy(target: visit(node.target), value: visit(node.value)) + end + + # Copy a ConstantPathTargetNode node + def visit_constant_path_target_node(node) + node.copy(parent: visit(node.parent)) + end + + # Copy a ConstantPathWriteNode node + def visit_constant_path_write_node(node) + node.copy(target: visit(node.target), value: visit(node.value)) + end + + # Copy a ConstantReadNode node + def visit_constant_read_node(node) + node.copy + end + + # Copy a ConstantTargetNode node + def visit_constant_target_node(node) + node.copy + end + + # Copy a ConstantWriteNode node + def visit_constant_write_node(node) + node.copy(value: visit(node.value)) + end + + # Copy a DefNode node + def visit_def_node(node) + node.copy(receiver: visit(node.receiver), parameters: visit(node.parameters), body: visit(node.body)) + end + + # Copy a DefinedNode node + def visit_defined_node(node) + node.copy(value: visit(node.value)) + end + + # Copy a ElseNode node + def visit_else_node(node) + node.copy(statements: visit(node.statements)) + end + + # Copy a EmbeddedStatementsNode node + def visit_embedded_statements_node(node) + node.copy(statements: visit(node.statements)) + end + + # Copy a EmbeddedVariableNode node + def visit_embedded_variable_node(node) + node.copy(variable: visit(node.variable)) + end + + # Copy a EnsureNode node + def visit_ensure_node(node) + node.copy(statements: visit(node.statements)) + end + + # Copy a FalseNode node + def visit_false_node(node) + node.copy + end + + # Copy a FindPatternNode node + def visit_find_pattern_node(node) + node.copy(constant: visit(node.constant), left: visit(node.left), requireds: visit_all(node.requireds), right: visit(node.right)) + end + + # Copy a FlipFlopNode node + def visit_flip_flop_node(node) + node.copy(left: visit(node.left), right: visit(node.right)) + end + + # Copy a FloatNode node + def visit_float_node(node) + node.copy + end + + # Copy a ForNode node + def visit_for_node(node) + node.copy(index: visit(node.index), collection: visit(node.collection), statements: visit(node.statements)) + end + + # Copy a ForwardingArgumentsNode node + def visit_forwarding_arguments_node(node) + node.copy + end + + # Copy a ForwardingParameterNode node + def visit_forwarding_parameter_node(node) + node.copy + end + + # Copy a ForwardingSuperNode node + def visit_forwarding_super_node(node) + node.copy(block: visit(node.block)) + end + + # Copy a GlobalVariableAndWriteNode node + def visit_global_variable_and_write_node(node) + node.copy(value: visit(node.value)) + end + + # Copy a GlobalVariableOperatorWriteNode node + def visit_global_variable_operator_write_node(node) + node.copy(value: visit(node.value)) + end + + # Copy a GlobalVariableOrWriteNode node + def visit_global_variable_or_write_node(node) + node.copy(value: visit(node.value)) + end + + # Copy a GlobalVariableReadNode node + def visit_global_variable_read_node(node) + node.copy + end + + # Copy a GlobalVariableTargetNode node + def visit_global_variable_target_node(node) + node.copy + end + + # Copy a GlobalVariableWriteNode node + def visit_global_variable_write_node(node) + node.copy(value: visit(node.value)) + end + + # Copy a HashNode node + def visit_hash_node(node) + node.copy(elements: visit_all(node.elements)) + end + + # Copy a HashPatternNode node + def visit_hash_pattern_node(node) + node.copy(constant: visit(node.constant), elements: visit_all(node.elements), rest: visit(node.rest)) + end + + # Copy a IfNode node + def visit_if_node(node) + node.copy(predicate: visit(node.predicate), statements: visit(node.statements), subsequent: visit(node.subsequent)) + end + + # Copy a ImaginaryNode node + def visit_imaginary_node(node) + node.copy(numeric: visit(node.numeric)) + end + + # Copy a ImplicitNode node + def visit_implicit_node(node) + node.copy(value: visit(node.value)) + end + + # Copy a ImplicitRestNode node + def visit_implicit_rest_node(node) + node.copy + end + + # Copy a InNode node + def visit_in_node(node) + node.copy(pattern: visit(node.pattern), statements: visit(node.statements)) + end + + # Copy a IndexAndWriteNode node + def visit_index_and_write_node(node) + node.copy(receiver: visit(node.receiver), arguments: visit(node.arguments), block: visit(node.block), value: visit(node.value)) + end + + # Copy a IndexOperatorWriteNode node + def visit_index_operator_write_node(node) + node.copy(receiver: visit(node.receiver), arguments: visit(node.arguments), block: visit(node.block), value: visit(node.value)) + end + + # Copy a IndexOrWriteNode node + def visit_index_or_write_node(node) + node.copy(receiver: visit(node.receiver), arguments: visit(node.arguments), block: visit(node.block), value: visit(node.value)) + end + + # Copy a IndexTargetNode node + def visit_index_target_node(node) + node.copy(receiver: visit(node.receiver), arguments: visit(node.arguments), block: visit(node.block)) + end + + # Copy a InstanceVariableAndWriteNode node + def visit_instance_variable_and_write_node(node) + node.copy(value: visit(node.value)) + end + + # Copy a InstanceVariableOperatorWriteNode node + def visit_instance_variable_operator_write_node(node) + node.copy(value: visit(node.value)) + end + + # Copy a InstanceVariableOrWriteNode node + def visit_instance_variable_or_write_node(node) + node.copy(value: visit(node.value)) + end + + # Copy a InstanceVariableReadNode node + def visit_instance_variable_read_node(node) + node.copy + end + + # Copy a InstanceVariableTargetNode node + def visit_instance_variable_target_node(node) + node.copy + end + + # Copy a InstanceVariableWriteNode node + def visit_instance_variable_write_node(node) + node.copy(value: visit(node.value)) + end + + # Copy a IntegerNode node + def visit_integer_node(node) + node.copy + end + + # Copy a InterpolatedMatchLastLineNode node + def visit_interpolated_match_last_line_node(node) + node.copy(parts: visit_all(node.parts)) + end + + # Copy a InterpolatedRegularExpressionNode node + def visit_interpolated_regular_expression_node(node) + node.copy(parts: visit_all(node.parts)) + end + + # Copy a InterpolatedStringNode node + def visit_interpolated_string_node(node) + node.copy(parts: visit_all(node.parts)) + end + + # Copy a InterpolatedSymbolNode node + def visit_interpolated_symbol_node(node) + node.copy(parts: visit_all(node.parts)) + end + + # Copy a InterpolatedXStringNode node + def visit_interpolated_x_string_node(node) + node.copy(parts: visit_all(node.parts)) + end + + # Copy a ItLocalVariableReadNode node + def visit_it_local_variable_read_node(node) + node.copy + end + + # Copy a ItParametersNode node + def visit_it_parameters_node(node) + node.copy + end + + # Copy a KeywordHashNode node + def visit_keyword_hash_node(node) + node.copy(elements: visit_all(node.elements)) + end + + # Copy a KeywordRestParameterNode node + def visit_keyword_rest_parameter_node(node) + node.copy + end + + # Copy a LambdaNode node + def visit_lambda_node(node) + node.copy(parameters: visit(node.parameters), body: visit(node.body)) + end + + # Copy a LocalVariableAndWriteNode node + def visit_local_variable_and_write_node(node) + node.copy(value: visit(node.value)) + end + + # Copy a LocalVariableOperatorWriteNode node + def visit_local_variable_operator_write_node(node) + node.copy(value: visit(node.value)) + end + + # Copy a LocalVariableOrWriteNode node + def visit_local_variable_or_write_node(node) + node.copy(value: visit(node.value)) + end + + # Copy a LocalVariableReadNode node + def visit_local_variable_read_node(node) + node.copy + end + + # Copy a LocalVariableTargetNode node + def visit_local_variable_target_node(node) + node.copy + end + + # Copy a LocalVariableWriteNode node + def visit_local_variable_write_node(node) + node.copy(value: visit(node.value)) + end + + # Copy a MatchLastLineNode node + def visit_match_last_line_node(node) + node.copy + end + + # Copy a MatchPredicateNode node + def visit_match_predicate_node(node) + node.copy(value: visit(node.value), pattern: visit(node.pattern)) + end + + # Copy a MatchRequiredNode node + def visit_match_required_node(node) + node.copy(value: visit(node.value), pattern: visit(node.pattern)) + end + + # Copy a MatchWriteNode node + def visit_match_write_node(node) + node.copy(call: visit(node.call), targets: visit_all(node.targets)) + end + + # Copy a MissingNode node + def visit_missing_node(node) + node.copy + end + + # Copy a ModuleNode node + def visit_module_node(node) + node.copy(constant_path: visit(node.constant_path), body: visit(node.body)) + end + + # Copy a MultiTargetNode node + def visit_multi_target_node(node) + node.copy(lefts: visit_all(node.lefts), rest: visit(node.rest), rights: visit_all(node.rights)) + end + + # Copy a MultiWriteNode node + def visit_multi_write_node(node) + node.copy(lefts: visit_all(node.lefts), rest: visit(node.rest), rights: visit_all(node.rights), value: visit(node.value)) + end + + # Copy a NextNode node + def visit_next_node(node) + node.copy(arguments: visit(node.arguments)) + end + + # Copy a NilNode node + def visit_nil_node(node) + node.copy + end + + # Copy a NoKeywordsParameterNode node + def visit_no_keywords_parameter_node(node) + node.copy + end + + # Copy a NumberedParametersNode node + def visit_numbered_parameters_node(node) + node.copy + end + + # Copy a NumberedReferenceReadNode node + def visit_numbered_reference_read_node(node) + node.copy + end + + # Copy a OptionalKeywordParameterNode node + def visit_optional_keyword_parameter_node(node) + node.copy(value: visit(node.value)) + end + + # Copy a OptionalParameterNode node + def visit_optional_parameter_node(node) + node.copy(value: visit(node.value)) + end + + # Copy a OrNode node + def visit_or_node(node) + node.copy(left: visit(node.left), right: visit(node.right)) + end + + # Copy a ParametersNode node + def visit_parameters_node(node) + node.copy(requireds: visit_all(node.requireds), optionals: visit_all(node.optionals), rest: visit(node.rest), posts: visit_all(node.posts), keywords: visit_all(node.keywords), keyword_rest: visit(node.keyword_rest), block: visit(node.block)) + end + + # Copy a ParenthesesNode node + def visit_parentheses_node(node) + node.copy(body: visit(node.body)) + end + + # Copy a PinnedExpressionNode node + def visit_pinned_expression_node(node) + node.copy(expression: visit(node.expression)) + end + + # Copy a PinnedVariableNode node + def visit_pinned_variable_node(node) + node.copy(variable: visit(node.variable)) + end + + # Copy a PostExecutionNode node + def visit_post_execution_node(node) + node.copy(statements: visit(node.statements)) + end + + # Copy a PreExecutionNode node + def visit_pre_execution_node(node) + node.copy(statements: visit(node.statements)) + end + + # Copy a ProgramNode node + def visit_program_node(node) + node.copy(statements: visit(node.statements)) + end + + # Copy a RangeNode node + def visit_range_node(node) + node.copy(left: visit(node.left), right: visit(node.right)) + end + + # Copy a RationalNode node + def visit_rational_node(node) + node.copy + end + + # Copy a RedoNode node + def visit_redo_node(node) + node.copy + end + + # Copy a RegularExpressionNode node + def visit_regular_expression_node(node) + node.copy + end + + # Copy a RequiredKeywordParameterNode node + def visit_required_keyword_parameter_node(node) + node.copy + end + + # Copy a RequiredParameterNode node + def visit_required_parameter_node(node) + node.copy + end + + # Copy a RescueModifierNode node + def visit_rescue_modifier_node(node) + node.copy(expression: visit(node.expression), rescue_expression: visit(node.rescue_expression)) + end + + # Copy a RescueNode node + def visit_rescue_node(node) + node.copy(exceptions: visit_all(node.exceptions), reference: visit(node.reference), statements: visit(node.statements), subsequent: visit(node.subsequent)) + end + + # Copy a RestParameterNode node + def visit_rest_parameter_node(node) + node.copy + end + + # Copy a RetryNode node + def visit_retry_node(node) + node.copy + end + + # Copy a ReturnNode node + def visit_return_node(node) + node.copy(arguments: visit(node.arguments)) + end + + # Copy a SelfNode node + def visit_self_node(node) + node.copy + end + + # Copy a ShareableConstantNode node + def visit_shareable_constant_node(node) + node.copy(write: visit(node.write)) + end + + # Copy a SingletonClassNode node + def visit_singleton_class_node(node) + node.copy(expression: visit(node.expression), body: visit(node.body)) + end + + # Copy a SourceEncodingNode node + def visit_source_encoding_node(node) + node.copy + end + + # Copy a SourceFileNode node + def visit_source_file_node(node) + node.copy + end + + # Copy a SourceLineNode node + def visit_source_line_node(node) + node.copy + end + + # Copy a SplatNode node + def visit_splat_node(node) + node.copy(expression: visit(node.expression)) + end + + # Copy a StatementsNode node + def visit_statements_node(node) + node.copy(body: visit_all(node.body)) + end + + # Copy a StringNode node + def visit_string_node(node) + node.copy + end + + # Copy a SuperNode node + def visit_super_node(node) + node.copy(arguments: visit(node.arguments), block: visit(node.block)) + end + + # Copy a SymbolNode node + def visit_symbol_node(node) + node.copy + end + + # Copy a TrueNode node + def visit_true_node(node) + node.copy + end + + # Copy a UndefNode node + def visit_undef_node(node) + node.copy(names: visit_all(node.names)) + end + + # Copy a UnlessNode node + def visit_unless_node(node) + node.copy(predicate: visit(node.predicate), statements: visit(node.statements), else_clause: visit(node.else_clause)) + end + + # Copy a UntilNode node + def visit_until_node(node) + node.copy(predicate: visit(node.predicate), statements: visit(node.statements)) + end + + # Copy a WhenNode node + def visit_when_node(node) + node.copy(conditions: visit_all(node.conditions), statements: visit(node.statements)) + end + + # Copy a WhileNode node + def visit_while_node(node) + node.copy(predicate: visit(node.predicate), statements: visit(node.statements)) + end + + # Copy a XStringNode node + def visit_x_string_node(node) + node.copy + end + + # Copy a YieldNode node + def visit_yield_node(node) + node.copy(arguments: visit(node.arguments)) + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/node.rb b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/node.rb new file mode 100644 index 0000000..c0a2a69 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/node.rb @@ -0,0 +1,19966 @@ +# frozen_string_literal: true +# :markup: markdown + +=begin +-- +This file is generated by the templates/template.rb script and should not be +modified manually. See templates/lib/prism/node.rb.erb +if you are looking to modify the template +++ +=end + +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 unique identifier for this node. This is used in a very specific + # use case where you want to keep around a reference to a node without + # having to keep around the syntax tree in memory. This unique identifier + # will be consistent across multiple parses of the same source code. + attr_reader :node_id + + # Save this node using a saved source so that it can be retrieved later. + def save(repository) + repository.enter(node_id, :itself) + end + + # 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 + + # Save the location using a saved source so that it can be retrieved later. + def save_location(repository) + repository.enter(node_id, :location) + end + + # Delegates to the start_line of the associated location object. + def start_line + location.start_line + end + + # Delegates to the end_line of the associated location object. + def end_line + location.end_line + 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 + + # Delegates to the start_character_offset of the associated location object. + def start_character_offset + location.start_character_offset + end + + # Delegates to the end_character_offset of the associated location object. + def end_character_offset + location.end_character_offset + end + + # Delegates to the cached_start_code_units_offset of the associated location + # object. + def cached_start_code_units_offset(cache) + location.cached_start_code_units_offset(cache) + end + + # Delegates to the cached_end_code_units_offset of the associated location + # object. + def cached_end_code_units_offset(cache) + location.cached_end_code_units_offset(cache) + end + + # Delegates to the start_column of the associated location object. + def start_column + location.start_column + end + + # Delegates to the end_column of the associated location object. + def end_column + location.end_column + end + + # Delegates to the start_character_column of the associated location object. + def start_character_column + location.start_character_column + end + + # Delegates to the end_character_column of the associated location object. + def end_character_column + location.end_character_column + end + + # Delegates to the cached_start_code_units_column of the associated location + # object. + def cached_start_code_units_column(cache) + location.cached_start_code_units_column(cache) + end + + # Delegates to the cached_end_code_units_column of the associated location + # object. + def cached_end_code_units_column(cache) + location.cached_end_code_units_column(cache) + end + + # Delegates to the leading_comments of the associated location object. + def leading_comments + location.leading_comments + end + + # Delegates to the trailing_comments of the associated location object. + def trailing_comments + location.trailing_comments + end + + # Delegates to the comments of the associated location object. + def comments + location.comments + end + + # Returns all of the lines of the source code associated with this node. + def source_lines + location.source_lines + end + + # An alias for source_lines, used to mimic the API from + # RubyVM::AbstractSyntaxTree to make it easier to migrate. + alias script_lines source_lines + + # 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 + + # An bitset of flags for this node. There are certain flags that are common + # for all nodes, and then some nodes have specific flags. + attr_reader :flags + protected :flags + + # Returns true if the node has the newline flag set. + def newline? + flags.anybits?(NodeFlags::NEWLINE) + end + + # Returns true if the node has the static literal flag set. + def static_literal? + flags.anybits?(NodeFlags::STATIC_LITERAL) + 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 nodes that are descendants of this node that contain the + # given line and column. This is useful for locating a node that is selected + # based on the line and column of the source code. + # + # Important to note is that the column given to this method should be in + # bytes, as opposed to characters or code units. + def tunnel(line, column) + queue = [self] #: Array[Prism::node] + result = [] #: Array[Prism::node] + offset = source.byte_offset(line, column) + + while (node = queue.shift) + result << node + + node.each_child_node do |child_node| + if child_node.start_offset <= offset && offset < child_node.end_offset + queue << child_node + break + end + end + end + + result + end + + # Returns the first node that matches the given block when visited in a + # breadth-first search. This is useful for finding a node that matches a + # particular condition. + # + # node.breadth_first_search { |node| node.node_id == node_id } + # + def breadth_first_search(&block) + queue = [self] #: Array[Prism::node] + + while (node = queue.shift) + return node if yield node + queue.concat(node.compact_child_nodes) + end + + nil + end + alias find breadth_first_search + + # Returns all of the nodes that match the given block when visited in a + # breadth-first search. This is useful for finding all nodes that match a + # particular condition. + # + # node.breadth_first_search_all { |node| node.is_a?(Prism::CallNode) } + # + def breadth_first_search_all(&block) + queue = [self] #: Array[Prism::node] + results = [] #: Array[Prism::node] + + while (node = queue.shift) + results << node if yield node + queue.concat(node.compact_child_nodes) + end + + results + end + alias find_all breadth_first_search_all + + # 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 + + # With a block given, yields each child node. Without a block, returns + # an enumerator that contains each child node. Excludes any `nil`s in + # the place of optional nodes that were not present. + def each_child_node + raise NoMethodError, "undefined method `each_child_node' for #{inspect}" + end + + # 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 string representation of the node. + def inspect + raise NoMethodError, "undefined method `inspect' for #{inspect}" + 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 + raise NoMethodError, "undefined method `type' for #{inspect}" + 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 + raise NoMethodError, "undefined method `type' for #{inspect}" + end + end + + # Represents the use of the `alias` keyword to alias a global variable. + # + # alias $foo $bar + # ^^^^^^^^^^^^^^^ + class AliasGlobalVariableNode < Node + # Initialize a new AliasGlobalVariableNode node. + def initialize(source, node_id, location, flags, new_name, old_name, keyword_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @new_name = new_name + @old_name = old_name + @keyword_loc = keyword_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_alias_global_variable_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [new_name, old_name] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield new_name + yield old_name + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [new_name, old_name] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [new_name, old_name, keyword_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?new_name: GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode, ?old_name: GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode | SymbolNode | MissingNode, ?keyword_loc: Location) -> AliasGlobalVariableNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, new_name: self.new_name, old_name: self.old_name, keyword_loc: self.keyword_loc) + AliasGlobalVariableNode.new(source, node_id, location, flags, new_name, old_name, keyword_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, new_name: GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode, old_name: GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode | SymbolNode | MissingNode, keyword_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, new_name: new_name, old_name: old_name, keyword_loc: keyword_loc } + end + + # Represents the new name of the global variable that can be used after aliasing. + # + # alias $foo $bar + # ^^^^ + attr_reader :new_name + + # Represents the old name of the global variable that can be used before aliasing. + # + # alias $foo $bar + # ^^^^ + attr_reader :old_name + + # The location of the `alias` keyword. + # + # alias $foo $bar + # ^^^^^ + def keyword_loc + location = @keyword_loc + return location if location.is_a?(Location) + @keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_keyword_loc(repository) + repository.enter(node_id, :keyword_loc) + end + + # def keyword: () -> String + def keyword + keyword_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :alias_global_variable_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :alias_global_variable_node + 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?(AliasGlobalVariableNode) && + (new_name === other.new_name) && + (old_name === other.old_name) && + (keyword_loc.nil? == other.keyword_loc.nil?) + end + end + + # Represents the use of the `alias` keyword to alias a method. + # + # alias foo bar + # ^^^^^^^^^^^^^ + class AliasMethodNode < Node + # Initialize a new AliasMethodNode node. + def initialize(source, node_id, location, flags, new_name, old_name, keyword_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @new_name = new_name + @old_name = old_name + @keyword_loc = keyword_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_alias_method_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [new_name, old_name] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield new_name + yield old_name + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [new_name, old_name] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [new_name, old_name, keyword_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?new_name: SymbolNode | InterpolatedSymbolNode, ?old_name: SymbolNode | InterpolatedSymbolNode | GlobalVariableReadNode | MissingNode, ?keyword_loc: Location) -> AliasMethodNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, new_name: self.new_name, old_name: self.old_name, keyword_loc: self.keyword_loc) + AliasMethodNode.new(source, node_id, location, flags, new_name, old_name, keyword_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, new_name: SymbolNode | InterpolatedSymbolNode, old_name: SymbolNode | InterpolatedSymbolNode | GlobalVariableReadNode | MissingNode, keyword_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, new_name: new_name, old_name: old_name, keyword_loc: keyword_loc } + end + + # Represents the new name of the method that will be aliased. + # + # alias foo bar + # ^^^ + # + # alias :foo :bar + # ^^^^ + # + # alias :"#{foo}" :"#{bar}" + # ^^^^^^^^^ + attr_reader :new_name + + # Represents the old name of the method that will be aliased. + # + # alias foo bar + # ^^^ + # + # alias :foo :bar + # ^^^^ + # + # alias :"#{foo}" :"#{bar}" + # ^^^^^^^^^ + attr_reader :old_name + + # Represents the location of the `alias` keyword. + # + # alias foo bar + # ^^^^^ + def keyword_loc + location = @keyword_loc + return location if location.is_a?(Location) + @keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_keyword_loc(repository) + repository.enter(node_id, :keyword_loc) + end + + # def keyword: () -> String + def keyword + keyword_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :alias_method_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :alias_method_node + 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?(AliasMethodNode) && + (new_name === other.new_name) && + (old_name === other.old_name) && + (keyword_loc.nil? == other.keyword_loc.nil?) + end + end + + # Represents an alternation pattern in pattern matching. + # + # foo => bar | baz + # ^^^^^^^^^ + class AlternationPatternNode < Node + # Initialize a new AlternationPatternNode node. + def initialize(source, node_id, location, flags, left, right, operator_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @left = left + @right = right + @operator_loc = operator_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_alternation_pattern_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [left, right] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield left + yield right + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [left, right] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [left, right, operator_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?left: Prism::node, ?right: Prism::node, ?operator_loc: Location) -> AlternationPatternNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, left: self.left, right: self.right, operator_loc: self.operator_loc) + AlternationPatternNode.new(source, node_id, location, flags, left, right, operator_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, left: Prism::node, right: Prism::node, operator_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, left: left, right: right, operator_loc: operator_loc } + end + + # Represents the left side of the expression. + # + # foo => bar | baz + # ^^^ + attr_reader :left + + # Represents the right side of the expression. + # + # foo => bar | baz + # ^^^ + attr_reader :right + + # Represents the alternation operator location. + # + # foo => bar | baz + # ^ + def operator_loc + location = @operator_loc + return location if location.is_a?(Location) + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) + end + + # def operator: () -> String + def operator + operator_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :alternation_pattern_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :alternation_pattern_node + 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?(AlternationPatternNode) && + (left === other.left) && + (right === other.right) && + (operator_loc.nil? == other.operator_loc.nil?) + end + end + + # Represents the use of the `&&` operator or the `and` keyword. + # + # left and right + # ^^^^^^^^^^^^^^ + class AndNode < Node + # Initialize a new AndNode node. + def initialize(source, node_id, location, flags, left, right, operator_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @left = left + @right = right + @operator_loc = operator_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_and_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [left, right] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield left + yield right + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [left, right] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [left, right, operator_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?left: Prism::node, ?right: Prism::node, ?operator_loc: Location) -> AndNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, left: self.left, right: self.right, operator_loc: self.operator_loc) + AndNode.new(source, node_id, location, flags, left, right, operator_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, left: Prism::node, right: Prism::node, operator_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, left: left, right: right, operator_loc: operator_loc } + end + + # 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 + # ^ + attr_reader :left + + # Represents the right side of the expression. + # + # left && right + # ^^^^^ + # + # 1 and 2 + # ^ + attr_reader :right + + # The location of the `and` keyword or the `&&` operator. + # + # left and right + # ^^^ + def operator_loc + location = @operator_loc + return location if location.is_a?(Location) + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) + end + + # def operator: () -> String + def operator + operator_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :and_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :and_node + 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?(AndNode) && + (left === other.left) && + (right === other.right) && + (operator_loc.nil? == other.operator_loc.nil?) + end + end + + # Represents a set of arguments to a method or a keyword. + # + # return foo, bar, baz + # ^^^^^^^^^^^^^ + class ArgumentsNode < Node + # Initialize a new ArgumentsNode node. + def initialize(source, node_id, location, flags, arguments) + @source = source + @node_id = node_id + @location = location + @flags = flags + @arguments = arguments + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_arguments_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [*arguments] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + arguments.each { |node| yield node } + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [*arguments] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [*arguments] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?arguments: Array[Prism::node]) -> ArgumentsNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, arguments: self.arguments) + ArgumentsNode.new(source, node_id, location, flags, arguments) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, arguments: Array[Prism::node] } + def deconstruct_keys(keys) + { node_id: node_id, location: location, arguments: arguments } + end + + # def contains_forwarding?: () -> bool + def contains_forwarding? + flags.anybits?(ArgumentsNodeFlags::CONTAINS_FORWARDING) + end + + # def contains_keywords?: () -> bool + def contains_keywords? + flags.anybits?(ArgumentsNodeFlags::CONTAINS_KEYWORDS) + end + + # def contains_keyword_splat?: () -> bool + def contains_keyword_splat? + flags.anybits?(ArgumentsNodeFlags::CONTAINS_KEYWORD_SPLAT) + end + + # def contains_splat?: () -> bool + def contains_splat? + flags.anybits?(ArgumentsNodeFlags::CONTAINS_SPLAT) + end + + # def contains_multiple_splats?: () -> bool + def contains_multiple_splats? + flags.anybits?(ArgumentsNodeFlags::CONTAINS_MULTIPLE_SPLATS) + end + + # The list of arguments, if present. These can be any [non-void expressions](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + # + # foo(bar, baz) + # ^^^^^^^^ + attr_reader :arguments + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :arguments_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :arguments_node + 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?(ArgumentsNode) && + (flags === other.flags) && + (arguments.length == other.arguments.length) && + arguments.zip(other.arguments).all? { |left, right| left === right } + end + end + + # Represents an array literal. This can be a regular array using brackets or a special array using % like %w or %i. + # + # [1, 2, 3] + # ^^^^^^^^^ + class ArrayNode < Node + # Initialize a new ArrayNode node. + def initialize(source, node_id, location, flags, elements, opening_loc, closing_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @elements = elements + @opening_loc = opening_loc + @closing_loc = closing_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_array_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [*elements] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + elements.each { |node| yield node } + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [*elements] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [*elements, *opening_loc, *closing_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?elements: Array[Prism::node], ?opening_loc: Location?, ?closing_loc: Location?) -> ArrayNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, elements: self.elements, opening_loc: self.opening_loc, closing_loc: self.closing_loc) + ArrayNode.new(source, node_id, location, flags, elements, opening_loc, closing_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, elements: Array[Prism::node], opening_loc: Location?, closing_loc: Location? } + def deconstruct_keys(keys) + { node_id: node_id, location: location, elements: elements, opening_loc: opening_loc, closing_loc: closing_loc } + end + + # def contains_splat?: () -> bool + def contains_splat? + flags.anybits?(ArrayNodeFlags::CONTAINS_SPLAT) + end + + # 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. + attr_reader :elements + + # 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 + def opening_loc + location = @opening_loc + case location + when nil + nil + when Location + location + else + @opening_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + def save_opening_loc(repository) + repository.enter(node_id, :opening_loc) unless @opening_loc.nil? + end + + # 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 + def closing_loc + location = @closing_loc + case location + when nil + nil + when Location + location + else + @closing_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + def save_closing_loc(repository) + repository.enter(node_id, :closing_loc) unless @closing_loc.nil? + end + + # def opening: () -> String? + def opening + opening_loc&.slice + end + + # def closing: () -> String? + def closing + closing_loc&.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :array_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :array_node + 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?(ArrayNode) && + (flags === other.flags) && + (elements.length == other.elements.length) && + elements.zip(other.elements).all? { |left, right| left === right } && + (opening_loc.nil? == other.opening_loc.nil?) && + (closing_loc.nil? == other.closing_loc.nil?) + end + end + + # Represents an array pattern in pattern matching. + # + # foo in 1, 2 + # ^^^^^^^^^^^ + # + # foo in [1, 2] + # ^^^^^^^^^^^^^ + # + # foo in *bar + # ^^^^^^^^^^^ + # + # foo in Bar[] + # ^^^^^^^^^^^^ + # + # foo in Bar[1, 2, 3] + # ^^^^^^^^^^^^^^^^^^^ + class ArrayPatternNode < Node + # Initialize a new ArrayPatternNode node. + def initialize(source, node_id, location, flags, constant, requireds, rest, posts, opening_loc, closing_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @constant = constant + @requireds = requireds + @rest = rest + @posts = posts + @opening_loc = opening_loc + @closing_loc = closing_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_array_pattern_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [constant, *requireds, rest, *posts] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield constant if constant + requireds.each { |node| yield node } + yield rest if rest + posts.each { |node| yield node } + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << constant if constant + compact.concat(requireds) + compact << rest if rest + compact.concat(posts) + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [*constant, *requireds, *rest, *posts, *opening_loc, *closing_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?constant: ConstantPathNode | ConstantReadNode | nil, ?requireds: Array[Prism::node], ?rest: Prism::node?, ?posts: Array[Prism::node], ?opening_loc: Location?, ?closing_loc: Location?) -> ArrayPatternNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, constant: self.constant, requireds: self.requireds, rest: self.rest, posts: self.posts, opening_loc: self.opening_loc, closing_loc: self.closing_loc) + ArrayPatternNode.new(source, node_id, location, flags, constant, requireds, rest, posts, opening_loc, closing_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, constant: ConstantPathNode | ConstantReadNode | nil, requireds: Array[Prism::node], rest: Prism::node?, posts: Array[Prism::node], opening_loc: Location?, closing_loc: Location? } + def deconstruct_keys(keys) + { node_id: node_id, location: location, constant: constant, requireds: requireds, rest: rest, posts: posts, opening_loc: opening_loc, closing_loc: closing_loc } + end + + # Represents the optional constant preceding the Array + # + # foo in Bar[] + # ^^^ + # + # foo in Bar[1, 2, 3] + # ^^^ + # + # foo in Bar::Baz[1, 2, 3] + # ^^^^^^^^ + attr_reader :constant + + # Represents the required elements of the array pattern. + # + # foo in [1, 2] + # ^ ^ + attr_reader :requireds + + # Represents the rest element of the array pattern. + # + # foo in *bar + # ^^^^ + attr_reader :rest + + # Represents the elements after the rest element of the array pattern. + # + # foo in *bar, baz + # ^^^ + attr_reader :posts + + # Represents the opening location of the array pattern. + # + # foo in [1, 2] + # ^ + def opening_loc + location = @opening_loc + case location + when nil + nil + when Location + location + else + @opening_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + def save_opening_loc(repository) + repository.enter(node_id, :opening_loc) unless @opening_loc.nil? + end + + # Represents the closing location of the array pattern. + # + # foo in [1, 2] + # ^ + def closing_loc + location = @closing_loc + case location + when nil + nil + when Location + location + else + @closing_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + def save_closing_loc(repository) + repository.enter(node_id, :closing_loc) unless @closing_loc.nil? + end + + # def opening: () -> String? + def opening + opening_loc&.slice + end + + # def closing: () -> String? + def closing + closing_loc&.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :array_pattern_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :array_pattern_node + 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?(ArrayPatternNode) && + (constant === other.constant) && + (requireds.length == other.requireds.length) && + requireds.zip(other.requireds).all? { |left, right| left === right } && + (rest === other.rest) && + (posts.length == other.posts.length) && + posts.zip(other.posts).all? { |left, right| left === right } && + (opening_loc.nil? == other.opening_loc.nil?) && + (closing_loc.nil? == other.closing_loc.nil?) + end + end + + # Represents a hash key/value pair. + # + # { a => b } + # ^^^^^^ + class AssocNode < Node + # Initialize a new AssocNode node. + def initialize(source, node_id, location, flags, key, value, operator_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @key = key + @value = value + @operator_loc = operator_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_assoc_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [key, value] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield key + yield value + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [key, value] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [key, value, *operator_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?key: Prism::node, ?value: Prism::node, ?operator_loc: Location?) -> AssocNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, key: self.key, value: self.value, operator_loc: self.operator_loc) + AssocNode.new(source, node_id, location, flags, key, value, operator_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, key: Prism::node, value: Prism::node, operator_loc: Location? } + def deconstruct_keys(keys) + { node_id: node_id, location: location, key: key, value: value, operator_loc: operator_loc } + end + + # 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 } + # ^^^^^^^^^^ + attr_reader :key + + # 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 } + # ^ + attr_reader :value + + # The location of the `=>` operator, if present. + # + # { foo => bar } + # ^^ + def operator_loc + location = @operator_loc + case location + when nil + nil + when Location + location + else + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) unless @operator_loc.nil? + end + + # def operator: () -> String? + def operator + operator_loc&.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :assoc_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :assoc_node + 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?(AssocNode) && + (key === other.key) && + (value === other.value) && + (operator_loc.nil? == other.operator_loc.nil?) + end + end + + # Represents a splat in a hash literal. + # + # { **foo } + # ^^^^^ + class AssocSplatNode < Node + # Initialize a new AssocSplatNode node. + def initialize(source, node_id, location, flags, value, operator_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @value = value + @operator_loc = operator_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_assoc_splat_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [value] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield value if value + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << value if value + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [*value, operator_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?value: Prism::node?, ?operator_loc: Location) -> AssocSplatNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, value: self.value, operator_loc: self.operator_loc) + AssocSplatNode.new(source, node_id, location, flags, value, operator_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, value: Prism::node?, operator_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, value: value, operator_loc: operator_loc } + end + + # The value to be splatted, if present. Will be missing when keyword rest argument forwarding is used. + # + # { **foo } + # ^^^ + attr_reader :value + + # The location of the `**` operator. + # + # { **x } + # ^^ + def operator_loc + location = @operator_loc + return location if location.is_a?(Location) + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) + end + + # def operator: () -> String + def operator + operator_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :assoc_splat_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :assoc_splat_node + 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?(AssocSplatNode) && + (value === other.value) && + (operator_loc.nil? == other.operator_loc.nil?) + end + end + + # Represents reading a reference to a field in the previous match. + # + # $' + # ^^ + class BackReferenceReadNode < Node + # Initialize a new BackReferenceReadNode node. + def initialize(source, node_id, location, flags, name) + @source = source + @node_id = node_id + @location = location + @flags = flags + @name = name + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_back_reference_read_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> BackReferenceReadNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name) + BackReferenceReadNode.new(source, node_id, location, flags, name) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol } + def deconstruct_keys(keys) + { node_id: node_id, location: location, name: name } + end + + # The name of the back-reference variable, including the leading `$`. + # + # $& # name `:$&` + # + # $+ # name `:$+` + attr_reader :name + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :back_reference_read_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :back_reference_read_node + 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?(BackReferenceReadNode) && + (name === other.name) + end + end + + # Represents a begin statement. + # + # begin + # foo + # end + # ^^^^^ + class BeginNode < Node + # Initialize a new BeginNode node. + def initialize(source, node_id, location, flags, begin_keyword_loc, statements, rescue_clause, else_clause, ensure_clause, end_keyword_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @begin_keyword_loc = begin_keyword_loc + @statements = statements + @rescue_clause = rescue_clause + @else_clause = else_clause + @ensure_clause = ensure_clause + @end_keyword_loc = end_keyword_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_begin_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [statements, rescue_clause, else_clause, ensure_clause] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield statements if statements + yield rescue_clause if rescue_clause + yield else_clause if else_clause + yield ensure_clause if ensure_clause + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << statements if statements + compact << rescue_clause if rescue_clause + compact << else_clause if else_clause + compact << ensure_clause if ensure_clause + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [*begin_keyword_loc, *statements, *rescue_clause, *else_clause, *ensure_clause, *end_keyword_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?begin_keyword_loc: Location?, ?statements: StatementsNode?, ?rescue_clause: RescueNode?, ?else_clause: ElseNode?, ?ensure_clause: EnsureNode?, ?end_keyword_loc: Location?) -> BeginNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, begin_keyword_loc: self.begin_keyword_loc, statements: self.statements, rescue_clause: self.rescue_clause, else_clause: self.else_clause, ensure_clause: self.ensure_clause, end_keyword_loc: self.end_keyword_loc) + BeginNode.new(source, node_id, location, flags, begin_keyword_loc, statements, rescue_clause, else_clause, ensure_clause, end_keyword_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, begin_keyword_loc: Location?, statements: StatementsNode?, rescue_clause: RescueNode?, else_clause: ElseNode?, ensure_clause: EnsureNode?, end_keyword_loc: Location? } + def deconstruct_keys(keys) + { node_id: node_id, location: location, begin_keyword_loc: begin_keyword_loc, statements: statements, rescue_clause: rescue_clause, else_clause: else_clause, ensure_clause: ensure_clause, end_keyword_loc: end_keyword_loc } + end + + # Represents the location of the `begin` keyword. + # + # begin x end + # ^^^^^ + def begin_keyword_loc + location = @begin_keyword_loc + case location + when nil + nil + when Location + location + else + @begin_keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the begin_keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_begin_keyword_loc(repository) + repository.enter(node_id, :begin_keyword_loc) unless @begin_keyword_loc.nil? + end + + # Represents the statements within the begin block. + # + # begin x end + # ^ + attr_reader :statements + + # Represents the rescue clause within the begin block. + # + # begin x; rescue y; end + # ^^^^^^^^ + attr_reader :rescue_clause + + # Represents the else clause within the begin block. + # + # begin x; rescue y; else z; end + # ^^^^^^ + attr_reader :else_clause + + # Represents the ensure clause within the begin block. + # + # begin x; ensure y; end + # ^^^^^^^^ + attr_reader :ensure_clause + + # Represents the location of the `end` keyword. + # + # begin x end + # ^^^ + def end_keyword_loc + location = @end_keyword_loc + case location + when nil + nil + when Location + location + else + @end_keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the end_keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_end_keyword_loc(repository) + repository.enter(node_id, :end_keyword_loc) unless @end_keyword_loc.nil? + end + + # def begin_keyword: () -> String? + def begin_keyword + begin_keyword_loc&.slice + end + + # def end_keyword: () -> String? + def end_keyword + end_keyword_loc&.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :begin_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :begin_node + 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?(BeginNode) && + (begin_keyword_loc.nil? == other.begin_keyword_loc.nil?) && + (statements === other.statements) && + (rescue_clause === other.rescue_clause) && + (else_clause === other.else_clause) && + (ensure_clause === other.ensure_clause) && + (end_keyword_loc.nil? == other.end_keyword_loc.nil?) + end + end + + # Represents a block argument using `&`. + # + # bar(&args) + # ^^^^^^^^^^ + class BlockArgumentNode < Node + # Initialize a new BlockArgumentNode node. + def initialize(source, node_id, location, flags, expression, operator_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @expression = expression + @operator_loc = operator_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_block_argument_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [expression] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield expression if expression + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << expression if expression + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [*expression, operator_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?expression: Prism::node?, ?operator_loc: Location) -> BlockArgumentNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, expression: self.expression, operator_loc: self.operator_loc) + BlockArgumentNode.new(source, node_id, location, flags, expression, operator_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, expression: Prism::node?, operator_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, expression: expression, operator_loc: operator_loc } + end + + # The expression that is being passed as a block argument. This can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + # + # foo(&args) + # ^^^^^ + attr_reader :expression + + # Represents the location of the `&` operator. + # + # foo(&args) + # ^ + def operator_loc + location = @operator_loc + return location if location.is_a?(Location) + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) + end + + # def operator: () -> String + def operator + operator_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :block_argument_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :block_argument_node + 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?(BlockArgumentNode) && + (expression === other.expression) && + (operator_loc.nil? == other.operator_loc.nil?) + end + end + + # Represents a block local variable. + # + # a { |; b| } + # ^ + class BlockLocalVariableNode < Node + # Initialize a new BlockLocalVariableNode node. + def initialize(source, node_id, location, flags, name) + @source = source + @node_id = node_id + @location = location + @flags = flags + @name = name + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_block_local_variable_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> BlockLocalVariableNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name) + BlockLocalVariableNode.new(source, node_id, location, flags, name) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol } + def deconstruct_keys(keys) + { node_id: node_id, location: location, name: name } + end + + # def repeated_parameter?: () -> bool + def repeated_parameter? + flags.anybits?(ParameterFlags::REPEATED_PARAMETER) + end + + # The name of the block local variable. + # + # a { |; b| } # name `:b` + # ^ + attr_reader :name + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :block_local_variable_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :block_local_variable_node + 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?(BlockLocalVariableNode) && + (flags === other.flags) && + (name === other.name) + end + end + + # Represents a block of ruby code. + # + # [1, 2, 3].each { |i| puts x } + # ^^^^^^^^^^^^^^ + class BlockNode < Node + # Initialize a new BlockNode node. + def initialize(source, node_id, location, flags, locals, parameters, body, opening_loc, closing_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @locals = locals + @parameters = parameters + @body = body + @opening_loc = opening_loc + @closing_loc = closing_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_block_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [parameters, body] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield parameters if parameters + yield body if body + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << parameters if parameters + compact << body if body + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [*parameters, *body, opening_loc, closing_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?locals: Array[Symbol], ?parameters: BlockParametersNode | NumberedParametersNode | ItParametersNode | nil, ?body: StatementsNode | BeginNode | nil, ?opening_loc: Location, ?closing_loc: Location) -> BlockNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, locals: self.locals, parameters: self.parameters, body: self.body, opening_loc: self.opening_loc, closing_loc: self.closing_loc) + BlockNode.new(source, node_id, location, flags, locals, parameters, body, opening_loc, closing_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, locals: Array[Symbol], parameters: BlockParametersNode | NumberedParametersNode | ItParametersNode | nil, body: StatementsNode | BeginNode | nil, opening_loc: Location, closing_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, locals: locals, parameters: parameters, body: body, opening_loc: opening_loc, closing_loc: closing_loc } + end + + # The local variables declared in the block. + # + # [1, 2, 3].each { |i| puts x } # locals: [:i] + # ^ + attr_reader :locals + + # The parameters of the block. + # + # [1, 2, 3].each { |i| puts x } + # ^^^ + # [1, 2, 3].each { puts _1 } + # ^^^^^^^^^^^ + # [1, 2, 3].each { puts it } + # ^^^^^^^^^^^ + attr_reader :parameters + + # The body of the block. + # + # [1, 2, 3].each { |i| puts x } + # ^^^^^^ + attr_reader :body + + # Represents the location of the opening `{` or `do`. + # + # [1, 2, 3].each { |i| puts x } + # ^ + def opening_loc + location = @opening_loc + return location if location.is_a?(Location) + @opening_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + def save_opening_loc(repository) + repository.enter(node_id, :opening_loc) + end + + # Represents the location of the closing `}` or `end`. + # + # [1, 2, 3].each { |i| puts x } + # ^ + def closing_loc + location = @closing_loc + return location if location.is_a?(Location) + @closing_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + def save_closing_loc(repository) + repository.enter(node_id, :closing_loc) + end + + # def opening: () -> String + def opening + opening_loc.slice + end + + # def closing: () -> String + def closing + closing_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :block_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :block_node + 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?(BlockNode) && + (locals.length == other.locals.length) && + locals.zip(other.locals).all? { |left, right| left === right } && + (parameters === other.parameters) && + (body === other.body) && + (opening_loc.nil? == other.opening_loc.nil?) && + (closing_loc.nil? == other.closing_loc.nil?) + end + end + + # Represents a block parameter of a method, block, or lambda definition. + # + # def a(&b) + # ^^ + # end + class BlockParameterNode < Node + # Initialize a new BlockParameterNode node. + def initialize(source, node_id, location, flags, name, name_loc, operator_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @name = name + @name_loc = name_loc + @operator_loc = operator_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_block_parameter_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [*name_loc, operator_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol?, ?name_loc: Location?, ?operator_loc: Location) -> BlockParameterNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc, operator_loc: self.operator_loc) + BlockParameterNode.new(source, node_id, location, flags, name, name_loc, operator_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol?, name_loc: Location?, operator_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, name: name, name_loc: name_loc, operator_loc: operator_loc } + end + + # def repeated_parameter?: () -> bool + def repeated_parameter? + flags.anybits?(ParameterFlags::REPEATED_PARAMETER) + end + + # The name of the block parameter. + # + # def a(&b) # name `:b` + # ^ + # end + attr_reader :name + + # Represents the location of the block parameter name. + # + # def a(&b) + # ^ + def name_loc + location = @name_loc + case location + when nil + nil + when Location + location + else + @name_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + def save_name_loc(repository) + repository.enter(node_id, :name_loc) unless @name_loc.nil? + end + + # Represents the location of the `&` operator. + # + # def a(&b) + # ^ + # end + def operator_loc + location = @operator_loc + return location if location.is_a?(Location) + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) + end + + # def operator: () -> String + def operator + operator_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :block_parameter_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :block_parameter_node + 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?(BlockParameterNode) && + (flags === other.flags) && + (name === other.name) && + (name_loc.nil? == other.name_loc.nil?) && + (operator_loc.nil? == other.operator_loc.nil?) + end + end + + # Represents a block's parameters declaration. + # + # -> (a, b = 1; local) { } + # ^^^^^^^^^^^^^^^^^ + # + # foo do |a, b = 1; local| + # ^^^^^^^^^^^^^^^^^ + # end + class BlockParametersNode < Node + # Initialize a new BlockParametersNode node. + def initialize(source, node_id, location, flags, parameters, locals, opening_loc, closing_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @parameters = parameters + @locals = locals + @opening_loc = opening_loc + @closing_loc = closing_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_block_parameters_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [parameters, *locals] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield parameters if parameters + locals.each { |node| yield node } + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << parameters if parameters + compact.concat(locals) + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [*parameters, *locals, *opening_loc, *closing_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?parameters: ParametersNode?, ?locals: Array[BlockLocalVariableNode], ?opening_loc: Location?, ?closing_loc: Location?) -> BlockParametersNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, parameters: self.parameters, locals: self.locals, opening_loc: self.opening_loc, closing_loc: self.closing_loc) + BlockParametersNode.new(source, node_id, location, flags, parameters, locals, opening_loc, closing_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, parameters: ParametersNode?, locals: Array[BlockLocalVariableNode], opening_loc: Location?, closing_loc: Location? } + def deconstruct_keys(keys) + { node_id: node_id, location: location, parameters: parameters, locals: locals, opening_loc: opening_loc, closing_loc: closing_loc } + end + + # Represents the parameters of the block. + # + # -> (a, b = 1; local) { } + # ^^^^^^^^ + # + # foo do |a, b = 1; local| + # ^^^^^^^^ + # end + attr_reader :parameters + + # Represents the local variables of the block. + # + # -> (a, b = 1; local) { } + # ^^^^^ + # + # foo do |a, b = 1; local| + # ^^^^^ + # end + attr_reader :locals + + # Represents the opening location of the block parameters. + # + # -> (a, b = 1; local) { } + # ^ + # + # foo do |a, b = 1; local| + # ^ + # end + def opening_loc + location = @opening_loc + case location + when nil + nil + when Location + location + else + @opening_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + def save_opening_loc(repository) + repository.enter(node_id, :opening_loc) unless @opening_loc.nil? + end + + # Represents the closing location of the block parameters. + # + # -> (a, b = 1; local) { } + # ^ + # + # foo do |a, b = 1; local| + # ^ + # end + def closing_loc + location = @closing_loc + case location + when nil + nil + when Location + location + else + @closing_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + def save_closing_loc(repository) + repository.enter(node_id, :closing_loc) unless @closing_loc.nil? + end + + # def opening: () -> String? + def opening + opening_loc&.slice + end + + # def closing: () -> String? + def closing + closing_loc&.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :block_parameters_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :block_parameters_node + 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?(BlockParametersNode) && + (parameters === other.parameters) && + (locals.length == other.locals.length) && + locals.zip(other.locals).all? { |left, right| left === right } && + (opening_loc.nil? == other.opening_loc.nil?) && + (closing_loc.nil? == other.closing_loc.nil?) + end + end + + # Represents the use of the `break` keyword. + # + # break foo + # ^^^^^^^^^ + class BreakNode < Node + # Initialize a new BreakNode node. + def initialize(source, node_id, location, flags, arguments, keyword_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @arguments = arguments + @keyword_loc = keyword_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_break_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [arguments] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield arguments if arguments + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << arguments if arguments + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [*arguments, keyword_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?arguments: ArgumentsNode?, ?keyword_loc: Location) -> BreakNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, arguments: self.arguments, keyword_loc: self.keyword_loc) + BreakNode.new(source, node_id, location, flags, arguments, keyword_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, arguments: ArgumentsNode?, keyword_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, arguments: arguments, keyword_loc: keyword_loc } + end + + # 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 + # ^^^ + attr_reader :arguments + + # The location of the `break` keyword. + # + # break foo + # ^^^^^ + def keyword_loc + location = @keyword_loc + return location if location.is_a?(Location) + @keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_keyword_loc(repository) + repository.enter(node_id, :keyword_loc) + end + + # def keyword: () -> String + def keyword + keyword_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :break_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :break_node + 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?(BreakNode) && + (arguments === other.arguments) && + (keyword_loc.nil? == other.keyword_loc.nil?) + end + end + + # Represents the use of the `&&=` operator on a call. + # + # foo.bar &&= value + # ^^^^^^^^^^^^^^^^^ + class CallAndWriteNode < Node + # Initialize a new CallAndWriteNode node. + def initialize(source, node_id, location, flags, receiver, call_operator_loc, message_loc, read_name, write_name, operator_loc, value) + @source = source + @node_id = node_id + @location = location + @flags = flags + @receiver = receiver + @call_operator_loc = call_operator_loc + @message_loc = message_loc + @read_name = read_name + @write_name = write_name + @operator_loc = operator_loc + @value = value + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_call_and_write_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [receiver, value] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield receiver if receiver + yield value + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << receiver if receiver + compact << value + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [*receiver, *call_operator_loc, *message_loc, operator_loc, value] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?receiver: Prism::node?, ?call_operator_loc: Location?, ?message_loc: Location?, ?read_name: Symbol, ?write_name: Symbol, ?operator_loc: Location, ?value: Prism::node) -> CallAndWriteNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, receiver: self.receiver, call_operator_loc: self.call_operator_loc, message_loc: self.message_loc, read_name: self.read_name, write_name: self.write_name, operator_loc: self.operator_loc, value: self.value) + CallAndWriteNode.new(source, node_id, location, flags, receiver, call_operator_loc, message_loc, read_name, write_name, operator_loc, value) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, receiver: Prism::node?, call_operator_loc: Location?, message_loc: Location?, read_name: Symbol, write_name: Symbol, operator_loc: Location, value: Prism::node } + def deconstruct_keys(keys) + { node_id: node_id, location: location, receiver: receiver, call_operator_loc: call_operator_loc, message_loc: message_loc, read_name: read_name, write_name: write_name, operator_loc: operator_loc, value: value } + end + + # def safe_navigation?: () -> bool + def safe_navigation? + flags.anybits?(CallNodeFlags::SAFE_NAVIGATION) + end + + # def variable_call?: () -> bool + def variable_call? + flags.anybits?(CallNodeFlags::VARIABLE_CALL) + end + + # def attribute_write?: () -> bool + def attribute_write? + flags.anybits?(CallNodeFlags::ATTRIBUTE_WRITE) + end + + # def ignore_visibility?: () -> bool + def ignore_visibility? + flags.anybits?(CallNodeFlags::IGNORE_VISIBILITY) + end + + # 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 &&= value + # ^^^ + attr_reader :receiver + + # Represents the location of the call operator. + # + # foo.bar &&= value + # ^ + def call_operator_loc + location = @call_operator_loc + case location + when nil + nil + when Location + location + else + @call_operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the call_operator_loc location using the given saved source so that + # it can be retrieved later. + def save_call_operator_loc(repository) + repository.enter(node_id, :call_operator_loc) unless @call_operator_loc.nil? + end + + # Represents the location of the message. + # + # foo.bar &&= value + # ^^^ + def message_loc + location = @message_loc + case location + when nil + nil + when Location + location + else + @message_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the message_loc location using the given saved source so that + # it can be retrieved later. + def save_message_loc(repository) + repository.enter(node_id, :message_loc) unless @message_loc.nil? + end + + # Represents the name of the method being called. + # + # foo.bar &&= value # read_name `:bar` + # ^^^ + attr_reader :read_name + + # Represents the name of the method being written to. + # + # foo.bar &&= value # write_name `:bar=` + # ^^^ + attr_reader :write_name + + # Represents the location of the operator. + # + # foo.bar &&= value + # ^^^ + def operator_loc + location = @operator_loc + return location if location.is_a?(Location) + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) + end + + # Represents the value being assigned. + # + # foo.bar &&= value + # ^^^^^ + attr_reader :value + + # def call_operator: () -> String? + def call_operator + call_operator_loc&.slice + end + + # def message: () -> String? + def message + message_loc&.slice + end + + # def operator: () -> String + def operator + operator_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :call_and_write_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :call_and_write_node + 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?(CallAndWriteNode) && + (flags === other.flags) && + (receiver === other.receiver) && + (call_operator_loc.nil? == other.call_operator_loc.nil?) && + (message_loc.nil? == other.message_loc.nil?) && + (read_name === other.read_name) && + (write_name === other.write_name) && + (operator_loc.nil? == other.operator_loc.nil?) && + (value === other.value) + end + end + + # Represents a method call, in all of the various forms that can take. + # + # foo + # ^^^ + # + # foo() + # ^^^^^ + # + # +foo + # ^^^^ + # + # foo + bar + # ^^^^^^^^^ + # + # foo.bar + # ^^^^^^^ + # + # foo&.bar + # ^^^^^^^^ + class CallNode < Node + # Initialize a new CallNode node. + def initialize(source, node_id, location, flags, receiver, call_operator_loc, name, message_loc, opening_loc, arguments, closing_loc, equal_loc, block) + @source = source + @node_id = node_id + @location = location + @flags = flags + @receiver = receiver + @call_operator_loc = call_operator_loc + @name = name + @message_loc = message_loc + @opening_loc = opening_loc + @arguments = arguments + @closing_loc = closing_loc + @equal_loc = equal_loc + @block = block + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_call_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [receiver, arguments, block] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield receiver if receiver + yield arguments if arguments + yield block if block + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << receiver if receiver + compact << arguments if arguments + compact << block if block + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [*receiver, *call_operator_loc, *message_loc, *opening_loc, *arguments, *closing_loc, *equal_loc, *block] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?receiver: Prism::node?, ?call_operator_loc: Location?, ?name: Symbol, ?message_loc: Location?, ?opening_loc: Location?, ?arguments: ArgumentsNode?, ?closing_loc: Location?, ?equal_loc: Location?, ?block: BlockNode | BlockArgumentNode | nil) -> CallNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, receiver: self.receiver, call_operator_loc: self.call_operator_loc, name: self.name, message_loc: self.message_loc, opening_loc: self.opening_loc, arguments: self.arguments, closing_loc: self.closing_loc, equal_loc: self.equal_loc, block: self.block) + CallNode.new(source, node_id, location, flags, receiver, call_operator_loc, name, message_loc, opening_loc, arguments, closing_loc, equal_loc, block) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, receiver: Prism::node?, call_operator_loc: Location?, name: Symbol, message_loc: Location?, opening_loc: Location?, arguments: ArgumentsNode?, closing_loc: Location?, equal_loc: Location?, block: BlockNode | BlockArgumentNode | nil } + def deconstruct_keys(keys) + { node_id: node_id, location: location, receiver: receiver, call_operator_loc: call_operator_loc, name: name, message_loc: message_loc, opening_loc: opening_loc, arguments: arguments, closing_loc: closing_loc, equal_loc: equal_loc, block: block } + end + + # def safe_navigation?: () -> bool + def safe_navigation? + flags.anybits?(CallNodeFlags::SAFE_NAVIGATION) + end + + # def variable_call?: () -> bool + def variable_call? + flags.anybits?(CallNodeFlags::VARIABLE_CALL) + end + + # def attribute_write?: () -> bool + def attribute_write? + flags.anybits?(CallNodeFlags::ATTRIBUTE_WRITE) + end + + # def ignore_visibility?: () -> bool + def ignore_visibility? + flags.anybits?(CallNodeFlags::IGNORE_VISIBILITY) + end + + # 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 + # ^^^ + attr_reader :receiver + + # Represents the location of the call operator. + # + # foo.bar + # ^ + # + # foo&.bar + # ^^ + def call_operator_loc + location = @call_operator_loc + case location + when nil + nil + when Location + location + else + @call_operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the call_operator_loc location using the given saved source so that + # it can be retrieved later. + def save_call_operator_loc(repository) + repository.enter(node_id, :call_operator_loc) unless @call_operator_loc.nil? + end + + # Represents the name of the method being called. + # + # foo.bar # name `:foo` + # ^^^ + attr_reader :name + + # Represents the location of the message. + # + # foo.bar + # ^^^ + def message_loc + location = @message_loc + case location + when nil + nil + when Location + location + else + @message_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the message_loc location using the given saved source so that + # it can be retrieved later. + def save_message_loc(repository) + repository.enter(node_id, :message_loc) unless @message_loc.nil? + end + + # Represents the location of the left parenthesis. + # foo(bar) + # ^ + def opening_loc + location = @opening_loc + case location + when nil + nil + when Location + location + else + @opening_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + def save_opening_loc(repository) + repository.enter(node_id, :opening_loc) unless @opening_loc.nil? + end + + # Represents the arguments to the method call. These can be any [non-void expressions](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + # + # foo(bar) + # ^^^ + attr_reader :arguments + + # Represents the location of the right parenthesis. + # + # foo(bar) + # ^ + def closing_loc + location = @closing_loc + case location + when nil + nil + when Location + location + else + @closing_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + def save_closing_loc(repository) + repository.enter(node_id, :closing_loc) unless @closing_loc.nil? + end + + # Represents the location of the equal sign, in the case that this is an attribute write. + # + # foo.bar = value + # ^ + # + # foo[bar] = value + # ^ + def equal_loc + location = @equal_loc + case location + when nil + nil + when Location + location + else + @equal_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the equal_loc location using the given saved source so that + # it can be retrieved later. + def save_equal_loc(repository) + repository.enter(node_id, :equal_loc) unless @equal_loc.nil? + end + + # Represents the block that is being passed to the method. + # + # foo { |a| a } + # ^^^^^^^^^ + attr_reader :block + + # def call_operator: () -> String? + def call_operator + call_operator_loc&.slice + end + + # def message: () -> String? + def message + message_loc&.slice + end + + # def opening: () -> String? + def opening + opening_loc&.slice + end + + # def closing: () -> String? + def closing + closing_loc&.slice + end + + # def equal: () -> String? + def equal + equal_loc&.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :call_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :call_node + 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?(CallNode) && + (flags === other.flags) && + (receiver === other.receiver) && + (call_operator_loc.nil? == other.call_operator_loc.nil?) && + (name === other.name) && + (message_loc.nil? == other.message_loc.nil?) && + (opening_loc.nil? == other.opening_loc.nil?) && + (arguments === other.arguments) && + (closing_loc.nil? == other.closing_loc.nil?) && + (equal_loc.nil? == other.equal_loc.nil?) && + (block === other.block) + end + end + + # Represents the use of an assignment operator on a call. + # + # foo.bar += baz + # ^^^^^^^^^^^^^^ + class CallOperatorWriteNode < Node + # Initialize a new CallOperatorWriteNode node. + def initialize(source, node_id, location, flags, receiver, call_operator_loc, message_loc, read_name, write_name, binary_operator, binary_operator_loc, value) + @source = source + @node_id = node_id + @location = location + @flags = flags + @receiver = receiver + @call_operator_loc = call_operator_loc + @message_loc = message_loc + @read_name = read_name + @write_name = write_name + @binary_operator = binary_operator + @binary_operator_loc = binary_operator_loc + @value = value + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_call_operator_write_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [receiver, value] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield receiver if receiver + yield value + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << receiver if receiver + compact << value + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [*receiver, *call_operator_loc, *message_loc, binary_operator_loc, value] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?receiver: Prism::node?, ?call_operator_loc: Location?, ?message_loc: Location?, ?read_name: Symbol, ?write_name: Symbol, ?binary_operator: Symbol, ?binary_operator_loc: Location, ?value: Prism::node) -> CallOperatorWriteNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, receiver: self.receiver, call_operator_loc: self.call_operator_loc, message_loc: self.message_loc, read_name: self.read_name, write_name: self.write_name, binary_operator: self.binary_operator, binary_operator_loc: self.binary_operator_loc, value: self.value) + CallOperatorWriteNode.new(source, node_id, location, flags, receiver, call_operator_loc, message_loc, read_name, write_name, binary_operator, binary_operator_loc, value) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, receiver: Prism::node?, call_operator_loc: Location?, message_loc: Location?, read_name: Symbol, write_name: Symbol, binary_operator: Symbol, binary_operator_loc: Location, value: Prism::node } + def deconstruct_keys(keys) + { node_id: node_id, location: location, receiver: receiver, call_operator_loc: call_operator_loc, message_loc: message_loc, read_name: read_name, write_name: write_name, binary_operator: binary_operator, binary_operator_loc: binary_operator_loc, value: value } + end + + # def safe_navigation?: () -> bool + def safe_navigation? + flags.anybits?(CallNodeFlags::SAFE_NAVIGATION) + end + + # def variable_call?: () -> bool + def variable_call? + flags.anybits?(CallNodeFlags::VARIABLE_CALL) + end + + # def attribute_write?: () -> bool + def attribute_write? + flags.anybits?(CallNodeFlags::ATTRIBUTE_WRITE) + end + + # def ignore_visibility?: () -> bool + def ignore_visibility? + flags.anybits?(CallNodeFlags::IGNORE_VISIBILITY) + end + + # The object that the method is being called on. This can be either `nil` or any [non-void expressions](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + # + # foo.bar += value + # ^^^ + attr_reader :receiver + + # Represents the location of the call operator. + # + # foo.bar += value + # ^ + def call_operator_loc + location = @call_operator_loc + case location + when nil + nil + when Location + location + else + @call_operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the call_operator_loc location using the given saved source so that + # it can be retrieved later. + def save_call_operator_loc(repository) + repository.enter(node_id, :call_operator_loc) unless @call_operator_loc.nil? + end + + # Represents the location of the message. + # + # foo.bar += value + # ^^^ + def message_loc + location = @message_loc + case location + when nil + nil + when Location + location + else + @message_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the message_loc location using the given saved source so that + # it can be retrieved later. + def save_message_loc(repository) + repository.enter(node_id, :message_loc) unless @message_loc.nil? + end + + # Represents the name of the method being called. + # + # foo.bar += value # read_name `:bar` + # ^^^ + attr_reader :read_name + + # Represents the name of the method being written to. + # + # foo.bar += value # write_name `:bar=` + # ^^^ + attr_reader :write_name + + # Represents the binary operator being used. + # + # foo.bar += value # binary_operator `:+` + # ^ + attr_reader :binary_operator + + # Represents the location of the binary operator. + # + # foo.bar += value + # ^^ + def binary_operator_loc + location = @binary_operator_loc + return location if location.is_a?(Location) + @binary_operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the binary_operator_loc location using the given saved source so that + # it can be retrieved later. + def save_binary_operator_loc(repository) + repository.enter(node_id, :binary_operator_loc) + end + + # Represents the value being assigned. + # + # foo.bar += value + # ^^^^^ + attr_reader :value + + # def call_operator: () -> String? + def call_operator + call_operator_loc&.slice + end + + # def message: () -> String? + def message + message_loc&.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :call_operator_write_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :call_operator_write_node + 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?(CallOperatorWriteNode) && + (flags === other.flags) && + (receiver === other.receiver) && + (call_operator_loc.nil? == other.call_operator_loc.nil?) && + (message_loc.nil? == other.message_loc.nil?) && + (read_name === other.read_name) && + (write_name === other.write_name) && + (binary_operator === other.binary_operator) && + (binary_operator_loc.nil? == other.binary_operator_loc.nil?) && + (value === other.value) + end + end + + # Represents the use of the `||=` operator on a call. + # + # foo.bar ||= value + # ^^^^^^^^^^^^^^^^^ + class CallOrWriteNode < Node + # Initialize a new CallOrWriteNode node. + def initialize(source, node_id, location, flags, receiver, call_operator_loc, message_loc, read_name, write_name, operator_loc, value) + @source = source + @node_id = node_id + @location = location + @flags = flags + @receiver = receiver + @call_operator_loc = call_operator_loc + @message_loc = message_loc + @read_name = read_name + @write_name = write_name + @operator_loc = operator_loc + @value = value + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_call_or_write_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [receiver, value] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield receiver if receiver + yield value + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << receiver if receiver + compact << value + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [*receiver, *call_operator_loc, *message_loc, operator_loc, value] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?receiver: Prism::node?, ?call_operator_loc: Location?, ?message_loc: Location?, ?read_name: Symbol, ?write_name: Symbol, ?operator_loc: Location, ?value: Prism::node) -> CallOrWriteNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, receiver: self.receiver, call_operator_loc: self.call_operator_loc, message_loc: self.message_loc, read_name: self.read_name, write_name: self.write_name, operator_loc: self.operator_loc, value: self.value) + CallOrWriteNode.new(source, node_id, location, flags, receiver, call_operator_loc, message_loc, read_name, write_name, operator_loc, value) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, receiver: Prism::node?, call_operator_loc: Location?, message_loc: Location?, read_name: Symbol, write_name: Symbol, operator_loc: Location, value: Prism::node } + def deconstruct_keys(keys) + { node_id: node_id, location: location, receiver: receiver, call_operator_loc: call_operator_loc, message_loc: message_loc, read_name: read_name, write_name: write_name, operator_loc: operator_loc, value: value } + end + + # def safe_navigation?: () -> bool + def safe_navigation? + flags.anybits?(CallNodeFlags::SAFE_NAVIGATION) + end + + # def variable_call?: () -> bool + def variable_call? + flags.anybits?(CallNodeFlags::VARIABLE_CALL) + end + + # def attribute_write?: () -> bool + def attribute_write? + flags.anybits?(CallNodeFlags::ATTRIBUTE_WRITE) + end + + # def ignore_visibility?: () -> bool + def ignore_visibility? + flags.anybits?(CallNodeFlags::IGNORE_VISIBILITY) + end + + # The object that the method is being called on. This can be either `nil` or any [non-void expressions](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + # + # foo.bar ||= value + # ^^^ + attr_reader :receiver + + # Represents the location of the call operator. + # + # foo.bar ||= value + # ^ + def call_operator_loc + location = @call_operator_loc + case location + when nil + nil + when Location + location + else + @call_operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the call_operator_loc location using the given saved source so that + # it can be retrieved later. + def save_call_operator_loc(repository) + repository.enter(node_id, :call_operator_loc) unless @call_operator_loc.nil? + end + + # Represents the location of the message. + # + # foo.bar ||= value + # ^^^ + def message_loc + location = @message_loc + case location + when nil + nil + when Location + location + else + @message_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the message_loc location using the given saved source so that + # it can be retrieved later. + def save_message_loc(repository) + repository.enter(node_id, :message_loc) unless @message_loc.nil? + end + + # Represents the name of the method being called. + # + # foo.bar ||= value # read_name `:bar` + # ^^^ + attr_reader :read_name + + # Represents the name of the method being written to. + # + # foo.bar ||= value # write_name `:bar=` + # ^^^ + attr_reader :write_name + + # Represents the location of the operator. + # + # foo.bar ||= value + # ^^^ + def operator_loc + location = @operator_loc + return location if location.is_a?(Location) + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) + end + + # Represents the value being assigned. + # + # foo.bar ||= value + # ^^^^^ + attr_reader :value + + # def call_operator: () -> String? + def call_operator + call_operator_loc&.slice + end + + # def message: () -> String? + def message + message_loc&.slice + end + + # def operator: () -> String + def operator + operator_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :call_or_write_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :call_or_write_node + 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?(CallOrWriteNode) && + (flags === other.flags) && + (receiver === other.receiver) && + (call_operator_loc.nil? == other.call_operator_loc.nil?) && + (message_loc.nil? == other.message_loc.nil?) && + (read_name === other.read_name) && + (write_name === other.write_name) && + (operator_loc.nil? == other.operator_loc.nil?) && + (value === other.value) + end + end + + # Represents assigning to a method call. + # + # foo.bar, = 1 + # ^^^^^^^ + # + # begin + # rescue => foo.bar + # ^^^^^^^ + # end + # + # for foo.bar in baz do end + # ^^^^^^^ + class CallTargetNode < Node + # Initialize a new CallTargetNode node. + def initialize(source, node_id, location, flags, receiver, call_operator_loc, name, message_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @receiver = receiver + @call_operator_loc = call_operator_loc + @name = name + @message_loc = message_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_call_target_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [receiver] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield receiver + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [receiver] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [receiver, call_operator_loc, message_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?receiver: Prism::node, ?call_operator_loc: Location, ?name: Symbol, ?message_loc: Location) -> CallTargetNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, receiver: self.receiver, call_operator_loc: self.call_operator_loc, name: self.name, message_loc: self.message_loc) + CallTargetNode.new(source, node_id, location, flags, receiver, call_operator_loc, name, message_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, receiver: Prism::node, call_operator_loc: Location, name: Symbol, message_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, receiver: receiver, call_operator_loc: call_operator_loc, name: name, message_loc: message_loc } + end + + # def safe_navigation?: () -> bool + def safe_navigation? + flags.anybits?(CallNodeFlags::SAFE_NAVIGATION) + end + + # def variable_call?: () -> bool + def variable_call? + flags.anybits?(CallNodeFlags::VARIABLE_CALL) + end + + # def attribute_write?: () -> bool + def attribute_write? + flags.anybits?(CallNodeFlags::ATTRIBUTE_WRITE) + end + + # def ignore_visibility?: () -> bool + def ignore_visibility? + flags.anybits?(CallNodeFlags::IGNORE_VISIBILITY) + end + + # The object that the method is being called on. This can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + # + # foo.bar = 1 + # ^^^ + attr_reader :receiver + + # Represents the location of the call operator. + # + # foo.bar = 1 + # ^ + def call_operator_loc + location = @call_operator_loc + return location if location.is_a?(Location) + @call_operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the call_operator_loc location using the given saved source so that + # it can be retrieved later. + def save_call_operator_loc(repository) + repository.enter(node_id, :call_operator_loc) + end + + # Represents the name of the method being called. + # + # foo.bar = 1 # name `:foo` + # ^^^ + attr_reader :name + + # Represents the location of the message. + # + # foo.bar = 1 + # ^^^ + def message_loc + location = @message_loc + return location if location.is_a?(Location) + @message_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the message_loc location using the given saved source so that + # it can be retrieved later. + def save_message_loc(repository) + repository.enter(node_id, :message_loc) + end + + # def call_operator: () -> String + def call_operator + call_operator_loc.slice + end + + # def message: () -> String + def message + message_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :call_target_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :call_target_node + 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?(CallTargetNode) && + (flags === other.flags) && + (receiver === other.receiver) && + (call_operator_loc.nil? == other.call_operator_loc.nil?) && + (name === other.name) && + (message_loc.nil? == other.message_loc.nil?) + end + end + + # Represents assigning to a local variable in pattern matching. + # + # foo => [bar => baz] + # ^^^^^^^^^^^^ + class CapturePatternNode < Node + # Initialize a new CapturePatternNode node. + def initialize(source, node_id, location, flags, value, target, operator_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @value = value + @target = target + @operator_loc = operator_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_capture_pattern_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [value, target] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield value + yield target + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [value, target] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [value, target, operator_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?value: Prism::node, ?target: LocalVariableTargetNode, ?operator_loc: Location) -> CapturePatternNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, value: self.value, target: self.target, operator_loc: self.operator_loc) + CapturePatternNode.new(source, node_id, location, flags, value, target, operator_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, value: Prism::node, target: LocalVariableTargetNode, operator_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, value: value, target: target, operator_loc: operator_loc } + end + + # Represents the value to capture. + # + # foo => bar + # ^^^ + attr_reader :value + + # Represents the target of the capture. + # + # foo => bar + # ^^^ + attr_reader :target + + # Represents the location of the `=>` operator. + # + # foo => bar + # ^^ + def operator_loc + location = @operator_loc + return location if location.is_a?(Location) + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) + end + + # def operator: () -> String + def operator + operator_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :capture_pattern_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :capture_pattern_node + 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?(CapturePatternNode) && + (value === other.value) && + (target === other.target) && + (operator_loc.nil? == other.operator_loc.nil?) + end + end + + # Represents the use of a case statement for pattern matching. + # + # case true + # in false + # end + # ^^^^^^^^^ + class CaseMatchNode < Node + # Initialize a new CaseMatchNode node. + def initialize(source, node_id, location, flags, predicate, conditions, else_clause, case_keyword_loc, end_keyword_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @predicate = predicate + @conditions = conditions + @else_clause = else_clause + @case_keyword_loc = case_keyword_loc + @end_keyword_loc = end_keyword_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_case_match_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [predicate, *conditions, else_clause] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield predicate if predicate + conditions.each { |node| yield node } + yield else_clause if else_clause + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << predicate if predicate + compact.concat(conditions) + compact << else_clause if else_clause + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [*predicate, *conditions, *else_clause, case_keyword_loc, end_keyword_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?predicate: Prism::node?, ?conditions: Array[InNode], ?else_clause: ElseNode?, ?case_keyword_loc: Location, ?end_keyword_loc: Location) -> CaseMatchNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, predicate: self.predicate, conditions: self.conditions, else_clause: self.else_clause, case_keyword_loc: self.case_keyword_loc, end_keyword_loc: self.end_keyword_loc) + CaseMatchNode.new(source, node_id, location, flags, predicate, conditions, else_clause, case_keyword_loc, end_keyword_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, predicate: Prism::node?, conditions: Array[InNode], else_clause: ElseNode?, case_keyword_loc: Location, end_keyword_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, predicate: predicate, conditions: conditions, else_clause: else_clause, case_keyword_loc: case_keyword_loc, end_keyword_loc: end_keyword_loc } + end + + # Represents the predicate of the case match. This can be either `nil` or any [non-void expressions](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + # + # case true; in false; end + # ^^^^ + attr_reader :predicate + + # Represents the conditions of the case match. + # + # case true; in false; end + # ^^^^^^^^ + attr_reader :conditions + + # Represents the else clause of the case match. + # + # case true; in false; else; end + # ^^^^ + attr_reader :else_clause + + # Represents the location of the `case` keyword. + # + # case true; in false; end + # ^^^^ + def case_keyword_loc + location = @case_keyword_loc + return location if location.is_a?(Location) + @case_keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the case_keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_case_keyword_loc(repository) + repository.enter(node_id, :case_keyword_loc) + end + + # Represents the location of the `end` keyword. + # + # case true; in false; end + # ^^^ + def end_keyword_loc + location = @end_keyword_loc + return location if location.is_a?(Location) + @end_keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the end_keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_end_keyword_loc(repository) + repository.enter(node_id, :end_keyword_loc) + end + + # def case_keyword: () -> String + def case_keyword + case_keyword_loc.slice + end + + # def end_keyword: () -> String + def end_keyword + end_keyword_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :case_match_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :case_match_node + 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?(CaseMatchNode) && + (predicate === other.predicate) && + (conditions.length == other.conditions.length) && + conditions.zip(other.conditions).all? { |left, right| left === right } && + (else_clause === other.else_clause) && + (case_keyword_loc.nil? == other.case_keyword_loc.nil?) && + (end_keyword_loc.nil? == other.end_keyword_loc.nil?) + end + end + + # Represents the use of a case statement. + # + # case true + # when false + # end + # ^^^^^^^^^^ + class CaseNode < Node + # Initialize a new CaseNode node. + def initialize(source, node_id, location, flags, predicate, conditions, else_clause, case_keyword_loc, end_keyword_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @predicate = predicate + @conditions = conditions + @else_clause = else_clause + @case_keyword_loc = case_keyword_loc + @end_keyword_loc = end_keyword_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_case_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [predicate, *conditions, else_clause] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield predicate if predicate + conditions.each { |node| yield node } + yield else_clause if else_clause + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << predicate if predicate + compact.concat(conditions) + compact << else_clause if else_clause + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [*predicate, *conditions, *else_clause, case_keyword_loc, end_keyword_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?predicate: Prism::node?, ?conditions: Array[WhenNode], ?else_clause: ElseNode?, ?case_keyword_loc: Location, ?end_keyword_loc: Location) -> CaseNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, predicate: self.predicate, conditions: self.conditions, else_clause: self.else_clause, case_keyword_loc: self.case_keyword_loc, end_keyword_loc: self.end_keyword_loc) + CaseNode.new(source, node_id, location, flags, predicate, conditions, else_clause, case_keyword_loc, end_keyword_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, predicate: Prism::node?, conditions: Array[WhenNode], else_clause: ElseNode?, case_keyword_loc: Location, end_keyword_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, predicate: predicate, conditions: conditions, else_clause: else_clause, case_keyword_loc: case_keyword_loc, end_keyword_loc: end_keyword_loc } + end + + # Represents the predicate of the case statement. This can be either `nil` or any [non-void expressions](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + # + # case true; when false; end + # ^^^^ + attr_reader :predicate + + # Represents the conditions of the case statement. + # + # case true; when false; end + # ^^^^^^^^^^ + attr_reader :conditions + + # Represents the else clause of the case statement. + # + # case true; when false; else; end + # ^^^^ + attr_reader :else_clause + + # Represents the location of the `case` keyword. + # + # case true; when false; end + # ^^^^ + def case_keyword_loc + location = @case_keyword_loc + return location if location.is_a?(Location) + @case_keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the case_keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_case_keyword_loc(repository) + repository.enter(node_id, :case_keyword_loc) + end + + # Represents the location of the `end` keyword. + # + # case true; when false; end + # ^^^ + def end_keyword_loc + location = @end_keyword_loc + return location if location.is_a?(Location) + @end_keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the end_keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_end_keyword_loc(repository) + repository.enter(node_id, :end_keyword_loc) + end + + # def case_keyword: () -> String + def case_keyword + case_keyword_loc.slice + end + + # def end_keyword: () -> String + def end_keyword + end_keyword_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :case_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :case_node + 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?(CaseNode) && + (predicate === other.predicate) && + (conditions.length == other.conditions.length) && + conditions.zip(other.conditions).all? { |left, right| left === right } && + (else_clause === other.else_clause) && + (case_keyword_loc.nil? == other.case_keyword_loc.nil?) && + (end_keyword_loc.nil? == other.end_keyword_loc.nil?) + end + end + + # Represents a class declaration involving the `class` keyword. + # + # class Foo end + # ^^^^^^^^^^^^^ + class ClassNode < Node + # Initialize a new ClassNode node. + def initialize(source, node_id, location, flags, locals, class_keyword_loc, constant_path, inheritance_operator_loc, superclass, body, end_keyword_loc, name) + @source = source + @node_id = node_id + @location = location + @flags = flags + @locals = locals + @class_keyword_loc = class_keyword_loc + @constant_path = constant_path + @inheritance_operator_loc = inheritance_operator_loc + @superclass = superclass + @body = body + @end_keyword_loc = end_keyword_loc + @name = name + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_class_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [constant_path, superclass, body] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield constant_path + yield superclass if superclass + yield body if body + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << constant_path + compact << superclass if superclass + compact << body if body + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [class_keyword_loc, constant_path, *inheritance_operator_loc, *superclass, *body, end_keyword_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?locals: Array[Symbol], ?class_keyword_loc: Location, ?constant_path: ConstantReadNode | ConstantPathNode | CallNode, ?inheritance_operator_loc: Location?, ?superclass: Prism::node?, ?body: StatementsNode | BeginNode | nil, ?end_keyword_loc: Location, ?name: Symbol) -> ClassNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, locals: self.locals, class_keyword_loc: self.class_keyword_loc, constant_path: self.constant_path, inheritance_operator_loc: self.inheritance_operator_loc, superclass: self.superclass, body: self.body, end_keyword_loc: self.end_keyword_loc, name: self.name) + ClassNode.new(source, node_id, location, flags, locals, class_keyword_loc, constant_path, inheritance_operator_loc, superclass, body, end_keyword_loc, name) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, locals: Array[Symbol], class_keyword_loc: Location, constant_path: ConstantReadNode | ConstantPathNode | CallNode, inheritance_operator_loc: Location?, superclass: Prism::node?, body: StatementsNode | BeginNode | nil, end_keyword_loc: Location, name: Symbol } + def deconstruct_keys(keys) + { node_id: node_id, location: location, locals: locals, class_keyword_loc: class_keyword_loc, constant_path: constant_path, inheritance_operator_loc: inheritance_operator_loc, superclass: superclass, body: body, end_keyword_loc: end_keyword_loc, name: name } + end + + # attr_reader locals: Array[Symbol] + attr_reader :locals + + # Represents the location of the `class` keyword. + # + # class Foo end + # ^^^^^ + def class_keyword_loc + location = @class_keyword_loc + return location if location.is_a?(Location) + @class_keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the class_keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_class_keyword_loc(repository) + repository.enter(node_id, :class_keyword_loc) + end + + # attr_reader constant_path: ConstantReadNode | ConstantPathNode | CallNode + attr_reader :constant_path + + # Represents the location of the `<` operator. + # + # class Foo < Bar + # ^ + def inheritance_operator_loc + location = @inheritance_operator_loc + case location + when nil + nil + when Location + location + else + @inheritance_operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the inheritance_operator_loc location using the given saved source so that + # it can be retrieved later. + def save_inheritance_operator_loc(repository) + repository.enter(node_id, :inheritance_operator_loc) unless @inheritance_operator_loc.nil? + end + + # Represents the superclass of the class. + # + # class Foo < Bar + # ^^^ + attr_reader :superclass + + # Represents the body of the class. + # + # class Foo + # foo + # ^^^ + attr_reader :body + + # Represents the location of the `end` keyword. + # + # class Foo end + # ^^^ + def end_keyword_loc + location = @end_keyword_loc + return location if location.is_a?(Location) + @end_keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the end_keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_end_keyword_loc(repository) + repository.enter(node_id, :end_keyword_loc) + end + + # The name of the class. + # + # class Foo end # name `:Foo` + attr_reader :name + + # def class_keyword: () -> String + def class_keyword + class_keyword_loc.slice + end + + # def inheritance_operator: () -> String? + def inheritance_operator + inheritance_operator_loc&.slice + end + + # def end_keyword: () -> String + def end_keyword + end_keyword_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :class_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :class_node + 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?(ClassNode) && + (locals.length == other.locals.length) && + locals.zip(other.locals).all? { |left, right| left === right } && + (class_keyword_loc.nil? == other.class_keyword_loc.nil?) && + (constant_path === other.constant_path) && + (inheritance_operator_loc.nil? == other.inheritance_operator_loc.nil?) && + (superclass === other.superclass) && + (body === other.body) && + (end_keyword_loc.nil? == other.end_keyword_loc.nil?) && + (name === other.name) + end + end + + # Represents the use of the `&&=` operator for assignment to a class variable. + # + # @@target &&= value + # ^^^^^^^^^^^^^^^^^^ + class ClassVariableAndWriteNode < Node + # Initialize a new ClassVariableAndWriteNode node. + def initialize(source, node_id, location, flags, name, name_loc, operator_loc, value) + @source = source + @node_id = node_id + @location = location + @flags = flags + @name = name + @name_loc = name_loc + @operator_loc = operator_loc + @value = value + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_class_variable_and_write_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [value] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield value + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [value] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [name_loc, operator_loc, value] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> ClassVariableAndWriteNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc, operator_loc: self.operator_loc, value: self.value) + ClassVariableAndWriteNode.new(source, node_id, location, flags, name, name_loc, operator_loc, value) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, name_loc: Location, operator_loc: Location, value: Prism::node } + def deconstruct_keys(keys) + { node_id: node_id, location: location, name: name, name_loc: name_loc, operator_loc: operator_loc, value: value } + end + + # 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). + # + # @@target &&= value # name `:@@target` + # ^^^^^^^^ + attr_reader :name + + # Represents the location of the variable name. + # + # @@target &&= value + # ^^^^^^^^ + def name_loc + location = @name_loc + return location if location.is_a?(Location) + @name_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + def save_name_loc(repository) + repository.enter(node_id, :name_loc) + end + + # Represents the location of the `&&=` operator. + # + # @@target &&= value + # ^^^ + def operator_loc + location = @operator_loc + return location if location.is_a?(Location) + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) + end + + # Represents the value being assigned. This can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + # + # @@target &&= value + # ^^^^^ + attr_reader :value + + # def operator: () -> String + def operator + operator_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :class_variable_and_write_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :class_variable_and_write_node + 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?(ClassVariableAndWriteNode) && + (name === other.name) && + (name_loc.nil? == other.name_loc.nil?) && + (operator_loc.nil? == other.operator_loc.nil?) && + (value === other.value) + end + end + + # Represents assigning to a class variable using an operator that isn't `=`. + # + # @@target += value + # ^^^^^^^^^^^^^^^^^ + class ClassVariableOperatorWriteNode < Node + # Initialize a new ClassVariableOperatorWriteNode node. + def initialize(source, node_id, location, flags, name, name_loc, binary_operator_loc, value, binary_operator) + @source = source + @node_id = node_id + @location = location + @flags = flags + @name = name + @name_loc = name_loc + @binary_operator_loc = binary_operator_loc + @value = value + @binary_operator = binary_operator + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_class_variable_operator_write_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [value] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield value + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [value] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [name_loc, binary_operator_loc, value] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?binary_operator_loc: Location, ?value: Prism::node, ?binary_operator: Symbol) -> ClassVariableOperatorWriteNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc, binary_operator_loc: self.binary_operator_loc, value: self.value, binary_operator: self.binary_operator) + ClassVariableOperatorWriteNode.new(source, node_id, location, flags, name, name_loc, binary_operator_loc, value, binary_operator) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, name_loc: Location, binary_operator_loc: Location, value: Prism::node, binary_operator: Symbol } + def deconstruct_keys(keys) + { node_id: node_id, location: location, name: name, name_loc: name_loc, binary_operator_loc: binary_operator_loc, value: value, binary_operator: binary_operator } + end + + # attr_reader name: Symbol + attr_reader :name + + # attr_reader name_loc: Location + def name_loc + location = @name_loc + return location if location.is_a?(Location) + @name_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + def save_name_loc(repository) + repository.enter(node_id, :name_loc) + end + + # attr_reader binary_operator_loc: Location + def binary_operator_loc + location = @binary_operator_loc + return location if location.is_a?(Location) + @binary_operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the binary_operator_loc location using the given saved source so that + # it can be retrieved later. + def save_binary_operator_loc(repository) + repository.enter(node_id, :binary_operator_loc) + end + + # attr_reader value: Prism::node + attr_reader :value + + # attr_reader binary_operator: Symbol + attr_reader :binary_operator + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :class_variable_operator_write_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :class_variable_operator_write_node + 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?(ClassVariableOperatorWriteNode) && + (name === other.name) && + (name_loc.nil? == other.name_loc.nil?) && + (binary_operator_loc.nil? == other.binary_operator_loc.nil?) && + (value === other.value) && + (binary_operator === other.binary_operator) + end + end + + # Represents the use of the `||=` operator for assignment to a class variable. + # + # @@target ||= value + # ^^^^^^^^^^^^^^^^^^ + class ClassVariableOrWriteNode < Node + # Initialize a new ClassVariableOrWriteNode node. + def initialize(source, node_id, location, flags, name, name_loc, operator_loc, value) + @source = source + @node_id = node_id + @location = location + @flags = flags + @name = name + @name_loc = name_loc + @operator_loc = operator_loc + @value = value + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_class_variable_or_write_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [value] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield value + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [value] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [name_loc, operator_loc, value] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> ClassVariableOrWriteNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc, operator_loc: self.operator_loc, value: self.value) + ClassVariableOrWriteNode.new(source, node_id, location, flags, name, name_loc, operator_loc, value) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, name_loc: Location, operator_loc: Location, value: Prism::node } + def deconstruct_keys(keys) + { node_id: node_id, location: location, name: name, name_loc: name_loc, operator_loc: operator_loc, value: value } + end + + # attr_reader name: Symbol + attr_reader :name + + # attr_reader name_loc: Location + def name_loc + location = @name_loc + return location if location.is_a?(Location) + @name_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + def save_name_loc(repository) + repository.enter(node_id, :name_loc) + end + + # attr_reader operator_loc: Location + def operator_loc + location = @operator_loc + return location if location.is_a?(Location) + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) + end + + # attr_reader value: Prism::node + attr_reader :value + + # def operator: () -> String + def operator + operator_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :class_variable_or_write_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :class_variable_or_write_node + 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?(ClassVariableOrWriteNode) && + (name === other.name) && + (name_loc.nil? == other.name_loc.nil?) && + (operator_loc.nil? == other.operator_loc.nil?) && + (value === other.value) + end + end + + # Represents referencing a class variable. + # + # @@foo + # ^^^^^ + class ClassVariableReadNode < Node + # Initialize a new ClassVariableReadNode node. + def initialize(source, node_id, location, flags, name) + @source = source + @node_id = node_id + @location = location + @flags = flags + @name = name + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_class_variable_read_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> ClassVariableReadNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name) + ClassVariableReadNode.new(source, node_id, location, flags, name) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol } + def deconstruct_keys(keys) + { node_id: node_id, location: location, name: name } + end + + # 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` + attr_reader :name + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :class_variable_read_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :class_variable_read_node + 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?(ClassVariableReadNode) && + (name === other.name) + end + end + + # Represents writing to a class variable in a context that doesn't have an explicit value. + # + # @@foo, @@bar = baz + # ^^^^^ ^^^^^ + class ClassVariableTargetNode < Node + # Initialize a new ClassVariableTargetNode node. + def initialize(source, node_id, location, flags, name) + @source = source + @node_id = node_id + @location = location + @flags = flags + @name = name + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_class_variable_target_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> ClassVariableTargetNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name) + ClassVariableTargetNode.new(source, node_id, location, flags, name) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol } + def deconstruct_keys(keys) + { node_id: node_id, location: location, name: name } + end + + # attr_reader name: Symbol + attr_reader :name + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :class_variable_target_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :class_variable_target_node + 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?(ClassVariableTargetNode) && + (name === other.name) + end + end + + # Represents writing to a class variable. + # + # @@foo = 1 + # ^^^^^^^^^ + class ClassVariableWriteNode < Node + # Initialize a new ClassVariableWriteNode node. + def initialize(source, node_id, location, flags, name, name_loc, value, operator_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @name = name + @name_loc = name_loc + @value = value + @operator_loc = operator_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_class_variable_write_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [value] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield value + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [value] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [name_loc, value, operator_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?value: Prism::node, ?operator_loc: Location) -> ClassVariableWriteNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc, value: self.value, operator_loc: self.operator_loc) + ClassVariableWriteNode.new(source, node_id, location, flags, name, name_loc, value, operator_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, name_loc: Location, value: Prism::node, operator_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, name: name, name_loc: name_loc, value: value, operator_loc: operator_loc } + end + + # 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` + attr_reader :name + + # The location of the variable name. + # + # @@foo = :bar + # ^^^^^ + def name_loc + location = @name_loc + return location if location.is_a?(Location) + @name_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + def save_name_loc(repository) + repository.enter(node_id, :name_loc) + end + + # 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 + # ^^^ + attr_reader :value + + # The location of the `=` operator. + # + # @@foo = :bar + # ^ + def operator_loc + location = @operator_loc + return location if location.is_a?(Location) + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) + end + + # def operator: () -> String + def operator + operator_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :class_variable_write_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :class_variable_write_node + 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?(ClassVariableWriteNode) && + (name === other.name) && + (name_loc.nil? == other.name_loc.nil?) && + (value === other.value) && + (operator_loc.nil? == other.operator_loc.nil?) + end + end + + # Represents the use of the `&&=` operator for assignment to a constant. + # + # Target &&= value + # ^^^^^^^^^^^^^^^^ + class ConstantAndWriteNode < Node + # Initialize a new ConstantAndWriteNode node. + def initialize(source, node_id, location, flags, name, name_loc, operator_loc, value) + @source = source + @node_id = node_id + @location = location + @flags = flags + @name = name + @name_loc = name_loc + @operator_loc = operator_loc + @value = value + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_constant_and_write_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [value] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield value + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [value] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [name_loc, operator_loc, value] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> ConstantAndWriteNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc, operator_loc: self.operator_loc, value: self.value) + ConstantAndWriteNode.new(source, node_id, location, flags, name, name_loc, operator_loc, value) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, name_loc: Location, operator_loc: Location, value: Prism::node } + def deconstruct_keys(keys) + { node_id: node_id, location: location, name: name, name_loc: name_loc, operator_loc: operator_loc, value: value } + end + + # attr_reader name: Symbol + attr_reader :name + + # attr_reader name_loc: Location + def name_loc + location = @name_loc + return location if location.is_a?(Location) + @name_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + def save_name_loc(repository) + repository.enter(node_id, :name_loc) + end + + # attr_reader operator_loc: Location + def operator_loc + location = @operator_loc + return location if location.is_a?(Location) + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) + end + + # attr_reader value: Prism::node + attr_reader :value + + # def operator: () -> String + def operator + operator_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :constant_and_write_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :constant_and_write_node + 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?(ConstantAndWriteNode) && + (name === other.name) && + (name_loc.nil? == other.name_loc.nil?) && + (operator_loc.nil? == other.operator_loc.nil?) && + (value === other.value) + end + end + + # Represents assigning to a constant using an operator that isn't `=`. + # + # Target += value + # ^^^^^^^^^^^^^^^ + class ConstantOperatorWriteNode < Node + # Initialize a new ConstantOperatorWriteNode node. + def initialize(source, node_id, location, flags, name, name_loc, binary_operator_loc, value, binary_operator) + @source = source + @node_id = node_id + @location = location + @flags = flags + @name = name + @name_loc = name_loc + @binary_operator_loc = binary_operator_loc + @value = value + @binary_operator = binary_operator + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_constant_operator_write_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [value] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield value + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [value] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [name_loc, binary_operator_loc, value] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?binary_operator_loc: Location, ?value: Prism::node, ?binary_operator: Symbol) -> ConstantOperatorWriteNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc, binary_operator_loc: self.binary_operator_loc, value: self.value, binary_operator: self.binary_operator) + ConstantOperatorWriteNode.new(source, node_id, location, flags, name, name_loc, binary_operator_loc, value, binary_operator) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, name_loc: Location, binary_operator_loc: Location, value: Prism::node, binary_operator: Symbol } + def deconstruct_keys(keys) + { node_id: node_id, location: location, name: name, name_loc: name_loc, binary_operator_loc: binary_operator_loc, value: value, binary_operator: binary_operator } + end + + # attr_reader name: Symbol + attr_reader :name + + # attr_reader name_loc: Location + def name_loc + location = @name_loc + return location if location.is_a?(Location) + @name_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + def save_name_loc(repository) + repository.enter(node_id, :name_loc) + end + + # attr_reader binary_operator_loc: Location + def binary_operator_loc + location = @binary_operator_loc + return location if location.is_a?(Location) + @binary_operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the binary_operator_loc location using the given saved source so that + # it can be retrieved later. + def save_binary_operator_loc(repository) + repository.enter(node_id, :binary_operator_loc) + end + + # attr_reader value: Prism::node + attr_reader :value + + # attr_reader binary_operator: Symbol + attr_reader :binary_operator + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :constant_operator_write_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :constant_operator_write_node + 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?(ConstantOperatorWriteNode) && + (name === other.name) && + (name_loc.nil? == other.name_loc.nil?) && + (binary_operator_loc.nil? == other.binary_operator_loc.nil?) && + (value === other.value) && + (binary_operator === other.binary_operator) + end + end + + # Represents the use of the `||=` operator for assignment to a constant. + # + # Target ||= value + # ^^^^^^^^^^^^^^^^ + class ConstantOrWriteNode < Node + # Initialize a new ConstantOrWriteNode node. + def initialize(source, node_id, location, flags, name, name_loc, operator_loc, value) + @source = source + @node_id = node_id + @location = location + @flags = flags + @name = name + @name_loc = name_loc + @operator_loc = operator_loc + @value = value + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_constant_or_write_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [value] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield value + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [value] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [name_loc, operator_loc, value] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> ConstantOrWriteNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc, operator_loc: self.operator_loc, value: self.value) + ConstantOrWriteNode.new(source, node_id, location, flags, name, name_loc, operator_loc, value) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, name_loc: Location, operator_loc: Location, value: Prism::node } + def deconstruct_keys(keys) + { node_id: node_id, location: location, name: name, name_loc: name_loc, operator_loc: operator_loc, value: value } + end + + # attr_reader name: Symbol + attr_reader :name + + # attr_reader name_loc: Location + def name_loc + location = @name_loc + return location if location.is_a?(Location) + @name_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + def save_name_loc(repository) + repository.enter(node_id, :name_loc) + end + + # attr_reader operator_loc: Location + def operator_loc + location = @operator_loc + return location if location.is_a?(Location) + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) + end + + # attr_reader value: Prism::node + attr_reader :value + + # def operator: () -> String + def operator + operator_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :constant_or_write_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :constant_or_write_node + 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?(ConstantOrWriteNode) && + (name === other.name) && + (name_loc.nil? == other.name_loc.nil?) && + (operator_loc.nil? == other.operator_loc.nil?) && + (value === other.value) + end + end + + # Represents the use of the `&&=` operator for assignment to a constant path. + # + # Parent::Child &&= value + # ^^^^^^^^^^^^^^^^^^^^^^^ + class ConstantPathAndWriteNode < Node + # Initialize a new ConstantPathAndWriteNode node. + def initialize(source, node_id, location, flags, target, operator_loc, value) + @source = source + @node_id = node_id + @location = location + @flags = flags + @target = target + @operator_loc = operator_loc + @value = value + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_constant_path_and_write_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [target, value] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield target + yield value + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [target, value] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [target, operator_loc, value] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?target: ConstantPathNode, ?operator_loc: Location, ?value: Prism::node) -> ConstantPathAndWriteNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, target: self.target, operator_loc: self.operator_loc, value: self.value) + ConstantPathAndWriteNode.new(source, node_id, location, flags, target, operator_loc, value) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, target: ConstantPathNode, operator_loc: Location, value: Prism::node } + def deconstruct_keys(keys) + { node_id: node_id, location: location, target: target, operator_loc: operator_loc, value: value } + end + + # attr_reader target: ConstantPathNode + attr_reader :target + + # attr_reader operator_loc: Location + def operator_loc + location = @operator_loc + return location if location.is_a?(Location) + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) + end + + # attr_reader value: Prism::node + attr_reader :value + + # def operator: () -> String + def operator + operator_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :constant_path_and_write_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :constant_path_and_write_node + 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?(ConstantPathAndWriteNode) && + (target === other.target) && + (operator_loc.nil? == other.operator_loc.nil?) && + (value === other.value) + end + end + + # Represents accessing a constant through a path of `::` operators. + # + # Foo::Bar + # ^^^^^^^^ + class ConstantPathNode < Node + # Initialize a new ConstantPathNode node. + def initialize(source, node_id, location, flags, parent, name, delimiter_loc, name_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @parent = parent + @name = name + @delimiter_loc = delimiter_loc + @name_loc = name_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_constant_path_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [parent] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield parent if parent + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << parent if parent + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [*parent, delimiter_loc, name_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?parent: Prism::node?, ?name: Symbol?, ?delimiter_loc: Location, ?name_loc: Location) -> ConstantPathNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, parent: self.parent, name: self.name, delimiter_loc: self.delimiter_loc, name_loc: self.name_loc) + ConstantPathNode.new(source, node_id, location, flags, parent, name, delimiter_loc, name_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, parent: Prism::node?, name: Symbol?, delimiter_loc: Location, name_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, parent: parent, name: name, delimiter_loc: delimiter_loc, name_loc: name_loc } + end + + # 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 + # ^^^ + attr_reader :parent + + # The name of the constant being accessed. This could be `nil` in the event of a syntax error. + attr_reader :name + + # The location of the `::` delimiter. + # + # ::Foo + # ^^ + # + # One::Two + # ^^ + def delimiter_loc + location = @delimiter_loc + return location if location.is_a?(Location) + @delimiter_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the delimiter_loc location using the given saved source so that + # it can be retrieved later. + def save_delimiter_loc(repository) + repository.enter(node_id, :delimiter_loc) + end + + # The location of the name of the constant. + # + # ::Foo + # ^^^ + # + # One::Two + # ^^^ + def name_loc + location = @name_loc + return location if location.is_a?(Location) + @name_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + def save_name_loc(repository) + repository.enter(node_id, :name_loc) + end + + # def delimiter: () -> String + def delimiter + delimiter_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :constant_path_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :constant_path_node + 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?(ConstantPathNode) && + (parent === other.parent) && + (name === other.name) && + (delimiter_loc.nil? == other.delimiter_loc.nil?) && + (name_loc.nil? == other.name_loc.nil?) + end + end + + # Represents assigning to a constant path using an operator that isn't `=`. + # + # Parent::Child += value + # ^^^^^^^^^^^^^^^^^^^^^^ + class ConstantPathOperatorWriteNode < Node + # Initialize a new ConstantPathOperatorWriteNode node. + def initialize(source, node_id, location, flags, target, binary_operator_loc, value, binary_operator) + @source = source + @node_id = node_id + @location = location + @flags = flags + @target = target + @binary_operator_loc = binary_operator_loc + @value = value + @binary_operator = binary_operator + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_constant_path_operator_write_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [target, value] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield target + yield value + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [target, value] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [target, binary_operator_loc, value] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?target: ConstantPathNode, ?binary_operator_loc: Location, ?value: Prism::node, ?binary_operator: Symbol) -> ConstantPathOperatorWriteNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, target: self.target, binary_operator_loc: self.binary_operator_loc, value: self.value, binary_operator: self.binary_operator) + ConstantPathOperatorWriteNode.new(source, node_id, location, flags, target, binary_operator_loc, value, binary_operator) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, target: ConstantPathNode, binary_operator_loc: Location, value: Prism::node, binary_operator: Symbol } + def deconstruct_keys(keys) + { node_id: node_id, location: location, target: target, binary_operator_loc: binary_operator_loc, value: value, binary_operator: binary_operator } + end + + # attr_reader target: ConstantPathNode + attr_reader :target + + # attr_reader binary_operator_loc: Location + def binary_operator_loc + location = @binary_operator_loc + return location if location.is_a?(Location) + @binary_operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the binary_operator_loc location using the given saved source so that + # it can be retrieved later. + def save_binary_operator_loc(repository) + repository.enter(node_id, :binary_operator_loc) + end + + # attr_reader value: Prism::node + attr_reader :value + + # attr_reader binary_operator: Symbol + attr_reader :binary_operator + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :constant_path_operator_write_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :constant_path_operator_write_node + 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?(ConstantPathOperatorWriteNode) && + (target === other.target) && + (binary_operator_loc.nil? == other.binary_operator_loc.nil?) && + (value === other.value) && + (binary_operator === other.binary_operator) + end + end + + # Represents the use of the `||=` operator for assignment to a constant path. + # + # Parent::Child ||= value + # ^^^^^^^^^^^^^^^^^^^^^^^ + class ConstantPathOrWriteNode < Node + # Initialize a new ConstantPathOrWriteNode node. + def initialize(source, node_id, location, flags, target, operator_loc, value) + @source = source + @node_id = node_id + @location = location + @flags = flags + @target = target + @operator_loc = operator_loc + @value = value + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_constant_path_or_write_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [target, value] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield target + yield value + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [target, value] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [target, operator_loc, value] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?target: ConstantPathNode, ?operator_loc: Location, ?value: Prism::node) -> ConstantPathOrWriteNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, target: self.target, operator_loc: self.operator_loc, value: self.value) + ConstantPathOrWriteNode.new(source, node_id, location, flags, target, operator_loc, value) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, target: ConstantPathNode, operator_loc: Location, value: Prism::node } + def deconstruct_keys(keys) + { node_id: node_id, location: location, target: target, operator_loc: operator_loc, value: value } + end + + # attr_reader target: ConstantPathNode + attr_reader :target + + # attr_reader operator_loc: Location + def operator_loc + location = @operator_loc + return location if location.is_a?(Location) + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) + end + + # attr_reader value: Prism::node + attr_reader :value + + # def operator: () -> String + def operator + operator_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :constant_path_or_write_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :constant_path_or_write_node + 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?(ConstantPathOrWriteNode) && + (target === other.target) && + (operator_loc.nil? == other.operator_loc.nil?) && + (value === other.value) + end + end + + # Represents writing to a constant path in a context that doesn't have an explicit value. + # + # Foo::Foo, Bar::Bar = baz + # ^^^^^^^^ ^^^^^^^^ + class ConstantPathTargetNode < Node + # Initialize a new ConstantPathTargetNode node. + def initialize(source, node_id, location, flags, parent, name, delimiter_loc, name_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @parent = parent + @name = name + @delimiter_loc = delimiter_loc + @name_loc = name_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_constant_path_target_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [parent] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield parent if parent + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << parent if parent + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [*parent, delimiter_loc, name_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?parent: Prism::node?, ?name: Symbol?, ?delimiter_loc: Location, ?name_loc: Location) -> ConstantPathTargetNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, parent: self.parent, name: self.name, delimiter_loc: self.delimiter_loc, name_loc: self.name_loc) + ConstantPathTargetNode.new(source, node_id, location, flags, parent, name, delimiter_loc, name_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, parent: Prism::node?, name: Symbol?, delimiter_loc: Location, name_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, parent: parent, name: name, delimiter_loc: delimiter_loc, name_loc: name_loc } + end + + # attr_reader parent: Prism::node? + attr_reader :parent + + # attr_reader name: Symbol? + attr_reader :name + + # attr_reader delimiter_loc: Location + def delimiter_loc + location = @delimiter_loc + return location if location.is_a?(Location) + @delimiter_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the delimiter_loc location using the given saved source so that + # it can be retrieved later. + def save_delimiter_loc(repository) + repository.enter(node_id, :delimiter_loc) + end + + # attr_reader name_loc: Location + def name_loc + location = @name_loc + return location if location.is_a?(Location) + @name_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + def save_name_loc(repository) + repository.enter(node_id, :name_loc) + end + + # def delimiter: () -> String + def delimiter + delimiter_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :constant_path_target_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :constant_path_target_node + 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?(ConstantPathTargetNode) && + (parent === other.parent) && + (name === other.name) && + (delimiter_loc.nil? == other.delimiter_loc.nil?) && + (name_loc.nil? == other.name_loc.nil?) + end + end + + # Represents writing to a constant path. + # + # ::Foo = 1 + # ^^^^^^^^^ + # + # Foo::Bar = 1 + # ^^^^^^^^^^^^ + # + # ::Foo::Bar = 1 + # ^^^^^^^^^^^^^^ + class ConstantPathWriteNode < Node + # Initialize a new ConstantPathWriteNode node. + def initialize(source, node_id, location, flags, target, operator_loc, value) + @source = source + @node_id = node_id + @location = location + @flags = flags + @target = target + @operator_loc = operator_loc + @value = value + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_constant_path_write_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [target, value] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield target + yield value + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [target, value] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [target, operator_loc, value] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?target: ConstantPathNode, ?operator_loc: Location, ?value: Prism::node) -> ConstantPathWriteNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, target: self.target, operator_loc: self.operator_loc, value: self.value) + ConstantPathWriteNode.new(source, node_id, location, flags, target, operator_loc, value) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, target: ConstantPathNode, operator_loc: Location, value: Prism::node } + def deconstruct_keys(keys) + { node_id: node_id, location: location, target: target, operator_loc: operator_loc, value: value } + end + + # A node representing the constant path being written to. + # + # Foo::Bar = 1 + # ^^^^^^^^ + # + # ::Foo = :abc + # ^^^^^ + attr_reader :target + + # The location of the `=` operator. + # + # ::ABC = 123 + # ^ + def operator_loc + location = @operator_loc + return location if location.is_a?(Location) + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) + end + + # 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 + # ^^^^ + attr_reader :value + + # def operator: () -> String + def operator + operator_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :constant_path_write_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :constant_path_write_node + 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?(ConstantPathWriteNode) && + (target === other.target) && + (operator_loc.nil? == other.operator_loc.nil?) && + (value === other.value) + end + end + + # Represents referencing a constant. + # + # Foo + # ^^^ + class ConstantReadNode < Node + # Initialize a new ConstantReadNode node. + def initialize(source, node_id, location, flags, name) + @source = source + @node_id = node_id + @location = location + @flags = flags + @name = name + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_constant_read_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> ConstantReadNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name) + ConstantReadNode.new(source, node_id, location, flags, name) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol } + def deconstruct_keys(keys) + { node_id: node_id, location: location, name: name } + end + + # 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` + attr_reader :name + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :constant_read_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :constant_read_node + 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?(ConstantReadNode) && + (name === other.name) + end + end + + # Represents writing to a constant in a context that doesn't have an explicit value. + # + # Foo, Bar = baz + # ^^^ ^^^ + class ConstantTargetNode < Node + # Initialize a new ConstantTargetNode node. + def initialize(source, node_id, location, flags, name) + @source = source + @node_id = node_id + @location = location + @flags = flags + @name = name + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_constant_target_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> ConstantTargetNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name) + ConstantTargetNode.new(source, node_id, location, flags, name) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol } + def deconstruct_keys(keys) + { node_id: node_id, location: location, name: name } + end + + # attr_reader name: Symbol + attr_reader :name + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :constant_target_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :constant_target_node + 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?(ConstantTargetNode) && + (name === other.name) + end + end + + # Represents writing to a constant. + # + # Foo = 1 + # ^^^^^^^ + class ConstantWriteNode < Node + # Initialize a new ConstantWriteNode node. + def initialize(source, node_id, location, flags, name, name_loc, value, operator_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @name = name + @name_loc = name_loc + @value = value + @operator_loc = operator_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_constant_write_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [value] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield value + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [value] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [name_loc, value, operator_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?value: Prism::node, ?operator_loc: Location) -> ConstantWriteNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc, value: self.value, operator_loc: self.operator_loc) + ConstantWriteNode.new(source, node_id, location, flags, name, name_loc, value, operator_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, name_loc: Location, value: Prism::node, operator_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, name: name, name_loc: name_loc, value: value, operator_loc: operator_loc } + end + + # 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` + attr_reader :name + + # The location of the constant name. + # + # FOO = 1 + # ^^^ + def name_loc + location = @name_loc + return location if location.is_a?(Location) + @name_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + def save_name_loc(repository) + repository.enter(node_id, :name_loc) + end + + # 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 + # ^^^^^^^^^ + attr_reader :value + + # The location of the `=` operator. + # + # FOO = :bar + # ^ + def operator_loc + location = @operator_loc + return location if location.is_a?(Location) + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) + end + + # def operator: () -> String + def operator + operator_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :constant_write_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :constant_write_node + 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?(ConstantWriteNode) && + (name === other.name) && + (name_loc.nil? == other.name_loc.nil?) && + (value === other.value) && + (operator_loc.nil? == other.operator_loc.nil?) + end + end + + # Represents a method definition. + # + # def method + # end + # ^^^^^^^^^^ + class DefNode < Node + # Initialize a new DefNode node. + def initialize(source, node_id, location, flags, name, name_loc, receiver, parameters, body, locals, def_keyword_loc, operator_loc, lparen_loc, rparen_loc, equal_loc, end_keyword_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @name = name + @name_loc = name_loc + @receiver = receiver + @parameters = parameters + @body = body + @locals = locals + @def_keyword_loc = def_keyword_loc + @operator_loc = operator_loc + @lparen_loc = lparen_loc + @rparen_loc = rparen_loc + @equal_loc = equal_loc + @end_keyword_loc = end_keyword_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_def_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [receiver, parameters, body] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield receiver if receiver + yield parameters if parameters + yield body if body + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << receiver if receiver + compact << parameters if parameters + compact << body if body + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [name_loc, *receiver, *parameters, *body, def_keyword_loc, *operator_loc, *lparen_loc, *rparen_loc, *equal_loc, *end_keyword_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?receiver: Prism::node?, ?parameters: ParametersNode?, ?body: StatementsNode | BeginNode | nil, ?locals: Array[Symbol], ?def_keyword_loc: Location, ?operator_loc: Location?, ?lparen_loc: Location?, ?rparen_loc: Location?, ?equal_loc: Location?, ?end_keyword_loc: Location?) -> DefNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc, receiver: self.receiver, parameters: self.parameters, body: self.body, locals: self.locals, def_keyword_loc: self.def_keyword_loc, operator_loc: self.operator_loc, lparen_loc: self.lparen_loc, rparen_loc: self.rparen_loc, equal_loc: self.equal_loc, end_keyword_loc: self.end_keyword_loc) + DefNode.new(source, node_id, location, flags, name, name_loc, receiver, parameters, body, locals, def_keyword_loc, operator_loc, lparen_loc, rparen_loc, equal_loc, end_keyword_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, name_loc: Location, receiver: Prism::node?, parameters: ParametersNode?, body: StatementsNode | BeginNode | nil, locals: Array[Symbol], def_keyword_loc: Location, operator_loc: Location?, lparen_loc: Location?, rparen_loc: Location?, equal_loc: Location?, end_keyword_loc: Location? } + def deconstruct_keys(keys) + { node_id: node_id, location: location, name: name, name_loc: name_loc, receiver: receiver, parameters: parameters, body: body, locals: locals, def_keyword_loc: def_keyword_loc, operator_loc: operator_loc, lparen_loc: lparen_loc, rparen_loc: rparen_loc, equal_loc: equal_loc, end_keyword_loc: end_keyword_loc } + end + + # attr_reader name: Symbol + attr_reader :name + + # attr_reader name_loc: Location + def name_loc + location = @name_loc + return location if location.is_a?(Location) + @name_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + def save_name_loc(repository) + repository.enter(node_id, :name_loc) + end + + # attr_reader receiver: Prism::node? + attr_reader :receiver + + # attr_reader parameters: ParametersNode? + attr_reader :parameters + + # attr_reader body: StatementsNode | BeginNode | nil + attr_reader :body + + # attr_reader locals: Array[Symbol] + attr_reader :locals + + # attr_reader def_keyword_loc: Location + def def_keyword_loc + location = @def_keyword_loc + return location if location.is_a?(Location) + @def_keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the def_keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_def_keyword_loc(repository) + repository.enter(node_id, :def_keyword_loc) + end + + # attr_reader operator_loc: Location? + def operator_loc + location = @operator_loc + case location + when nil + nil + when Location + location + else + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) unless @operator_loc.nil? + end + + # attr_reader lparen_loc: Location? + def lparen_loc + location = @lparen_loc + case location + when nil + nil + when Location + location + else + @lparen_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the lparen_loc location using the given saved source so that + # it can be retrieved later. + def save_lparen_loc(repository) + repository.enter(node_id, :lparen_loc) unless @lparen_loc.nil? + end + + # attr_reader rparen_loc: Location? + def rparen_loc + location = @rparen_loc + case location + when nil + nil + when Location + location + else + @rparen_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the rparen_loc location using the given saved source so that + # it can be retrieved later. + def save_rparen_loc(repository) + repository.enter(node_id, :rparen_loc) unless @rparen_loc.nil? + end + + # attr_reader equal_loc: Location? + def equal_loc + location = @equal_loc + case location + when nil + nil + when Location + location + else + @equal_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the equal_loc location using the given saved source so that + # it can be retrieved later. + def save_equal_loc(repository) + repository.enter(node_id, :equal_loc) unless @equal_loc.nil? + end + + # attr_reader end_keyword_loc: Location? + def end_keyword_loc + location = @end_keyword_loc + case location + when nil + nil + when Location + location + else + @end_keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the end_keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_end_keyword_loc(repository) + repository.enter(node_id, :end_keyword_loc) unless @end_keyword_loc.nil? + end + + # def def_keyword: () -> String + def def_keyword + def_keyword_loc.slice + end + + # def operator: () -> String? + def operator + operator_loc&.slice + end + + # def lparen: () -> String? + def lparen + lparen_loc&.slice + end + + # def rparen: () -> String? + def rparen + rparen_loc&.slice + end + + # def equal: () -> String? + def equal + equal_loc&.slice + end + + # def end_keyword: () -> String? + def end_keyword + end_keyword_loc&.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :def_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :def_node + 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?(DefNode) && + (name === other.name) && + (name_loc.nil? == other.name_loc.nil?) && + (receiver === other.receiver) && + (parameters === other.parameters) && + (body === other.body) && + (locals.length == other.locals.length) && + locals.zip(other.locals).all? { |left, right| left === right } && + (def_keyword_loc.nil? == other.def_keyword_loc.nil?) && + (operator_loc.nil? == other.operator_loc.nil?) && + (lparen_loc.nil? == other.lparen_loc.nil?) && + (rparen_loc.nil? == other.rparen_loc.nil?) && + (equal_loc.nil? == other.equal_loc.nil?) && + (end_keyword_loc.nil? == other.end_keyword_loc.nil?) + end + end + + # Represents the use of the `defined?` keyword. + # + # defined?(a) + # ^^^^^^^^^^^ + class DefinedNode < Node + # Initialize a new DefinedNode node. + def initialize(source, node_id, location, flags, lparen_loc, value, rparen_loc, keyword_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @lparen_loc = lparen_loc + @value = value + @rparen_loc = rparen_loc + @keyword_loc = keyword_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_defined_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [value] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield value + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [value] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [*lparen_loc, value, *rparen_loc, keyword_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?lparen_loc: Location?, ?value: Prism::node, ?rparen_loc: Location?, ?keyword_loc: Location) -> DefinedNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, lparen_loc: self.lparen_loc, value: self.value, rparen_loc: self.rparen_loc, keyword_loc: self.keyword_loc) + DefinedNode.new(source, node_id, location, flags, lparen_loc, value, rparen_loc, keyword_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, lparen_loc: Location?, value: Prism::node, rparen_loc: Location?, keyword_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, lparen_loc: lparen_loc, value: value, rparen_loc: rparen_loc, keyword_loc: keyword_loc } + end + + # attr_reader lparen_loc: Location? + def lparen_loc + location = @lparen_loc + case location + when nil + nil + when Location + location + else + @lparen_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the lparen_loc location using the given saved source so that + # it can be retrieved later. + def save_lparen_loc(repository) + repository.enter(node_id, :lparen_loc) unless @lparen_loc.nil? + end + + # attr_reader value: Prism::node + attr_reader :value + + # attr_reader rparen_loc: Location? + def rparen_loc + location = @rparen_loc + case location + when nil + nil + when Location + location + else + @rparen_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the rparen_loc location using the given saved source so that + # it can be retrieved later. + def save_rparen_loc(repository) + repository.enter(node_id, :rparen_loc) unless @rparen_loc.nil? + end + + # attr_reader keyword_loc: Location + def keyword_loc + location = @keyword_loc + return location if location.is_a?(Location) + @keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_keyword_loc(repository) + repository.enter(node_id, :keyword_loc) + end + + # def lparen: () -> String? + def lparen + lparen_loc&.slice + end + + # def rparen: () -> String? + def rparen + rparen_loc&.slice + end + + # def keyword: () -> String + def keyword + keyword_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :defined_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :defined_node + 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?(DefinedNode) && + (lparen_loc.nil? == other.lparen_loc.nil?) && + (value === other.value) && + (rparen_loc.nil? == other.rparen_loc.nil?) && + (keyword_loc.nil? == other.keyword_loc.nil?) + end + end + + # Represents an `else` clause in a `case`, `if`, or `unless` statement. + # + # if a then b else c end + # ^^^^^^^^^^ + class ElseNode < Node + # Initialize a new ElseNode node. + def initialize(source, node_id, location, flags, else_keyword_loc, statements, end_keyword_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @else_keyword_loc = else_keyword_loc + @statements = statements + @end_keyword_loc = end_keyword_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_else_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [statements] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield statements if statements + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << statements if statements + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [else_keyword_loc, *statements, *end_keyword_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?else_keyword_loc: Location, ?statements: StatementsNode?, ?end_keyword_loc: Location?) -> ElseNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, else_keyword_loc: self.else_keyword_loc, statements: self.statements, end_keyword_loc: self.end_keyword_loc) + ElseNode.new(source, node_id, location, flags, else_keyword_loc, statements, end_keyword_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, else_keyword_loc: Location, statements: StatementsNode?, end_keyword_loc: Location? } + def deconstruct_keys(keys) + { node_id: node_id, location: location, else_keyword_loc: else_keyword_loc, statements: statements, end_keyword_loc: end_keyword_loc } + end + + # attr_reader else_keyword_loc: Location + def else_keyword_loc + location = @else_keyword_loc + return location if location.is_a?(Location) + @else_keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the else_keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_else_keyword_loc(repository) + repository.enter(node_id, :else_keyword_loc) + end + + # attr_reader statements: StatementsNode? + attr_reader :statements + + # attr_reader end_keyword_loc: Location? + def end_keyword_loc + location = @end_keyword_loc + case location + when nil + nil + when Location + location + else + @end_keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the end_keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_end_keyword_loc(repository) + repository.enter(node_id, :end_keyword_loc) unless @end_keyword_loc.nil? + end + + # def else_keyword: () -> String + def else_keyword + else_keyword_loc.slice + end + + # def end_keyword: () -> String? + def end_keyword + end_keyword_loc&.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :else_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :else_node + 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?(ElseNode) && + (else_keyword_loc.nil? == other.else_keyword_loc.nil?) && + (statements === other.statements) && + (end_keyword_loc.nil? == other.end_keyword_loc.nil?) + end + end + + # Represents an interpolated set of statements. + # + # "foo #{bar}" + # ^^^^^^ + class EmbeddedStatementsNode < Node + # Initialize a new EmbeddedStatementsNode node. + def initialize(source, node_id, location, flags, opening_loc, statements, closing_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @opening_loc = opening_loc + @statements = statements + @closing_loc = closing_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_embedded_statements_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [statements] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield statements if statements + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << statements if statements + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [opening_loc, *statements, closing_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?statements: StatementsNode?, ?closing_loc: Location) -> EmbeddedStatementsNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, opening_loc: self.opening_loc, statements: self.statements, closing_loc: self.closing_loc) + EmbeddedStatementsNode.new(source, node_id, location, flags, opening_loc, statements, closing_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, opening_loc: Location, statements: StatementsNode?, closing_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, opening_loc: opening_loc, statements: statements, closing_loc: closing_loc } + end + + # attr_reader opening_loc: Location + def opening_loc + location = @opening_loc + return location if location.is_a?(Location) + @opening_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + def save_opening_loc(repository) + repository.enter(node_id, :opening_loc) + end + + # attr_reader statements: StatementsNode? + attr_reader :statements + + # attr_reader closing_loc: Location + def closing_loc + location = @closing_loc + return location if location.is_a?(Location) + @closing_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + def save_closing_loc(repository) + repository.enter(node_id, :closing_loc) + end + + # def opening: () -> String + def opening + opening_loc.slice + end + + # def closing: () -> String + def closing + closing_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :embedded_statements_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :embedded_statements_node + 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?(EmbeddedStatementsNode) && + (opening_loc.nil? == other.opening_loc.nil?) && + (statements === other.statements) && + (closing_loc.nil? == other.closing_loc.nil?) + end + end + + # Represents an interpolated variable. + # + # "foo #@bar" + # ^^^^^ + class EmbeddedVariableNode < Node + # Initialize a new EmbeddedVariableNode node. + def initialize(source, node_id, location, flags, operator_loc, variable) + @source = source + @node_id = node_id + @location = location + @flags = flags + @operator_loc = operator_loc + @variable = variable + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_embedded_variable_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [variable] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield variable + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [variable] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [operator_loc, variable] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?operator_loc: Location, ?variable: InstanceVariableReadNode | ClassVariableReadNode | GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode) -> EmbeddedVariableNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, operator_loc: self.operator_loc, variable: self.variable) + EmbeddedVariableNode.new(source, node_id, location, flags, operator_loc, variable) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, operator_loc: Location, variable: InstanceVariableReadNode | ClassVariableReadNode | GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode } + def deconstruct_keys(keys) + { node_id: node_id, location: location, operator_loc: operator_loc, variable: variable } + end + + # attr_reader operator_loc: Location + def operator_loc + location = @operator_loc + return location if location.is_a?(Location) + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) + end + + # attr_reader variable: InstanceVariableReadNode | ClassVariableReadNode | GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode + attr_reader :variable + + # def operator: () -> String + def operator + operator_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :embedded_variable_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :embedded_variable_node + 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?(EmbeddedVariableNode) && + (operator_loc.nil? == other.operator_loc.nil?) && + (variable === other.variable) + end + end + + # Represents an `ensure` clause in a `begin` statement. + # + # begin + # foo + # ensure + # ^^^^^^ + # bar + # end + class EnsureNode < Node + # Initialize a new EnsureNode node. + def initialize(source, node_id, location, flags, ensure_keyword_loc, statements, end_keyword_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @ensure_keyword_loc = ensure_keyword_loc + @statements = statements + @end_keyword_loc = end_keyword_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_ensure_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [statements] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield statements if statements + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << statements if statements + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [ensure_keyword_loc, *statements, end_keyword_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?ensure_keyword_loc: Location, ?statements: StatementsNode?, ?end_keyword_loc: Location) -> EnsureNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, ensure_keyword_loc: self.ensure_keyword_loc, statements: self.statements, end_keyword_loc: self.end_keyword_loc) + EnsureNode.new(source, node_id, location, flags, ensure_keyword_loc, statements, end_keyword_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, ensure_keyword_loc: Location, statements: StatementsNode?, end_keyword_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, ensure_keyword_loc: ensure_keyword_loc, statements: statements, end_keyword_loc: end_keyword_loc } + end + + # attr_reader ensure_keyword_loc: Location + def ensure_keyword_loc + location = @ensure_keyword_loc + return location if location.is_a?(Location) + @ensure_keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the ensure_keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_ensure_keyword_loc(repository) + repository.enter(node_id, :ensure_keyword_loc) + end + + # attr_reader statements: StatementsNode? + attr_reader :statements + + # attr_reader end_keyword_loc: Location + def end_keyword_loc + location = @end_keyword_loc + return location if location.is_a?(Location) + @end_keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the end_keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_end_keyword_loc(repository) + repository.enter(node_id, :end_keyword_loc) + end + + # def ensure_keyword: () -> String + def ensure_keyword + ensure_keyword_loc.slice + end + + # def end_keyword: () -> String + def end_keyword + end_keyword_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :ensure_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :ensure_node + 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?(EnsureNode) && + (ensure_keyword_loc.nil? == other.ensure_keyword_loc.nil?) && + (statements === other.statements) && + (end_keyword_loc.nil? == other.end_keyword_loc.nil?) + end + end + + # Represents the use of the literal `false` keyword. + # + # false + # ^^^^^ + class FalseNode < Node + # Initialize a new FalseNode node. + def initialize(source, node_id, location, flags) + @source = source + @node_id = node_id + @location = location + @flags = flags + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_false_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer) -> FalseNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags) + FalseNode.new(source, node_id, location, flags) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location } + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :false_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :false_node + 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?(FalseNode) + end + end + + # Represents a find pattern in pattern matching. + # + # foo in *bar, baz, *qux + # ^^^^^^^^^^^^^^^ + # + # foo in [*bar, baz, *qux] + # ^^^^^^^^^^^^^^^^^ + # + # foo in Foo(*bar, baz, *qux) + # ^^^^^^^^^^^^^^^^^^^^ + # + # foo => *bar, baz, *qux + # ^^^^^^^^^^^^^^^ + class FindPatternNode < Node + # Initialize a new FindPatternNode node. + def initialize(source, node_id, location, flags, constant, left, requireds, right, opening_loc, closing_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @constant = constant + @left = left + @requireds = requireds + @right = right + @opening_loc = opening_loc + @closing_loc = closing_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_find_pattern_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [constant, left, *requireds, right] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield constant if constant + yield left + requireds.each { |node| yield node } + yield right + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << constant if constant + compact << left + compact.concat(requireds) + compact << right + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [*constant, left, *requireds, right, *opening_loc, *closing_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?constant: ConstantPathNode | ConstantReadNode | nil, ?left: SplatNode, ?requireds: Array[Prism::node], ?right: SplatNode | MissingNode, ?opening_loc: Location?, ?closing_loc: Location?) -> FindPatternNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, constant: self.constant, left: self.left, requireds: self.requireds, right: self.right, opening_loc: self.opening_loc, closing_loc: self.closing_loc) + FindPatternNode.new(source, node_id, location, flags, constant, left, requireds, right, opening_loc, closing_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, constant: ConstantPathNode | ConstantReadNode | nil, left: SplatNode, requireds: Array[Prism::node], right: SplatNode | MissingNode, opening_loc: Location?, closing_loc: Location? } + def deconstruct_keys(keys) + { node_id: node_id, location: location, constant: constant, left: left, requireds: requireds, right: right, opening_loc: opening_loc, closing_loc: closing_loc } + end + + # Represents the optional constant preceding the pattern + # + # foo in Foo(*bar, baz, *qux) + # ^^^ + attr_reader :constant + + # Represents the first wildcard node in the pattern. + # + # foo in *bar, baz, *qux + # ^^^^ + # + # foo in Foo(*bar, baz, *qux) + # ^^^^ + attr_reader :left + + # Represents the nodes in between the wildcards. + # + # foo in *bar, baz, *qux + # ^^^ + # + # foo in Foo(*bar, baz, 1, *qux) + # ^^^^^^ + attr_reader :requireds + + # Represents the second wildcard node in the pattern. + # + # foo in *bar, baz, *qux + # ^^^^ + # + # foo in Foo(*bar, baz, *qux) + # ^^^^ + attr_reader :right + + # The location of the opening brace. + # + # foo in [*bar, baz, *qux] + # ^ + # + # foo in Foo(*bar, baz, *qux) + # ^ + def opening_loc + location = @opening_loc + case location + when nil + nil + when Location + location + else + @opening_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + def save_opening_loc(repository) + repository.enter(node_id, :opening_loc) unless @opening_loc.nil? + end + + # The location of the closing brace. + # + # foo in [*bar, baz, *qux] + # ^ + # + # foo in Foo(*bar, baz, *qux) + # ^ + def closing_loc + location = @closing_loc + case location + when nil + nil + when Location + location + else + @closing_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + def save_closing_loc(repository) + repository.enter(node_id, :closing_loc) unless @closing_loc.nil? + end + + # def opening: () -> String? + def opening + opening_loc&.slice + end + + # def closing: () -> String? + def closing + closing_loc&.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :find_pattern_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :find_pattern_node + 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?(FindPatternNode) && + (constant === other.constant) && + (left === other.left) && + (requireds.length == other.requireds.length) && + requireds.zip(other.requireds).all? { |left, right| left === right } && + (right === other.right) && + (opening_loc.nil? == other.opening_loc.nil?) && + (closing_loc.nil? == other.closing_loc.nil?) + end + end + + # Represents the use of the `..` or `...` operators to create flip flops. + # + # baz if foo .. bar + # ^^^^^^^^^^ + class FlipFlopNode < Node + # Initialize a new FlipFlopNode node. + def initialize(source, node_id, location, flags, left, right, operator_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @left = left + @right = right + @operator_loc = operator_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_flip_flop_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [left, right] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield left if left + yield right if right + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << left if left + compact << right if right + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [*left, *right, operator_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?left: Prism::node?, ?right: Prism::node?, ?operator_loc: Location) -> FlipFlopNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, left: self.left, right: self.right, operator_loc: self.operator_loc) + FlipFlopNode.new(source, node_id, location, flags, left, right, operator_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, left: Prism::node?, right: Prism::node?, operator_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, left: left, right: right, operator_loc: operator_loc } + end + + # def exclude_end?: () -> bool + def exclude_end? + flags.anybits?(RangeFlags::EXCLUDE_END) + end + + # attr_reader left: Prism::node? + attr_reader :left + + # attr_reader right: Prism::node? + attr_reader :right + + # attr_reader operator_loc: Location + def operator_loc + location = @operator_loc + return location if location.is_a?(Location) + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) + end + + # def operator: () -> String + def operator + operator_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :flip_flop_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :flip_flop_node + 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?(FlipFlopNode) && + (flags === other.flags) && + (left === other.left) && + (right === other.right) && + (operator_loc.nil? == other.operator_loc.nil?) + end + end + + # Represents a floating point number literal. + # + # 1.0 + # ^^^ + class FloatNode < Node + # Initialize a new FloatNode node. + def initialize(source, node_id, location, flags, value) + @source = source + @node_id = node_id + @location = location + @flags = flags + @value = value + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_float_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?value: Float) -> FloatNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, value: self.value) + FloatNode.new(source, node_id, location, flags, value) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, value: Float } + def deconstruct_keys(keys) + { node_id: node_id, location: location, value: value } + end + + # The value of the floating point number as a Float. + attr_reader :value + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :float_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :float_node + 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?(FloatNode) && + (value === other.value) + end + end + + # Represents the use of the `for` keyword. + # + # for i in a end + # ^^^^^^^^^^^^^^ + class ForNode < Node + # Initialize a new ForNode node. + def initialize(source, node_id, location, flags, index, collection, statements, for_keyword_loc, in_keyword_loc, do_keyword_loc, end_keyword_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @index = index + @collection = collection + @statements = statements + @for_keyword_loc = for_keyword_loc + @in_keyword_loc = in_keyword_loc + @do_keyword_loc = do_keyword_loc + @end_keyword_loc = end_keyword_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_for_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [index, collection, statements] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield index + yield collection + yield statements if statements + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << index + compact << collection + compact << statements if statements + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [index, collection, *statements, for_keyword_loc, in_keyword_loc, *do_keyword_loc, end_keyword_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?index: LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | BackReferenceReadNode | NumberedReferenceReadNode | MissingNode, ?collection: Prism::node, ?statements: StatementsNode?, ?for_keyword_loc: Location, ?in_keyword_loc: Location, ?do_keyword_loc: Location?, ?end_keyword_loc: Location) -> ForNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, index: self.index, collection: self.collection, statements: self.statements, for_keyword_loc: self.for_keyword_loc, in_keyword_loc: self.in_keyword_loc, do_keyword_loc: self.do_keyword_loc, end_keyword_loc: self.end_keyword_loc) + ForNode.new(source, node_id, location, flags, index, collection, statements, for_keyword_loc, in_keyword_loc, do_keyword_loc, end_keyword_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, index: LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | BackReferenceReadNode | NumberedReferenceReadNode | MissingNode, collection: Prism::node, statements: StatementsNode?, for_keyword_loc: Location, in_keyword_loc: Location, do_keyword_loc: Location?, end_keyword_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, index: index, collection: collection, statements: statements, for_keyword_loc: for_keyword_loc, in_keyword_loc: in_keyword_loc, do_keyword_loc: do_keyword_loc, end_keyword_loc: end_keyword_loc } + end + + # The index expression for `for` loops. + # + # for i in a end + # ^ + attr_reader :index + + # The collection to iterate over. + # + # for i in a end + # ^ + attr_reader :collection + + # Represents the body of statements to execute for each iteration of the loop. + # + # for i in a + # foo(i) + # ^^^^^^ + # end + attr_reader :statements + + # The location of the `for` keyword. + # + # for i in a end + # ^^^ + def for_keyword_loc + location = @for_keyword_loc + return location if location.is_a?(Location) + @for_keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the for_keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_for_keyword_loc(repository) + repository.enter(node_id, :for_keyword_loc) + end + + # The location of the `in` keyword. + # + # for i in a end + # ^^ + def in_keyword_loc + location = @in_keyword_loc + return location if location.is_a?(Location) + @in_keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the in_keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_in_keyword_loc(repository) + repository.enter(node_id, :in_keyword_loc) + end + + # The location of the `do` keyword, if present. + # + # for i in a do end + # ^^ + def do_keyword_loc + location = @do_keyword_loc + case location + when nil + nil + when Location + location + else + @do_keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the do_keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_do_keyword_loc(repository) + repository.enter(node_id, :do_keyword_loc) unless @do_keyword_loc.nil? + end + + # The location of the `end` keyword. + # + # for i in a end + # ^^^ + def end_keyword_loc + location = @end_keyword_loc + return location if location.is_a?(Location) + @end_keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the end_keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_end_keyword_loc(repository) + repository.enter(node_id, :end_keyword_loc) + end + + # def for_keyword: () -> String + def for_keyword + for_keyword_loc.slice + end + + # def in_keyword: () -> String + def in_keyword + in_keyword_loc.slice + end + + # def do_keyword: () -> String? + def do_keyword + do_keyword_loc&.slice + end + + # def end_keyword: () -> String + def end_keyword + end_keyword_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :for_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :for_node + 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?(ForNode) && + (index === other.index) && + (collection === other.collection) && + (statements === other.statements) && + (for_keyword_loc.nil? == other.for_keyword_loc.nil?) && + (in_keyword_loc.nil? == other.in_keyword_loc.nil?) && + (do_keyword_loc.nil? == other.do_keyword_loc.nil?) && + (end_keyword_loc.nil? == other.end_keyword_loc.nil?) + end + end + + # Represents forwarding all arguments to this method to another method. + # + # def foo(...) + # bar(...) + # ^^^ + # end + class ForwardingArgumentsNode < Node + # Initialize a new ForwardingArgumentsNode node. + def initialize(source, node_id, location, flags) + @source = source + @node_id = node_id + @location = location + @flags = flags + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_forwarding_arguments_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer) -> ForwardingArgumentsNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags) + ForwardingArgumentsNode.new(source, node_id, location, flags) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location } + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :forwarding_arguments_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :forwarding_arguments_node + 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?(ForwardingArgumentsNode) + end + end + + # Represents the use of the forwarding parameter in a method, block, or lambda declaration. + # + # def foo(...) + # ^^^ + # end + class ForwardingParameterNode < Node + # Initialize a new ForwardingParameterNode node. + def initialize(source, node_id, location, flags) + @source = source + @node_id = node_id + @location = location + @flags = flags + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_forwarding_parameter_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer) -> ForwardingParameterNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags) + ForwardingParameterNode.new(source, node_id, location, flags) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location } + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :forwarding_parameter_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :forwarding_parameter_node + 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?(ForwardingParameterNode) + end + end + + # Represents the use of the `super` keyword without parentheses or arguments, but which might have a block. + # + # super + # ^^^^^ + # + # super { 123 } + # ^^^^^^^^^^^^^ + # + # If it has any other arguments, it would be a `SuperNode` instead. + class ForwardingSuperNode < Node + # Initialize a new ForwardingSuperNode node. + def initialize(source, node_id, location, flags, block) + @source = source + @node_id = node_id + @location = location + @flags = flags + @block = block + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_forwarding_super_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [block] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield block if block + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << block if block + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [*block] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?block: BlockNode?) -> ForwardingSuperNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, block: self.block) + ForwardingSuperNode.new(source, node_id, location, flags, block) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, block: BlockNode? } + def deconstruct_keys(keys) + { node_id: node_id, location: location, block: block } + end + + # All other arguments are forwarded as normal, except the original block is replaced with the new block. + attr_reader :block + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :forwarding_super_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :forwarding_super_node + 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?(ForwardingSuperNode) && + (block === other.block) + end + end + + # Represents the use of the `&&=` operator for assignment to a global variable. + # + # $target &&= value + # ^^^^^^^^^^^^^^^^^ + class GlobalVariableAndWriteNode < Node + # Initialize a new GlobalVariableAndWriteNode node. + def initialize(source, node_id, location, flags, name, name_loc, operator_loc, value) + @source = source + @node_id = node_id + @location = location + @flags = flags + @name = name + @name_loc = name_loc + @operator_loc = operator_loc + @value = value + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_global_variable_and_write_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [value] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield value + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [value] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [name_loc, operator_loc, value] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> GlobalVariableAndWriteNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc, operator_loc: self.operator_loc, value: self.value) + GlobalVariableAndWriteNode.new(source, node_id, location, flags, name, name_loc, operator_loc, value) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, name_loc: Location, operator_loc: Location, value: Prism::node } + def deconstruct_keys(keys) + { node_id: node_id, location: location, name: name, name_loc: name_loc, operator_loc: operator_loc, value: value } + end + + # attr_reader name: Symbol + attr_reader :name + + # attr_reader name_loc: Location + def name_loc + location = @name_loc + return location if location.is_a?(Location) + @name_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + def save_name_loc(repository) + repository.enter(node_id, :name_loc) + end + + # attr_reader operator_loc: Location + def operator_loc + location = @operator_loc + return location if location.is_a?(Location) + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) + end + + # attr_reader value: Prism::node + attr_reader :value + + # def operator: () -> String + def operator + operator_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :global_variable_and_write_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :global_variable_and_write_node + 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?(GlobalVariableAndWriteNode) && + (name === other.name) && + (name_loc.nil? == other.name_loc.nil?) && + (operator_loc.nil? == other.operator_loc.nil?) && + (value === other.value) + end + end + + # Represents assigning to a global variable using an operator that isn't `=`. + # + # $target += value + # ^^^^^^^^^^^^^^^^ + class GlobalVariableOperatorWriteNode < Node + # Initialize a new GlobalVariableOperatorWriteNode node. + def initialize(source, node_id, location, flags, name, name_loc, binary_operator_loc, value, binary_operator) + @source = source + @node_id = node_id + @location = location + @flags = flags + @name = name + @name_loc = name_loc + @binary_operator_loc = binary_operator_loc + @value = value + @binary_operator = binary_operator + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_global_variable_operator_write_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [value] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield value + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [value] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [name_loc, binary_operator_loc, value] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?binary_operator_loc: Location, ?value: Prism::node, ?binary_operator: Symbol) -> GlobalVariableOperatorWriteNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc, binary_operator_loc: self.binary_operator_loc, value: self.value, binary_operator: self.binary_operator) + GlobalVariableOperatorWriteNode.new(source, node_id, location, flags, name, name_loc, binary_operator_loc, value, binary_operator) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, name_loc: Location, binary_operator_loc: Location, value: Prism::node, binary_operator: Symbol } + def deconstruct_keys(keys) + { node_id: node_id, location: location, name: name, name_loc: name_loc, binary_operator_loc: binary_operator_loc, value: value, binary_operator: binary_operator } + end + + # attr_reader name: Symbol + attr_reader :name + + # attr_reader name_loc: Location + def name_loc + location = @name_loc + return location if location.is_a?(Location) + @name_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + def save_name_loc(repository) + repository.enter(node_id, :name_loc) + end + + # attr_reader binary_operator_loc: Location + def binary_operator_loc + location = @binary_operator_loc + return location if location.is_a?(Location) + @binary_operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the binary_operator_loc location using the given saved source so that + # it can be retrieved later. + def save_binary_operator_loc(repository) + repository.enter(node_id, :binary_operator_loc) + end + + # attr_reader value: Prism::node + attr_reader :value + + # attr_reader binary_operator: Symbol + attr_reader :binary_operator + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :global_variable_operator_write_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :global_variable_operator_write_node + 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?(GlobalVariableOperatorWriteNode) && + (name === other.name) && + (name_loc.nil? == other.name_loc.nil?) && + (binary_operator_loc.nil? == other.binary_operator_loc.nil?) && + (value === other.value) && + (binary_operator === other.binary_operator) + end + end + + # Represents the use of the `||=` operator for assignment to a global variable. + # + # $target ||= value + # ^^^^^^^^^^^^^^^^^ + class GlobalVariableOrWriteNode < Node + # Initialize a new GlobalVariableOrWriteNode node. + def initialize(source, node_id, location, flags, name, name_loc, operator_loc, value) + @source = source + @node_id = node_id + @location = location + @flags = flags + @name = name + @name_loc = name_loc + @operator_loc = operator_loc + @value = value + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_global_variable_or_write_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [value] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield value + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [value] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [name_loc, operator_loc, value] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> GlobalVariableOrWriteNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc, operator_loc: self.operator_loc, value: self.value) + GlobalVariableOrWriteNode.new(source, node_id, location, flags, name, name_loc, operator_loc, value) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, name_loc: Location, operator_loc: Location, value: Prism::node } + def deconstruct_keys(keys) + { node_id: node_id, location: location, name: name, name_loc: name_loc, operator_loc: operator_loc, value: value } + end + + # attr_reader name: Symbol + attr_reader :name + + # attr_reader name_loc: Location + def name_loc + location = @name_loc + return location if location.is_a?(Location) + @name_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + def save_name_loc(repository) + repository.enter(node_id, :name_loc) + end + + # attr_reader operator_loc: Location + def operator_loc + location = @operator_loc + return location if location.is_a?(Location) + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) + end + + # attr_reader value: Prism::node + attr_reader :value + + # def operator: () -> String + def operator + operator_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :global_variable_or_write_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :global_variable_or_write_node + 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?(GlobalVariableOrWriteNode) && + (name === other.name) && + (name_loc.nil? == other.name_loc.nil?) && + (operator_loc.nil? == other.operator_loc.nil?) && + (value === other.value) + end + end + + # Represents referencing a global variable. + # + # $foo + # ^^^^ + class GlobalVariableReadNode < Node + # Initialize a new GlobalVariableReadNode node. + def initialize(source, node_id, location, flags, name) + @source = source + @node_id = node_id + @location = location + @flags = flags + @name = name + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_global_variable_read_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> GlobalVariableReadNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name) + GlobalVariableReadNode.new(source, node_id, location, flags, name) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol } + def deconstruct_keys(keys) + { node_id: node_id, location: location, name: name } + end + + # 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` + attr_reader :name + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :global_variable_read_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :global_variable_read_node + 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?(GlobalVariableReadNode) && + (name === other.name) + end + end + + # Represents writing to a global variable in a context that doesn't have an explicit value. + # + # $foo, $bar = baz + # ^^^^ ^^^^ + class GlobalVariableTargetNode < Node + # Initialize a new GlobalVariableTargetNode node. + def initialize(source, node_id, location, flags, name) + @source = source + @node_id = node_id + @location = location + @flags = flags + @name = name + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_global_variable_target_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> GlobalVariableTargetNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name) + GlobalVariableTargetNode.new(source, node_id, location, flags, name) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol } + def deconstruct_keys(keys) + { node_id: node_id, location: location, name: name } + end + + # attr_reader name: Symbol + attr_reader :name + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :global_variable_target_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :global_variable_target_node + 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?(GlobalVariableTargetNode) && + (name === other.name) + end + end + + # Represents writing to a global variable. + # + # $foo = 1 + # ^^^^^^^^ + class GlobalVariableWriteNode < Node + # Initialize a new GlobalVariableWriteNode node. + def initialize(source, node_id, location, flags, name, name_loc, value, operator_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @name = name + @name_loc = name_loc + @value = value + @operator_loc = operator_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_global_variable_write_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [value] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield value + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [value] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [name_loc, value, operator_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?value: Prism::node, ?operator_loc: Location) -> GlobalVariableWriteNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc, value: self.value, operator_loc: self.operator_loc) + GlobalVariableWriteNode.new(source, node_id, location, flags, name, name_loc, value, operator_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, name_loc: Location, value: Prism::node, operator_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, name: name, name_loc: name_loc, value: value, operator_loc: operator_loc } + end + + # 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` + attr_reader :name + + # The location of the global variable's name. + # + # $foo = :bar + # ^^^^ + def name_loc + location = @name_loc + return location if location.is_a?(Location) + @name_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + def save_name_loc(repository) + repository.enter(node_id, :name_loc) + end + + # 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 + # ^^^ + attr_reader :value + + # The location of the `=` operator. + # + # $foo = :bar + # ^ + def operator_loc + location = @operator_loc + return location if location.is_a?(Location) + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) + end + + # def operator: () -> String + def operator + operator_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :global_variable_write_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :global_variable_write_node + 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?(GlobalVariableWriteNode) && + (name === other.name) && + (name_loc.nil? == other.name_loc.nil?) && + (value === other.value) && + (operator_loc.nil? == other.operator_loc.nil?) + end + end + + # Represents a hash literal. + # + # { a => b } + # ^^^^^^^^^^ + class HashNode < Node + # Initialize a new HashNode node. + def initialize(source, node_id, location, flags, opening_loc, elements, closing_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @opening_loc = opening_loc + @elements = elements + @closing_loc = closing_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_hash_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [*elements] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + elements.each { |node| yield node } + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [*elements] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [opening_loc, *elements, closing_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?elements: Array[AssocNode | AssocSplatNode], ?closing_loc: Location) -> HashNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, opening_loc: self.opening_loc, elements: self.elements, closing_loc: self.closing_loc) + HashNode.new(source, node_id, location, flags, opening_loc, elements, closing_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, opening_loc: Location, elements: Array[AssocNode | AssocSplatNode], closing_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, opening_loc: opening_loc, elements: elements, closing_loc: closing_loc } + end + + # The location of the opening brace. + # + # { a => b } + # ^ + def opening_loc + location = @opening_loc + return location if location.is_a?(Location) + @opening_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + def save_opening_loc(repository) + repository.enter(node_id, :opening_loc) + end + + # The elements of the hash. These can be either `AssocNode`s or `AssocSplatNode`s. + # + # { a: b } + # ^^^^ + # + # { **foo } + # ^^^^^ + attr_reader :elements + + # The location of the closing brace. + # + # { a => b } + # ^ + def closing_loc + location = @closing_loc + return location if location.is_a?(Location) + @closing_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + def save_closing_loc(repository) + repository.enter(node_id, :closing_loc) + end + + # def opening: () -> String + def opening + opening_loc.slice + end + + # def closing: () -> String + def closing + closing_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :hash_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :hash_node + 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?(HashNode) && + (opening_loc.nil? == other.opening_loc.nil?) && + (elements.length == other.elements.length) && + elements.zip(other.elements).all? { |left, right| left === right } && + (closing_loc.nil? == other.closing_loc.nil?) + end + end + + # Represents a hash pattern in pattern matching. + # + # foo => { a: 1, b: 2 } + # ^^^^^^^^^^^^^^ + # + # foo => { a: 1, b: 2, **c } + # ^^^^^^^^^^^^^^^^^^^ + # + # foo => Bar[a: 1, b: 2] + # ^^^^^^^^^^^^^^^ + # + # foo in { a: 1, b: 2 } + # ^^^^^^^^^^^^^^ + class HashPatternNode < Node + # Initialize a new HashPatternNode node. + def initialize(source, node_id, location, flags, constant, elements, rest, opening_loc, closing_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @constant = constant + @elements = elements + @rest = rest + @opening_loc = opening_loc + @closing_loc = closing_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_hash_pattern_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [constant, *elements, rest] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield constant if constant + elements.each { |node| yield node } + yield rest if rest + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << constant if constant + compact.concat(elements) + compact << rest if rest + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [*constant, *elements, *rest, *opening_loc, *closing_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?constant: ConstantPathNode | ConstantReadNode | nil, ?elements: Array[AssocNode], ?rest: AssocSplatNode | NoKeywordsParameterNode | nil, ?opening_loc: Location?, ?closing_loc: Location?) -> HashPatternNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, constant: self.constant, elements: self.elements, rest: self.rest, opening_loc: self.opening_loc, closing_loc: self.closing_loc) + HashPatternNode.new(source, node_id, location, flags, constant, elements, rest, opening_loc, closing_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, constant: ConstantPathNode | ConstantReadNode | nil, elements: Array[AssocNode], rest: AssocSplatNode | NoKeywordsParameterNode | nil, opening_loc: Location?, closing_loc: Location? } + def deconstruct_keys(keys) + { node_id: node_id, location: location, constant: constant, elements: elements, rest: rest, opening_loc: opening_loc, closing_loc: closing_loc } + end + + # Represents the optional constant preceding the Hash. + # + # foo => Bar[a: 1, b: 2] + # ^^^ + # + # foo => Bar::Baz[a: 1, b: 2] + # ^^^^^^^^ + attr_reader :constant + + # Represents the explicit named hash keys and values. + # + # foo => { a: 1, b:, ** } + # ^^^^^^^^ + attr_reader :elements + + # Represents the rest of the Hash keys and values. This can be named, unnamed, or explicitly forbidden via `**nil`, this last one results in a `NoKeywordsParameterNode`. + # + # foo => { a: 1, b:, **c } + # ^^^ + # + # foo => { a: 1, b:, ** } + # ^^ + # + # foo => { a: 1, b:, **nil } + # ^^^^^ + attr_reader :rest + + # The location of the opening brace. + # + # foo => { a: 1 } + # ^ + # + # foo => Bar[a: 1] + # ^ + def opening_loc + location = @opening_loc + case location + when nil + nil + when Location + location + else + @opening_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + def save_opening_loc(repository) + repository.enter(node_id, :opening_loc) unless @opening_loc.nil? + end + + # The location of the closing brace. + # + # foo => { a: 1 } + # ^ + # + # foo => Bar[a: 1] + # ^ + def closing_loc + location = @closing_loc + case location + when nil + nil + when Location + location + else + @closing_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + def save_closing_loc(repository) + repository.enter(node_id, :closing_loc) unless @closing_loc.nil? + end + + # def opening: () -> String? + def opening + opening_loc&.slice + end + + # def closing: () -> String? + def closing + closing_loc&.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :hash_pattern_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :hash_pattern_node + 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?(HashPatternNode) && + (constant === other.constant) && + (elements.length == other.elements.length) && + elements.zip(other.elements).all? { |left, right| left === right } && + (rest === other.rest) && + (opening_loc.nil? == other.opening_loc.nil?) && + (closing_loc.nil? == other.closing_loc.nil?) + end + end + + # 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 + # ^^^^^^^^^^^^^^^ + class IfNode < Node + # Initialize a new IfNode node. + def initialize(source, node_id, location, flags, if_keyword_loc, predicate, then_keyword_loc, statements, subsequent, end_keyword_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @if_keyword_loc = if_keyword_loc + @predicate = predicate + @then_keyword_loc = then_keyword_loc + @statements = statements + @subsequent = subsequent + @end_keyword_loc = end_keyword_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_if_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [predicate, statements, subsequent] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield predicate + yield statements if statements + yield subsequent if subsequent + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << predicate + compact << statements if statements + compact << subsequent if subsequent + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [*if_keyword_loc, predicate, *then_keyword_loc, *statements, *subsequent, *end_keyword_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?if_keyword_loc: Location?, ?predicate: Prism::node, ?then_keyword_loc: Location?, ?statements: StatementsNode?, ?subsequent: ElseNode | IfNode | nil, ?end_keyword_loc: Location?) -> IfNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, if_keyword_loc: self.if_keyword_loc, predicate: self.predicate, then_keyword_loc: self.then_keyword_loc, statements: self.statements, subsequent: self.subsequent, end_keyword_loc: self.end_keyword_loc) + IfNode.new(source, node_id, location, flags, if_keyword_loc, predicate, then_keyword_loc, statements, subsequent, end_keyword_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, if_keyword_loc: Location?, predicate: Prism::node, then_keyword_loc: Location?, statements: StatementsNode?, subsequent: ElseNode | IfNode | nil, end_keyword_loc: Location? } + def deconstruct_keys(keys) + { node_id: node_id, location: location, if_keyword_loc: if_keyword_loc, predicate: predicate, then_keyword_loc: then_keyword_loc, statements: statements, subsequent: subsequent, end_keyword_loc: end_keyword_loc } + end + + # 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. + def if_keyword_loc + location = @if_keyword_loc + case location + when nil + nil + when Location + location + else + @if_keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the if_keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_if_keyword_loc(repository) + repository.enter(node_id, :if_keyword_loc) unless @if_keyword_loc.nil? + end + + # The node for the condition the `IfNode` is testing. + # + # if foo + # ^^^ + # bar + # end + # + # bar if foo + # ^^^ + # + # foo ? bar : baz + # ^^^ + attr_reader :predicate + + # The location of the `then` keyword (if present) or the `?` in a ternary expression, `nil` otherwise. + # + # if foo then bar end + # ^^^^ + # + # a ? b : c + # ^ + def then_keyword_loc + location = @then_keyword_loc + case location + when nil + nil + when Location + location + else + @then_keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the then_keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_then_keyword_loc(repository) + repository.enter(node_id, :then_keyword_loc) unless @then_keyword_loc.nil? + end + + # 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 + attr_reader :statements + + # 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 + # ^^^^^^^^^^^^ + attr_reader :subsequent + + # The location of the `end` keyword if present, `nil` otherwise. + # + # if foo + # bar + # end + # ^^^ + def end_keyword_loc + location = @end_keyword_loc + case location + when nil + nil + when Location + location + else + @end_keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the end_keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_end_keyword_loc(repository) + repository.enter(node_id, :end_keyword_loc) unless @end_keyword_loc.nil? + end + + # def if_keyword: () -> String? + def if_keyword + if_keyword_loc&.slice + end + + # def then_keyword: () -> String? + def then_keyword + then_keyword_loc&.slice + end + + # def end_keyword: () -> String? + def end_keyword + end_keyword_loc&.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :if_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :if_node + 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?(IfNode) && + (if_keyword_loc.nil? == other.if_keyword_loc.nil?) && + (predicate === other.predicate) && + (then_keyword_loc.nil? == other.then_keyword_loc.nil?) && + (statements === other.statements) && + (subsequent === other.subsequent) && + (end_keyword_loc.nil? == other.end_keyword_loc.nil?) + end + end + + # Represents an imaginary number literal. + # + # 1.0i + # ^^^^ + class ImaginaryNode < Node + # Initialize a new ImaginaryNode node. + def initialize(source, node_id, location, flags, numeric) + @source = source + @node_id = node_id + @location = location + @flags = flags + @numeric = numeric + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_imaginary_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [numeric] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield numeric + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [numeric] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [numeric] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?numeric: FloatNode | IntegerNode | RationalNode) -> ImaginaryNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, numeric: self.numeric) + ImaginaryNode.new(source, node_id, location, flags, numeric) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, numeric: FloatNode | IntegerNode | RationalNode } + def deconstruct_keys(keys) + { node_id: node_id, location: location, numeric: numeric } + end + + # attr_reader numeric: FloatNode | IntegerNode | RationalNode + attr_reader :numeric + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :imaginary_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :imaginary_node + 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?(ImaginaryNode) && + (numeric === other.numeric) + end + end + + # 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: } + # ^^^^ + class ImplicitNode < Node + # Initialize a new ImplicitNode node. + def initialize(source, node_id, location, flags, value) + @source = source + @node_id = node_id + @location = location + @flags = flags + @value = value + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_implicit_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [value] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield value + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [value] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [value] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?value: LocalVariableReadNode | CallNode | ConstantReadNode | LocalVariableTargetNode) -> ImplicitNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, value: self.value) + ImplicitNode.new(source, node_id, location, flags, value) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, value: LocalVariableReadNode | CallNode | ConstantReadNode | LocalVariableTargetNode } + def deconstruct_keys(keys) + { node_id: node_id, location: location, value: value } + end + + # attr_reader value: LocalVariableReadNode | CallNode | ConstantReadNode | LocalVariableTargetNode + attr_reader :value + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :implicit_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :implicit_node + 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?(ImplicitNode) && + (value === other.value) + end + end + + # Represents using a trailing comma to indicate an implicit rest parameter. + # + # foo { |bar,| } + # ^ + # + # foo in [bar,] + # ^ + # + # for foo, in bar do end + # ^ + # + # foo, = bar + # ^ + class ImplicitRestNode < Node + # Initialize a new ImplicitRestNode node. + def initialize(source, node_id, location, flags) + @source = source + @node_id = node_id + @location = location + @flags = flags + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_implicit_rest_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer) -> ImplicitRestNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags) + ImplicitRestNode.new(source, node_id, location, flags) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location } + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :implicit_rest_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :implicit_rest_node + 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?(ImplicitRestNode) + end + end + + # Represents the use of the `in` keyword in a case statement. + # + # case a; in b then c end + # ^^^^^^^^^^^ + class InNode < Node + # Initialize a new InNode node. + def initialize(source, node_id, location, flags, pattern, statements, in_loc, then_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @pattern = pattern + @statements = statements + @in_loc = in_loc + @then_loc = then_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_in_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [pattern, statements] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield pattern + yield statements if statements + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << pattern + compact << statements if statements + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [pattern, *statements, in_loc, *then_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?pattern: Prism::node, ?statements: StatementsNode?, ?in_loc: Location, ?then_loc: Location?) -> InNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, pattern: self.pattern, statements: self.statements, in_loc: self.in_loc, then_loc: self.then_loc) + InNode.new(source, node_id, location, flags, pattern, statements, in_loc, then_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, pattern: Prism::node, statements: StatementsNode?, in_loc: Location, then_loc: Location? } + def deconstruct_keys(keys) + { node_id: node_id, location: location, pattern: pattern, statements: statements, in_loc: in_loc, then_loc: then_loc } + end + + # attr_reader pattern: Prism::node + attr_reader :pattern + + # attr_reader statements: StatementsNode? + attr_reader :statements + + # attr_reader in_loc: Location + def in_loc + location = @in_loc + return location if location.is_a?(Location) + @in_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the in_loc location using the given saved source so that + # it can be retrieved later. + def save_in_loc(repository) + repository.enter(node_id, :in_loc) + end + + # attr_reader then_loc: Location? + def then_loc + location = @then_loc + case location + when nil + nil + when Location + location + else + @then_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the then_loc location using the given saved source so that + # it can be retrieved later. + def save_then_loc(repository) + repository.enter(node_id, :then_loc) unless @then_loc.nil? + end + + # def in: () -> String + def in + in_loc.slice + end + + # def then: () -> String? + def then + then_loc&.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :in_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :in_node + 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?(InNode) && + (pattern === other.pattern) && + (statements === other.statements) && + (in_loc.nil? == other.in_loc.nil?) && + (then_loc.nil? == other.then_loc.nil?) + end + end + + # Represents the use of the `&&=` operator on a call to the `[]` method. + # + # foo.bar[baz] &&= value + # ^^^^^^^^^^^^^^^^^^^^^^ + class IndexAndWriteNode < Node + # Initialize a new IndexAndWriteNode node. + def initialize(source, node_id, location, flags, receiver, call_operator_loc, opening_loc, arguments, closing_loc, block, operator_loc, value) + @source = source + @node_id = node_id + @location = location + @flags = flags + @receiver = receiver + @call_operator_loc = call_operator_loc + @opening_loc = opening_loc + @arguments = arguments + @closing_loc = closing_loc + @block = block + @operator_loc = operator_loc + @value = value + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_index_and_write_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [receiver, arguments, block, value] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield receiver if receiver + yield arguments if arguments + yield block if block + yield value + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << receiver if receiver + compact << arguments if arguments + compact << block if block + compact << value + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [*receiver, *call_operator_loc, opening_loc, *arguments, closing_loc, *block, operator_loc, value] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?receiver: Prism::node?, ?call_operator_loc: Location?, ?opening_loc: Location, ?arguments: ArgumentsNode?, ?closing_loc: Location, ?block: BlockArgumentNode?, ?operator_loc: Location, ?value: Prism::node) -> IndexAndWriteNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, receiver: self.receiver, call_operator_loc: self.call_operator_loc, opening_loc: self.opening_loc, arguments: self.arguments, closing_loc: self.closing_loc, block: self.block, operator_loc: self.operator_loc, value: self.value) + IndexAndWriteNode.new(source, node_id, location, flags, receiver, call_operator_loc, opening_loc, arguments, closing_loc, block, operator_loc, value) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, receiver: Prism::node?, call_operator_loc: Location?, opening_loc: Location, arguments: ArgumentsNode?, closing_loc: Location, block: BlockArgumentNode?, operator_loc: Location, value: Prism::node } + def deconstruct_keys(keys) + { node_id: node_id, location: location, receiver: receiver, call_operator_loc: call_operator_loc, opening_loc: opening_loc, arguments: arguments, closing_loc: closing_loc, block: block, operator_loc: operator_loc, value: value } + end + + # def safe_navigation?: () -> bool + def safe_navigation? + flags.anybits?(CallNodeFlags::SAFE_NAVIGATION) + end + + # def variable_call?: () -> bool + def variable_call? + flags.anybits?(CallNodeFlags::VARIABLE_CALL) + end + + # def attribute_write?: () -> bool + def attribute_write? + flags.anybits?(CallNodeFlags::ATTRIBUTE_WRITE) + end + + # def ignore_visibility?: () -> bool + def ignore_visibility? + flags.anybits?(CallNodeFlags::IGNORE_VISIBILITY) + end + + # attr_reader receiver: Prism::node? + attr_reader :receiver + + # attr_reader call_operator_loc: Location? + def call_operator_loc + location = @call_operator_loc + case location + when nil + nil + when Location + location + else + @call_operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the call_operator_loc location using the given saved source so that + # it can be retrieved later. + def save_call_operator_loc(repository) + repository.enter(node_id, :call_operator_loc) unless @call_operator_loc.nil? + end + + # attr_reader opening_loc: Location + def opening_loc + location = @opening_loc + return location if location.is_a?(Location) + @opening_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + def save_opening_loc(repository) + repository.enter(node_id, :opening_loc) + end + + # attr_reader arguments: ArgumentsNode? + attr_reader :arguments + + # attr_reader closing_loc: Location + def closing_loc + location = @closing_loc + return location if location.is_a?(Location) + @closing_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + def save_closing_loc(repository) + repository.enter(node_id, :closing_loc) + end + + # attr_reader block: BlockArgumentNode? + attr_reader :block + + # attr_reader operator_loc: Location + def operator_loc + location = @operator_loc + return location if location.is_a?(Location) + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) + end + + # attr_reader value: Prism::node + attr_reader :value + + # def call_operator: () -> String? + def call_operator + call_operator_loc&.slice + end + + # def opening: () -> String + def opening + opening_loc.slice + end + + # def closing: () -> String + def closing + closing_loc.slice + end + + # def operator: () -> String + def operator + operator_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :index_and_write_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :index_and_write_node + 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?(IndexAndWriteNode) && + (flags === other.flags) && + (receiver === other.receiver) && + (call_operator_loc.nil? == other.call_operator_loc.nil?) && + (opening_loc.nil? == other.opening_loc.nil?) && + (arguments === other.arguments) && + (closing_loc.nil? == other.closing_loc.nil?) && + (block === other.block) && + (operator_loc.nil? == other.operator_loc.nil?) && + (value === other.value) + end + end + + # Represents the use of an assignment operator on a call to `[]`. + # + # foo.bar[baz] += value + # ^^^^^^^^^^^^^^^^^^^^^ + class IndexOperatorWriteNode < Node + # Initialize a new IndexOperatorWriteNode node. + def initialize(source, node_id, location, flags, receiver, call_operator_loc, opening_loc, arguments, closing_loc, block, binary_operator, binary_operator_loc, value) + @source = source + @node_id = node_id + @location = location + @flags = flags + @receiver = receiver + @call_operator_loc = call_operator_loc + @opening_loc = opening_loc + @arguments = arguments + @closing_loc = closing_loc + @block = block + @binary_operator = binary_operator + @binary_operator_loc = binary_operator_loc + @value = value + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_index_operator_write_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [receiver, arguments, block, value] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield receiver if receiver + yield arguments if arguments + yield block if block + yield value + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << receiver if receiver + compact << arguments if arguments + compact << block if block + compact << value + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [*receiver, *call_operator_loc, opening_loc, *arguments, closing_loc, *block, binary_operator_loc, value] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?receiver: Prism::node?, ?call_operator_loc: Location?, ?opening_loc: Location, ?arguments: ArgumentsNode?, ?closing_loc: Location, ?block: BlockArgumentNode?, ?binary_operator: Symbol, ?binary_operator_loc: Location, ?value: Prism::node) -> IndexOperatorWriteNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, receiver: self.receiver, call_operator_loc: self.call_operator_loc, opening_loc: self.opening_loc, arguments: self.arguments, closing_loc: self.closing_loc, block: self.block, binary_operator: self.binary_operator, binary_operator_loc: self.binary_operator_loc, value: self.value) + IndexOperatorWriteNode.new(source, node_id, location, flags, receiver, call_operator_loc, opening_loc, arguments, closing_loc, block, binary_operator, binary_operator_loc, value) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, receiver: Prism::node?, call_operator_loc: Location?, opening_loc: Location, arguments: ArgumentsNode?, closing_loc: Location, block: BlockArgumentNode?, binary_operator: Symbol, binary_operator_loc: Location, value: Prism::node } + def deconstruct_keys(keys) + { node_id: node_id, location: location, receiver: receiver, call_operator_loc: call_operator_loc, opening_loc: opening_loc, arguments: arguments, closing_loc: closing_loc, block: block, binary_operator: binary_operator, binary_operator_loc: binary_operator_loc, value: value } + end + + # def safe_navigation?: () -> bool + def safe_navigation? + flags.anybits?(CallNodeFlags::SAFE_NAVIGATION) + end + + # def variable_call?: () -> bool + def variable_call? + flags.anybits?(CallNodeFlags::VARIABLE_CALL) + end + + # def attribute_write?: () -> bool + def attribute_write? + flags.anybits?(CallNodeFlags::ATTRIBUTE_WRITE) + end + + # def ignore_visibility?: () -> bool + def ignore_visibility? + flags.anybits?(CallNodeFlags::IGNORE_VISIBILITY) + end + + # attr_reader receiver: Prism::node? + attr_reader :receiver + + # attr_reader call_operator_loc: Location? + def call_operator_loc + location = @call_operator_loc + case location + when nil + nil + when Location + location + else + @call_operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the call_operator_loc location using the given saved source so that + # it can be retrieved later. + def save_call_operator_loc(repository) + repository.enter(node_id, :call_operator_loc) unless @call_operator_loc.nil? + end + + # attr_reader opening_loc: Location + def opening_loc + location = @opening_loc + return location if location.is_a?(Location) + @opening_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + def save_opening_loc(repository) + repository.enter(node_id, :opening_loc) + end + + # attr_reader arguments: ArgumentsNode? + attr_reader :arguments + + # attr_reader closing_loc: Location + def closing_loc + location = @closing_loc + return location if location.is_a?(Location) + @closing_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + def save_closing_loc(repository) + repository.enter(node_id, :closing_loc) + end + + # attr_reader block: BlockArgumentNode? + attr_reader :block + + # attr_reader binary_operator: Symbol + attr_reader :binary_operator + + # attr_reader binary_operator_loc: Location + def binary_operator_loc + location = @binary_operator_loc + return location if location.is_a?(Location) + @binary_operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the binary_operator_loc location using the given saved source so that + # it can be retrieved later. + def save_binary_operator_loc(repository) + repository.enter(node_id, :binary_operator_loc) + end + + # attr_reader value: Prism::node + attr_reader :value + + # def call_operator: () -> String? + def call_operator + call_operator_loc&.slice + end + + # def opening: () -> String + def opening + opening_loc.slice + end + + # def closing: () -> String + def closing + closing_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :index_operator_write_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :index_operator_write_node + 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?(IndexOperatorWriteNode) && + (flags === other.flags) && + (receiver === other.receiver) && + (call_operator_loc.nil? == other.call_operator_loc.nil?) && + (opening_loc.nil? == other.opening_loc.nil?) && + (arguments === other.arguments) && + (closing_loc.nil? == other.closing_loc.nil?) && + (block === other.block) && + (binary_operator === other.binary_operator) && + (binary_operator_loc.nil? == other.binary_operator_loc.nil?) && + (value === other.value) + end + end + + # Represents the use of the `||=` operator on a call to `[]`. + # + # foo.bar[baz] ||= value + # ^^^^^^^^^^^^^^^^^^^^^^ + class IndexOrWriteNode < Node + # Initialize a new IndexOrWriteNode node. + def initialize(source, node_id, location, flags, receiver, call_operator_loc, opening_loc, arguments, closing_loc, block, operator_loc, value) + @source = source + @node_id = node_id + @location = location + @flags = flags + @receiver = receiver + @call_operator_loc = call_operator_loc + @opening_loc = opening_loc + @arguments = arguments + @closing_loc = closing_loc + @block = block + @operator_loc = operator_loc + @value = value + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_index_or_write_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [receiver, arguments, block, value] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield receiver if receiver + yield arguments if arguments + yield block if block + yield value + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << receiver if receiver + compact << arguments if arguments + compact << block if block + compact << value + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [*receiver, *call_operator_loc, opening_loc, *arguments, closing_loc, *block, operator_loc, value] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?receiver: Prism::node?, ?call_operator_loc: Location?, ?opening_loc: Location, ?arguments: ArgumentsNode?, ?closing_loc: Location, ?block: BlockArgumentNode?, ?operator_loc: Location, ?value: Prism::node) -> IndexOrWriteNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, receiver: self.receiver, call_operator_loc: self.call_operator_loc, opening_loc: self.opening_loc, arguments: self.arguments, closing_loc: self.closing_loc, block: self.block, operator_loc: self.operator_loc, value: self.value) + IndexOrWriteNode.new(source, node_id, location, flags, receiver, call_operator_loc, opening_loc, arguments, closing_loc, block, operator_loc, value) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, receiver: Prism::node?, call_operator_loc: Location?, opening_loc: Location, arguments: ArgumentsNode?, closing_loc: Location, block: BlockArgumentNode?, operator_loc: Location, value: Prism::node } + def deconstruct_keys(keys) + { node_id: node_id, location: location, receiver: receiver, call_operator_loc: call_operator_loc, opening_loc: opening_loc, arguments: arguments, closing_loc: closing_loc, block: block, operator_loc: operator_loc, value: value } + end + + # def safe_navigation?: () -> bool + def safe_navigation? + flags.anybits?(CallNodeFlags::SAFE_NAVIGATION) + end + + # def variable_call?: () -> bool + def variable_call? + flags.anybits?(CallNodeFlags::VARIABLE_CALL) + end + + # def attribute_write?: () -> bool + def attribute_write? + flags.anybits?(CallNodeFlags::ATTRIBUTE_WRITE) + end + + # def ignore_visibility?: () -> bool + def ignore_visibility? + flags.anybits?(CallNodeFlags::IGNORE_VISIBILITY) + end + + # attr_reader receiver: Prism::node? + attr_reader :receiver + + # attr_reader call_operator_loc: Location? + def call_operator_loc + location = @call_operator_loc + case location + when nil + nil + when Location + location + else + @call_operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the call_operator_loc location using the given saved source so that + # it can be retrieved later. + def save_call_operator_loc(repository) + repository.enter(node_id, :call_operator_loc) unless @call_operator_loc.nil? + end + + # attr_reader opening_loc: Location + def opening_loc + location = @opening_loc + return location if location.is_a?(Location) + @opening_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + def save_opening_loc(repository) + repository.enter(node_id, :opening_loc) + end + + # attr_reader arguments: ArgumentsNode? + attr_reader :arguments + + # attr_reader closing_loc: Location + def closing_loc + location = @closing_loc + return location if location.is_a?(Location) + @closing_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + def save_closing_loc(repository) + repository.enter(node_id, :closing_loc) + end + + # attr_reader block: BlockArgumentNode? + attr_reader :block + + # attr_reader operator_loc: Location + def operator_loc + location = @operator_loc + return location if location.is_a?(Location) + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) + end + + # attr_reader value: Prism::node + attr_reader :value + + # def call_operator: () -> String? + def call_operator + call_operator_loc&.slice + end + + # def opening: () -> String + def opening + opening_loc.slice + end + + # def closing: () -> String + def closing + closing_loc.slice + end + + # def operator: () -> String + def operator + operator_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :index_or_write_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :index_or_write_node + 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?(IndexOrWriteNode) && + (flags === other.flags) && + (receiver === other.receiver) && + (call_operator_loc.nil? == other.call_operator_loc.nil?) && + (opening_loc.nil? == other.opening_loc.nil?) && + (arguments === other.arguments) && + (closing_loc.nil? == other.closing_loc.nil?) && + (block === other.block) && + (operator_loc.nil? == other.operator_loc.nil?) && + (value === other.value) + end + end + + # Represents assigning to an index. + # + # foo[bar], = 1 + # ^^^^^^^^ + # + # begin + # rescue => foo[bar] + # ^^^^^^^^ + # end + # + # for foo[bar] in baz do end + # ^^^^^^^^ + class IndexTargetNode < Node + # Initialize a new IndexTargetNode node. + def initialize(source, node_id, location, flags, receiver, opening_loc, arguments, closing_loc, block) + @source = source + @node_id = node_id + @location = location + @flags = flags + @receiver = receiver + @opening_loc = opening_loc + @arguments = arguments + @closing_loc = closing_loc + @block = block + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_index_target_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [receiver, arguments, block] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield receiver + yield arguments if arguments + yield block if block + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << receiver + compact << arguments if arguments + compact << block if block + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [receiver, opening_loc, *arguments, closing_loc, *block] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?receiver: Prism::node, ?opening_loc: Location, ?arguments: ArgumentsNode?, ?closing_loc: Location, ?block: BlockArgumentNode?) -> IndexTargetNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, receiver: self.receiver, opening_loc: self.opening_loc, arguments: self.arguments, closing_loc: self.closing_loc, block: self.block) + IndexTargetNode.new(source, node_id, location, flags, receiver, opening_loc, arguments, closing_loc, block) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, receiver: Prism::node, opening_loc: Location, arguments: ArgumentsNode?, closing_loc: Location, block: BlockArgumentNode? } + def deconstruct_keys(keys) + { node_id: node_id, location: location, receiver: receiver, opening_loc: opening_loc, arguments: arguments, closing_loc: closing_loc, block: block } + end + + # def safe_navigation?: () -> bool + def safe_navigation? + flags.anybits?(CallNodeFlags::SAFE_NAVIGATION) + end + + # def variable_call?: () -> bool + def variable_call? + flags.anybits?(CallNodeFlags::VARIABLE_CALL) + end + + # def attribute_write?: () -> bool + def attribute_write? + flags.anybits?(CallNodeFlags::ATTRIBUTE_WRITE) + end + + # def ignore_visibility?: () -> bool + def ignore_visibility? + flags.anybits?(CallNodeFlags::IGNORE_VISIBILITY) + end + + # attr_reader receiver: Prism::node + attr_reader :receiver + + # attr_reader opening_loc: Location + def opening_loc + location = @opening_loc + return location if location.is_a?(Location) + @opening_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + def save_opening_loc(repository) + repository.enter(node_id, :opening_loc) + end + + # attr_reader arguments: ArgumentsNode? + attr_reader :arguments + + # attr_reader closing_loc: Location + def closing_loc + location = @closing_loc + return location if location.is_a?(Location) + @closing_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + def save_closing_loc(repository) + repository.enter(node_id, :closing_loc) + end + + # attr_reader block: BlockArgumentNode? + attr_reader :block + + # def opening: () -> String + def opening + opening_loc.slice + end + + # def closing: () -> String + def closing + closing_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :index_target_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :index_target_node + 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?(IndexTargetNode) && + (flags === other.flags) && + (receiver === other.receiver) && + (opening_loc.nil? == other.opening_loc.nil?) && + (arguments === other.arguments) && + (closing_loc.nil? == other.closing_loc.nil?) && + (block === other.block) + end + end + + # Represents the use of the `&&=` operator for assignment to an instance variable. + # + # @target &&= value + # ^^^^^^^^^^^^^^^^^ + class InstanceVariableAndWriteNode < Node + # Initialize a new InstanceVariableAndWriteNode node. + def initialize(source, node_id, location, flags, name, name_loc, operator_loc, value) + @source = source + @node_id = node_id + @location = location + @flags = flags + @name = name + @name_loc = name_loc + @operator_loc = operator_loc + @value = value + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_instance_variable_and_write_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [value] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield value + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [value] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [name_loc, operator_loc, value] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> InstanceVariableAndWriteNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc, operator_loc: self.operator_loc, value: self.value) + InstanceVariableAndWriteNode.new(source, node_id, location, flags, name, name_loc, operator_loc, value) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, name_loc: Location, operator_loc: Location, value: Prism::node } + def deconstruct_keys(keys) + { node_id: node_id, location: location, name: name, name_loc: name_loc, operator_loc: operator_loc, value: value } + end + + # attr_reader name: Symbol + attr_reader :name + + # attr_reader name_loc: Location + def name_loc + location = @name_loc + return location if location.is_a?(Location) + @name_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + def save_name_loc(repository) + repository.enter(node_id, :name_loc) + end + + # attr_reader operator_loc: Location + def operator_loc + location = @operator_loc + return location if location.is_a?(Location) + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) + end + + # attr_reader value: Prism::node + attr_reader :value + + # def operator: () -> String + def operator + operator_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :instance_variable_and_write_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :instance_variable_and_write_node + 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?(InstanceVariableAndWriteNode) && + (name === other.name) && + (name_loc.nil? == other.name_loc.nil?) && + (operator_loc.nil? == other.operator_loc.nil?) && + (value === other.value) + end + end + + # Represents assigning to an instance variable using an operator that isn't `=`. + # + # @target += value + # ^^^^^^^^^^^^^^^^ + class InstanceVariableOperatorWriteNode < Node + # Initialize a new InstanceVariableOperatorWriteNode node. + def initialize(source, node_id, location, flags, name, name_loc, binary_operator_loc, value, binary_operator) + @source = source + @node_id = node_id + @location = location + @flags = flags + @name = name + @name_loc = name_loc + @binary_operator_loc = binary_operator_loc + @value = value + @binary_operator = binary_operator + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_instance_variable_operator_write_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [value] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield value + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [value] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [name_loc, binary_operator_loc, value] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?binary_operator_loc: Location, ?value: Prism::node, ?binary_operator: Symbol) -> InstanceVariableOperatorWriteNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc, binary_operator_loc: self.binary_operator_loc, value: self.value, binary_operator: self.binary_operator) + InstanceVariableOperatorWriteNode.new(source, node_id, location, flags, name, name_loc, binary_operator_loc, value, binary_operator) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, name_loc: Location, binary_operator_loc: Location, value: Prism::node, binary_operator: Symbol } + def deconstruct_keys(keys) + { node_id: node_id, location: location, name: name, name_loc: name_loc, binary_operator_loc: binary_operator_loc, value: value, binary_operator: binary_operator } + end + + # attr_reader name: Symbol + attr_reader :name + + # attr_reader name_loc: Location + def name_loc + location = @name_loc + return location if location.is_a?(Location) + @name_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + def save_name_loc(repository) + repository.enter(node_id, :name_loc) + end + + # attr_reader binary_operator_loc: Location + def binary_operator_loc + location = @binary_operator_loc + return location if location.is_a?(Location) + @binary_operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the binary_operator_loc location using the given saved source so that + # it can be retrieved later. + def save_binary_operator_loc(repository) + repository.enter(node_id, :binary_operator_loc) + end + + # attr_reader value: Prism::node + attr_reader :value + + # attr_reader binary_operator: Symbol + attr_reader :binary_operator + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :instance_variable_operator_write_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :instance_variable_operator_write_node + 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?(InstanceVariableOperatorWriteNode) && + (name === other.name) && + (name_loc.nil? == other.name_loc.nil?) && + (binary_operator_loc.nil? == other.binary_operator_loc.nil?) && + (value === other.value) && + (binary_operator === other.binary_operator) + end + end + + # Represents the use of the `||=` operator for assignment to an instance variable. + # + # @target ||= value + # ^^^^^^^^^^^^^^^^^ + class InstanceVariableOrWriteNode < Node + # Initialize a new InstanceVariableOrWriteNode node. + def initialize(source, node_id, location, flags, name, name_loc, operator_loc, value) + @source = source + @node_id = node_id + @location = location + @flags = flags + @name = name + @name_loc = name_loc + @operator_loc = operator_loc + @value = value + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_instance_variable_or_write_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [value] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield value + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [value] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [name_loc, operator_loc, value] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> InstanceVariableOrWriteNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc, operator_loc: self.operator_loc, value: self.value) + InstanceVariableOrWriteNode.new(source, node_id, location, flags, name, name_loc, operator_loc, value) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, name_loc: Location, operator_loc: Location, value: Prism::node } + def deconstruct_keys(keys) + { node_id: node_id, location: location, name: name, name_loc: name_loc, operator_loc: operator_loc, value: value } + end + + # attr_reader name: Symbol + attr_reader :name + + # attr_reader name_loc: Location + def name_loc + location = @name_loc + return location if location.is_a?(Location) + @name_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + def save_name_loc(repository) + repository.enter(node_id, :name_loc) + end + + # attr_reader operator_loc: Location + def operator_loc + location = @operator_loc + return location if location.is_a?(Location) + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) + end + + # attr_reader value: Prism::node + attr_reader :value + + # def operator: () -> String + def operator + operator_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :instance_variable_or_write_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :instance_variable_or_write_node + 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?(InstanceVariableOrWriteNode) && + (name === other.name) && + (name_loc.nil? == other.name_loc.nil?) && + (operator_loc.nil? == other.operator_loc.nil?) && + (value === other.value) + end + end + + # Represents referencing an instance variable. + # + # @foo + # ^^^^ + class InstanceVariableReadNode < Node + # Initialize a new InstanceVariableReadNode node. + def initialize(source, node_id, location, flags, name) + @source = source + @node_id = node_id + @location = location + @flags = flags + @name = name + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_instance_variable_read_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> InstanceVariableReadNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name) + InstanceVariableReadNode.new(source, node_id, location, flags, name) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol } + def deconstruct_keys(keys) + { node_id: node_id, location: location, name: name } + end + + # 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` + attr_reader :name + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :instance_variable_read_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :instance_variable_read_node + 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?(InstanceVariableReadNode) && + (name === other.name) + end + end + + # Represents writing to an instance variable in a context that doesn't have an explicit value. + # + # @foo, @bar = baz + # ^^^^ ^^^^ + class InstanceVariableTargetNode < Node + # Initialize a new InstanceVariableTargetNode node. + def initialize(source, node_id, location, flags, name) + @source = source + @node_id = node_id + @location = location + @flags = flags + @name = name + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_instance_variable_target_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> InstanceVariableTargetNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name) + InstanceVariableTargetNode.new(source, node_id, location, flags, name) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol } + def deconstruct_keys(keys) + { node_id: node_id, location: location, name: name } + end + + # attr_reader name: Symbol + attr_reader :name + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :instance_variable_target_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :instance_variable_target_node + 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?(InstanceVariableTargetNode) && + (name === other.name) + end + end + + # Represents writing to an instance variable. + # + # @foo = 1 + # ^^^^^^^^ + class InstanceVariableWriteNode < Node + # Initialize a new InstanceVariableWriteNode node. + def initialize(source, node_id, location, flags, name, name_loc, value, operator_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @name = name + @name_loc = name_loc + @value = value + @operator_loc = operator_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_instance_variable_write_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [value] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield value + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [value] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [name_loc, value, operator_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?value: Prism::node, ?operator_loc: Location) -> InstanceVariableWriteNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc, value: self.value, operator_loc: self.operator_loc) + InstanceVariableWriteNode.new(source, node_id, location, flags, name, name_loc, value, operator_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, name_loc: Location, value: Prism::node, operator_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, name: name, name_loc: name_loc, value: value, operator_loc: operator_loc } + end + + # 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` + attr_reader :name + + # The location of the variable name. + # + # @_x = 1 + # ^^^ + def name_loc + location = @name_loc + return location if location.is_a?(Location) + @name_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + def save_name_loc(repository) + repository.enter(node_id, :name_loc) + end + + # 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 + # ^^^^ + attr_reader :value + + # The location of the `=` operator. + # + # @x = y + # ^ + def operator_loc + location = @operator_loc + return location if location.is_a?(Location) + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) + end + + # def operator: () -> String + def operator + operator_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :instance_variable_write_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :instance_variable_write_node + 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?(InstanceVariableWriteNode) && + (name === other.name) && + (name_loc.nil? == other.name_loc.nil?) && + (value === other.value) && + (operator_loc.nil? == other.operator_loc.nil?) + end + end + + # Represents an integer number literal. + # + # 1 + # ^ + class IntegerNode < Node + # Initialize a new IntegerNode node. + def initialize(source, node_id, location, flags, value) + @source = source + @node_id = node_id + @location = location + @flags = flags + @value = value + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_integer_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?value: Integer) -> IntegerNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, value: self.value) + IntegerNode.new(source, node_id, location, flags, value) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, value: Integer } + def deconstruct_keys(keys) + { node_id: node_id, location: location, value: value } + end + + # def binary?: () -> bool + def binary? + flags.anybits?(IntegerBaseFlags::BINARY) + end + + # def decimal?: () -> bool + def decimal? + flags.anybits?(IntegerBaseFlags::DECIMAL) + end + + # def octal?: () -> bool + def octal? + flags.anybits?(IntegerBaseFlags::OCTAL) + end + + # def hexadecimal?: () -> bool + def hexadecimal? + flags.anybits?(IntegerBaseFlags::HEXADECIMAL) + end + + # The value of the integer literal as a number. + attr_reader :value + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :integer_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :integer_node + 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?(IntegerNode) && + (flags === other.flags) && + (value === other.value) + end + end + + # 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 + # ^^^^^^^^^^^^^^^^ + class InterpolatedMatchLastLineNode < Node + # Initialize a new InterpolatedMatchLastLineNode node. + def initialize(source, node_id, location, flags, opening_loc, parts, closing_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @opening_loc = opening_loc + @parts = parts + @closing_loc = closing_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_interpolated_match_last_line_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [*parts] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + parts.each { |node| yield node } + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [*parts] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [opening_loc, *parts, closing_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode], ?closing_loc: Location) -> InterpolatedMatchLastLineNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, opening_loc: self.opening_loc, parts: self.parts, closing_loc: self.closing_loc) + InterpolatedMatchLastLineNode.new(source, node_id, location, flags, opening_loc, parts, closing_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, opening_loc: Location, parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode], closing_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, opening_loc: opening_loc, parts: parts, closing_loc: closing_loc } + end + + # def ignore_case?: () -> bool + def ignore_case? + flags.anybits?(RegularExpressionFlags::IGNORE_CASE) + end + + # def extended?: () -> bool + def extended? + flags.anybits?(RegularExpressionFlags::EXTENDED) + end + + # def multi_line?: () -> bool + def multi_line? + flags.anybits?(RegularExpressionFlags::MULTI_LINE) + end + + # def once?: () -> bool + def once? + flags.anybits?(RegularExpressionFlags::ONCE) + end + + # def euc_jp?: () -> bool + def euc_jp? + flags.anybits?(RegularExpressionFlags::EUC_JP) + end + + # def ascii_8bit?: () -> bool + def ascii_8bit? + flags.anybits?(RegularExpressionFlags::ASCII_8BIT) + end + + # def windows_31j?: () -> bool + def windows_31j? + flags.anybits?(RegularExpressionFlags::WINDOWS_31J) + end + + # def utf_8?: () -> bool + def utf_8? + flags.anybits?(RegularExpressionFlags::UTF_8) + end + + # def forced_utf8_encoding?: () -> bool + def forced_utf8_encoding? + flags.anybits?(RegularExpressionFlags::FORCED_UTF8_ENCODING) + end + + # def forced_binary_encoding?: () -> bool + def forced_binary_encoding? + flags.anybits?(RegularExpressionFlags::FORCED_BINARY_ENCODING) + end + + # def forced_us_ascii_encoding?: () -> bool + def forced_us_ascii_encoding? + flags.anybits?(RegularExpressionFlags::FORCED_US_ASCII_ENCODING) + end + + # attr_reader opening_loc: Location + def opening_loc + location = @opening_loc + return location if location.is_a?(Location) + @opening_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + def save_opening_loc(repository) + repository.enter(node_id, :opening_loc) + end + + # attr_reader parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode] + attr_reader :parts + + # attr_reader closing_loc: Location + def closing_loc + location = @closing_loc + return location if location.is_a?(Location) + @closing_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + def save_closing_loc(repository) + repository.enter(node_id, :closing_loc) + end + + # def opening: () -> String + def opening + opening_loc.slice + end + + # def closing: () -> String + def closing + closing_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :interpolated_match_last_line_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :interpolated_match_last_line_node + 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?(InterpolatedMatchLastLineNode) && + (flags === other.flags) && + (opening_loc.nil? == other.opening_loc.nil?) && + (parts.length == other.parts.length) && + parts.zip(other.parts).all? { |left, right| left === right } && + (closing_loc.nil? == other.closing_loc.nil?) + end + end + + # Represents a regular expression literal that contains interpolation. + # + # /foo #{bar} baz/ + # ^^^^^^^^^^^^^^^^ + class InterpolatedRegularExpressionNode < Node + # Initialize a new InterpolatedRegularExpressionNode node. + def initialize(source, node_id, location, flags, opening_loc, parts, closing_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @opening_loc = opening_loc + @parts = parts + @closing_loc = closing_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_interpolated_regular_expression_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [*parts] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + parts.each { |node| yield node } + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [*parts] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [opening_loc, *parts, closing_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode], ?closing_loc: Location) -> InterpolatedRegularExpressionNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, opening_loc: self.opening_loc, parts: self.parts, closing_loc: self.closing_loc) + InterpolatedRegularExpressionNode.new(source, node_id, location, flags, opening_loc, parts, closing_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, opening_loc: Location, parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode], closing_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, opening_loc: opening_loc, parts: parts, closing_loc: closing_loc } + end + + # def ignore_case?: () -> bool + def ignore_case? + flags.anybits?(RegularExpressionFlags::IGNORE_CASE) + end + + # def extended?: () -> bool + def extended? + flags.anybits?(RegularExpressionFlags::EXTENDED) + end + + # def multi_line?: () -> bool + def multi_line? + flags.anybits?(RegularExpressionFlags::MULTI_LINE) + end + + # def once?: () -> bool + def once? + flags.anybits?(RegularExpressionFlags::ONCE) + end + + # def euc_jp?: () -> bool + def euc_jp? + flags.anybits?(RegularExpressionFlags::EUC_JP) + end + + # def ascii_8bit?: () -> bool + def ascii_8bit? + flags.anybits?(RegularExpressionFlags::ASCII_8BIT) + end + + # def windows_31j?: () -> bool + def windows_31j? + flags.anybits?(RegularExpressionFlags::WINDOWS_31J) + end + + # def utf_8?: () -> bool + def utf_8? + flags.anybits?(RegularExpressionFlags::UTF_8) + end + + # def forced_utf8_encoding?: () -> bool + def forced_utf8_encoding? + flags.anybits?(RegularExpressionFlags::FORCED_UTF8_ENCODING) + end + + # def forced_binary_encoding?: () -> bool + def forced_binary_encoding? + flags.anybits?(RegularExpressionFlags::FORCED_BINARY_ENCODING) + end + + # def forced_us_ascii_encoding?: () -> bool + def forced_us_ascii_encoding? + flags.anybits?(RegularExpressionFlags::FORCED_US_ASCII_ENCODING) + end + + # attr_reader opening_loc: Location + def opening_loc + location = @opening_loc + return location if location.is_a?(Location) + @opening_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + def save_opening_loc(repository) + repository.enter(node_id, :opening_loc) + end + + # attr_reader parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode] + attr_reader :parts + + # attr_reader closing_loc: Location + def closing_loc + location = @closing_loc + return location if location.is_a?(Location) + @closing_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + def save_closing_loc(repository) + repository.enter(node_id, :closing_loc) + end + + # def opening: () -> String + def opening + opening_loc.slice + end + + # def closing: () -> String + def closing + closing_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :interpolated_regular_expression_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :interpolated_regular_expression_node + 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?(InterpolatedRegularExpressionNode) && + (flags === other.flags) && + (opening_loc.nil? == other.opening_loc.nil?) && + (parts.length == other.parts.length) && + parts.zip(other.parts).all? { |left, right| left === right } && + (closing_loc.nil? == other.closing_loc.nil?) + end + end + + # Represents a string literal that contains interpolation. + # + # "foo #{bar} baz" + # ^^^^^^^^^^^^^^^^ + class InterpolatedStringNode < Node + # Initialize a new InterpolatedStringNode node. + def initialize(source, node_id, location, flags, opening_loc, parts, closing_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @opening_loc = opening_loc + @parts = parts + @closing_loc = closing_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_interpolated_string_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [*parts] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + parts.each { |node| yield node } + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [*parts] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [*opening_loc, *parts, *closing_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location?, ?parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode | InterpolatedStringNode | XStringNode | InterpolatedXStringNode | SymbolNode | InterpolatedSymbolNode], ?closing_loc: Location?) -> InterpolatedStringNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, opening_loc: self.opening_loc, parts: self.parts, closing_loc: self.closing_loc) + InterpolatedStringNode.new(source, node_id, location, flags, opening_loc, parts, closing_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, opening_loc: Location?, parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode | InterpolatedStringNode | XStringNode | InterpolatedXStringNode | SymbolNode | InterpolatedSymbolNode], closing_loc: Location? } + def deconstruct_keys(keys) + { node_id: node_id, location: location, opening_loc: opening_loc, parts: parts, closing_loc: closing_loc } + end + + # def frozen?: () -> bool + def frozen? + flags.anybits?(InterpolatedStringNodeFlags::FROZEN) + end + + # def mutable?: () -> bool + def mutable? + flags.anybits?(InterpolatedStringNodeFlags::MUTABLE) + end + + # attr_reader opening_loc: Location? + def opening_loc + location = @opening_loc + case location + when nil + nil + when Location + location + else + @opening_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + def save_opening_loc(repository) + repository.enter(node_id, :opening_loc) unless @opening_loc.nil? + end + + # attr_reader parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode | InterpolatedStringNode | XStringNode | InterpolatedXStringNode | SymbolNode | InterpolatedSymbolNode] + attr_reader :parts + + # attr_reader closing_loc: Location? + def closing_loc + location = @closing_loc + case location + when nil + nil + when Location + location + else + @closing_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + def save_closing_loc(repository) + repository.enter(node_id, :closing_loc) unless @closing_loc.nil? + end + + # def opening: () -> String? + def opening + opening_loc&.slice + end + + # def closing: () -> String? + def closing + closing_loc&.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :interpolated_string_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :interpolated_string_node + 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?(InterpolatedStringNode) && + (flags === other.flags) && + (opening_loc.nil? == other.opening_loc.nil?) && + (parts.length == other.parts.length) && + parts.zip(other.parts).all? { |left, right| left === right } && + (closing_loc.nil? == other.closing_loc.nil?) + end + end + + # Represents a symbol literal that contains interpolation. + # + # :"foo #{bar} baz" + # ^^^^^^^^^^^^^^^^^ + class InterpolatedSymbolNode < Node + # Initialize a new InterpolatedSymbolNode node. + def initialize(source, node_id, location, flags, opening_loc, parts, closing_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @opening_loc = opening_loc + @parts = parts + @closing_loc = closing_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_interpolated_symbol_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [*parts] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + parts.each { |node| yield node } + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [*parts] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [*opening_loc, *parts, *closing_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location?, ?parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode], ?closing_loc: Location?) -> InterpolatedSymbolNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, opening_loc: self.opening_loc, parts: self.parts, closing_loc: self.closing_loc) + InterpolatedSymbolNode.new(source, node_id, location, flags, opening_loc, parts, closing_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, opening_loc: Location?, parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode], closing_loc: Location? } + def deconstruct_keys(keys) + { node_id: node_id, location: location, opening_loc: opening_loc, parts: parts, closing_loc: closing_loc } + end + + # attr_reader opening_loc: Location? + def opening_loc + location = @opening_loc + case location + when nil + nil + when Location + location + else + @opening_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + def save_opening_loc(repository) + repository.enter(node_id, :opening_loc) unless @opening_loc.nil? + end + + # attr_reader parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode] + attr_reader :parts + + # attr_reader closing_loc: Location? + def closing_loc + location = @closing_loc + case location + when nil + nil + when Location + location + else + @closing_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + def save_closing_loc(repository) + repository.enter(node_id, :closing_loc) unless @closing_loc.nil? + end + + # def opening: () -> String? + def opening + opening_loc&.slice + end + + # def closing: () -> String? + def closing + closing_loc&.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :interpolated_symbol_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :interpolated_symbol_node + 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?(InterpolatedSymbolNode) && + (opening_loc.nil? == other.opening_loc.nil?) && + (parts.length == other.parts.length) && + parts.zip(other.parts).all? { |left, right| left === right } && + (closing_loc.nil? == other.closing_loc.nil?) + end + end + + # Represents an xstring literal that contains interpolation. + # + # `foo #{bar} baz` + # ^^^^^^^^^^^^^^^^ + class InterpolatedXStringNode < Node + # Initialize a new InterpolatedXStringNode node. + def initialize(source, node_id, location, flags, opening_loc, parts, closing_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @opening_loc = opening_loc + @parts = parts + @closing_loc = closing_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_interpolated_x_string_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [*parts] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + parts.each { |node| yield node } + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [*parts] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [opening_loc, *parts, closing_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode], ?closing_loc: Location) -> InterpolatedXStringNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, opening_loc: self.opening_loc, parts: self.parts, closing_loc: self.closing_loc) + InterpolatedXStringNode.new(source, node_id, location, flags, opening_loc, parts, closing_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, opening_loc: Location, parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode], closing_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, opening_loc: opening_loc, parts: parts, closing_loc: closing_loc } + end + + # attr_reader opening_loc: Location + def opening_loc + location = @opening_loc + return location if location.is_a?(Location) + @opening_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + def save_opening_loc(repository) + repository.enter(node_id, :opening_loc) + end + + # attr_reader parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode] + attr_reader :parts + + # attr_reader closing_loc: Location + def closing_loc + location = @closing_loc + return location if location.is_a?(Location) + @closing_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + def save_closing_loc(repository) + repository.enter(node_id, :closing_loc) + end + + # def opening: () -> String + def opening + opening_loc.slice + end + + # def closing: () -> String + def closing + closing_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :interpolated_x_string_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :interpolated_x_string_node + 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?(InterpolatedXStringNode) && + (opening_loc.nil? == other.opening_loc.nil?) && + (parts.length == other.parts.length) && + parts.zip(other.parts).all? { |left, right| left === right } && + (closing_loc.nil? == other.closing_loc.nil?) + end + end + + # Represents reading from the implicit `it` local variable. + # + # -> { it } + # ^^ + class ItLocalVariableReadNode < Node + # Initialize a new ItLocalVariableReadNode node. + def initialize(source, node_id, location, flags) + @source = source + @node_id = node_id + @location = location + @flags = flags + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_it_local_variable_read_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer) -> ItLocalVariableReadNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags) + ItLocalVariableReadNode.new(source, node_id, location, flags) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location } + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :it_local_variable_read_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :it_local_variable_read_node + 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?(ItLocalVariableReadNode) + end + end + + # Represents an implicit set of parameters through the use of the `it` keyword within a block or lambda. + # + # -> { it + it } + # ^^^^^^^^^^^^^^ + class ItParametersNode < Node + # Initialize a new ItParametersNode node. + def initialize(source, node_id, location, flags) + @source = source + @node_id = node_id + @location = location + @flags = flags + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_it_parameters_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer) -> ItParametersNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags) + ItParametersNode.new(source, node_id, location, flags) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location } + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :it_parameters_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :it_parameters_node + 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?(ItParametersNode) + end + end + + # Represents a hash literal without opening and closing braces. + # + # foo(a: b) + # ^^^^ + class KeywordHashNode < Node + # Initialize a new KeywordHashNode node. + def initialize(source, node_id, location, flags, elements) + @source = source + @node_id = node_id + @location = location + @flags = flags + @elements = elements + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_keyword_hash_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [*elements] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + elements.each { |node| yield node } + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [*elements] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [*elements] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?elements: Array[AssocNode | AssocSplatNode]) -> KeywordHashNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, elements: self.elements) + KeywordHashNode.new(source, node_id, location, flags, elements) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, elements: Array[AssocNode | AssocSplatNode] } + def deconstruct_keys(keys) + { node_id: node_id, location: location, elements: elements } + end + + # def symbol_keys?: () -> bool + def symbol_keys? + flags.anybits?(KeywordHashNodeFlags::SYMBOL_KEYS) + end + + # attr_reader elements: Array[AssocNode | AssocSplatNode] + attr_reader :elements + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :keyword_hash_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :keyword_hash_node + 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?(KeywordHashNode) && + (flags === other.flags) && + (elements.length == other.elements.length) && + elements.zip(other.elements).all? { |left, right| left === right } + end + end + + # Represents a keyword rest parameter to a method, block, or lambda definition. + # + # def a(**b) + # ^^^ + # end + class KeywordRestParameterNode < Node + # Initialize a new KeywordRestParameterNode node. + def initialize(source, node_id, location, flags, name, name_loc, operator_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @name = name + @name_loc = name_loc + @operator_loc = operator_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_keyword_rest_parameter_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [*name_loc, operator_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol?, ?name_loc: Location?, ?operator_loc: Location) -> KeywordRestParameterNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc, operator_loc: self.operator_loc) + KeywordRestParameterNode.new(source, node_id, location, flags, name, name_loc, operator_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol?, name_loc: Location?, operator_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, name: name, name_loc: name_loc, operator_loc: operator_loc } + end + + # def repeated_parameter?: () -> bool + def repeated_parameter? + flags.anybits?(ParameterFlags::REPEATED_PARAMETER) + end + + # attr_reader name: Symbol? + attr_reader :name + + # attr_reader name_loc: Location? + def name_loc + location = @name_loc + case location + when nil + nil + when Location + location + else + @name_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + def save_name_loc(repository) + repository.enter(node_id, :name_loc) unless @name_loc.nil? + end + + # attr_reader operator_loc: Location + def operator_loc + location = @operator_loc + return location if location.is_a?(Location) + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) + end + + # def operator: () -> String + def operator + operator_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :keyword_rest_parameter_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :keyword_rest_parameter_node + 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?(KeywordRestParameterNode) && + (flags === other.flags) && + (name === other.name) && + (name_loc.nil? == other.name_loc.nil?) && + (operator_loc.nil? == other.operator_loc.nil?) + end + end + + # Represents using a lambda literal (not the lambda method call). + # + # ->(value) { value * 2 } + # ^^^^^^^^^^^^^^^^^^^^^^^ + class LambdaNode < Node + # Initialize a new LambdaNode node. + def initialize(source, node_id, location, flags, locals, operator_loc, opening_loc, closing_loc, parameters, body) + @source = source + @node_id = node_id + @location = location + @flags = flags + @locals = locals + @operator_loc = operator_loc + @opening_loc = opening_loc + @closing_loc = closing_loc + @parameters = parameters + @body = body + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_lambda_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [parameters, body] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield parameters if parameters + yield body if body + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << parameters if parameters + compact << body if body + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [operator_loc, opening_loc, closing_loc, *parameters, *body] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?locals: Array[Symbol], ?operator_loc: Location, ?opening_loc: Location, ?closing_loc: Location, ?parameters: BlockParametersNode | NumberedParametersNode | ItParametersNode | nil, ?body: StatementsNode | BeginNode | nil) -> LambdaNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, locals: self.locals, operator_loc: self.operator_loc, opening_loc: self.opening_loc, closing_loc: self.closing_loc, parameters: self.parameters, body: self.body) + LambdaNode.new(source, node_id, location, flags, locals, operator_loc, opening_loc, closing_loc, parameters, body) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, locals: Array[Symbol], operator_loc: Location, opening_loc: Location, closing_loc: Location, parameters: BlockParametersNode | NumberedParametersNode | ItParametersNode | nil, body: StatementsNode | BeginNode | nil } + def deconstruct_keys(keys) + { node_id: node_id, location: location, locals: locals, operator_loc: operator_loc, opening_loc: opening_loc, closing_loc: closing_loc, parameters: parameters, body: body } + end + + # attr_reader locals: Array[Symbol] + attr_reader :locals + + # attr_reader operator_loc: Location + def operator_loc + location = @operator_loc + return location if location.is_a?(Location) + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) + end + + # attr_reader opening_loc: Location + def opening_loc + location = @opening_loc + return location if location.is_a?(Location) + @opening_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + def save_opening_loc(repository) + repository.enter(node_id, :opening_loc) + end + + # attr_reader closing_loc: Location + def closing_loc + location = @closing_loc + return location if location.is_a?(Location) + @closing_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + def save_closing_loc(repository) + repository.enter(node_id, :closing_loc) + end + + # attr_reader parameters: BlockParametersNode | NumberedParametersNode | ItParametersNode | nil + attr_reader :parameters + + # attr_reader body: StatementsNode | BeginNode | nil + attr_reader :body + + # def operator: () -> String + def operator + operator_loc.slice + end + + # def opening: () -> String + def opening + opening_loc.slice + end + + # def closing: () -> String + def closing + closing_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :lambda_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :lambda_node + 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?(LambdaNode) && + (locals.length == other.locals.length) && + locals.zip(other.locals).all? { |left, right| left === right } && + (operator_loc.nil? == other.operator_loc.nil?) && + (opening_loc.nil? == other.opening_loc.nil?) && + (closing_loc.nil? == other.closing_loc.nil?) && + (parameters === other.parameters) && + (body === other.body) + end + end + + # Represents the use of the `&&=` operator for assignment to a local variable. + # + # target &&= value + # ^^^^^^^^^^^^^^^^ + class LocalVariableAndWriteNode < Node + # Initialize a new LocalVariableAndWriteNode node. + def initialize(source, node_id, location, flags, name_loc, operator_loc, value, name, depth) + @source = source + @node_id = node_id + @location = location + @flags = flags + @name_loc = name_loc + @operator_loc = operator_loc + @value = value + @name = name + @depth = depth + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_local_variable_and_write_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [value] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield value + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [value] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [name_loc, operator_loc, value] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node, ?name: Symbol, ?depth: Integer) -> LocalVariableAndWriteNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name_loc: self.name_loc, operator_loc: self.operator_loc, value: self.value, name: self.name, depth: self.depth) + LocalVariableAndWriteNode.new(source, node_id, location, flags, name_loc, operator_loc, value, name, depth) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name_loc: Location, operator_loc: Location, value: Prism::node, name: Symbol, depth: Integer } + def deconstruct_keys(keys) + { node_id: node_id, location: location, name_loc: name_loc, operator_loc: operator_loc, value: value, name: name, depth: depth } + end + + # attr_reader name_loc: Location + def name_loc + location = @name_loc + return location if location.is_a?(Location) + @name_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + def save_name_loc(repository) + repository.enter(node_id, :name_loc) + end + + # attr_reader operator_loc: Location + def operator_loc + location = @operator_loc + return location if location.is_a?(Location) + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) + end + + # attr_reader value: Prism::node + attr_reader :value + + # attr_reader name: Symbol + attr_reader :name + + # attr_reader depth: Integer + attr_reader :depth + + # def operator: () -> String + def operator + operator_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :local_variable_and_write_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :local_variable_and_write_node + 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?(LocalVariableAndWriteNode) && + (name_loc.nil? == other.name_loc.nil?) && + (operator_loc.nil? == other.operator_loc.nil?) && + (value === other.value) && + (name === other.name) && + (depth === other.depth) + end + end + + # Represents assigning to a local variable using an operator that isn't `=`. + # + # target += value + # ^^^^^^^^^^^^^^^ + class LocalVariableOperatorWriteNode < Node + # Initialize a new LocalVariableOperatorWriteNode node. + def initialize(source, node_id, location, flags, name_loc, binary_operator_loc, value, name, binary_operator, depth) + @source = source + @node_id = node_id + @location = location + @flags = flags + @name_loc = name_loc + @binary_operator_loc = binary_operator_loc + @value = value + @name = name + @binary_operator = binary_operator + @depth = depth + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_local_variable_operator_write_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [value] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield value + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [value] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [name_loc, binary_operator_loc, value] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name_loc: Location, ?binary_operator_loc: Location, ?value: Prism::node, ?name: Symbol, ?binary_operator: Symbol, ?depth: Integer) -> LocalVariableOperatorWriteNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name_loc: self.name_loc, binary_operator_loc: self.binary_operator_loc, value: self.value, name: self.name, binary_operator: self.binary_operator, depth: self.depth) + LocalVariableOperatorWriteNode.new(source, node_id, location, flags, name_loc, binary_operator_loc, value, name, binary_operator, depth) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name_loc: Location, binary_operator_loc: Location, value: Prism::node, name: Symbol, binary_operator: Symbol, depth: Integer } + def deconstruct_keys(keys) + { node_id: node_id, location: location, name_loc: name_loc, binary_operator_loc: binary_operator_loc, value: value, name: name, binary_operator: binary_operator, depth: depth } + end + + # attr_reader name_loc: Location + def name_loc + location = @name_loc + return location if location.is_a?(Location) + @name_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + def save_name_loc(repository) + repository.enter(node_id, :name_loc) + end + + # attr_reader binary_operator_loc: Location + def binary_operator_loc + location = @binary_operator_loc + return location if location.is_a?(Location) + @binary_operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the binary_operator_loc location using the given saved source so that + # it can be retrieved later. + def save_binary_operator_loc(repository) + repository.enter(node_id, :binary_operator_loc) + end + + # attr_reader value: Prism::node + attr_reader :value + + # attr_reader name: Symbol + attr_reader :name + + # attr_reader binary_operator: Symbol + attr_reader :binary_operator + + # attr_reader depth: Integer + attr_reader :depth + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :local_variable_operator_write_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :local_variable_operator_write_node + 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?(LocalVariableOperatorWriteNode) && + (name_loc.nil? == other.name_loc.nil?) && + (binary_operator_loc.nil? == other.binary_operator_loc.nil?) && + (value === other.value) && + (name === other.name) && + (binary_operator === other.binary_operator) && + (depth === other.depth) + end + end + + # Represents the use of the `||=` operator for assignment to a local variable. + # + # target ||= value + # ^^^^^^^^^^^^^^^^ + class LocalVariableOrWriteNode < Node + # Initialize a new LocalVariableOrWriteNode node. + def initialize(source, node_id, location, flags, name_loc, operator_loc, value, name, depth) + @source = source + @node_id = node_id + @location = location + @flags = flags + @name_loc = name_loc + @operator_loc = operator_loc + @value = value + @name = name + @depth = depth + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_local_variable_or_write_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [value] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield value + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [value] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [name_loc, operator_loc, value] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node, ?name: Symbol, ?depth: Integer) -> LocalVariableOrWriteNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name_loc: self.name_loc, operator_loc: self.operator_loc, value: self.value, name: self.name, depth: self.depth) + LocalVariableOrWriteNode.new(source, node_id, location, flags, name_loc, operator_loc, value, name, depth) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name_loc: Location, operator_loc: Location, value: Prism::node, name: Symbol, depth: Integer } + def deconstruct_keys(keys) + { node_id: node_id, location: location, name_loc: name_loc, operator_loc: operator_loc, value: value, name: name, depth: depth } + end + + # attr_reader name_loc: Location + def name_loc + location = @name_loc + return location if location.is_a?(Location) + @name_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + def save_name_loc(repository) + repository.enter(node_id, :name_loc) + end + + # attr_reader operator_loc: Location + def operator_loc + location = @operator_loc + return location if location.is_a?(Location) + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) + end + + # attr_reader value: Prism::node + attr_reader :value + + # attr_reader name: Symbol + attr_reader :name + + # attr_reader depth: Integer + attr_reader :depth + + # def operator: () -> String + def operator + operator_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :local_variable_or_write_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :local_variable_or_write_node + 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?(LocalVariableOrWriteNode) && + (name_loc.nil? == other.name_loc.nil?) && + (operator_loc.nil? == other.operator_loc.nil?) && + (value === other.value) && + (name === other.name) && + (depth === other.depth) + end + end + + # 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 + # ^^^ + class LocalVariableReadNode < Node + # Initialize a new LocalVariableReadNode node. + def initialize(source, node_id, location, flags, name, depth) + @source = source + @node_id = node_id + @location = location + @flags = flags + @name = name + @depth = depth + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_local_variable_read_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?depth: Integer) -> LocalVariableReadNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, depth: self.depth) + LocalVariableReadNode.new(source, node_id, location, flags, name, depth) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, depth: Integer } + def deconstruct_keys(keys) + { node_id: node_id, location: location, name: name, depth: depth } + end + + # 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` + attr_reader :name + + # 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). + attr_reader :depth + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :local_variable_read_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :local_variable_read_node + 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?(LocalVariableReadNode) && + (name === other.name) && + (depth === other.depth) + end + end + + # Represents writing to a local variable in a context that doesn't have an explicit value. + # + # foo, bar = baz + # ^^^ ^^^ + # + # foo => baz + # ^^^ + class LocalVariableTargetNode < Node + # Initialize a new LocalVariableTargetNode node. + def initialize(source, node_id, location, flags, name, depth) + @source = source + @node_id = node_id + @location = location + @flags = flags + @name = name + @depth = depth + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_local_variable_target_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?depth: Integer) -> LocalVariableTargetNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, depth: self.depth) + LocalVariableTargetNode.new(source, node_id, location, flags, name, depth) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, depth: Integer } + def deconstruct_keys(keys) + { node_id: node_id, location: location, name: name, depth: depth } + end + + # attr_reader name: Symbol + attr_reader :name + + # attr_reader depth: Integer + attr_reader :depth + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :local_variable_target_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :local_variable_target_node + 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?(LocalVariableTargetNode) && + (name === other.name) && + (depth === other.depth) + end + end + + # Represents writing to a local variable. + # + # foo = 1 + # ^^^^^^^ + class LocalVariableWriteNode < Node + # Initialize a new LocalVariableWriteNode node. + def initialize(source, node_id, location, flags, name, depth, name_loc, value, operator_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @name = name + @depth = depth + @name_loc = name_loc + @value = value + @operator_loc = operator_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_local_variable_write_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [value] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield value + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [value] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [name_loc, value, operator_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?depth: Integer, ?name_loc: Location, ?value: Prism::node, ?operator_loc: Location) -> LocalVariableWriteNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, depth: self.depth, name_loc: self.name_loc, value: self.value, operator_loc: self.operator_loc) + LocalVariableWriteNode.new(source, node_id, location, flags, name, depth, name_loc, value, operator_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, depth: Integer, name_loc: Location, value: Prism::node, operator_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, name: name, depth: depth, name_loc: name_loc, value: value, operator_loc: operator_loc } + end + + # 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` + attr_reader :name + + # 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). + attr_reader :depth + + # The location of the variable name. + # + # foo = :bar + # ^^^ + def name_loc + location = @name_loc + return location if location.is_a?(Location) + @name_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + def save_name_loc(repository) + repository.enter(node_id, :name_loc) + end + + # 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 + attr_reader :value + + # The location of the `=` operator. + # + # x = :y + # ^ + def operator_loc + location = @operator_loc + return location if location.is_a?(Location) + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) + end + + # def operator: () -> String + def operator + operator_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :local_variable_write_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :local_variable_write_node + 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?(LocalVariableWriteNode) && + (name === other.name) && + (depth === other.depth) && + (name_loc.nil? == other.name_loc.nil?) && + (value === other.value) && + (operator_loc.nil? == other.operator_loc.nil?) + end + end + + # 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 + # ^^^^^^ + class MatchLastLineNode < Node + # Initialize a new MatchLastLineNode node. + def initialize(source, node_id, location, flags, opening_loc, content_loc, closing_loc, unescaped) + @source = source + @node_id = node_id + @location = location + @flags = flags + @opening_loc = opening_loc + @content_loc = content_loc + @closing_loc = closing_loc + @unescaped = unescaped + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_match_last_line_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [opening_loc, content_loc, closing_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?content_loc: Location, ?closing_loc: Location, ?unescaped: String) -> MatchLastLineNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, opening_loc: self.opening_loc, content_loc: self.content_loc, closing_loc: self.closing_loc, unescaped: self.unescaped) + MatchLastLineNode.new(source, node_id, location, flags, opening_loc, content_loc, closing_loc, unescaped) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, opening_loc: Location, content_loc: Location, closing_loc: Location, unescaped: String } + def deconstruct_keys(keys) + { node_id: node_id, location: location, opening_loc: opening_loc, content_loc: content_loc, closing_loc: closing_loc, unescaped: unescaped } + end + + # def ignore_case?: () -> bool + def ignore_case? + flags.anybits?(RegularExpressionFlags::IGNORE_CASE) + end + + # def extended?: () -> bool + def extended? + flags.anybits?(RegularExpressionFlags::EXTENDED) + end + + # def multi_line?: () -> bool + def multi_line? + flags.anybits?(RegularExpressionFlags::MULTI_LINE) + end + + # def once?: () -> bool + def once? + flags.anybits?(RegularExpressionFlags::ONCE) + end + + # def euc_jp?: () -> bool + def euc_jp? + flags.anybits?(RegularExpressionFlags::EUC_JP) + end + + # def ascii_8bit?: () -> bool + def ascii_8bit? + flags.anybits?(RegularExpressionFlags::ASCII_8BIT) + end + + # def windows_31j?: () -> bool + def windows_31j? + flags.anybits?(RegularExpressionFlags::WINDOWS_31J) + end + + # def utf_8?: () -> bool + def utf_8? + flags.anybits?(RegularExpressionFlags::UTF_8) + end + + # def forced_utf8_encoding?: () -> bool + def forced_utf8_encoding? + flags.anybits?(RegularExpressionFlags::FORCED_UTF8_ENCODING) + end + + # def forced_binary_encoding?: () -> bool + def forced_binary_encoding? + flags.anybits?(RegularExpressionFlags::FORCED_BINARY_ENCODING) + end + + # def forced_us_ascii_encoding?: () -> bool + def forced_us_ascii_encoding? + flags.anybits?(RegularExpressionFlags::FORCED_US_ASCII_ENCODING) + end + + # attr_reader opening_loc: Location + def opening_loc + location = @opening_loc + return location if location.is_a?(Location) + @opening_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + def save_opening_loc(repository) + repository.enter(node_id, :opening_loc) + end + + # attr_reader content_loc: Location + def content_loc + location = @content_loc + return location if location.is_a?(Location) + @content_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the content_loc location using the given saved source so that + # it can be retrieved later. + def save_content_loc(repository) + repository.enter(node_id, :content_loc) + end + + # attr_reader closing_loc: Location + def closing_loc + location = @closing_loc + return location if location.is_a?(Location) + @closing_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + def save_closing_loc(repository) + repository.enter(node_id, :closing_loc) + end + + # attr_reader unescaped: String + attr_reader :unescaped + + # def opening: () -> String + def opening + opening_loc.slice + end + + # def content: () -> String + def content + content_loc.slice + end + + # def closing: () -> String + def closing + closing_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :match_last_line_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :match_last_line_node + 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?(MatchLastLineNode) && + (flags === other.flags) && + (opening_loc.nil? == other.opening_loc.nil?) && + (content_loc.nil? == other.content_loc.nil?) && + (closing_loc.nil? == other.closing_loc.nil?) && + (unescaped === other.unescaped) + end + end + + # Represents the use of the modifier `in` operator. + # + # foo in bar + # ^^^^^^^^^^ + class MatchPredicateNode < Node + # Initialize a new MatchPredicateNode node. + def initialize(source, node_id, location, flags, value, pattern, operator_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @value = value + @pattern = pattern + @operator_loc = operator_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_match_predicate_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [value, pattern] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield value + yield pattern + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [value, pattern] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [value, pattern, operator_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?value: Prism::node, ?pattern: Prism::node, ?operator_loc: Location) -> MatchPredicateNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, value: self.value, pattern: self.pattern, operator_loc: self.operator_loc) + MatchPredicateNode.new(source, node_id, location, flags, value, pattern, operator_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, value: Prism::node, pattern: Prism::node, operator_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, value: value, pattern: pattern, operator_loc: operator_loc } + end + + # attr_reader value: Prism::node + attr_reader :value + + # attr_reader pattern: Prism::node + attr_reader :pattern + + # attr_reader operator_loc: Location + def operator_loc + location = @operator_loc + return location if location.is_a?(Location) + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) + end + + # def operator: () -> String + def operator + operator_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :match_predicate_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :match_predicate_node + 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?(MatchPredicateNode) && + (value === other.value) && + (pattern === other.pattern) && + (operator_loc.nil? == other.operator_loc.nil?) + end + end + + # Represents the use of the `=>` operator. + # + # foo => bar + # ^^^^^^^^^^ + class MatchRequiredNode < Node + # Initialize a new MatchRequiredNode node. + def initialize(source, node_id, location, flags, value, pattern, operator_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @value = value + @pattern = pattern + @operator_loc = operator_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_match_required_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [value, pattern] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield value + yield pattern + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [value, pattern] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [value, pattern, operator_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?value: Prism::node, ?pattern: Prism::node, ?operator_loc: Location) -> MatchRequiredNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, value: self.value, pattern: self.pattern, operator_loc: self.operator_loc) + MatchRequiredNode.new(source, node_id, location, flags, value, pattern, operator_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, value: Prism::node, pattern: Prism::node, operator_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, value: value, pattern: pattern, operator_loc: operator_loc } + end + + # Represents the left-hand side of the operator. + # + # foo => bar + # ^^^ + attr_reader :value + + # Represents the right-hand side of the operator. The type of the node depends on the expression. + # + # Anything that looks like a local variable name (including `_`) will result in a `LocalVariableTargetNode`. + # + # foo => a # This is equivalent to writing `a = foo` + # ^ + # + # Using an explicit `Array` or combining expressions with `,` will result in a `ArrayPatternNode`. This can be preceded by a constant. + # + # foo => [a] + # ^^^ + # + # foo => a, b + # ^^^^ + # + # foo => Bar[a, b] + # ^^^^^^^^^ + # + # If the array pattern contains at least two wildcard matches, a `FindPatternNode` is created instead. + # + # foo => *, 1, *a + # ^^^^^ + # + # Using an explicit `Hash` or a constant with square brackets and hash keys in the square brackets will result in a `HashPatternNode`. + # + # foo => { a: 1, b: } + # + # foo => Bar[a: 1, b:] + # + # foo => Bar[**] + # + # To use any variable that needs run time evaluation, pinning is required. This results in a `PinnedVariableNode` + # + # foo => ^a + # ^^ + # + # Similar, any expression can be used with pinning. This results in a `PinnedExpressionNode`. + # + # foo => ^(a + 1) + # + # Anything else will result in the regular node for that expression, for example a `ConstantReadNode`. + # + # foo => CONST + attr_reader :pattern + + # The location of the operator. + # + # foo => bar + # ^^ + def operator_loc + location = @operator_loc + return location if location.is_a?(Location) + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) + end + + # def operator: () -> String + def operator + operator_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :match_required_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :match_required_node + 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?(MatchRequiredNode) && + (value === other.value) && + (pattern === other.pattern) && + (operator_loc.nil? == other.operator_loc.nil?) + end + end + + # Represents writing local variables using a regular expression match with named capture groups. + # + # /(?bar)/ =~ baz + # ^^^^^^^^^^^^^^^^^^^^ + class MatchWriteNode < Node + # Initialize a new MatchWriteNode node. + def initialize(source, node_id, location, flags, call, targets) + @source = source + @node_id = node_id + @location = location + @flags = flags + @call = call + @targets = targets + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_match_write_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [call, *targets] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield call + targets.each { |node| yield node } + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [call, *targets] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [call, *targets] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?call: CallNode, ?targets: Array[LocalVariableTargetNode]) -> MatchWriteNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, call: self.call, targets: self.targets) + MatchWriteNode.new(source, node_id, location, flags, call, targets) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, call: CallNode, targets: Array[LocalVariableTargetNode] } + def deconstruct_keys(keys) + { node_id: node_id, location: location, call: call, targets: targets } + end + + # attr_reader call: CallNode + attr_reader :call + + # attr_reader targets: Array[LocalVariableTargetNode] + attr_reader :targets + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :match_write_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :match_write_node + 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?(MatchWriteNode) && + (call === other.call) && + (targets.length == other.targets.length) && + targets.zip(other.targets).all? { |left, right| left === right } + end + end + + # Represents a node that is missing from the source and results in a syntax error. + class MissingNode < Node + # Initialize a new MissingNode node. + def initialize(source, node_id, location, flags) + @source = source + @node_id = node_id + @location = location + @flags = flags + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_missing_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer) -> MissingNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags) + MissingNode.new(source, node_id, location, flags) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location } + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :missing_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :missing_node + 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?(MissingNode) + end + end + + # Represents a module declaration involving the `module` keyword. + # + # module Foo end + # ^^^^^^^^^^^^^^ + class ModuleNode < Node + # Initialize a new ModuleNode node. + def initialize(source, node_id, location, flags, locals, module_keyword_loc, constant_path, body, end_keyword_loc, name) + @source = source + @node_id = node_id + @location = location + @flags = flags + @locals = locals + @module_keyword_loc = module_keyword_loc + @constant_path = constant_path + @body = body + @end_keyword_loc = end_keyword_loc + @name = name + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_module_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [constant_path, body] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield constant_path + yield body if body + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << constant_path + compact << body if body + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [module_keyword_loc, constant_path, *body, end_keyword_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?locals: Array[Symbol], ?module_keyword_loc: Location, ?constant_path: ConstantReadNode | ConstantPathNode | MissingNode, ?body: StatementsNode | BeginNode | nil, ?end_keyword_loc: Location, ?name: Symbol) -> ModuleNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, locals: self.locals, module_keyword_loc: self.module_keyword_loc, constant_path: self.constant_path, body: self.body, end_keyword_loc: self.end_keyword_loc, name: self.name) + ModuleNode.new(source, node_id, location, flags, locals, module_keyword_loc, constant_path, body, end_keyword_loc, name) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, locals: Array[Symbol], module_keyword_loc: Location, constant_path: ConstantReadNode | ConstantPathNode | MissingNode, body: StatementsNode | BeginNode | nil, end_keyword_loc: Location, name: Symbol } + def deconstruct_keys(keys) + { node_id: node_id, location: location, locals: locals, module_keyword_loc: module_keyword_loc, constant_path: constant_path, body: body, end_keyword_loc: end_keyword_loc, name: name } + end + + # attr_reader locals: Array[Symbol] + attr_reader :locals + + # attr_reader module_keyword_loc: Location + def module_keyword_loc + location = @module_keyword_loc + return location if location.is_a?(Location) + @module_keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the module_keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_module_keyword_loc(repository) + repository.enter(node_id, :module_keyword_loc) + end + + # attr_reader constant_path: ConstantReadNode | ConstantPathNode | MissingNode + attr_reader :constant_path + + # attr_reader body: StatementsNode | BeginNode | nil + attr_reader :body + + # attr_reader end_keyword_loc: Location + def end_keyword_loc + location = @end_keyword_loc + return location if location.is_a?(Location) + @end_keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the end_keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_end_keyword_loc(repository) + repository.enter(node_id, :end_keyword_loc) + end + + # attr_reader name: Symbol + attr_reader :name + + # def module_keyword: () -> String + def module_keyword + module_keyword_loc.slice + end + + # def end_keyword: () -> String + def end_keyword + end_keyword_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :module_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :module_node + 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?(ModuleNode) && + (locals.length == other.locals.length) && + locals.zip(other.locals).all? { |left, right| left === right } && + (module_keyword_loc.nil? == other.module_keyword_loc.nil?) && + (constant_path === other.constant_path) && + (body === other.body) && + (end_keyword_loc.nil? == other.end_keyword_loc.nil?) && + (name === other.name) + end + end + + # Represents a multi-target expression. + # + # a, (b, c) = 1, 2, 3 + # ^^^^^^ + # + # This can be a part of `MultiWriteNode` as above, or the target of a `for` loop + # + # for a, b in [[1, 2], [3, 4]] + # ^^^^ + class MultiTargetNode < Node + # Initialize a new MultiTargetNode node. + def initialize(source, node_id, location, flags, lefts, rest, rights, lparen_loc, rparen_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @lefts = lefts + @rest = rest + @rights = rights + @lparen_loc = lparen_loc + @rparen_loc = rparen_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_multi_target_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [*lefts, rest, *rights] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + lefts.each { |node| yield node } + yield rest if rest + rights.each { |node| yield node } + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact.concat(lefts) + compact << rest if rest + compact.concat(rights) + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [*lefts, *rest, *rights, *lparen_loc, *rparen_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?lefts: Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | RequiredParameterNode | BackReferenceReadNode | NumberedReferenceReadNode], ?rest: ImplicitRestNode | SplatNode | nil, ?rights: Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | RequiredParameterNode | BackReferenceReadNode | NumberedReferenceReadNode], ?lparen_loc: Location?, ?rparen_loc: Location?) -> MultiTargetNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, lefts: self.lefts, rest: self.rest, rights: self.rights, lparen_loc: self.lparen_loc, rparen_loc: self.rparen_loc) + MultiTargetNode.new(source, node_id, location, flags, lefts, rest, rights, lparen_loc, rparen_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, lefts: Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | RequiredParameterNode | BackReferenceReadNode | NumberedReferenceReadNode], rest: ImplicitRestNode | SplatNode | nil, rights: Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | RequiredParameterNode | BackReferenceReadNode | NumberedReferenceReadNode], lparen_loc: Location?, rparen_loc: Location? } + def deconstruct_keys(keys) + { node_id: node_id, location: location, lefts: lefts, rest: rest, rights: rights, lparen_loc: lparen_loc, rparen_loc: rparen_loc } + end + + # Represents the targets expressions before a splat node. + # + # a, (b, c, *) = 1, 2, 3, 4, 5 + # ^^^^ + # + # The splat node can be absent, in that case all target expressions are in the left field. + # + # a, (b, c) = 1, 2, 3, 4, 5 + # ^^^^ + attr_reader :lefts + + # Represents a splat node in the target expression. + # + # a, (b, *c) = 1, 2, 3, 4 + # ^^ + # + # The variable can be empty, this results in a `SplatNode` with a `nil` expression field. + # + # a, (b, *) = 1, 2, 3, 4 + # ^ + # + # If the `*` is omitted, this field will contain an `ImplicitRestNode` + # + # a, (b,) = 1, 2, 3, 4 + # ^ + attr_reader :rest + + # Represents the targets expressions after a splat node. + # + # a, (*, b, c) = 1, 2, 3, 4, 5 + # ^^^^ + attr_reader :rights + + # The location of the opening parenthesis. + # + # a, (b, c) = 1, 2, 3 + # ^ + def lparen_loc + location = @lparen_loc + case location + when nil + nil + when Location + location + else + @lparen_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the lparen_loc location using the given saved source so that + # it can be retrieved later. + def save_lparen_loc(repository) + repository.enter(node_id, :lparen_loc) unless @lparen_loc.nil? + end + + # The location of the closing parenthesis. + # + # a, (b, c) = 1, 2, 3 + # ^ + def rparen_loc + location = @rparen_loc + case location + when nil + nil + when Location + location + else + @rparen_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the rparen_loc location using the given saved source so that + # it can be retrieved later. + def save_rparen_loc(repository) + repository.enter(node_id, :rparen_loc) unless @rparen_loc.nil? + end + + # def lparen: () -> String? + def lparen + lparen_loc&.slice + end + + # def rparen: () -> String? + def rparen + rparen_loc&.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :multi_target_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :multi_target_node + 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?(MultiTargetNode) && + (lefts.length == other.lefts.length) && + lefts.zip(other.lefts).all? { |left, right| left === right } && + (rest === other.rest) && + (rights.length == other.rights.length) && + rights.zip(other.rights).all? { |left, right| left === right } && + (lparen_loc.nil? == other.lparen_loc.nil?) && + (rparen_loc.nil? == other.rparen_loc.nil?) + end + end + + # Represents a write to a multi-target expression. + # + # a, b, c = 1, 2, 3 + # ^^^^^^^^^^^^^^^^^ + class MultiWriteNode < Node + # Initialize a new MultiWriteNode node. + def initialize(source, node_id, location, flags, lefts, rest, rights, lparen_loc, rparen_loc, operator_loc, value) + @source = source + @node_id = node_id + @location = location + @flags = flags + @lefts = lefts + @rest = rest + @rights = rights + @lparen_loc = lparen_loc + @rparen_loc = rparen_loc + @operator_loc = operator_loc + @value = value + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_multi_write_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [*lefts, rest, *rights, value] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + lefts.each { |node| yield node } + yield rest if rest + rights.each { |node| yield node } + yield value + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact.concat(lefts) + compact << rest if rest + compact.concat(rights) + compact << value + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [*lefts, *rest, *rights, *lparen_loc, *rparen_loc, operator_loc, value] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?lefts: Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | BackReferenceReadNode | NumberedReferenceReadNode], ?rest: ImplicitRestNode | SplatNode | nil, ?rights: Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | BackReferenceReadNode | NumberedReferenceReadNode], ?lparen_loc: Location?, ?rparen_loc: Location?, ?operator_loc: Location, ?value: Prism::node) -> MultiWriteNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, lefts: self.lefts, rest: self.rest, rights: self.rights, lparen_loc: self.lparen_loc, rparen_loc: self.rparen_loc, operator_loc: self.operator_loc, value: self.value) + MultiWriteNode.new(source, node_id, location, flags, lefts, rest, rights, lparen_loc, rparen_loc, operator_loc, value) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, lefts: Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | BackReferenceReadNode | NumberedReferenceReadNode], rest: ImplicitRestNode | SplatNode | nil, rights: Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | BackReferenceReadNode | NumberedReferenceReadNode], lparen_loc: Location?, rparen_loc: Location?, operator_loc: Location, value: Prism::node } + def deconstruct_keys(keys) + { node_id: node_id, location: location, lefts: lefts, rest: rest, rights: rights, lparen_loc: lparen_loc, rparen_loc: rparen_loc, operator_loc: operator_loc, value: value } + end + + # Represents the targets expressions before a splat node. + # + # a, b, * = 1, 2, 3, 4, 5 + # ^^^^ + # + # The splat node can be absent, in that case all target expressions are in the left field. + # + # a, b, c = 1, 2, 3, 4, 5 + # ^^^^^^^ + attr_reader :lefts + + # Represents a splat node in the target expression. + # + # a, b, *c = 1, 2, 3, 4 + # ^^ + # + # The variable can be empty, this results in a `SplatNode` with a `nil` expression field. + # + # a, b, * = 1, 2, 3, 4 + # ^ + # + # If the `*` is omitted, this field will contain an `ImplicitRestNode` + # + # a, b, = 1, 2, 3, 4 + # ^ + attr_reader :rest + + # Represents the targets expressions after a splat node. + # + # a, *, b, c = 1, 2, 3, 4, 5 + # ^^^^ + attr_reader :rights + + # The location of the opening parenthesis. + # + # (a, b, c) = 1, 2, 3 + # ^ + def lparen_loc + location = @lparen_loc + case location + when nil + nil + when Location + location + else + @lparen_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the lparen_loc location using the given saved source so that + # it can be retrieved later. + def save_lparen_loc(repository) + repository.enter(node_id, :lparen_loc) unless @lparen_loc.nil? + end + + # The location of the closing parenthesis. + # + # (a, b, c) = 1, 2, 3 + # ^ + def rparen_loc + location = @rparen_loc + case location + when nil + nil + when Location + location + else + @rparen_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the rparen_loc location using the given saved source so that + # it can be retrieved later. + def save_rparen_loc(repository) + repository.enter(node_id, :rparen_loc) unless @rparen_loc.nil? + end + + # The location of the operator. + # + # a, b, c = 1, 2, 3 + # ^ + def operator_loc + location = @operator_loc + return location if location.is_a?(Location) + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) + end + + # The value to write to the targets. It can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + # + # a, b, c = 1, 2, 3 + # ^^^^^^^ + attr_reader :value + + # def lparen: () -> String? + def lparen + lparen_loc&.slice + end + + # def rparen: () -> String? + def rparen + rparen_loc&.slice + end + + # def operator: () -> String + def operator + operator_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :multi_write_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :multi_write_node + 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?(MultiWriteNode) && + (lefts.length == other.lefts.length) && + lefts.zip(other.lefts).all? { |left, right| left === right } && + (rest === other.rest) && + (rights.length == other.rights.length) && + rights.zip(other.rights).all? { |left, right| left === right } && + (lparen_loc.nil? == other.lparen_loc.nil?) && + (rparen_loc.nil? == other.rparen_loc.nil?) && + (operator_loc.nil? == other.operator_loc.nil?) && + (value === other.value) + end + end + + # Represents the use of the `next` keyword. + # + # next 1 + # ^^^^^^ + class NextNode < Node + # Initialize a new NextNode node. + def initialize(source, node_id, location, flags, arguments, keyword_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @arguments = arguments + @keyword_loc = keyword_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_next_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [arguments] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield arguments if arguments + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << arguments if arguments + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [*arguments, keyword_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?arguments: ArgumentsNode?, ?keyword_loc: Location) -> NextNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, arguments: self.arguments, keyword_loc: self.keyword_loc) + NextNode.new(source, node_id, location, flags, arguments, keyword_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, arguments: ArgumentsNode?, keyword_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, arguments: arguments, keyword_loc: keyword_loc } + end + + # attr_reader arguments: ArgumentsNode? + attr_reader :arguments + + # attr_reader keyword_loc: Location + def keyword_loc + location = @keyword_loc + return location if location.is_a?(Location) + @keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_keyword_loc(repository) + repository.enter(node_id, :keyword_loc) + end + + # def keyword: () -> String + def keyword + keyword_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :next_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :next_node + 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?(NextNode) && + (arguments === other.arguments) && + (keyword_loc.nil? == other.keyword_loc.nil?) + end + end + + # Represents the use of the `nil` keyword. + # + # nil + # ^^^ + class NilNode < Node + # Initialize a new NilNode node. + def initialize(source, node_id, location, flags) + @source = source + @node_id = node_id + @location = location + @flags = flags + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_nil_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer) -> NilNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags) + NilNode.new(source, node_id, location, flags) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location } + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :nil_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :nil_node + 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?(NilNode) + end + end + + # Represents the use of `**nil` inside method arguments. + # + # def a(**nil) + # ^^^^^ + # end + class NoKeywordsParameterNode < Node + # Initialize a new NoKeywordsParameterNode node. + def initialize(source, node_id, location, flags, operator_loc, keyword_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @operator_loc = operator_loc + @keyword_loc = keyword_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_no_keywords_parameter_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [operator_loc, keyword_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?operator_loc: Location, ?keyword_loc: Location) -> NoKeywordsParameterNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, operator_loc: self.operator_loc, keyword_loc: self.keyword_loc) + NoKeywordsParameterNode.new(source, node_id, location, flags, operator_loc, keyword_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, operator_loc: Location, keyword_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, operator_loc: operator_loc, keyword_loc: keyword_loc } + end + + # attr_reader operator_loc: Location + def operator_loc + location = @operator_loc + return location if location.is_a?(Location) + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) + end + + # attr_reader keyword_loc: Location + def keyword_loc + location = @keyword_loc + return location if location.is_a?(Location) + @keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_keyword_loc(repository) + repository.enter(node_id, :keyword_loc) + end + + # def operator: () -> String + def operator + operator_loc.slice + end + + # def keyword: () -> String + def keyword + keyword_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :no_keywords_parameter_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :no_keywords_parameter_node + 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?(NoKeywordsParameterNode) && + (operator_loc.nil? == other.operator_loc.nil?) && + (keyword_loc.nil? == other.keyword_loc.nil?) + end + end + + # Represents an implicit set of parameters through the use of numbered parameters within a block or lambda. + # + # -> { _1 + _2 } + # ^^^^^^^^^^^^^^ + class NumberedParametersNode < Node + # Initialize a new NumberedParametersNode node. + def initialize(source, node_id, location, flags, maximum) + @source = source + @node_id = node_id + @location = location + @flags = flags + @maximum = maximum + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_numbered_parameters_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?maximum: Integer) -> NumberedParametersNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, maximum: self.maximum) + NumberedParametersNode.new(source, node_id, location, flags, maximum) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, maximum: Integer } + def deconstruct_keys(keys) + { node_id: node_id, location: location, maximum: maximum } + end + + # attr_reader maximum: Integer + attr_reader :maximum + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :numbered_parameters_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :numbered_parameters_node + 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?(NumberedParametersNode) && + (maximum === other.maximum) + end + end + + # Represents reading a numbered reference to a capture in the previous match. + # + # $1 + # ^^ + class NumberedReferenceReadNode < Node + # Initialize a new NumberedReferenceReadNode node. + def initialize(source, node_id, location, flags, number) + @source = source + @node_id = node_id + @location = location + @flags = flags + @number = number + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_numbered_reference_read_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?number: Integer) -> NumberedReferenceReadNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, number: self.number) + NumberedReferenceReadNode.new(source, node_id, location, flags, number) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, number: Integer } + def deconstruct_keys(keys) + { node_id: node_id, location: location, number: number } + end + + # 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` + attr_reader :number + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :numbered_reference_read_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :numbered_reference_read_node + 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?(NumberedReferenceReadNode) && + (number === other.number) + end + end + + # Represents an optional keyword parameter to a method, block, or lambda definition. + # + # def a(b: 1) + # ^^^^ + # end + class OptionalKeywordParameterNode < Node + # Initialize a new OptionalKeywordParameterNode node. + def initialize(source, node_id, location, flags, name, name_loc, value) + @source = source + @node_id = node_id + @location = location + @flags = flags + @name = name + @name_loc = name_loc + @value = value + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_optional_keyword_parameter_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [value] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield value + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [value] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [name_loc, value] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?value: Prism::node) -> OptionalKeywordParameterNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc, value: self.value) + OptionalKeywordParameterNode.new(source, node_id, location, flags, name, name_loc, value) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, name_loc: Location, value: Prism::node } + def deconstruct_keys(keys) + { node_id: node_id, location: location, name: name, name_loc: name_loc, value: value } + end + + # def repeated_parameter?: () -> bool + def repeated_parameter? + flags.anybits?(ParameterFlags::REPEATED_PARAMETER) + end + + # attr_reader name: Symbol + attr_reader :name + + # attr_reader name_loc: Location + def name_loc + location = @name_loc + return location if location.is_a?(Location) + @name_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + def save_name_loc(repository) + repository.enter(node_id, :name_loc) + end + + # attr_reader value: Prism::node + attr_reader :value + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :optional_keyword_parameter_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :optional_keyword_parameter_node + 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?(OptionalKeywordParameterNode) && + (flags === other.flags) && + (name === other.name) && + (name_loc.nil? == other.name_loc.nil?) && + (value === other.value) + end + end + + # Represents an optional parameter to a method, block, or lambda definition. + # + # def a(b = 1) + # ^^^^^ + # end + class OptionalParameterNode < Node + # Initialize a new OptionalParameterNode node. + def initialize(source, node_id, location, flags, name, name_loc, operator_loc, value) + @source = source + @node_id = node_id + @location = location + @flags = flags + @name = name + @name_loc = name_loc + @operator_loc = operator_loc + @value = value + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_optional_parameter_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [value] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield value + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [value] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [name_loc, operator_loc, value] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> OptionalParameterNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc, operator_loc: self.operator_loc, value: self.value) + OptionalParameterNode.new(source, node_id, location, flags, name, name_loc, operator_loc, value) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, name_loc: Location, operator_loc: Location, value: Prism::node } + def deconstruct_keys(keys) + { node_id: node_id, location: location, name: name, name_loc: name_loc, operator_loc: operator_loc, value: value } + end + + # def repeated_parameter?: () -> bool + def repeated_parameter? + flags.anybits?(ParameterFlags::REPEATED_PARAMETER) + end + + # attr_reader name: Symbol + attr_reader :name + + # attr_reader name_loc: Location + def name_loc + location = @name_loc + return location if location.is_a?(Location) + @name_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + def save_name_loc(repository) + repository.enter(node_id, :name_loc) + end + + # attr_reader operator_loc: Location + def operator_loc + location = @operator_loc + return location if location.is_a?(Location) + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) + end + + # attr_reader value: Prism::node + attr_reader :value + + # def operator: () -> String + def operator + operator_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :optional_parameter_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :optional_parameter_node + 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?(OptionalParameterNode) && + (flags === other.flags) && + (name === other.name) && + (name_loc.nil? == other.name_loc.nil?) && + (operator_loc.nil? == other.operator_loc.nil?) && + (value === other.value) + end + end + + # Represents the use of the `||` operator or the `or` keyword. + # + # left or right + # ^^^^^^^^^^^^^ + class OrNode < Node + # Initialize a new OrNode node. + def initialize(source, node_id, location, flags, left, right, operator_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @left = left + @right = right + @operator_loc = operator_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_or_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [left, right] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield left + yield right + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [left, right] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [left, right, operator_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?left: Prism::node, ?right: Prism::node, ?operator_loc: Location) -> OrNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, left: self.left, right: self.right, operator_loc: self.operator_loc) + OrNode.new(source, node_id, location, flags, left, right, operator_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, left: Prism::node, right: Prism::node, operator_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, left: left, right: right, operator_loc: operator_loc } + end + + # 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 + # ^ + attr_reader :left + + # Represents the right side of the expression. + # + # left || right + # ^^^^^ + # + # 1 or 2 + # ^ + attr_reader :right + + # The location of the `or` keyword or the `||` operator. + # + # left or right + # ^^ + def operator_loc + location = @operator_loc + return location if location.is_a?(Location) + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) + end + + # def operator: () -> String + def operator + operator_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :or_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :or_node + 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?(OrNode) && + (left === other.left) && + (right === other.right) && + (operator_loc.nil? == other.operator_loc.nil?) + end + end + + # Represents the list of parameters on a method, block, or lambda definition. + # + # def a(b, c, d) + # ^^^^^^^ + # end + class ParametersNode < Node + # Initialize a new ParametersNode node. + def initialize(source, node_id, location, flags, requireds, optionals, rest, posts, keywords, keyword_rest, block) + @source = source + @node_id = node_id + @location = location + @flags = flags + @requireds = requireds + @optionals = optionals + @rest = rest + @posts = posts + @keywords = keywords + @keyword_rest = keyword_rest + @block = block + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_parameters_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [*requireds, *optionals, rest, *posts, *keywords, keyword_rest, block] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + requireds.each { |node| yield node } + optionals.each { |node| yield node } + yield rest if rest + posts.each { |node| yield node } + keywords.each { |node| yield node } + yield keyword_rest if keyword_rest + yield block if block + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact.concat(requireds) + compact.concat(optionals) + compact << rest if rest + compact.concat(posts) + compact.concat(keywords) + compact << keyword_rest if keyword_rest + compact << block if block + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [*requireds, *optionals, *rest, *posts, *keywords, *keyword_rest, *block] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?requireds: Array[RequiredParameterNode | MultiTargetNode], ?optionals: Array[OptionalParameterNode], ?rest: RestParameterNode | ImplicitRestNode | nil, ?posts: Array[RequiredParameterNode | MultiTargetNode | KeywordRestParameterNode | NoKeywordsParameterNode | ForwardingParameterNode], ?keywords: Array[RequiredKeywordParameterNode | OptionalKeywordParameterNode], ?keyword_rest: KeywordRestParameterNode | ForwardingParameterNode | NoKeywordsParameterNode | nil, ?block: BlockParameterNode?) -> ParametersNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, requireds: self.requireds, optionals: self.optionals, rest: self.rest, posts: self.posts, keywords: self.keywords, keyword_rest: self.keyword_rest, block: self.block) + ParametersNode.new(source, node_id, location, flags, requireds, optionals, rest, posts, keywords, keyword_rest, block) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, requireds: Array[RequiredParameterNode | MultiTargetNode], optionals: Array[OptionalParameterNode], rest: RestParameterNode | ImplicitRestNode | nil, posts: Array[RequiredParameterNode | MultiTargetNode | KeywordRestParameterNode | NoKeywordsParameterNode | ForwardingParameterNode], keywords: Array[RequiredKeywordParameterNode | OptionalKeywordParameterNode], keyword_rest: KeywordRestParameterNode | ForwardingParameterNode | NoKeywordsParameterNode | nil, block: BlockParameterNode? } + def deconstruct_keys(keys) + { node_id: node_id, location: location, requireds: requireds, optionals: optionals, rest: rest, posts: posts, keywords: keywords, keyword_rest: keyword_rest, block: block } + end + + # attr_reader requireds: Array[RequiredParameterNode | MultiTargetNode] + attr_reader :requireds + + # attr_reader optionals: Array[OptionalParameterNode] + attr_reader :optionals + + # attr_reader rest: RestParameterNode | ImplicitRestNode | nil + attr_reader :rest + + # attr_reader posts: Array[RequiredParameterNode | MultiTargetNode | KeywordRestParameterNode | NoKeywordsParameterNode | ForwardingParameterNode] + attr_reader :posts + + # attr_reader keywords: Array[RequiredKeywordParameterNode | OptionalKeywordParameterNode] + attr_reader :keywords + + # attr_reader keyword_rest: KeywordRestParameterNode | ForwardingParameterNode | NoKeywordsParameterNode | nil + attr_reader :keyword_rest + + # attr_reader block: BlockParameterNode? + attr_reader :block + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :parameters_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :parameters_node + 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?(ParametersNode) && + (requireds.length == other.requireds.length) && + requireds.zip(other.requireds).all? { |left, right| left === right } && + (optionals.length == other.optionals.length) && + optionals.zip(other.optionals).all? { |left, right| left === right } && + (rest === other.rest) && + (posts.length == other.posts.length) && + posts.zip(other.posts).all? { |left, right| left === right } && + (keywords.length == other.keywords.length) && + keywords.zip(other.keywords).all? { |left, right| left === right } && + (keyword_rest === other.keyword_rest) && + (block === other.block) + end + end + + # Represents a parenthesized expression + # + # (10 + 34) + # ^^^^^^^^^ + class ParenthesesNode < Node + # Initialize a new ParenthesesNode node. + def initialize(source, node_id, location, flags, body, opening_loc, closing_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @body = body + @opening_loc = opening_loc + @closing_loc = closing_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_parentheses_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [body] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield body if body + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << body if body + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [*body, opening_loc, closing_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?body: Prism::node?, ?opening_loc: Location, ?closing_loc: Location) -> ParenthesesNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, body: self.body, opening_loc: self.opening_loc, closing_loc: self.closing_loc) + ParenthesesNode.new(source, node_id, location, flags, body, opening_loc, closing_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, body: Prism::node?, opening_loc: Location, closing_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, body: body, opening_loc: opening_loc, closing_loc: closing_loc } + end + + # def multiple_statements?: () -> bool + def multiple_statements? + flags.anybits?(ParenthesesNodeFlags::MULTIPLE_STATEMENTS) + end + + # attr_reader body: Prism::node? + attr_reader :body + + # attr_reader opening_loc: Location + def opening_loc + location = @opening_loc + return location if location.is_a?(Location) + @opening_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + def save_opening_loc(repository) + repository.enter(node_id, :opening_loc) + end + + # attr_reader closing_loc: Location + def closing_loc + location = @closing_loc + return location if location.is_a?(Location) + @closing_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + def save_closing_loc(repository) + repository.enter(node_id, :closing_loc) + end + + # def opening: () -> String + def opening + opening_loc.slice + end + + # def closing: () -> String + def closing + closing_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :parentheses_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :parentheses_node + 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?(ParenthesesNode) && + (flags === other.flags) && + (body === other.body) && + (opening_loc.nil? == other.opening_loc.nil?) && + (closing_loc.nil? == other.closing_loc.nil?) + end + end + + # Represents the use of the `^` operator for pinning an expression in a pattern matching expression. + # + # foo in ^(bar) + # ^^^^^^ + class PinnedExpressionNode < Node + # Initialize a new PinnedExpressionNode node. + def initialize(source, node_id, location, flags, expression, operator_loc, lparen_loc, rparen_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @expression = expression + @operator_loc = operator_loc + @lparen_loc = lparen_loc + @rparen_loc = rparen_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_pinned_expression_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [expression] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield expression + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [expression] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [expression, operator_loc, lparen_loc, rparen_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?expression: Prism::node, ?operator_loc: Location, ?lparen_loc: Location, ?rparen_loc: Location) -> PinnedExpressionNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, expression: self.expression, operator_loc: self.operator_loc, lparen_loc: self.lparen_loc, rparen_loc: self.rparen_loc) + PinnedExpressionNode.new(source, node_id, location, flags, expression, operator_loc, lparen_loc, rparen_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, expression: Prism::node, operator_loc: Location, lparen_loc: Location, rparen_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, expression: expression, operator_loc: operator_loc, lparen_loc: lparen_loc, rparen_loc: rparen_loc } + end + + # The expression used in the pinned expression + # + # foo in ^(bar) + # ^^^ + attr_reader :expression + + # The location of the `^` operator + # + # foo in ^(bar) + # ^ + def operator_loc + location = @operator_loc + return location if location.is_a?(Location) + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) + end + + # The location of the opening parenthesis. + # + # foo in ^(bar) + # ^ + def lparen_loc + location = @lparen_loc + return location if location.is_a?(Location) + @lparen_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the lparen_loc location using the given saved source so that + # it can be retrieved later. + def save_lparen_loc(repository) + repository.enter(node_id, :lparen_loc) + end + + # The location of the closing parenthesis. + # + # foo in ^(bar) + # ^ + def rparen_loc + location = @rparen_loc + return location if location.is_a?(Location) + @rparen_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the rparen_loc location using the given saved source so that + # it can be retrieved later. + def save_rparen_loc(repository) + repository.enter(node_id, :rparen_loc) + end + + # def operator: () -> String + def operator + operator_loc.slice + end + + # def lparen: () -> String + def lparen + lparen_loc.slice + end + + # def rparen: () -> String + def rparen + rparen_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :pinned_expression_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :pinned_expression_node + 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?(PinnedExpressionNode) && + (expression === other.expression) && + (operator_loc.nil? == other.operator_loc.nil?) && + (lparen_loc.nil? == other.lparen_loc.nil?) && + (rparen_loc.nil? == other.rparen_loc.nil?) + end + end + + # Represents the use of the `^` operator for pinning a variable in a pattern matching expression. + # + # foo in ^bar + # ^^^^ + class PinnedVariableNode < Node + # Initialize a new PinnedVariableNode node. + def initialize(source, node_id, location, flags, variable, operator_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @variable = variable + @operator_loc = operator_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_pinned_variable_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [variable] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield variable + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [variable] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [variable, operator_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?variable: LocalVariableReadNode | InstanceVariableReadNode | ClassVariableReadNode | GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode | ItLocalVariableReadNode | MissingNode, ?operator_loc: Location) -> PinnedVariableNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, variable: self.variable, operator_loc: self.operator_loc) + PinnedVariableNode.new(source, node_id, location, flags, variable, operator_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, variable: LocalVariableReadNode | InstanceVariableReadNode | ClassVariableReadNode | GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode | ItLocalVariableReadNode | MissingNode, operator_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, variable: variable, operator_loc: operator_loc } + end + + # The variable used in the pinned expression + # + # foo in ^bar + # ^^^ + attr_reader :variable + + # The location of the `^` operator + # + # foo in ^bar + # ^ + def operator_loc + location = @operator_loc + return location if location.is_a?(Location) + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) + end + + # def operator: () -> String + def operator + operator_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :pinned_variable_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :pinned_variable_node + 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?(PinnedVariableNode) && + (variable === other.variable) && + (operator_loc.nil? == other.operator_loc.nil?) + end + end + + # Represents the use of the `END` keyword. + # + # END { foo } + # ^^^^^^^^^^^ + class PostExecutionNode < Node + # Initialize a new PostExecutionNode node. + def initialize(source, node_id, location, flags, statements, keyword_loc, opening_loc, closing_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @statements = statements + @keyword_loc = keyword_loc + @opening_loc = opening_loc + @closing_loc = closing_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_post_execution_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [statements] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield statements if statements + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << statements if statements + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [*statements, keyword_loc, opening_loc, closing_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?statements: StatementsNode?, ?keyword_loc: Location, ?opening_loc: Location, ?closing_loc: Location) -> PostExecutionNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, statements: self.statements, keyword_loc: self.keyword_loc, opening_loc: self.opening_loc, closing_loc: self.closing_loc) + PostExecutionNode.new(source, node_id, location, flags, statements, keyword_loc, opening_loc, closing_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, statements: StatementsNode?, keyword_loc: Location, opening_loc: Location, closing_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, statements: statements, keyword_loc: keyword_loc, opening_loc: opening_loc, closing_loc: closing_loc } + end + + # attr_reader statements: StatementsNode? + attr_reader :statements + + # attr_reader keyword_loc: Location + def keyword_loc + location = @keyword_loc + return location if location.is_a?(Location) + @keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_keyword_loc(repository) + repository.enter(node_id, :keyword_loc) + end + + # attr_reader opening_loc: Location + def opening_loc + location = @opening_loc + return location if location.is_a?(Location) + @opening_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + def save_opening_loc(repository) + repository.enter(node_id, :opening_loc) + end + + # attr_reader closing_loc: Location + def closing_loc + location = @closing_loc + return location if location.is_a?(Location) + @closing_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + def save_closing_loc(repository) + repository.enter(node_id, :closing_loc) + end + + # def keyword: () -> String + def keyword + keyword_loc.slice + end + + # def opening: () -> String + def opening + opening_loc.slice + end + + # def closing: () -> String + def closing + closing_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :post_execution_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :post_execution_node + 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?(PostExecutionNode) && + (statements === other.statements) && + (keyword_loc.nil? == other.keyword_loc.nil?) && + (opening_loc.nil? == other.opening_loc.nil?) && + (closing_loc.nil? == other.closing_loc.nil?) + end + end + + # Represents the use of the `BEGIN` keyword. + # + # BEGIN { foo } + # ^^^^^^^^^^^^^ + class PreExecutionNode < Node + # Initialize a new PreExecutionNode node. + def initialize(source, node_id, location, flags, statements, keyword_loc, opening_loc, closing_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @statements = statements + @keyword_loc = keyword_loc + @opening_loc = opening_loc + @closing_loc = closing_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_pre_execution_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [statements] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield statements if statements + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << statements if statements + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [*statements, keyword_loc, opening_loc, closing_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?statements: StatementsNode?, ?keyword_loc: Location, ?opening_loc: Location, ?closing_loc: Location) -> PreExecutionNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, statements: self.statements, keyword_loc: self.keyword_loc, opening_loc: self.opening_loc, closing_loc: self.closing_loc) + PreExecutionNode.new(source, node_id, location, flags, statements, keyword_loc, opening_loc, closing_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, statements: StatementsNode?, keyword_loc: Location, opening_loc: Location, closing_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, statements: statements, keyword_loc: keyword_loc, opening_loc: opening_loc, closing_loc: closing_loc } + end + + # attr_reader statements: StatementsNode? + attr_reader :statements + + # attr_reader keyword_loc: Location + def keyword_loc + location = @keyword_loc + return location if location.is_a?(Location) + @keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_keyword_loc(repository) + repository.enter(node_id, :keyword_loc) + end + + # attr_reader opening_loc: Location + def opening_loc + location = @opening_loc + return location if location.is_a?(Location) + @opening_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + def save_opening_loc(repository) + repository.enter(node_id, :opening_loc) + end + + # attr_reader closing_loc: Location + def closing_loc + location = @closing_loc + return location if location.is_a?(Location) + @closing_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + def save_closing_loc(repository) + repository.enter(node_id, :closing_loc) + end + + # def keyword: () -> String + def keyword + keyword_loc.slice + end + + # def opening: () -> String + def opening + opening_loc.slice + end + + # def closing: () -> String + def closing + closing_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :pre_execution_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :pre_execution_node + 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?(PreExecutionNode) && + (statements === other.statements) && + (keyword_loc.nil? == other.keyword_loc.nil?) && + (opening_loc.nil? == other.opening_loc.nil?) && + (closing_loc.nil? == other.closing_loc.nil?) + end + end + + # The top level node of any parse tree. + class ProgramNode < Node + # Initialize a new ProgramNode node. + def initialize(source, node_id, location, flags, locals, statements) + @source = source + @node_id = node_id + @location = location + @flags = flags + @locals = locals + @statements = statements + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_program_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [statements] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield statements + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [statements] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [statements] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?locals: Array[Symbol], ?statements: StatementsNode) -> ProgramNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, locals: self.locals, statements: self.statements) + ProgramNode.new(source, node_id, location, flags, locals, statements) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, locals: Array[Symbol], statements: StatementsNode } + def deconstruct_keys(keys) + { node_id: node_id, location: location, locals: locals, statements: statements } + end + + # attr_reader locals: Array[Symbol] + attr_reader :locals + + # attr_reader statements: StatementsNode + attr_reader :statements + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :program_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :program_node + 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?(ProgramNode) && + (locals.length == other.locals.length) && + locals.zip(other.locals).all? { |left, right| left === right } && + (statements === other.statements) + end + end + + # Represents the use of the `..` or `...` operators. + # + # 1..2 + # ^^^^ + # + # c if a =~ /left/ ... b =~ /right/ + # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + class RangeNode < Node + # Initialize a new RangeNode node. + def initialize(source, node_id, location, flags, left, right, operator_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @left = left + @right = right + @operator_loc = operator_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_range_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [left, right] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield left if left + yield right if right + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << left if left + compact << right if right + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [*left, *right, operator_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?left: Prism::node?, ?right: Prism::node?, ?operator_loc: Location) -> RangeNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, left: self.left, right: self.right, operator_loc: self.operator_loc) + RangeNode.new(source, node_id, location, flags, left, right, operator_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, left: Prism::node?, right: Prism::node?, operator_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, left: left, right: right, operator_loc: operator_loc } + end + + # def exclude_end?: () -> bool + def exclude_end? + flags.anybits?(RangeFlags::EXCLUDE_END) + end + + # 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 + # ^^^^^ + attr_reader :left + + # 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. + attr_reader :right + + # The location of the `..` or `...` operator. + def operator_loc + location = @operator_loc + return location if location.is_a?(Location) + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) + end + + # def operator: () -> String + def operator + operator_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :range_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :range_node + 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?(RangeNode) && + (flags === other.flags) && + (left === other.left) && + (right === other.right) && + (operator_loc.nil? == other.operator_loc.nil?) + end + end + + # Represents a rational number literal. + # + # 1.0r + # ^^^^ + class RationalNode < Node + # Initialize a new RationalNode node. + def initialize(source, node_id, location, flags, numerator, denominator) + @source = source + @node_id = node_id + @location = location + @flags = flags + @numerator = numerator + @denominator = denominator + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_rational_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?numerator: Integer, ?denominator: Integer) -> RationalNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, numerator: self.numerator, denominator: self.denominator) + RationalNode.new(source, node_id, location, flags, numerator, denominator) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, numerator: Integer, denominator: Integer } + def deconstruct_keys(keys) + { node_id: node_id, location: location, numerator: numerator, denominator: denominator } + end + + # def binary?: () -> bool + def binary? + flags.anybits?(IntegerBaseFlags::BINARY) + end + + # def decimal?: () -> bool + def decimal? + flags.anybits?(IntegerBaseFlags::DECIMAL) + end + + # def octal?: () -> bool + def octal? + flags.anybits?(IntegerBaseFlags::OCTAL) + end + + # def hexadecimal?: () -> bool + def hexadecimal? + flags.anybits?(IntegerBaseFlags::HEXADECIMAL) + end + + # The numerator of the rational number. + # + # 1.5r # numerator 3 + attr_reader :numerator + + # The denominator of the rational number. + # + # 1.5r # denominator 2 + attr_reader :denominator + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :rational_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :rational_node + 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?(RationalNode) && + (flags === other.flags) && + (numerator === other.numerator) && + (denominator === other.denominator) + end + end + + # Represents the use of the `redo` keyword. + # + # redo + # ^^^^ + class RedoNode < Node + # Initialize a new RedoNode node. + def initialize(source, node_id, location, flags) + @source = source + @node_id = node_id + @location = location + @flags = flags + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_redo_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer) -> RedoNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags) + RedoNode.new(source, node_id, location, flags) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location } + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :redo_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :redo_node + 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?(RedoNode) + end + end + + # Represents a regular expression literal with no interpolation. + # + # /foo/i + # ^^^^^^ + class RegularExpressionNode < Node + # Initialize a new RegularExpressionNode node. + def initialize(source, node_id, location, flags, opening_loc, content_loc, closing_loc, unescaped) + @source = source + @node_id = node_id + @location = location + @flags = flags + @opening_loc = opening_loc + @content_loc = content_loc + @closing_loc = closing_loc + @unescaped = unescaped + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_regular_expression_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [opening_loc, content_loc, closing_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?content_loc: Location, ?closing_loc: Location, ?unescaped: String) -> RegularExpressionNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, opening_loc: self.opening_loc, content_loc: self.content_loc, closing_loc: self.closing_loc, unescaped: self.unescaped) + RegularExpressionNode.new(source, node_id, location, flags, opening_loc, content_loc, closing_loc, unescaped) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, opening_loc: Location, content_loc: Location, closing_loc: Location, unescaped: String } + def deconstruct_keys(keys) + { node_id: node_id, location: location, opening_loc: opening_loc, content_loc: content_loc, closing_loc: closing_loc, unescaped: unescaped } + end + + # def ignore_case?: () -> bool + def ignore_case? + flags.anybits?(RegularExpressionFlags::IGNORE_CASE) + end + + # def extended?: () -> bool + def extended? + flags.anybits?(RegularExpressionFlags::EXTENDED) + end + + # def multi_line?: () -> bool + def multi_line? + flags.anybits?(RegularExpressionFlags::MULTI_LINE) + end + + # def once?: () -> bool + def once? + flags.anybits?(RegularExpressionFlags::ONCE) + end + + # def euc_jp?: () -> bool + def euc_jp? + flags.anybits?(RegularExpressionFlags::EUC_JP) + end + + # def ascii_8bit?: () -> bool + def ascii_8bit? + flags.anybits?(RegularExpressionFlags::ASCII_8BIT) + end + + # def windows_31j?: () -> bool + def windows_31j? + flags.anybits?(RegularExpressionFlags::WINDOWS_31J) + end + + # def utf_8?: () -> bool + def utf_8? + flags.anybits?(RegularExpressionFlags::UTF_8) + end + + # def forced_utf8_encoding?: () -> bool + def forced_utf8_encoding? + flags.anybits?(RegularExpressionFlags::FORCED_UTF8_ENCODING) + end + + # def forced_binary_encoding?: () -> bool + def forced_binary_encoding? + flags.anybits?(RegularExpressionFlags::FORCED_BINARY_ENCODING) + end + + # def forced_us_ascii_encoding?: () -> bool + def forced_us_ascii_encoding? + flags.anybits?(RegularExpressionFlags::FORCED_US_ASCII_ENCODING) + end + + # attr_reader opening_loc: Location + def opening_loc + location = @opening_loc + return location if location.is_a?(Location) + @opening_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + def save_opening_loc(repository) + repository.enter(node_id, :opening_loc) + end + + # attr_reader content_loc: Location + def content_loc + location = @content_loc + return location if location.is_a?(Location) + @content_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the content_loc location using the given saved source so that + # it can be retrieved later. + def save_content_loc(repository) + repository.enter(node_id, :content_loc) + end + + # attr_reader closing_loc: Location + def closing_loc + location = @closing_loc + return location if location.is_a?(Location) + @closing_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + def save_closing_loc(repository) + repository.enter(node_id, :closing_loc) + end + + # attr_reader unescaped: String + attr_reader :unescaped + + # def opening: () -> String + def opening + opening_loc.slice + end + + # def content: () -> String + def content + content_loc.slice + end + + # def closing: () -> String + def closing + closing_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :regular_expression_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :regular_expression_node + 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?(RegularExpressionNode) && + (flags === other.flags) && + (opening_loc.nil? == other.opening_loc.nil?) && + (content_loc.nil? == other.content_loc.nil?) && + (closing_loc.nil? == other.closing_loc.nil?) && + (unescaped === other.unescaped) + end + end + + # Represents a required keyword parameter to a method, block, or lambda definition. + # + # def a(b: ) + # ^^ + # end + class RequiredKeywordParameterNode < Node + # Initialize a new RequiredKeywordParameterNode node. + def initialize(source, node_id, location, flags, name, name_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @name = name + @name_loc = name_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_required_keyword_parameter_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [name_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location) -> RequiredKeywordParameterNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc) + RequiredKeywordParameterNode.new(source, node_id, location, flags, name, name_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, name_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, name: name, name_loc: name_loc } + end + + # def repeated_parameter?: () -> bool + def repeated_parameter? + flags.anybits?(ParameterFlags::REPEATED_PARAMETER) + end + + # attr_reader name: Symbol + attr_reader :name + + # attr_reader name_loc: Location + def name_loc + location = @name_loc + return location if location.is_a?(Location) + @name_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + def save_name_loc(repository) + repository.enter(node_id, :name_loc) + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :required_keyword_parameter_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :required_keyword_parameter_node + 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?(RequiredKeywordParameterNode) && + (flags === other.flags) && + (name === other.name) && + (name_loc.nil? == other.name_loc.nil?) + end + end + + # Represents a required parameter to a method, block, or lambda definition. + # + # def a(b) + # ^ + # end + class RequiredParameterNode < Node + # Initialize a new RequiredParameterNode node. + def initialize(source, node_id, location, flags, name) + @source = source + @node_id = node_id + @location = location + @flags = flags + @name = name + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_required_parameter_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> RequiredParameterNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name) + RequiredParameterNode.new(source, node_id, location, flags, name) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol } + def deconstruct_keys(keys) + { node_id: node_id, location: location, name: name } + end + + # def repeated_parameter?: () -> bool + def repeated_parameter? + flags.anybits?(ParameterFlags::REPEATED_PARAMETER) + end + + # attr_reader name: Symbol + attr_reader :name + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :required_parameter_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :required_parameter_node + 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?(RequiredParameterNode) && + (flags === other.flags) && + (name === other.name) + end + end + + # Represents an expression modified with a rescue. + # + # foo rescue nil + # ^^^^^^^^^^^^^^ + class RescueModifierNode < Node + # Initialize a new RescueModifierNode node. + def initialize(source, node_id, location, flags, expression, keyword_loc, rescue_expression) + @source = source + @node_id = node_id + @location = location + @flags = flags + @expression = expression + @keyword_loc = keyword_loc + @rescue_expression = rescue_expression + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_rescue_modifier_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [expression, rescue_expression] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield expression + yield rescue_expression + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [expression, rescue_expression] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [expression, keyword_loc, rescue_expression] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?expression: Prism::node, ?keyword_loc: Location, ?rescue_expression: Prism::node) -> RescueModifierNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, expression: self.expression, keyword_loc: self.keyword_loc, rescue_expression: self.rescue_expression) + RescueModifierNode.new(source, node_id, location, flags, expression, keyword_loc, rescue_expression) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, expression: Prism::node, keyword_loc: Location, rescue_expression: Prism::node } + def deconstruct_keys(keys) + { node_id: node_id, location: location, expression: expression, keyword_loc: keyword_loc, rescue_expression: rescue_expression } + end + + # attr_reader expression: Prism::node + attr_reader :expression + + # attr_reader keyword_loc: Location + def keyword_loc + location = @keyword_loc + return location if location.is_a?(Location) + @keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_keyword_loc(repository) + repository.enter(node_id, :keyword_loc) + end + + # attr_reader rescue_expression: Prism::node + attr_reader :rescue_expression + + # def keyword: () -> String + def keyword + keyword_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :rescue_modifier_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :rescue_modifier_node + 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?(RescueModifierNode) && + (expression === other.expression) && + (keyword_loc.nil? == other.keyword_loc.nil?) && + (rescue_expression === other.rescue_expression) + end + end + + # Represents a rescue statement. + # + # begin + # rescue Foo, *splat, Bar => ex + # foo + # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + # end + # + # `Foo, *splat, Bar` are in the `exceptions` field. `ex` is in the `reference` field. + class RescueNode < Node + # Initialize a new RescueNode node. + def initialize(source, node_id, location, flags, keyword_loc, exceptions, operator_loc, reference, then_keyword_loc, statements, subsequent) + @source = source + @node_id = node_id + @location = location + @flags = flags + @keyword_loc = keyword_loc + @exceptions = exceptions + @operator_loc = operator_loc + @reference = reference + @then_keyword_loc = then_keyword_loc + @statements = statements + @subsequent = subsequent + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_rescue_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [*exceptions, reference, statements, subsequent] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + exceptions.each { |node| yield node } + yield reference if reference + yield statements if statements + yield subsequent if subsequent + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact.concat(exceptions) + compact << reference if reference + compact << statements if statements + compact << subsequent if subsequent + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [keyword_loc, *exceptions, *operator_loc, *reference, *then_keyword_loc, *statements, *subsequent] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?keyword_loc: Location, ?exceptions: Array[Prism::node], ?operator_loc: Location?, ?reference: LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | BackReferenceReadNode | NumberedReferenceReadNode | MissingNode | nil, ?then_keyword_loc: Location?, ?statements: StatementsNode?, ?subsequent: RescueNode?) -> RescueNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, keyword_loc: self.keyword_loc, exceptions: self.exceptions, operator_loc: self.operator_loc, reference: self.reference, then_keyword_loc: self.then_keyword_loc, statements: self.statements, subsequent: self.subsequent) + RescueNode.new(source, node_id, location, flags, keyword_loc, exceptions, operator_loc, reference, then_keyword_loc, statements, subsequent) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, keyword_loc: Location, exceptions: Array[Prism::node], operator_loc: Location?, reference: LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | BackReferenceReadNode | NumberedReferenceReadNode | MissingNode | nil, then_keyword_loc: Location?, statements: StatementsNode?, subsequent: RescueNode? } + def deconstruct_keys(keys) + { node_id: node_id, location: location, keyword_loc: keyword_loc, exceptions: exceptions, operator_loc: operator_loc, reference: reference, then_keyword_loc: then_keyword_loc, statements: statements, subsequent: subsequent } + end + + # attr_reader keyword_loc: Location + def keyword_loc + location = @keyword_loc + return location if location.is_a?(Location) + @keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_keyword_loc(repository) + repository.enter(node_id, :keyword_loc) + end + + # attr_reader exceptions: Array[Prism::node] + attr_reader :exceptions + + # attr_reader operator_loc: Location? + def operator_loc + location = @operator_loc + case location + when nil + nil + when Location + location + else + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) unless @operator_loc.nil? + end + + # attr_reader reference: LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | BackReferenceReadNode | NumberedReferenceReadNode | MissingNode | nil + attr_reader :reference + + # attr_reader then_keyword_loc: Location? + def then_keyword_loc + location = @then_keyword_loc + case location + when nil + nil + when Location + location + else + @then_keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the then_keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_then_keyword_loc(repository) + repository.enter(node_id, :then_keyword_loc) unless @then_keyword_loc.nil? + end + + # attr_reader statements: StatementsNode? + attr_reader :statements + + # attr_reader subsequent: RescueNode? + attr_reader :subsequent + + # def keyword: () -> String + def keyword + keyword_loc.slice + end + + # def operator: () -> String? + def operator + operator_loc&.slice + end + + # def then_keyword: () -> String? + def then_keyword + then_keyword_loc&.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :rescue_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :rescue_node + 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?(RescueNode) && + (keyword_loc.nil? == other.keyword_loc.nil?) && + (exceptions.length == other.exceptions.length) && + exceptions.zip(other.exceptions).all? { |left, right| left === right } && + (operator_loc.nil? == other.operator_loc.nil?) && + (reference === other.reference) && + (then_keyword_loc.nil? == other.then_keyword_loc.nil?) && + (statements === other.statements) && + (subsequent === other.subsequent) + end + end + + # Represents a rest parameter to a method, block, or lambda definition. + # + # def a(*b) + # ^^ + # end + class RestParameterNode < Node + # Initialize a new RestParameterNode node. + def initialize(source, node_id, location, flags, name, name_loc, operator_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @name = name + @name_loc = name_loc + @operator_loc = operator_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_rest_parameter_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [*name_loc, operator_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol?, ?name_loc: Location?, ?operator_loc: Location) -> RestParameterNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc, operator_loc: self.operator_loc) + RestParameterNode.new(source, node_id, location, flags, name, name_loc, operator_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol?, name_loc: Location?, operator_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, name: name, name_loc: name_loc, operator_loc: operator_loc } + end + + # def repeated_parameter?: () -> bool + def repeated_parameter? + flags.anybits?(ParameterFlags::REPEATED_PARAMETER) + end + + # attr_reader name: Symbol? + attr_reader :name + + # attr_reader name_loc: Location? + def name_loc + location = @name_loc + case location + when nil + nil + when Location + location + else + @name_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + def save_name_loc(repository) + repository.enter(node_id, :name_loc) unless @name_loc.nil? + end + + # attr_reader operator_loc: Location + def operator_loc + location = @operator_loc + return location if location.is_a?(Location) + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) + end + + # def operator: () -> String + def operator + operator_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :rest_parameter_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :rest_parameter_node + 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?(RestParameterNode) && + (flags === other.flags) && + (name === other.name) && + (name_loc.nil? == other.name_loc.nil?) && + (operator_loc.nil? == other.operator_loc.nil?) + end + end + + # Represents the use of the `retry` keyword. + # + # retry + # ^^^^^ + class RetryNode < Node + # Initialize a new RetryNode node. + def initialize(source, node_id, location, flags) + @source = source + @node_id = node_id + @location = location + @flags = flags + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_retry_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer) -> RetryNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags) + RetryNode.new(source, node_id, location, flags) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location } + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :retry_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :retry_node + 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?(RetryNode) + end + end + + # Represents the use of the `return` keyword. + # + # return 1 + # ^^^^^^^^ + class ReturnNode < Node + # Initialize a new ReturnNode node. + def initialize(source, node_id, location, flags, keyword_loc, arguments) + @source = source + @node_id = node_id + @location = location + @flags = flags + @keyword_loc = keyword_loc + @arguments = arguments + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_return_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [arguments] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield arguments if arguments + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << arguments if arguments + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [keyword_loc, *arguments] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?keyword_loc: Location, ?arguments: ArgumentsNode?) -> ReturnNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, keyword_loc: self.keyword_loc, arguments: self.arguments) + ReturnNode.new(source, node_id, location, flags, keyword_loc, arguments) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, keyword_loc: Location, arguments: ArgumentsNode? } + def deconstruct_keys(keys) + { node_id: node_id, location: location, keyword_loc: keyword_loc, arguments: arguments } + end + + # attr_reader keyword_loc: Location + def keyword_loc + location = @keyword_loc + return location if location.is_a?(Location) + @keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_keyword_loc(repository) + repository.enter(node_id, :keyword_loc) + end + + # attr_reader arguments: ArgumentsNode? + attr_reader :arguments + + # def keyword: () -> String + def keyword + keyword_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :return_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :return_node + 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?(ReturnNode) && + (keyword_loc.nil? == other.keyword_loc.nil?) && + (arguments === other.arguments) + end + end + + # Represents the `self` keyword. + # + # self + # ^^^^ + class SelfNode < Node + # Initialize a new SelfNode node. + def initialize(source, node_id, location, flags) + @source = source + @node_id = node_id + @location = location + @flags = flags + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_self_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer) -> SelfNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags) + SelfNode.new(source, node_id, location, flags) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location } + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :self_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :self_node + 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?(SelfNode) + end + end + + # 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 } + # ^^^^^^^^^^^^ + class ShareableConstantNode < Node + # Initialize a new ShareableConstantNode node. + def initialize(source, node_id, location, flags, write) + @source = source + @node_id = node_id + @location = location + @flags = flags + @write = write + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_shareable_constant_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [write] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield write + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [write] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [write] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?write: ConstantWriteNode | ConstantAndWriteNode | ConstantOrWriteNode | ConstantOperatorWriteNode | ConstantPathWriteNode | ConstantPathAndWriteNode | ConstantPathOrWriteNode | ConstantPathOperatorWriteNode) -> ShareableConstantNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, write: self.write) + ShareableConstantNode.new(source, node_id, location, flags, write) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, write: ConstantWriteNode | ConstantAndWriteNode | ConstantOrWriteNode | ConstantOperatorWriteNode | ConstantPathWriteNode | ConstantPathAndWriteNode | ConstantPathOrWriteNode | ConstantPathOperatorWriteNode } + def deconstruct_keys(keys) + { node_id: node_id, location: location, write: write } + end + + # def literal?: () -> bool + def literal? + flags.anybits?(ShareableConstantNodeFlags::LITERAL) + end + + # def experimental_everything?: () -> bool + def experimental_everything? + flags.anybits?(ShareableConstantNodeFlags::EXPERIMENTAL_EVERYTHING) + end + + # def experimental_copy?: () -> bool + def experimental_copy? + flags.anybits?(ShareableConstantNodeFlags::EXPERIMENTAL_COPY) + end + + # The constant write that should be modified with the shareability state. + attr_reader :write + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :shareable_constant_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :shareable_constant_node + 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?(ShareableConstantNode) && + (flags === other.flags) && + (write === other.write) + end + end + + # Represents a singleton class declaration involving the `class` keyword. + # + # class << self end + # ^^^^^^^^^^^^^^^^^ + class SingletonClassNode < Node + # Initialize a new SingletonClassNode node. + def initialize(source, node_id, location, flags, locals, class_keyword_loc, operator_loc, expression, body, end_keyword_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @locals = locals + @class_keyword_loc = class_keyword_loc + @operator_loc = operator_loc + @expression = expression + @body = body + @end_keyword_loc = end_keyword_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_singleton_class_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [expression, body] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield expression + yield body if body + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << expression + compact << body if body + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [class_keyword_loc, operator_loc, expression, *body, end_keyword_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?locals: Array[Symbol], ?class_keyword_loc: Location, ?operator_loc: Location, ?expression: Prism::node, ?body: StatementsNode | BeginNode | nil, ?end_keyword_loc: Location) -> SingletonClassNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, locals: self.locals, class_keyword_loc: self.class_keyword_loc, operator_loc: self.operator_loc, expression: self.expression, body: self.body, end_keyword_loc: self.end_keyword_loc) + SingletonClassNode.new(source, node_id, location, flags, locals, class_keyword_loc, operator_loc, expression, body, end_keyword_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, locals: Array[Symbol], class_keyword_loc: Location, operator_loc: Location, expression: Prism::node, body: StatementsNode | BeginNode | nil, end_keyword_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, locals: locals, class_keyword_loc: class_keyword_loc, operator_loc: operator_loc, expression: expression, body: body, end_keyword_loc: end_keyword_loc } + end + + # attr_reader locals: Array[Symbol] + attr_reader :locals + + # attr_reader class_keyword_loc: Location + def class_keyword_loc + location = @class_keyword_loc + return location if location.is_a?(Location) + @class_keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the class_keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_class_keyword_loc(repository) + repository.enter(node_id, :class_keyword_loc) + end + + # attr_reader operator_loc: Location + def operator_loc + location = @operator_loc + return location if location.is_a?(Location) + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) + end + + # attr_reader expression: Prism::node + attr_reader :expression + + # attr_reader body: StatementsNode | BeginNode | nil + attr_reader :body + + # attr_reader end_keyword_loc: Location + def end_keyword_loc + location = @end_keyword_loc + return location if location.is_a?(Location) + @end_keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the end_keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_end_keyword_loc(repository) + repository.enter(node_id, :end_keyword_loc) + end + + # def class_keyword: () -> String + def class_keyword + class_keyword_loc.slice + end + + # def operator: () -> String + def operator + operator_loc.slice + end + + # def end_keyword: () -> String + def end_keyword + end_keyword_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :singleton_class_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :singleton_class_node + 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?(SingletonClassNode) && + (locals.length == other.locals.length) && + locals.zip(other.locals).all? { |left, right| left === right } && + (class_keyword_loc.nil? == other.class_keyword_loc.nil?) && + (operator_loc.nil? == other.operator_loc.nil?) && + (expression === other.expression) && + (body === other.body) && + (end_keyword_loc.nil? == other.end_keyword_loc.nil?) + end + end + + # Represents the use of the `__ENCODING__` keyword. + # + # __ENCODING__ + # ^^^^^^^^^^^^ + class SourceEncodingNode < Node + # Initialize a new SourceEncodingNode node. + def initialize(source, node_id, location, flags) + @source = source + @node_id = node_id + @location = location + @flags = flags + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_source_encoding_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer) -> SourceEncodingNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags) + SourceEncodingNode.new(source, node_id, location, flags) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location } + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :source_encoding_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :source_encoding_node + 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?(SourceEncodingNode) + end + end + + # Represents the use of the `__FILE__` keyword. + # + # __FILE__ + # ^^^^^^^^ + class SourceFileNode < Node + # Initialize a new SourceFileNode node. + def initialize(source, node_id, location, flags, filepath) + @source = source + @node_id = node_id + @location = location + @flags = flags + @filepath = filepath + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_source_file_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?filepath: String) -> SourceFileNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, filepath: self.filepath) + SourceFileNode.new(source, node_id, location, flags, filepath) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, filepath: String } + def deconstruct_keys(keys) + { node_id: node_id, location: location, filepath: filepath } + end + + # def forced_utf8_encoding?: () -> bool + def forced_utf8_encoding? + flags.anybits?(StringFlags::FORCED_UTF8_ENCODING) + end + + # def forced_binary_encoding?: () -> bool + def forced_binary_encoding? + flags.anybits?(StringFlags::FORCED_BINARY_ENCODING) + end + + # def frozen?: () -> bool + def frozen? + flags.anybits?(StringFlags::FROZEN) + end + + # def mutable?: () -> bool + def mutable? + flags.anybits?(StringFlags::MUTABLE) + end + + # Represents the file path being parsed. This corresponds directly to the `filepath` option given to the various `Prism::parse*` APIs. + attr_reader :filepath + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :source_file_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :source_file_node + 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?(SourceFileNode) && + (flags === other.flags) && + (filepath === other.filepath) + end + end + + # Represents the use of the `__LINE__` keyword. + # + # __LINE__ + # ^^^^^^^^ + class SourceLineNode < Node + # Initialize a new SourceLineNode node. + def initialize(source, node_id, location, flags) + @source = source + @node_id = node_id + @location = location + @flags = flags + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_source_line_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer) -> SourceLineNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags) + SourceLineNode.new(source, node_id, location, flags) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location } + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :source_line_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :source_line_node + 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?(SourceLineNode) + end + end + + # Represents the use of the splat operator. + # + # [*a] + # ^^ + class SplatNode < Node + # Initialize a new SplatNode node. + def initialize(source, node_id, location, flags, operator_loc, expression) + @source = source + @node_id = node_id + @location = location + @flags = flags + @operator_loc = operator_loc + @expression = expression + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_splat_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [expression] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield expression if expression + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << expression if expression + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [operator_loc, *expression] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?operator_loc: Location, ?expression: Prism::node?) -> SplatNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, operator_loc: self.operator_loc, expression: self.expression) + SplatNode.new(source, node_id, location, flags, operator_loc, expression) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, operator_loc: Location, expression: Prism::node? } + def deconstruct_keys(keys) + { node_id: node_id, location: location, operator_loc: operator_loc, expression: expression } + end + + # attr_reader operator_loc: Location + def operator_loc + location = @operator_loc + return location if location.is_a?(Location) + @operator_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + def save_operator_loc(repository) + repository.enter(node_id, :operator_loc) + end + + # attr_reader expression: Prism::node? + attr_reader :expression + + # def operator: () -> String + def operator + operator_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :splat_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :splat_node + 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?(SplatNode) && + (operator_loc.nil? == other.operator_loc.nil?) && + (expression === other.expression) + end + end + + # Represents a set of statements contained within some scope. + # + # foo; bar; baz + # ^^^^^^^^^^^^^ + class StatementsNode < Node + # Initialize a new StatementsNode node. + def initialize(source, node_id, location, flags, body) + @source = source + @node_id = node_id + @location = location + @flags = flags + @body = body + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_statements_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [*body] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + body.each { |node| yield node } + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [*body] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [*body] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?body: Array[Prism::node]) -> StatementsNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, body: self.body) + StatementsNode.new(source, node_id, location, flags, body) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, body: Array[Prism::node] } + def deconstruct_keys(keys) + { node_id: node_id, location: location, body: body } + end + + # attr_reader body: Array[Prism::node] + attr_reader :body + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :statements_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :statements_node + 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?(StatementsNode) && + (body.length == other.body.length) && + body.zip(other.body).all? { |left, right| left === right } + end + end + + # 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" + # ^^^^ ^^^^ + class StringNode < Node + # Initialize a new StringNode node. + def initialize(source, node_id, location, flags, opening_loc, content_loc, closing_loc, unescaped) + @source = source + @node_id = node_id + @location = location + @flags = flags + @opening_loc = opening_loc + @content_loc = content_loc + @closing_loc = closing_loc + @unescaped = unescaped + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_string_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [*opening_loc, content_loc, *closing_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location?, ?content_loc: Location, ?closing_loc: Location?, ?unescaped: String) -> StringNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, opening_loc: self.opening_loc, content_loc: self.content_loc, closing_loc: self.closing_loc, unescaped: self.unescaped) + StringNode.new(source, node_id, location, flags, opening_loc, content_loc, closing_loc, unescaped) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, opening_loc: Location?, content_loc: Location, closing_loc: Location?, unescaped: String } + def deconstruct_keys(keys) + { node_id: node_id, location: location, opening_loc: opening_loc, content_loc: content_loc, closing_loc: closing_loc, unescaped: unescaped } + end + + # def forced_utf8_encoding?: () -> bool + def forced_utf8_encoding? + flags.anybits?(StringFlags::FORCED_UTF8_ENCODING) + end + + # def forced_binary_encoding?: () -> bool + def forced_binary_encoding? + flags.anybits?(StringFlags::FORCED_BINARY_ENCODING) + end + + # def frozen?: () -> bool + def frozen? + flags.anybits?(StringFlags::FROZEN) + end + + # def mutable?: () -> bool + def mutable? + flags.anybits?(StringFlags::MUTABLE) + end + + # attr_reader opening_loc: Location? + def opening_loc + location = @opening_loc + case location + when nil + nil + when Location + location + else + @opening_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + def save_opening_loc(repository) + repository.enter(node_id, :opening_loc) unless @opening_loc.nil? + end + + # attr_reader content_loc: Location + def content_loc + location = @content_loc + return location if location.is_a?(Location) + @content_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the content_loc location using the given saved source so that + # it can be retrieved later. + def save_content_loc(repository) + repository.enter(node_id, :content_loc) + end + + # attr_reader closing_loc: Location? + def closing_loc + location = @closing_loc + case location + when nil + nil + when Location + location + else + @closing_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + def save_closing_loc(repository) + repository.enter(node_id, :closing_loc) unless @closing_loc.nil? + end + + # attr_reader unescaped: String + attr_reader :unescaped + + # def opening: () -> String? + def opening + opening_loc&.slice + end + + # def content: () -> String + def content + content_loc.slice + end + + # def closing: () -> String? + def closing + closing_loc&.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :string_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :string_node + 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?(StringNode) && + (flags === other.flags) && + (opening_loc.nil? == other.opening_loc.nil?) && + (content_loc.nil? == other.content_loc.nil?) && + (closing_loc.nil? == other.closing_loc.nil?) && + (unescaped === other.unescaped) + end + end + + # Represents the use of the `super` keyword with parentheses or arguments. + # + # super() + # ^^^^^^^ + # + # super foo, bar + # ^^^^^^^^^^^^^^ + # + # If no arguments are provided (except for a block), it would be a `ForwardingSuperNode` instead. + class SuperNode < Node + # Initialize a new SuperNode node. + def initialize(source, node_id, location, flags, keyword_loc, lparen_loc, arguments, rparen_loc, block) + @source = source + @node_id = node_id + @location = location + @flags = flags + @keyword_loc = keyword_loc + @lparen_loc = lparen_loc + @arguments = arguments + @rparen_loc = rparen_loc + @block = block + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_super_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [arguments, block] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield arguments if arguments + yield block if block + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << arguments if arguments + compact << block if block + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [keyword_loc, *lparen_loc, *arguments, *rparen_loc, *block] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?keyword_loc: Location, ?lparen_loc: Location?, ?arguments: ArgumentsNode?, ?rparen_loc: Location?, ?block: BlockNode | BlockArgumentNode | nil) -> SuperNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, keyword_loc: self.keyword_loc, lparen_loc: self.lparen_loc, arguments: self.arguments, rparen_loc: self.rparen_loc, block: self.block) + SuperNode.new(source, node_id, location, flags, keyword_loc, lparen_loc, arguments, rparen_loc, block) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, keyword_loc: Location, lparen_loc: Location?, arguments: ArgumentsNode?, rparen_loc: Location?, block: BlockNode | BlockArgumentNode | nil } + def deconstruct_keys(keys) + { node_id: node_id, location: location, keyword_loc: keyword_loc, lparen_loc: lparen_loc, arguments: arguments, rparen_loc: rparen_loc, block: block } + end + + # attr_reader keyword_loc: Location + def keyword_loc + location = @keyword_loc + return location if location.is_a?(Location) + @keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_keyword_loc(repository) + repository.enter(node_id, :keyword_loc) + end + + # attr_reader lparen_loc: Location? + def lparen_loc + location = @lparen_loc + case location + when nil + nil + when Location + location + else + @lparen_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the lparen_loc location using the given saved source so that + # it can be retrieved later. + def save_lparen_loc(repository) + repository.enter(node_id, :lparen_loc) unless @lparen_loc.nil? + end + + # Can be only `nil` when there are empty parentheses, like `super()`. + attr_reader :arguments + + # attr_reader rparen_loc: Location? + def rparen_loc + location = @rparen_loc + case location + when nil + nil + when Location + location + else + @rparen_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the rparen_loc location using the given saved source so that + # it can be retrieved later. + def save_rparen_loc(repository) + repository.enter(node_id, :rparen_loc) unless @rparen_loc.nil? + end + + # attr_reader block: BlockNode | BlockArgumentNode | nil + attr_reader :block + + # def keyword: () -> String + def keyword + keyword_loc.slice + end + + # def lparen: () -> String? + def lparen + lparen_loc&.slice + end + + # def rparen: () -> String? + def rparen + rparen_loc&.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :super_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :super_node + 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?(SuperNode) && + (keyword_loc.nil? == other.keyword_loc.nil?) && + (lparen_loc.nil? == other.lparen_loc.nil?) && + (arguments === other.arguments) && + (rparen_loc.nil? == other.rparen_loc.nil?) && + (block === other.block) + end + end + + # Represents a symbol literal or a symbol contained within a `%i` list. + # + # :foo + # ^^^^ + # + # %i[foo] + # ^^^ + class SymbolNode < Node + # Initialize a new SymbolNode node. + def initialize(source, node_id, location, flags, opening_loc, value_loc, closing_loc, unescaped) + @source = source + @node_id = node_id + @location = location + @flags = flags + @opening_loc = opening_loc + @value_loc = value_loc + @closing_loc = closing_loc + @unescaped = unescaped + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_symbol_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [*opening_loc, *value_loc, *closing_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location?, ?value_loc: Location?, ?closing_loc: Location?, ?unescaped: String) -> SymbolNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, opening_loc: self.opening_loc, value_loc: self.value_loc, closing_loc: self.closing_loc, unescaped: self.unescaped) + SymbolNode.new(source, node_id, location, flags, opening_loc, value_loc, closing_loc, unescaped) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, opening_loc: Location?, value_loc: Location?, closing_loc: Location?, unescaped: String } + def deconstruct_keys(keys) + { node_id: node_id, location: location, opening_loc: opening_loc, value_loc: value_loc, closing_loc: closing_loc, unescaped: unescaped } + end + + # def forced_utf8_encoding?: () -> bool + def forced_utf8_encoding? + flags.anybits?(SymbolFlags::FORCED_UTF8_ENCODING) + end + + # def forced_binary_encoding?: () -> bool + def forced_binary_encoding? + flags.anybits?(SymbolFlags::FORCED_BINARY_ENCODING) + end + + # def forced_us_ascii_encoding?: () -> bool + def forced_us_ascii_encoding? + flags.anybits?(SymbolFlags::FORCED_US_ASCII_ENCODING) + end + + # attr_reader opening_loc: Location? + def opening_loc + location = @opening_loc + case location + when nil + nil + when Location + location + else + @opening_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + def save_opening_loc(repository) + repository.enter(node_id, :opening_loc) unless @opening_loc.nil? + end + + # attr_reader value_loc: Location? + def value_loc + location = @value_loc + case location + when nil + nil + when Location + location + else + @value_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the value_loc location using the given saved source so that + # it can be retrieved later. + def save_value_loc(repository) + repository.enter(node_id, :value_loc) unless @value_loc.nil? + end + + # attr_reader closing_loc: Location? + def closing_loc + location = @closing_loc + case location + when nil + nil + when Location + location + else + @closing_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + def save_closing_loc(repository) + repository.enter(node_id, :closing_loc) unless @closing_loc.nil? + end + + # attr_reader unescaped: String + attr_reader :unescaped + + # def opening: () -> String? + def opening + opening_loc&.slice + end + + # def value: () -> String? + def value + value_loc&.slice + end + + # def closing: () -> String? + def closing + closing_loc&.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :symbol_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :symbol_node + 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?(SymbolNode) && + (flags === other.flags) && + (opening_loc.nil? == other.opening_loc.nil?) && + (value_loc.nil? == other.value_loc.nil?) && + (closing_loc.nil? == other.closing_loc.nil?) && + (unescaped === other.unescaped) + end + end + + # Represents the use of the literal `true` keyword. + # + # true + # ^^^^ + class TrueNode < Node + # Initialize a new TrueNode node. + def initialize(source, node_id, location, flags) + @source = source + @node_id = node_id + @location = location + @flags = flags + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_true_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer) -> TrueNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags) + TrueNode.new(source, node_id, location, flags) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location } + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :true_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :true_node + 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?(TrueNode) + end + end + + # Represents the use of the `undef` keyword. + # + # undef :foo, :bar, :baz + # ^^^^^^^^^^^^^^^^^^^^^^ + class UndefNode < Node + # Initialize a new UndefNode node. + def initialize(source, node_id, location, flags, names, keyword_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @names = names + @keyword_loc = keyword_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_undef_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [*names] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + names.each { |node| yield node } + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [*names] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [*names, keyword_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?names: Array[SymbolNode | InterpolatedSymbolNode], ?keyword_loc: Location) -> UndefNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, names: self.names, keyword_loc: self.keyword_loc) + UndefNode.new(source, node_id, location, flags, names, keyword_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, names: Array[SymbolNode | InterpolatedSymbolNode], keyword_loc: Location } + def deconstruct_keys(keys) + { node_id: node_id, location: location, names: names, keyword_loc: keyword_loc } + end + + # attr_reader names: Array[SymbolNode | InterpolatedSymbolNode] + attr_reader :names + + # attr_reader keyword_loc: Location + def keyword_loc + location = @keyword_loc + return location if location.is_a?(Location) + @keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_keyword_loc(repository) + repository.enter(node_id, :keyword_loc) + end + + # def keyword: () -> String + def keyword + keyword_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :undef_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :undef_node + 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?(UndefNode) && + (names.length == other.names.length) && + names.zip(other.names).all? { |left, right| left === right } && + (keyword_loc.nil? == other.keyword_loc.nil?) + end + end + + # Represents the use of the `unless` keyword, either in the block form or the modifier form. + # + # bar unless foo + # ^^^^^^^^^^^^^^ + # + # unless foo then bar end + # ^^^^^^^^^^^^^^^^^^^^^^^ + class UnlessNode < Node + # Initialize a new UnlessNode node. + def initialize(source, node_id, location, flags, keyword_loc, predicate, then_keyword_loc, statements, else_clause, end_keyword_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @keyword_loc = keyword_loc + @predicate = predicate + @then_keyword_loc = then_keyword_loc + @statements = statements + @else_clause = else_clause + @end_keyword_loc = end_keyword_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_unless_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [predicate, statements, else_clause] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield predicate + yield statements if statements + yield else_clause if else_clause + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << predicate + compact << statements if statements + compact << else_clause if else_clause + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [keyword_loc, predicate, *then_keyword_loc, *statements, *else_clause, *end_keyword_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?keyword_loc: Location, ?predicate: Prism::node, ?then_keyword_loc: Location?, ?statements: StatementsNode?, ?else_clause: ElseNode?, ?end_keyword_loc: Location?) -> UnlessNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, keyword_loc: self.keyword_loc, predicate: self.predicate, then_keyword_loc: self.then_keyword_loc, statements: self.statements, else_clause: self.else_clause, end_keyword_loc: self.end_keyword_loc) + UnlessNode.new(source, node_id, location, flags, keyword_loc, predicate, then_keyword_loc, statements, else_clause, end_keyword_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, keyword_loc: Location, predicate: Prism::node, then_keyword_loc: Location?, statements: StatementsNode?, else_clause: ElseNode?, end_keyword_loc: Location? } + def deconstruct_keys(keys) + { node_id: node_id, location: location, keyword_loc: keyword_loc, predicate: predicate, then_keyword_loc: then_keyword_loc, statements: statements, else_clause: else_clause, end_keyword_loc: end_keyword_loc } + end + + # The location of the `unless` keyword. + # + # unless cond then bar end + # ^^^^^^ + # + # bar unless cond + # ^^^^^^ + def keyword_loc + location = @keyword_loc + return location if location.is_a?(Location) + @keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_keyword_loc(repository) + repository.enter(node_id, :keyword_loc) + end + + # 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 + # ^^^^ + attr_reader :predicate + + # The location of the `then` keyword, if present. + # + # unless cond then bar end + # ^^^^ + def then_keyword_loc + location = @then_keyword_loc + case location + when nil + nil + when Location + location + else + @then_keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the then_keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_then_keyword_loc(repository) + repository.enter(node_id, :then_keyword_loc) unless @then_keyword_loc.nil? + end + + # 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 + # ^^^ + attr_reader :statements + + # The else clause of the unless expression, if present. + # + # unless cond then bar else baz end + # ^^^^^^^^ + attr_reader :else_clause + + # The location of the `end` keyword, if present. + # + # unless cond then bar end + # ^^^ + def end_keyword_loc + location = @end_keyword_loc + case location + when nil + nil + when Location + location + else + @end_keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the end_keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_end_keyword_loc(repository) + repository.enter(node_id, :end_keyword_loc) unless @end_keyword_loc.nil? + end + + # def keyword: () -> String + def keyword + keyword_loc.slice + end + + # def then_keyword: () -> String? + def then_keyword + then_keyword_loc&.slice + end + + # def end_keyword: () -> String? + def end_keyword + end_keyword_loc&.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :unless_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :unless_node + 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?(UnlessNode) && + (keyword_loc.nil? == other.keyword_loc.nil?) && + (predicate === other.predicate) && + (then_keyword_loc.nil? == other.then_keyword_loc.nil?) && + (statements === other.statements) && + (else_clause === other.else_clause) && + (end_keyword_loc.nil? == other.end_keyword_loc.nil?) + end + end + + # Represents the use of the `until` keyword, either in the block form or the modifier form. + # + # bar until foo + # ^^^^^^^^^^^^^ + # + # until foo do bar end + # ^^^^^^^^^^^^^^^^^^^^ + class UntilNode < Node + # Initialize a new UntilNode node. + def initialize(source, node_id, location, flags, keyword_loc, do_keyword_loc, closing_loc, predicate, statements) + @source = source + @node_id = node_id + @location = location + @flags = flags + @keyword_loc = keyword_loc + @do_keyword_loc = do_keyword_loc + @closing_loc = closing_loc + @predicate = predicate + @statements = statements + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_until_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [predicate, statements] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield predicate + yield statements if statements + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << predicate + compact << statements if statements + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [keyword_loc, *do_keyword_loc, *closing_loc, predicate, *statements] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?keyword_loc: Location, ?do_keyword_loc: Location?, ?closing_loc: Location?, ?predicate: Prism::node, ?statements: StatementsNode?) -> UntilNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, keyword_loc: self.keyword_loc, do_keyword_loc: self.do_keyword_loc, closing_loc: self.closing_loc, predicate: self.predicate, statements: self.statements) + UntilNode.new(source, node_id, location, flags, keyword_loc, do_keyword_loc, closing_loc, predicate, statements) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, keyword_loc: Location, do_keyword_loc: Location?, closing_loc: Location?, predicate: Prism::node, statements: StatementsNode? } + def deconstruct_keys(keys) + { node_id: node_id, location: location, keyword_loc: keyword_loc, do_keyword_loc: do_keyword_loc, closing_loc: closing_loc, predicate: predicate, statements: statements } + end + + # def begin_modifier?: () -> bool + def begin_modifier? + flags.anybits?(LoopFlags::BEGIN_MODIFIER) + end + + # attr_reader keyword_loc: Location + def keyword_loc + location = @keyword_loc + return location if location.is_a?(Location) + @keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_keyword_loc(repository) + repository.enter(node_id, :keyword_loc) + end + + # attr_reader do_keyword_loc: Location? + def do_keyword_loc + location = @do_keyword_loc + case location + when nil + nil + when Location + location + else + @do_keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the do_keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_do_keyword_loc(repository) + repository.enter(node_id, :do_keyword_loc) unless @do_keyword_loc.nil? + end + + # attr_reader closing_loc: Location? + def closing_loc + location = @closing_loc + case location + when nil + nil + when Location + location + else + @closing_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + def save_closing_loc(repository) + repository.enter(node_id, :closing_loc) unless @closing_loc.nil? + end + + # attr_reader predicate: Prism::node + attr_reader :predicate + + # attr_reader statements: StatementsNode? + attr_reader :statements + + # def keyword: () -> String + def keyword + keyword_loc.slice + end + + # def do_keyword: () -> String? + def do_keyword + do_keyword_loc&.slice + end + + # def closing: () -> String? + def closing + closing_loc&.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :until_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :until_node + 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?(UntilNode) && + (flags === other.flags) && + (keyword_loc.nil? == other.keyword_loc.nil?) && + (do_keyword_loc.nil? == other.do_keyword_loc.nil?) && + (closing_loc.nil? == other.closing_loc.nil?) && + (predicate === other.predicate) && + (statements === other.statements) + end + end + + # Represents the use of the `when` keyword within a case statement. + # + # case true + # when true + # ^^^^^^^^^ + # end + class WhenNode < Node + # Initialize a new WhenNode node. + def initialize(source, node_id, location, flags, keyword_loc, conditions, then_keyword_loc, statements) + @source = source + @node_id = node_id + @location = location + @flags = flags + @keyword_loc = keyword_loc + @conditions = conditions + @then_keyword_loc = then_keyword_loc + @statements = statements + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_when_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [*conditions, statements] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + conditions.each { |node| yield node } + yield statements if statements + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact.concat(conditions) + compact << statements if statements + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [keyword_loc, *conditions, *then_keyword_loc, *statements] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?keyword_loc: Location, ?conditions: Array[Prism::node], ?then_keyword_loc: Location?, ?statements: StatementsNode?) -> WhenNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, keyword_loc: self.keyword_loc, conditions: self.conditions, then_keyword_loc: self.then_keyword_loc, statements: self.statements) + WhenNode.new(source, node_id, location, flags, keyword_loc, conditions, then_keyword_loc, statements) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, keyword_loc: Location, conditions: Array[Prism::node], then_keyword_loc: Location?, statements: StatementsNode? } + def deconstruct_keys(keys) + { node_id: node_id, location: location, keyword_loc: keyword_loc, conditions: conditions, then_keyword_loc: then_keyword_loc, statements: statements } + end + + # attr_reader keyword_loc: Location + def keyword_loc + location = @keyword_loc + return location if location.is_a?(Location) + @keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_keyword_loc(repository) + repository.enter(node_id, :keyword_loc) + end + + # attr_reader conditions: Array[Prism::node] + attr_reader :conditions + + # attr_reader then_keyword_loc: Location? + def then_keyword_loc + location = @then_keyword_loc + case location + when nil + nil + when Location + location + else + @then_keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the then_keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_then_keyword_loc(repository) + repository.enter(node_id, :then_keyword_loc) unless @then_keyword_loc.nil? + end + + # attr_reader statements: StatementsNode? + attr_reader :statements + + # def keyword: () -> String + def keyword + keyword_loc.slice + end + + # def then_keyword: () -> String? + def then_keyword + then_keyword_loc&.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :when_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :when_node + 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?(WhenNode) && + (keyword_loc.nil? == other.keyword_loc.nil?) && + (conditions.length == other.conditions.length) && + conditions.zip(other.conditions).all? { |left, right| left === right } && + (then_keyword_loc.nil? == other.then_keyword_loc.nil?) && + (statements === other.statements) + end + end + + # Represents the use of the `while` keyword, either in the block form or the modifier form. + # + # bar while foo + # ^^^^^^^^^^^^^ + # + # while foo do bar end + # ^^^^^^^^^^^^^^^^^^^^ + class WhileNode < Node + # Initialize a new WhileNode node. + def initialize(source, node_id, location, flags, keyword_loc, do_keyword_loc, closing_loc, predicate, statements) + @source = source + @node_id = node_id + @location = location + @flags = flags + @keyword_loc = keyword_loc + @do_keyword_loc = do_keyword_loc + @closing_loc = closing_loc + @predicate = predicate + @statements = statements + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_while_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [predicate, statements] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield predicate + yield statements if statements + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << predicate + compact << statements if statements + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [keyword_loc, *do_keyword_loc, *closing_loc, predicate, *statements] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?keyword_loc: Location, ?do_keyword_loc: Location?, ?closing_loc: Location?, ?predicate: Prism::node, ?statements: StatementsNode?) -> WhileNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, keyword_loc: self.keyword_loc, do_keyword_loc: self.do_keyword_loc, closing_loc: self.closing_loc, predicate: self.predicate, statements: self.statements) + WhileNode.new(source, node_id, location, flags, keyword_loc, do_keyword_loc, closing_loc, predicate, statements) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, keyword_loc: Location, do_keyword_loc: Location?, closing_loc: Location?, predicate: Prism::node, statements: StatementsNode? } + def deconstruct_keys(keys) + { node_id: node_id, location: location, keyword_loc: keyword_loc, do_keyword_loc: do_keyword_loc, closing_loc: closing_loc, predicate: predicate, statements: statements } + end + + # def begin_modifier?: () -> bool + def begin_modifier? + flags.anybits?(LoopFlags::BEGIN_MODIFIER) + end + + # attr_reader keyword_loc: Location + def keyword_loc + location = @keyword_loc + return location if location.is_a?(Location) + @keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_keyword_loc(repository) + repository.enter(node_id, :keyword_loc) + end + + # attr_reader do_keyword_loc: Location? + def do_keyword_loc + location = @do_keyword_loc + case location + when nil + nil + when Location + location + else + @do_keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the do_keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_do_keyword_loc(repository) + repository.enter(node_id, :do_keyword_loc) unless @do_keyword_loc.nil? + end + + # attr_reader closing_loc: Location? + def closing_loc + location = @closing_loc + case location + when nil + nil + when Location + location + else + @closing_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + def save_closing_loc(repository) + repository.enter(node_id, :closing_loc) unless @closing_loc.nil? + end + + # attr_reader predicate: Prism::node + attr_reader :predicate + + # attr_reader statements: StatementsNode? + attr_reader :statements + + # def keyword: () -> String + def keyword + keyword_loc.slice + end + + # def do_keyword: () -> String? + def do_keyword + do_keyword_loc&.slice + end + + # def closing: () -> String? + def closing + closing_loc&.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :while_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :while_node + 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?(WhileNode) && + (flags === other.flags) && + (keyword_loc.nil? == other.keyword_loc.nil?) && + (do_keyword_loc.nil? == other.do_keyword_loc.nil?) && + (closing_loc.nil? == other.closing_loc.nil?) && + (predicate === other.predicate) && + (statements === other.statements) + end + end + + # Represents an xstring literal with no interpolation. + # + # `foo` + # ^^^^^ + class XStringNode < Node + # Initialize a new XStringNode node. + def initialize(source, node_id, location, flags, opening_loc, content_loc, closing_loc, unescaped) + @source = source + @node_id = node_id + @location = location + @flags = flags + @opening_loc = opening_loc + @content_loc = content_loc + @closing_loc = closing_loc + @unescaped = unescaped + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_x_string_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + [] + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [opening_loc, content_loc, closing_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?content_loc: Location, ?closing_loc: Location, ?unescaped: String) -> XStringNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, opening_loc: self.opening_loc, content_loc: self.content_loc, closing_loc: self.closing_loc, unescaped: self.unescaped) + XStringNode.new(source, node_id, location, flags, opening_loc, content_loc, closing_loc, unescaped) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, opening_loc: Location, content_loc: Location, closing_loc: Location, unescaped: String } + def deconstruct_keys(keys) + { node_id: node_id, location: location, opening_loc: opening_loc, content_loc: content_loc, closing_loc: closing_loc, unescaped: unescaped } + end + + # def forced_utf8_encoding?: () -> bool + def forced_utf8_encoding? + flags.anybits?(EncodingFlags::FORCED_UTF8_ENCODING) + end + + # def forced_binary_encoding?: () -> bool + def forced_binary_encoding? + flags.anybits?(EncodingFlags::FORCED_BINARY_ENCODING) + end + + # attr_reader opening_loc: Location + def opening_loc + location = @opening_loc + return location if location.is_a?(Location) + @opening_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + def save_opening_loc(repository) + repository.enter(node_id, :opening_loc) + end + + # attr_reader content_loc: Location + def content_loc + location = @content_loc + return location if location.is_a?(Location) + @content_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the content_loc location using the given saved source so that + # it can be retrieved later. + def save_content_loc(repository) + repository.enter(node_id, :content_loc) + end + + # attr_reader closing_loc: Location + def closing_loc + location = @closing_loc + return location if location.is_a?(Location) + @closing_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + def save_closing_loc(repository) + repository.enter(node_id, :closing_loc) + end + + # attr_reader unescaped: String + attr_reader :unescaped + + # def opening: () -> String + def opening + opening_loc.slice + end + + # def content: () -> String + def content + content_loc.slice + end + + # def closing: () -> String + def closing + closing_loc.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :x_string_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :x_string_node + 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?(XStringNode) && + (flags === other.flags) && + (opening_loc.nil? == other.opening_loc.nil?) && + (content_loc.nil? == other.content_loc.nil?) && + (closing_loc.nil? == other.closing_loc.nil?) && + (unescaped === other.unescaped) + end + end + + # Represents the use of the `yield` keyword. + # + # yield 1 + # ^^^^^^^ + class YieldNode < Node + # Initialize a new YieldNode node. + def initialize(source, node_id, location, flags, keyword_loc, lparen_loc, arguments, rparen_loc) + @source = source + @node_id = node_id + @location = location + @flags = flags + @keyword_loc = keyword_loc + @lparen_loc = lparen_loc + @arguments = arguments + @rparen_loc = rparen_loc + end + + # def accept: (Visitor visitor) -> void + def accept(visitor) + visitor.visit_yield_node(self) + end + + # def child_nodes: () -> Array[Node?] + def child_nodes + [arguments] + end + + # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def each_child_node + return to_enum(:each_child_node) unless block_given? + + yield arguments if arguments + end + + # def compact_child_nodes: () -> Array[Node] + def compact_child_nodes + compact = [] #: Array[Prism::node] + compact << arguments if arguments + compact + end + + # def comment_targets: () -> Array[Node | Location] + def comment_targets + [keyword_loc, *lparen_loc, *arguments, *rparen_loc] #: Array[Prism::node | Location] + end + + # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?keyword_loc: Location, ?lparen_loc: Location?, ?arguments: ArgumentsNode?, ?rparen_loc: Location?) -> YieldNode + def copy(node_id: self.node_id, location: self.location, flags: self.flags, keyword_loc: self.keyword_loc, lparen_loc: self.lparen_loc, arguments: self.arguments, rparen_loc: self.rparen_loc) + YieldNode.new(source, node_id, location, flags, keyword_loc, lparen_loc, arguments, rparen_loc) + end + + # def deconstruct: () -> Array[Node?] + alias deconstruct child_nodes + + # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, keyword_loc: Location, lparen_loc: Location?, arguments: ArgumentsNode?, rparen_loc: Location? } + def deconstruct_keys(keys) + { node_id: node_id, location: location, keyword_loc: keyword_loc, lparen_loc: lparen_loc, arguments: arguments, rparen_loc: rparen_loc } + end + + # attr_reader keyword_loc: Location + def keyword_loc + location = @keyword_loc + return location if location.is_a?(Location) + @keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + def save_keyword_loc(repository) + repository.enter(node_id, :keyword_loc) + end + + # attr_reader lparen_loc: Location? + def lparen_loc + location = @lparen_loc + case location + when nil + nil + when Location + location + else + @lparen_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the lparen_loc location using the given saved source so that + # it can be retrieved later. + def save_lparen_loc(repository) + repository.enter(node_id, :lparen_loc) unless @lparen_loc.nil? + end + + # attr_reader arguments: ArgumentsNode? + attr_reader :arguments + + # attr_reader rparen_loc: Location? + def rparen_loc + location = @rparen_loc + case location + when nil + nil + when Location + location + else + @rparen_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF) + end + end + + # Save the rparen_loc location using the given saved source so that + # it can be retrieved later. + def save_rparen_loc(repository) + repository.enter(node_id, :rparen_loc) unless @rparen_loc.nil? + end + + # def keyword: () -> String + def keyword + keyword_loc.slice + end + + # def lparen: () -> String? + def lparen + lparen_loc&.slice + end + + # def rparen: () -> String? + def rparen + rparen_loc&.slice + end + + # def inspect -> String + def inspect + InspectVisitor.compose(self) + end + + # Return a symbol representation of this node type. See `Node#type`. + def type + :yield_node + end + + # Return a symbol representation of this node type. See `Node::type`. + def self.type + :yield_node + 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?(YieldNode) && + (keyword_loc.nil? == other.keyword_loc.nil?) && + (lparen_loc.nil? == other.lparen_loc.nil?) && + (arguments === other.arguments) && + (rparen_loc.nil? == other.rparen_loc.nil?) + end + end + + # Flags for arguments nodes. + module ArgumentsNodeFlags + # if the arguments contain forwarding + CONTAINS_FORWARDING = 1 << 2 + + # if the arguments contain keywords + CONTAINS_KEYWORDS = 1 << 3 + + # if the arguments contain a keyword splat + CONTAINS_KEYWORD_SPLAT = 1 << 4 + + # if the arguments contain a splat + CONTAINS_SPLAT = 1 << 5 + + # if the arguments contain multiple splats + CONTAINS_MULTIPLE_SPLATS = 1 << 6 + end + + # Flags for array nodes. + module ArrayNodeFlags + # if array contains splat nodes + CONTAINS_SPLAT = 1 << 2 + end + + # Flags for call nodes. + module CallNodeFlags + # &. operator + SAFE_NAVIGATION = 1 << 2 + + # a call that could have been a local variable + VARIABLE_CALL = 1 << 3 + + # a call that is an attribute write, so the value being written should be returned + ATTRIBUTE_WRITE = 1 << 4 + + # a call that ignores method visibility + IGNORE_VISIBILITY = 1 << 5 + end + + # Flags for nodes that have unescaped content. + module EncodingFlags + # internal bytes forced the encoding to UTF-8 + FORCED_UTF8_ENCODING = 1 << 2 + + # internal bytes forced the encoding to binary + FORCED_BINARY_ENCODING = 1 << 3 + end + + # Flags for integer nodes that correspond to the base of the integer. + module IntegerBaseFlags + # 0b prefix + BINARY = 1 << 2 + + # 0d or no prefix + DECIMAL = 1 << 3 + + # 0o or 0 prefix + OCTAL = 1 << 4 + + # 0x prefix + HEXADECIMAL = 1 << 5 + end + + # Flags for interpolated string nodes that indicated mutability if they are also marked as literals. + module InterpolatedStringNodeFlags + # frozen by virtue of a `frozen_string_literal: true` comment or `--enable-frozen-string-literal`; only for adjacent string literals like `'a' 'b'` + FROZEN = 1 << 2 + + # mutable by virtue of a `frozen_string_literal: false` comment or `--disable-frozen-string-literal`; only for adjacent string literals like `'a' 'b'` + MUTABLE = 1 << 3 + end + + # Flags for keyword hash nodes. + module KeywordHashNodeFlags + # a keyword hash which only has `AssocNode` elements all with symbol keys, which means the elements can be treated as keyword arguments + SYMBOL_KEYS = 1 << 2 + end + + # Flags for while and until loop nodes. + module LoopFlags + # a loop after a begin statement, so the body is executed first before the condition + BEGIN_MODIFIER = 1 << 2 + end + + # Flags for parameter nodes. + module ParameterFlags + # a parameter name that has been repeated in the method signature + REPEATED_PARAMETER = 1 << 2 + end + + # Flags for parentheses nodes. + module ParenthesesNodeFlags + # parentheses that contain multiple potentially void statements + MULTIPLE_STATEMENTS = 1 << 2 + end + + # Flags for range and flip-flop nodes. + module RangeFlags + # ... operator + EXCLUDE_END = 1 << 2 + end + + # Flags for regular expression and match last line nodes. + module RegularExpressionFlags + # i - ignores the case of characters when matching + IGNORE_CASE = 1 << 2 + + # x - ignores whitespace and allows comments in regular expressions + EXTENDED = 1 << 3 + + # m - allows $ to match the end of lines within strings + MULTI_LINE = 1 << 4 + + # o - only interpolates values into the regular expression once + ONCE = 1 << 5 + + # e - forces the EUC-JP encoding + EUC_JP = 1 << 6 + + # n - forces the ASCII-8BIT encoding + ASCII_8BIT = 1 << 7 + + # s - forces the Windows-31J encoding + WINDOWS_31J = 1 << 8 + + # u - forces the UTF-8 encoding + UTF_8 = 1 << 9 + + # internal bytes forced the encoding to UTF-8 + FORCED_UTF8_ENCODING = 1 << 10 + + # internal bytes forced the encoding to binary + FORCED_BINARY_ENCODING = 1 << 11 + + # internal bytes forced the encoding to US-ASCII + FORCED_US_ASCII_ENCODING = 1 << 12 + end + + # Flags for shareable constant nodes. + module ShareableConstantNodeFlags + # constant writes that should be modified with shareable constant value literal + LITERAL = 1 << 2 + + # constant writes that should be modified with shareable constant value experimental everything + EXPERIMENTAL_EVERYTHING = 1 << 3 + + # constant writes that should be modified with shareable constant value experimental copy + EXPERIMENTAL_COPY = 1 << 4 + end + + # Flags for string nodes. + module StringFlags + # internal bytes forced the encoding to UTF-8 + FORCED_UTF8_ENCODING = 1 << 2 + + # internal bytes forced the encoding to binary + FORCED_BINARY_ENCODING = 1 << 3 + + # frozen by virtue of a `frozen_string_literal: true` comment or `--enable-frozen-string-literal` + FROZEN = 1 << 4 + + # mutable by virtue of a `frozen_string_literal: false` comment or `--disable-frozen-string-literal` + MUTABLE = 1 << 5 + end + + # Flags for symbol nodes. + module SymbolFlags + # internal bytes forced the encoding to UTF-8 + FORCED_UTF8_ENCODING = 1 << 2 + + # internal bytes forced the encoding to binary + FORCED_BINARY_ENCODING = 1 << 3 + + # internal bytes forced the encoding to US-ASCII + FORCED_US_ASCII_ENCODING = 1 << 4 + end + + # The flags that are common to all nodes. + module NodeFlags + # A flag to indicate that the node is a candidate to emit a :line event + # through tracepoint when compiled. + NEWLINE = 1 + + # A flag to indicate that the value that the node represents is a value that + # can be determined at parse-time. + STATIC_LITERAL = 2 + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/node_ext.rb b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/node_ext.rb new file mode 100644 index 0000000..469e54c --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/node_ext.rb @@ -0,0 +1,511 @@ +# frozen_string_literal: true +# :markup: markdown + +#-- +# Here we are reopening the prism module to provide methods on nodes that aren't +# templated and are meant as convenience methods. +#++ +module Prism + class Node + def deprecated(*replacements) # :nodoc: + location = caller_locations(1, 1) + location = location[0].label if location + suggest = replacements.map { |replacement| "#{self.class}##{replacement}" } + + warn(<<~MSG, uplevel: 1, category: :deprecated) + [deprecation]: #{self.class}##{location} is deprecated and will be \ + removed in the next major version. Use #{suggest.join("/")} instead. + #{(caller(1, 3) || []).join("\n")} + MSG + end + end + + module RegularExpressionOptions # :nodoc: + # Returns a numeric value that represents the flags that were used to create + # the regular expression. + def options + o = 0 + o |= Regexp::IGNORECASE if flags.anybits?(RegularExpressionFlags::IGNORE_CASE) + o |= Regexp::EXTENDED if flags.anybits?(RegularExpressionFlags::EXTENDED) + o |= Regexp::MULTILINE if flags.anybits?(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, + -1, + location, + frozen? ? InterpolatedStringNodeFlags::FROZEN : 0, + opening_loc, + [copy(location: content_loc, opening_loc: nil, closing_loc: nil)], + closing_loc + ) + 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, + -1, + location, + flags, + opening_loc, + [StringNode.new(source, node_id, content_loc, 0, nil, content_loc, nil, unescaped)], + closing_loc + ) + 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(numerator, denominator) + end + + # Returns the value of the node as an IntegerNode or a FloatNode. This + # method is deprecated in favor of #value or #numerator/#denominator. + def numeric + deprecated("value", "numerator", "denominator") + + if denominator == 1 + IntegerNode.new(source, -1, location.chop, flags, numerator) + else + FloatNode.new(source, -1, location.chop, 0, numerator.to_f / denominator) + end + 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) + name = current.name + if name.nil? + raise MissingNodesInConstantPathError, "Constant path contains missing nodes. Cannot compute full name" + end + + parts.unshift(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 + + # Previously, we had a child node on this class that contained either a + # constant read or a missing node. To not cause a breaking change, we + # continue to supply that API. + def child + deprecated("name", "name_loc") + + if name + ConstantReadNode.new(source, -1, name_loc, 0, name) + else + MissingNode.new(source, -1, location, 0) + end + 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 name.nil? + raise ConstantPathNode::MissingNodesInConstantPathError, "Constant target path contains missing nodes. Cannot compute full name" + end + + parts.push(name) + end + + # Returns the full name of this constant path. For example: "Foo::Bar" + def full_name + full_name_parts.join("::") + end + + # Previously, we had a child node on this class that contained either a + # constant read or a missing node. To not cause a breaking change, we + # continue to supply that API. + def child + deprecated("name", "name_loc") + + if name + ConstantReadNode.new(source, -1, name_loc, 0, name) + else + MissingNode.new(source, -1, location, 0) + end + 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| + case param + when MultiTargetNode + names << [:req] + when NoKeywordsParameterNode, KeywordRestParameterNode, ForwardingParameterNode + # 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 + + class CallNode < Node + # When a call node has the attribute_write flag set, it means that the call + # is using the attribute write syntax. This is either a method call to []= + # or a method call to a method that ends with =. Either way, the = sign is + # present in the source. + # + # Prism returns the message_loc _without_ the = sign attached, because there + # can be any amount of space between the message and the = sign. However, + # sometimes you want the location of the full message including the inner + # space and the = sign. This method provides that. + def full_message_loc + attribute_write? ? message_loc&.adjoin("=") : message_loc + end + end + + class CallOperatorWriteNode < Node + # Returns the binary operator used to modify the receiver. This method is + # deprecated in favor of #binary_operator. + def operator + deprecated("binary_operator") + binary_operator + end + + # Returns the location of the binary operator used to modify the receiver. + # This method is deprecated in favor of #binary_operator_loc. + def operator_loc + deprecated("binary_operator_loc") + binary_operator_loc + end + end + + class ClassVariableOperatorWriteNode < Node + # Returns the binary operator used to modify the receiver. This method is + # deprecated in favor of #binary_operator. + def operator + deprecated("binary_operator") + binary_operator + end + + # Returns the location of the binary operator used to modify the receiver. + # This method is deprecated in favor of #binary_operator_loc. + def operator_loc + deprecated("binary_operator_loc") + binary_operator_loc + end + end + + class ConstantOperatorWriteNode < Node + # Returns the binary operator used to modify the receiver. This method is + # deprecated in favor of #binary_operator. + def operator + deprecated("binary_operator") + binary_operator + end + + # Returns the location of the binary operator used to modify the receiver. + # This method is deprecated in favor of #binary_operator_loc. + def operator_loc + deprecated("binary_operator_loc") + binary_operator_loc + end + end + + class ConstantPathOperatorWriteNode < Node + # Returns the binary operator used to modify the receiver. This method is + # deprecated in favor of #binary_operator. + def operator + deprecated("binary_operator") + binary_operator + end + + # Returns the location of the binary operator used to modify the receiver. + # This method is deprecated in favor of #binary_operator_loc. + def operator_loc + deprecated("binary_operator_loc") + binary_operator_loc + end + end + + class GlobalVariableOperatorWriteNode < Node + # Returns the binary operator used to modify the receiver. This method is + # deprecated in favor of #binary_operator. + def operator + deprecated("binary_operator") + binary_operator + end + + # Returns the location of the binary operator used to modify the receiver. + # This method is deprecated in favor of #binary_operator_loc. + def operator_loc + deprecated("binary_operator_loc") + binary_operator_loc + end + end + + class IndexOperatorWriteNode < Node + # Returns the binary operator used to modify the receiver. This method is + # deprecated in favor of #binary_operator. + def operator + deprecated("binary_operator") + binary_operator + end + + # Returns the location of the binary operator used to modify the receiver. + # This method is deprecated in favor of #binary_operator_loc. + def operator_loc + deprecated("binary_operator_loc") + binary_operator_loc + end + end + + class InstanceVariableOperatorWriteNode < Node + # Returns the binary operator used to modify the receiver. This method is + # deprecated in favor of #binary_operator. + def operator + deprecated("binary_operator") + binary_operator + end + + # Returns the location of the binary operator used to modify the receiver. + # This method is deprecated in favor of #binary_operator_loc. + def operator_loc + deprecated("binary_operator_loc") + binary_operator_loc + end + end + + class LocalVariableOperatorWriteNode < Node + # Returns the binary operator used to modify the receiver. This method is + # deprecated in favor of #binary_operator. + def operator + deprecated("binary_operator") + binary_operator + end + + # Returns the location of the binary operator used to modify the receiver. + # This method is deprecated in favor of #binary_operator_loc. + def operator_loc + deprecated("binary_operator_loc") + binary_operator_loc + end + end + + class CaseMatchNode < Node + # Returns the else clause of the case match node. This method is deprecated + # in favor of #else_clause. + def consequent + deprecated("else_clause") + else_clause + end + end + + class CaseNode < Node + # Returns the else clause of the case node. This method is deprecated in + # favor of #else_clause. + def consequent + deprecated("else_clause") + else_clause + end + end + + class IfNode < Node + # Returns the subsequent if/elsif/else clause of the if node. This method is + # deprecated in favor of #subsequent. + def consequent + deprecated("subsequent") + subsequent + end + end + + class RescueNode < Node + # Returns the subsequent rescue clause of the rescue node. This method is + # deprecated in favor of #subsequent. + def consequent + deprecated("subsequent") + subsequent + end + end + + class UnlessNode < Node + # Returns the else clause of the unless node. This method is deprecated in + # favor of #else_clause. + def consequent + deprecated("else_clause") + else_clause + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/pack.rb b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/pack.rb new file mode 100644 index 0000000..166c04c --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/pack.rb @@ -0,0 +1,230 @@ +# frozen_string_literal: true +# :markup: markdown +# 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/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/parse_result.rb b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/parse_result.rb new file mode 100644 index 0000000..be1c13f --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/parse_result.rb @@ -0,0 +1,907 @@ +# frozen_string_literal: true +# :markup: markdown + +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 + # Create a new source object with the given source code. This method should + # be used instead of `new` and it will return either a `Source` or a + # specialized and more performant `ASCIISource` if no multibyte characters + # are present in the source code. + def self.for(source, start_line = 1, offsets = []) + if source.ascii_only? + ASCIISource.new(source, start_line, offsets) + elsif source.encoding == Encoding::BINARY + source.force_encoding(Encoding::UTF_8) + + if source.valid_encoding? + new(source, start_line, offsets) + else + # This is an extremely niche use case where the file is marked as + # binary, contains multi-byte characters, and those characters are not + # valid UTF-8. In this case we'll mark it as binary and fall back to + # treating everything as a single-byte character. This _may_ cause + # problems when asking for code units, but it appears to be the + # cleanest solution at the moment. + source.force_encoding(Encoding::BINARY) + ASCIISource.new(source, start_line, offsets) + end + else + new(source, start_line, offsets) + end + end + + # 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 + + # Replace the value of start_line with the given value. + def replace_start_line(start_line) + @start_line = start_line + end + + # Replace the value of offsets with the given value. + def replace_offsets(offsets) + @offsets.replace(offsets) + 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 + + # Returns the lines of the source code as an array of strings. + def lines + source.lines + 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 + + # Converts the line number and column in bytes to a byte offset. + def byte_offset(line, column) + normal = line - @start_line + raise IndexError if normal < 0 + offsets.fetch(normal) + column + rescue IndexError + raise ArgumentError, "line #{line} is out of range" + 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. + # + # We purposefully replace invalid and undefined characters with replacement + # characters in this conversion. This happens for two reasons. First, it's + # possible that the given byte offset will not occur on a character + # boundary. Second, it's possible that the source code will contain a + # character that has no equivalent in the given encoding. + def code_units_offset(byte_offset, encoding) + byteslice = (source.byteslice(0, byte_offset) or raise).encode(encoding, invalid: :replace, undef: :replace) + + if encoding == Encoding::UTF_16LE || encoding == Encoding::UTF_16BE + byteslice.bytesize / 2 + else + byteslice.length + end + end + + # Generate a cache that targets a specific encoding for calculating code + # unit offsets. + def code_units_cache(encoding) + CodeUnitsCache.new(source, encoding) + 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 + + # Freeze this object and the objects it contains. + def deep_freeze + source.freeze + offsets.freeze + freeze + end + + private + + # Binary search through the offsets to find the line number for the given + # byte offset. + def find_line(byte_offset) + index = offsets.bsearch_index { |offset| offset > byte_offset } || offsets.length + index - 1 + end + end + + # A cache that can be used to quickly compute code unit offsets from byte + # offsets. It purposefully provides only a single #[] method to access the + # cache in order to minimize surface area. + # + # Note that there are some known issues here that may or may not be addressed + # in the future: + # + # * The first is that there are issues when the cache computes values that are + # not on character boundaries. This can result in subsequent computations + # being off by one or more code units. + # * The second is that this cache is currently unbounded. In theory we could + # introduce some kind of LRU cache to limit the number of entries, but this + # has not yet been implemented. + # + class CodeUnitsCache + class UTF16Counter # :nodoc: + def initialize(source, encoding) + @source = source + @encoding = encoding + end + + def count(byte_offset, byte_length) + @source.byteslice(byte_offset, byte_length).encode(@encoding, invalid: :replace, undef: :replace).bytesize / 2 + end + end + + class LengthCounter # :nodoc: + def initialize(source, encoding) + @source = source + @encoding = encoding + end + + def count(byte_offset, byte_length) + @source.byteslice(byte_offset, byte_length).encode(@encoding, invalid: :replace, undef: :replace).length + end + end + + private_constant :UTF16Counter, :LengthCounter + + # Initialize a new cache with the given source and encoding. + def initialize(source, encoding) + @source = source + @counter = + if encoding == Encoding::UTF_16LE || encoding == Encoding::UTF_16BE + UTF16Counter.new(source, encoding) + else + LengthCounter.new(source, encoding) + end + + @cache = {} #: Hash[Integer, Integer] + @offsets = [] #: Array[Integer] + end + + # Retrieve the code units offset from the given byte offset. + def [](byte_offset) + @cache[byte_offset] ||= + if (index = @offsets.bsearch_index { |offset| offset > byte_offset }).nil? + @offsets << byte_offset + @counter.count(0, byte_offset) + elsif index == 0 + @offsets.unshift(byte_offset) + @counter.count(0, byte_offset) + else + @offsets.insert(index, byte_offset) + offset = @offsets[index - 1] + @cache[offset] + @counter.count(offset, byte_offset - offset) + end + end + end + + # Specialized version of Prism::Source for source code that includes ASCII + # characters only. This class is used to apply performance optimizations that + # cannot be applied to sources that include multibyte characters. + # + # In the extremely rare case that a source includes multi-byte characters but + # is marked as binary because of a magic encoding comment and it cannot be + # eagerly converted to UTF-8, this class will be used as well. This is because + # at that point we will treat everything as single-byte characters. + class ASCIISource < Source + # Return the character offset for the given byte offset. + def character_offset(byte_offset) + byte_offset + end + + # Return the column number in characters for the given byte offset. + def character_column(byte_offset) + byte_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) + byte_offset + end + + # Returns a cache that is the identity function in order to maintain the + # same interface. We can do this because code units are always equivalent to + # byte offsets for ASCII-only sources. + def code_units_cache(encoding) + ->(byte_offset) { byte_offset } + end + + # Specialized version of `code_units_column` that does not depend on + # `code_units_offset`, which is a more expensive operation. This is + # essentially the same as `Prism::Source#column`. + def code_units_column(byte_offset, encoding) + byte_offset - line_start(byte_offset) + 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 + "#" + end + + # Returns all of the lines of the source code associated with this location. + def source_lines + source.lines + 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 start offset from the start of the file in code units using the given + # cache to fetch or calculate the value. + def cached_start_code_units_offset(cache) + cache[start_offset] + 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 end offset from the start of the file in code units using the given + # cache to fetch or calculate the value. + def cached_end_code_units_offset(cache) + cache[end_offset] + 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 start column in code units using the given cache to fetch or calculate + # the value. + def cached_start_code_units_column(cache) + cache[start_offset] - cache[source.line_start(start_offset)] + 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 + + # The end column in code units using the given cache to fetch or calculate + # the value. + def cached_end_code_units_column(cache) + cache[end_offset] - cache[source.line_start(end_offset)] + 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 + + # Join this location with the first occurrence of the string in the source + # that occurs after this location on the same line, and return the new + # location. This will raise an error if the string does not exist. + def adjoin(string) + line_suffix = source.slice(end_offset, source.line_end(end_offset) - end_offset) + + line_suffix_index = line_suffix.byteindex(string) + raise "Could not find #{string}" if line_suffix_index.nil? + + Location.new(source, start_offset, length + line_suffix_index + string.bytesize) + 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 + "#" + 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 + "#" + 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 + "#" + 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 + "#" + 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 + "#" + 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 + + # Create a code units cache for the given encoding. + def code_units_cache(encoding) + source.code_units_cache(encoding) + end + end + + # This is a result specific to the `parse` and `parse_file` methods. + class ParseResult < Result + autoload :Comments, "prism/parse_result/comments" + autoload :Errors, "prism/parse_result/errors" + autoload :Newlines, "prism/parse_result/newlines" + + private_constant :Comments + private_constant :Errors + private_constant :Newlines + + # 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 + + # Attach the list of comments to their respective locations in the tree. + def attach_comments! + Comments.new(self).attach! # steep:ignore + end + + # Walk the tree and mark nodes that are on a new line, loosely emulating + # the behavior of CRuby's `:line` tracepoint event. + def mark_newlines! + value.accept(Newlines.new(source.offsets.size)) # steep:ignore + end + + # Returns a string representation of the syntax tree with the errors + # displayed inline. + def errors_format + Errors.new(self).format + 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 + + # Returns a string representation of this token. + def inspect + location + super + end + + # Freeze this object and the objects it contains. + def deep_freeze + value.freeze + location.freeze + freeze + end + end + + # This object is passed to the various Prism.* methods that accept the + # `scopes` option as an element of the list. It defines both the local + # variables visible at that scope as well as the forwarding parameters + # available at that scope. + class Scope + # The list of local variables that are defined in this scope. This should be + # defined as an array of symbols. + attr_reader :locals + + # The list of local variables that are forwarded to the next scope. This + # should by defined as an array of symbols containing the specific values of + # :*, :**, :&, or :"...". + attr_reader :forwarding + + # Create a new scope object with the given locals and forwarding. + def initialize(locals, forwarding) + @locals = locals + @forwarding = forwarding + end + end + + # Create a new scope with the given locals and forwarding options that is + # suitable for passing into one of the Prism.* methods that accepts the + # `scopes` option. + def self.scope(locals: [], forwarding: []) + Scope.new(locals, forwarding) + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/parse_result/comments.rb b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/parse_result/comments.rb new file mode 100644 index 0000000..3e93316 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/parse_result/comments.rb @@ -0,0 +1,188 @@ +# frozen_string_literal: true +# :markup: markdown + +module Prism + class ParseResult < Result + # 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 + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/parse_result/errors.rb b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/parse_result/errors.rb new file mode 100644 index 0000000..26c376b --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/parse_result/errors.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true +# :markup: markdown + +require "stringio" + +module Prism + class ParseResult < Result + # An object to represent the set of errors on a parse result. This object + # can be used to format the errors in a human-readable way. + class Errors + # The parse result that contains the errors. + attr_reader :parse_result + + # Initialize a new set of errors from the given parse result. + def initialize(parse_result) + @parse_result = parse_result + end + + # Formats the errors in a human-readable way and return them as a string. + def format + error_lines = {} #: Hash[Integer, Array[ParseError]] + parse_result.errors.each do |error| + location = error.location + (location.start_line..location.end_line).each do |line| + error_lines[line] ||= [] + error_lines[line] << error + end + end + + source_lines = parse_result.source.source.lines + source_lines << "" if error_lines.key?(source_lines.size + 1) + + io = StringIO.new + source_lines.each.with_index(1) do |line, line_number| + io.puts(line) + + (error_lines.delete(line_number) || []).each do |error| + location = error.location + + case line_number + when location.start_line + io.print(" " * location.start_column + "^") + + if location.start_line == location.end_line + if location.start_column != location.end_column + io.print("~" * (location.end_column - location.start_column - 1)) + end + + io.puts(" " + error.message) + else + io.puts("~" * (line.bytesize - location.start_column)) + end + when location.end_line + io.puts("~" * location.end_column + " " + error.message) + else + io.puts("~" * line.bytesize) + end + end + end + + io.puts + io.string + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/parse_result/newlines.rb b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/parse_result/newlines.rb new file mode 100644 index 0000000..e7fd62c --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/parse_result/newlines.rb @@ -0,0 +1,155 @@ +# frozen_string_literal: true +# :markup: markdown + +module Prism + class ParseResult < Result + # 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. + # + # This file is autoloaded only when `mark_newlines!` is called, so the + # re-opening of the various nodes in this file will only be performed in + # that case. We do that to avoid storing the extra `@newline` instance + # variable on every node if we don't need it. + class Newlines < Visitor + # Create a new Newlines visitor with the given newline offsets. + def initialize(lines) + # @type var lines: Integer + @lines = Array.new(1 + lines, false) + end + + # Permit block/lambda nodes to mark newlines within themselves. + def visit_block_node(node) + old_lines = @lines + @lines = Array.new(old_lines.size, false) + + begin + super(node) + ensure + @lines = old_lines + end + end + + alias_method :visit_lambda_node, :visit_block_node + + # Mark if/unless nodes as newlines. + def visit_if_node(node) + node.newline_flag!(@lines) + 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.newline_flag!(@lines) + end + super(node) + end + end + end + + class Node + def newline_flag? # :nodoc: + !!defined?(@newline_flag) + end + + def newline_flag!(lines) # :nodoc: + line = location.start_line + unless lines[line] + lines[line] = true + @newline_flag = true + end + end + end + + class BeginNode < Node + def newline_flag!(lines) # :nodoc: + # Never mark BeginNode with a newline flag, mark children instead. + end + end + + class ParenthesesNode < Node + def newline_flag!(lines) # :nodoc: + # Never mark ParenthesesNode with a newline flag, mark children instead. + end + end + + class IfNode < Node + def newline_flag!(lines) # :nodoc: + predicate.newline_flag!(lines) + end + end + + class UnlessNode < Node + def newline_flag!(lines) # :nodoc: + predicate.newline_flag!(lines) + end + end + + class UntilNode < Node + def newline_flag!(lines) # :nodoc: + predicate.newline_flag!(lines) + end + end + + class WhileNode < Node + def newline_flag!(lines) # :nodoc: + predicate.newline_flag!(lines) + end + end + + class RescueModifierNode < Node + def newline_flag!(lines) # :nodoc: + expression.newline_flag!(lines) + end + end + + class InterpolatedMatchLastLineNode < Node + def newline_flag!(lines) # :nodoc: + first = parts.first + first.newline_flag!(lines) if first + end + end + + class InterpolatedRegularExpressionNode < Node + def newline_flag!(lines) # :nodoc: + first = parts.first + first.newline_flag!(lines) if first + end + end + + class InterpolatedStringNode < Node + def newline_flag!(lines) # :nodoc: + first = parts.first + first.newline_flag!(lines) if first + end + end + + class InterpolatedSymbolNode < Node + def newline_flag!(lines) # :nodoc: + first = parts.first + first.newline_flag!(lines) if first + end + end + + class InterpolatedXStringNode < Node + def newline_flag!(lines) # :nodoc: + first = parts.first + first.newline_flag!(lines) if first + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/pattern.rb b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/pattern.rb new file mode 100644 index 0000000..6ad2d9e --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/pattern.rb @@ -0,0 +1,269 @@ +# frozen_string_literal: true +# :markup: markdown + +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 ConstantPathNode[...] 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" + name = node.name + raise CompilationError, node.inspect if name.nil? + + compile_constant_name(node, name) + else + compile_error(node) + end + end + + # in ConstantReadNode + # in String + def compile_constant_read_node(node) + compile_constant_name(node, node.name) + end + + # Compile a name associated with a constant. + def compile_constant_name(node, name) + if Prism.const_defined?(name, false) + clazz = Prism.const_get(name) + + ->(other) { clazz === other } + elsif Object.const_defined?(name, false) + clazz = Object.const_get(name) + + ->(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/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/polyfill/append_as_bytes.rb b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/polyfill/append_as_bytes.rb new file mode 100644 index 0000000..24218bd --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/polyfill/append_as_bytes.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +# Polyfill for String#append_as_bytes, which didn't exist until Ruby 3.4. +if !("".respond_to?(:append_as_bytes)) + String.include( + Module.new { + def append_as_bytes(*args) + args.each do |arg| + arg = Integer === arg ? [arg].pack("C") : arg.b + self.<<(arg) # steep:ignore + end + end + } + ) +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/polyfill/byteindex.rb b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/polyfill/byteindex.rb new file mode 100644 index 0000000..98c6089 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/polyfill/byteindex.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +# Polyfill for String#byteindex, which didn't exist until Ruby 3.2. +if !("".respond_to?(:byteindex)) + String.include( + Module.new { + def byteindex(needle, offset = 0) + charindex = index(needle, offset) + slice(0...charindex).bytesize if charindex + end + } + ) +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/polyfill/scan_byte.rb b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/polyfill/scan_byte.rb new file mode 100644 index 0000000..9276e50 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/polyfill/scan_byte.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +require "strscan" + +# Polyfill for StringScanner#scan_byte, which didn't exist until Ruby 3.4. +if !(StringScanner.method_defined?(:scan_byte)) + StringScanner.include( + Module.new { + def scan_byte # :nodoc: + get_byte&.b&.ord + end + } + ) +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/polyfill/unpack1.rb b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/polyfill/unpack1.rb new file mode 100644 index 0000000..3fa9b5a --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/polyfill/unpack1.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/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/polyfill/warn.rb b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/polyfill/warn.rb new file mode 100644 index 0000000..76a4264 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/polyfill/warn.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +# Polyfill for Kernel#warn with the category parameter. Not all Ruby engines +# have Method#parameters implemented, so we check the arity instead if +# necessary. +if (method = Kernel.instance_method(:warn)).respond_to?(:parameters) ? method.parameters.none? { |_, name| name == :category } : (method.arity == -1) + Kernel.prepend( + Module.new { + def warn(*msgs, uplevel: nil, category: nil) # :nodoc: + case uplevel + when nil + super(*msgs) + when Integer + super(*msgs, uplevel: uplevel + 1) + else + super(*msgs, uplevel: uplevel.to_int + 1) + end + end + } + ) + + Object.prepend( + Module.new { + def warn(*msgs, uplevel: nil, category: nil) # :nodoc: + case uplevel + when nil + super(*msgs) + when Integer + super(*msgs, uplevel: uplevel + 1) + else + super(*msgs, uplevel: uplevel.to_int + 1) + end + end + } + ) +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/prism.so b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/prism.so new file mode 100755 index 0000000..45f2b38 Binary files /dev/null and b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/prism.so differ diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/reflection.rb b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/reflection.rb new file mode 100644 index 0000000..3ac0722 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/reflection.rb @@ -0,0 +1,416 @@ +# frozen_string_literal: true +# :markup: markdown + +=begin +-- +This file is generated by the templates/template.rb script and should not be +modified manually. See templates/lib/prism/reflection.rb.erb +if you are looking to modify the template +++ +=end + +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 + when :alias_global_variable_node + [NodeField.new(:new_name), NodeField.new(:old_name), LocationField.new(:keyword_loc)] + when :alias_method_node + [NodeField.new(:new_name), NodeField.new(:old_name), LocationField.new(:keyword_loc)] + when :alternation_pattern_node + [NodeField.new(:left), NodeField.new(:right), LocationField.new(:operator_loc)] + when :and_node + [NodeField.new(:left), NodeField.new(:right), LocationField.new(:operator_loc)] + when :arguments_node + [FlagsField.new(:flags, [:contains_forwarding?, :contains_keywords?, :contains_keyword_splat?, :contains_splat?, :contains_multiple_splats?]), NodeListField.new(:arguments)] + when :array_node + [FlagsField.new(:flags, [:contains_splat?]), NodeListField.new(:elements), OptionalLocationField.new(:opening_loc), OptionalLocationField.new(:closing_loc)] + when :array_pattern_node + [OptionalNodeField.new(:constant), NodeListField.new(:requireds), OptionalNodeField.new(:rest), NodeListField.new(:posts), OptionalLocationField.new(:opening_loc), OptionalLocationField.new(:closing_loc)] + when :assoc_node + [NodeField.new(:key), NodeField.new(:value), OptionalLocationField.new(:operator_loc)] + when :assoc_splat_node + [OptionalNodeField.new(:value), LocationField.new(:operator_loc)] + when :back_reference_read_node + [ConstantField.new(:name)] + when :begin_node + [OptionalLocationField.new(:begin_keyword_loc), OptionalNodeField.new(:statements), OptionalNodeField.new(:rescue_clause), OptionalNodeField.new(:else_clause), OptionalNodeField.new(:ensure_clause), OptionalLocationField.new(:end_keyword_loc)] + when :block_argument_node + [OptionalNodeField.new(:expression), LocationField.new(:operator_loc)] + when :block_local_variable_node + [FlagsField.new(:flags, [:repeated_parameter?]), ConstantField.new(:name)] + when :block_node + [ConstantListField.new(:locals), OptionalNodeField.new(:parameters), OptionalNodeField.new(:body), LocationField.new(:opening_loc), LocationField.new(:closing_loc)] + when :block_parameter_node + [FlagsField.new(:flags, [:repeated_parameter?]), OptionalConstantField.new(:name), OptionalLocationField.new(:name_loc), LocationField.new(:operator_loc)] + when :block_parameters_node + [OptionalNodeField.new(:parameters), NodeListField.new(:locals), OptionalLocationField.new(:opening_loc), OptionalLocationField.new(:closing_loc)] + when :break_node + [OptionalNodeField.new(:arguments), LocationField.new(:keyword_loc)] + when :call_and_write_node + [FlagsField.new(:flags, [:safe_navigation?, :variable_call?, :attribute_write?, :ignore_visibility?]), OptionalNodeField.new(:receiver), OptionalLocationField.new(:call_operator_loc), OptionalLocationField.new(:message_loc), ConstantField.new(:read_name), ConstantField.new(:write_name), LocationField.new(:operator_loc), NodeField.new(:value)] + when :call_node + [FlagsField.new(:flags, [:safe_navigation?, :variable_call?, :attribute_write?, :ignore_visibility?]), OptionalNodeField.new(:receiver), OptionalLocationField.new(:call_operator_loc), ConstantField.new(:name), OptionalLocationField.new(:message_loc), OptionalLocationField.new(:opening_loc), OptionalNodeField.new(:arguments), OptionalLocationField.new(:closing_loc), OptionalLocationField.new(:equal_loc), OptionalNodeField.new(:block)] + when :call_operator_write_node + [FlagsField.new(:flags, [:safe_navigation?, :variable_call?, :attribute_write?, :ignore_visibility?]), OptionalNodeField.new(:receiver), OptionalLocationField.new(:call_operator_loc), OptionalLocationField.new(:message_loc), ConstantField.new(:read_name), ConstantField.new(:write_name), ConstantField.new(:binary_operator), LocationField.new(:binary_operator_loc), NodeField.new(:value)] + when :call_or_write_node + [FlagsField.new(:flags, [:safe_navigation?, :variable_call?, :attribute_write?, :ignore_visibility?]), OptionalNodeField.new(:receiver), OptionalLocationField.new(:call_operator_loc), OptionalLocationField.new(:message_loc), ConstantField.new(:read_name), ConstantField.new(:write_name), LocationField.new(:operator_loc), NodeField.new(:value)] + when :call_target_node + [FlagsField.new(:flags, [:safe_navigation?, :variable_call?, :attribute_write?, :ignore_visibility?]), NodeField.new(:receiver), LocationField.new(:call_operator_loc), ConstantField.new(:name), LocationField.new(:message_loc)] + when :capture_pattern_node + [NodeField.new(:value), NodeField.new(:target), LocationField.new(:operator_loc)] + when :case_match_node + [OptionalNodeField.new(:predicate), NodeListField.new(:conditions), OptionalNodeField.new(:else_clause), LocationField.new(:case_keyword_loc), LocationField.new(:end_keyword_loc)] + when :case_node + [OptionalNodeField.new(:predicate), NodeListField.new(:conditions), OptionalNodeField.new(:else_clause), LocationField.new(:case_keyword_loc), LocationField.new(:end_keyword_loc)] + when :class_node + [ConstantListField.new(:locals), LocationField.new(:class_keyword_loc), NodeField.new(:constant_path), OptionalLocationField.new(:inheritance_operator_loc), OptionalNodeField.new(:superclass), OptionalNodeField.new(:body), LocationField.new(:end_keyword_loc), ConstantField.new(:name)] + when :class_variable_and_write_node + [ConstantField.new(:name), LocationField.new(:name_loc), LocationField.new(:operator_loc), NodeField.new(:value)] + when :class_variable_operator_write_node + [ConstantField.new(:name), LocationField.new(:name_loc), LocationField.new(:binary_operator_loc), NodeField.new(:value), ConstantField.new(:binary_operator)] + when :class_variable_or_write_node + [ConstantField.new(:name), LocationField.new(:name_loc), LocationField.new(:operator_loc), NodeField.new(:value)] + when :class_variable_read_node + [ConstantField.new(:name)] + when :class_variable_target_node + [ConstantField.new(:name)] + when :class_variable_write_node + [ConstantField.new(:name), LocationField.new(:name_loc), NodeField.new(:value), LocationField.new(:operator_loc)] + when :constant_and_write_node + [ConstantField.new(:name), LocationField.new(:name_loc), LocationField.new(:operator_loc), NodeField.new(:value)] + when :constant_operator_write_node + [ConstantField.new(:name), LocationField.new(:name_loc), LocationField.new(:binary_operator_loc), NodeField.new(:value), ConstantField.new(:binary_operator)] + when :constant_or_write_node + [ConstantField.new(:name), LocationField.new(:name_loc), LocationField.new(:operator_loc), NodeField.new(:value)] + when :constant_path_and_write_node + [NodeField.new(:target), LocationField.new(:operator_loc), NodeField.new(:value)] + when :constant_path_node + [OptionalNodeField.new(:parent), OptionalConstantField.new(:name), LocationField.new(:delimiter_loc), LocationField.new(:name_loc)] + when :constant_path_operator_write_node + [NodeField.new(:target), LocationField.new(:binary_operator_loc), NodeField.new(:value), ConstantField.new(:binary_operator)] + when :constant_path_or_write_node + [NodeField.new(:target), LocationField.new(:operator_loc), NodeField.new(:value)] + when :constant_path_target_node + [OptionalNodeField.new(:parent), OptionalConstantField.new(:name), LocationField.new(:delimiter_loc), LocationField.new(:name_loc)] + when :constant_path_write_node + [NodeField.new(:target), LocationField.new(:operator_loc), NodeField.new(:value)] + when :constant_read_node + [ConstantField.new(:name)] + when :constant_target_node + [ConstantField.new(:name)] + when :constant_write_node + [ConstantField.new(:name), LocationField.new(:name_loc), NodeField.new(:value), LocationField.new(:operator_loc)] + when :def_node + [ConstantField.new(:name), LocationField.new(:name_loc), OptionalNodeField.new(:receiver), OptionalNodeField.new(:parameters), OptionalNodeField.new(:body), ConstantListField.new(:locals), LocationField.new(:def_keyword_loc), OptionalLocationField.new(:operator_loc), OptionalLocationField.new(:lparen_loc), OptionalLocationField.new(:rparen_loc), OptionalLocationField.new(:equal_loc), OptionalLocationField.new(:end_keyword_loc)] + when :defined_node + [OptionalLocationField.new(:lparen_loc), NodeField.new(:value), OptionalLocationField.new(:rparen_loc), LocationField.new(:keyword_loc)] + when :else_node + [LocationField.new(:else_keyword_loc), OptionalNodeField.new(:statements), OptionalLocationField.new(:end_keyword_loc)] + when :embedded_statements_node + [LocationField.new(:opening_loc), OptionalNodeField.new(:statements), LocationField.new(:closing_loc)] + when :embedded_variable_node + [LocationField.new(:operator_loc), NodeField.new(:variable)] + when :ensure_node + [LocationField.new(:ensure_keyword_loc), OptionalNodeField.new(:statements), LocationField.new(:end_keyword_loc)] + when :false_node + [] + when :find_pattern_node + [OptionalNodeField.new(:constant), NodeField.new(:left), NodeListField.new(:requireds), NodeField.new(:right), OptionalLocationField.new(:opening_loc), OptionalLocationField.new(:closing_loc)] + when :flip_flop_node + [FlagsField.new(:flags, [:exclude_end?]), OptionalNodeField.new(:left), OptionalNodeField.new(:right), LocationField.new(:operator_loc)] + when :float_node + [FloatField.new(:value)] + when :for_node + [NodeField.new(:index), NodeField.new(:collection), OptionalNodeField.new(:statements), LocationField.new(:for_keyword_loc), LocationField.new(:in_keyword_loc), OptionalLocationField.new(:do_keyword_loc), LocationField.new(:end_keyword_loc)] + when :forwarding_arguments_node + [] + when :forwarding_parameter_node + [] + when :forwarding_super_node + [OptionalNodeField.new(:block)] + when :global_variable_and_write_node + [ConstantField.new(:name), LocationField.new(:name_loc), LocationField.new(:operator_loc), NodeField.new(:value)] + when :global_variable_operator_write_node + [ConstantField.new(:name), LocationField.new(:name_loc), LocationField.new(:binary_operator_loc), NodeField.new(:value), ConstantField.new(:binary_operator)] + when :global_variable_or_write_node + [ConstantField.new(:name), LocationField.new(:name_loc), LocationField.new(:operator_loc), NodeField.new(:value)] + when :global_variable_read_node + [ConstantField.new(:name)] + when :global_variable_target_node + [ConstantField.new(:name)] + when :global_variable_write_node + [ConstantField.new(:name), LocationField.new(:name_loc), NodeField.new(:value), LocationField.new(:operator_loc)] + when :hash_node + [LocationField.new(:opening_loc), NodeListField.new(:elements), LocationField.new(:closing_loc)] + when :hash_pattern_node + [OptionalNodeField.new(:constant), NodeListField.new(:elements), OptionalNodeField.new(:rest), OptionalLocationField.new(:opening_loc), OptionalLocationField.new(:closing_loc)] + when :if_node + [OptionalLocationField.new(:if_keyword_loc), NodeField.new(:predicate), OptionalLocationField.new(:then_keyword_loc), OptionalNodeField.new(:statements), OptionalNodeField.new(:subsequent), OptionalLocationField.new(:end_keyword_loc)] + when :imaginary_node + [NodeField.new(:numeric)] + when :implicit_node + [NodeField.new(:value)] + when :implicit_rest_node + [] + when :in_node + [NodeField.new(:pattern), OptionalNodeField.new(:statements), LocationField.new(:in_loc), OptionalLocationField.new(:then_loc)] + when :index_and_write_node + [FlagsField.new(:flags, [:safe_navigation?, :variable_call?, :attribute_write?, :ignore_visibility?]), OptionalNodeField.new(:receiver), OptionalLocationField.new(:call_operator_loc), LocationField.new(:opening_loc), OptionalNodeField.new(:arguments), LocationField.new(:closing_loc), OptionalNodeField.new(:block), LocationField.new(:operator_loc), NodeField.new(:value)] + when :index_operator_write_node + [FlagsField.new(:flags, [:safe_navigation?, :variable_call?, :attribute_write?, :ignore_visibility?]), OptionalNodeField.new(:receiver), OptionalLocationField.new(:call_operator_loc), LocationField.new(:opening_loc), OptionalNodeField.new(:arguments), LocationField.new(:closing_loc), OptionalNodeField.new(:block), ConstantField.new(:binary_operator), LocationField.new(:binary_operator_loc), NodeField.new(:value)] + when :index_or_write_node + [FlagsField.new(:flags, [:safe_navigation?, :variable_call?, :attribute_write?, :ignore_visibility?]), OptionalNodeField.new(:receiver), OptionalLocationField.new(:call_operator_loc), LocationField.new(:opening_loc), OptionalNodeField.new(:arguments), LocationField.new(:closing_loc), OptionalNodeField.new(:block), LocationField.new(:operator_loc), NodeField.new(:value)] + when :index_target_node + [FlagsField.new(:flags, [:safe_navigation?, :variable_call?, :attribute_write?, :ignore_visibility?]), NodeField.new(:receiver), LocationField.new(:opening_loc), OptionalNodeField.new(:arguments), LocationField.new(:closing_loc), OptionalNodeField.new(:block)] + when :instance_variable_and_write_node + [ConstantField.new(:name), LocationField.new(:name_loc), LocationField.new(:operator_loc), NodeField.new(:value)] + when :instance_variable_operator_write_node + [ConstantField.new(:name), LocationField.new(:name_loc), LocationField.new(:binary_operator_loc), NodeField.new(:value), ConstantField.new(:binary_operator)] + when :instance_variable_or_write_node + [ConstantField.new(:name), LocationField.new(:name_loc), LocationField.new(:operator_loc), NodeField.new(:value)] + when :instance_variable_read_node + [ConstantField.new(:name)] + when :instance_variable_target_node + [ConstantField.new(:name)] + when :instance_variable_write_node + [ConstantField.new(:name), LocationField.new(:name_loc), NodeField.new(:value), LocationField.new(:operator_loc)] + when :integer_node + [FlagsField.new(:flags, [:binary?, :decimal?, :octal?, :hexadecimal?]), IntegerField.new(:value)] + when :interpolated_match_last_line_node + [FlagsField.new(:flags, [:ignore_case?, :extended?, :multi_line?, :once?, :euc_jp?, :ascii_8bit?, :windows_31j?, :utf_8?, :forced_utf8_encoding?, :forced_binary_encoding?, :forced_us_ascii_encoding?]), LocationField.new(:opening_loc), NodeListField.new(:parts), LocationField.new(:closing_loc)] + when :interpolated_regular_expression_node + [FlagsField.new(:flags, [:ignore_case?, :extended?, :multi_line?, :once?, :euc_jp?, :ascii_8bit?, :windows_31j?, :utf_8?, :forced_utf8_encoding?, :forced_binary_encoding?, :forced_us_ascii_encoding?]), LocationField.new(:opening_loc), NodeListField.new(:parts), LocationField.new(:closing_loc)] + when :interpolated_string_node + [FlagsField.new(:flags, [:frozen?, :mutable?]), OptionalLocationField.new(:opening_loc), NodeListField.new(:parts), OptionalLocationField.new(:closing_loc)] + when :interpolated_symbol_node + [OptionalLocationField.new(:opening_loc), NodeListField.new(:parts), OptionalLocationField.new(:closing_loc)] + when :interpolated_x_string_node + [LocationField.new(:opening_loc), NodeListField.new(:parts), LocationField.new(:closing_loc)] + when :it_local_variable_read_node + [] + when :it_parameters_node + [] + when :keyword_hash_node + [FlagsField.new(:flags, [:symbol_keys?]), NodeListField.new(:elements)] + when :keyword_rest_parameter_node + [FlagsField.new(:flags, [:repeated_parameter?]), OptionalConstantField.new(:name), OptionalLocationField.new(:name_loc), LocationField.new(:operator_loc)] + when :lambda_node + [ConstantListField.new(:locals), LocationField.new(:operator_loc), LocationField.new(:opening_loc), LocationField.new(:closing_loc), OptionalNodeField.new(:parameters), OptionalNodeField.new(:body)] + when :local_variable_and_write_node + [LocationField.new(:name_loc), LocationField.new(:operator_loc), NodeField.new(:value), ConstantField.new(:name), IntegerField.new(:depth)] + when :local_variable_operator_write_node + [LocationField.new(:name_loc), LocationField.new(:binary_operator_loc), NodeField.new(:value), ConstantField.new(:name), ConstantField.new(:binary_operator), IntegerField.new(:depth)] + when :local_variable_or_write_node + [LocationField.new(:name_loc), LocationField.new(:operator_loc), NodeField.new(:value), ConstantField.new(:name), IntegerField.new(:depth)] + when :local_variable_read_node + [ConstantField.new(:name), IntegerField.new(:depth)] + when :local_variable_target_node + [ConstantField.new(:name), IntegerField.new(:depth)] + when :local_variable_write_node + [ConstantField.new(:name), IntegerField.new(:depth), LocationField.new(:name_loc), NodeField.new(:value), LocationField.new(:operator_loc)] + when :match_last_line_node + [FlagsField.new(:flags, [:ignore_case?, :extended?, :multi_line?, :once?, :euc_jp?, :ascii_8bit?, :windows_31j?, :utf_8?, :forced_utf8_encoding?, :forced_binary_encoding?, :forced_us_ascii_encoding?]), LocationField.new(:opening_loc), LocationField.new(:content_loc), LocationField.new(:closing_loc), StringField.new(:unescaped)] + when :match_predicate_node + [NodeField.new(:value), NodeField.new(:pattern), LocationField.new(:operator_loc)] + when :match_required_node + [NodeField.new(:value), NodeField.new(:pattern), LocationField.new(:operator_loc)] + when :match_write_node + [NodeField.new(:call), NodeListField.new(:targets)] + when :missing_node + [] + when :module_node + [ConstantListField.new(:locals), LocationField.new(:module_keyword_loc), NodeField.new(:constant_path), OptionalNodeField.new(:body), LocationField.new(:end_keyword_loc), ConstantField.new(:name)] + when :multi_target_node + [NodeListField.new(:lefts), OptionalNodeField.new(:rest), NodeListField.new(:rights), OptionalLocationField.new(:lparen_loc), OptionalLocationField.new(:rparen_loc)] + when :multi_write_node + [NodeListField.new(:lefts), OptionalNodeField.new(:rest), NodeListField.new(:rights), OptionalLocationField.new(:lparen_loc), OptionalLocationField.new(:rparen_loc), LocationField.new(:operator_loc), NodeField.new(:value)] + when :next_node + [OptionalNodeField.new(:arguments), LocationField.new(:keyword_loc)] + when :nil_node + [] + when :no_keywords_parameter_node + [LocationField.new(:operator_loc), LocationField.new(:keyword_loc)] + when :numbered_parameters_node + [IntegerField.new(:maximum)] + when :numbered_reference_read_node + [IntegerField.new(:number)] + when :optional_keyword_parameter_node + [FlagsField.new(:flags, [:repeated_parameter?]), ConstantField.new(:name), LocationField.new(:name_loc), NodeField.new(:value)] + when :optional_parameter_node + [FlagsField.new(:flags, [:repeated_parameter?]), ConstantField.new(:name), LocationField.new(:name_loc), LocationField.new(:operator_loc), NodeField.new(:value)] + when :or_node + [NodeField.new(:left), NodeField.new(:right), LocationField.new(:operator_loc)] + when :parameters_node + [NodeListField.new(:requireds), NodeListField.new(:optionals), OptionalNodeField.new(:rest), NodeListField.new(:posts), NodeListField.new(:keywords), OptionalNodeField.new(:keyword_rest), OptionalNodeField.new(:block)] + when :parentheses_node + [FlagsField.new(:flags, [:multiple_statements?]), OptionalNodeField.new(:body), LocationField.new(:opening_loc), LocationField.new(:closing_loc)] + when :pinned_expression_node + [NodeField.new(:expression), LocationField.new(:operator_loc), LocationField.new(:lparen_loc), LocationField.new(:rparen_loc)] + when :pinned_variable_node + [NodeField.new(:variable), LocationField.new(:operator_loc)] + when :post_execution_node + [OptionalNodeField.new(:statements), LocationField.new(:keyword_loc), LocationField.new(:opening_loc), LocationField.new(:closing_loc)] + when :pre_execution_node + [OptionalNodeField.new(:statements), LocationField.new(:keyword_loc), LocationField.new(:opening_loc), LocationField.new(:closing_loc)] + when :program_node + [ConstantListField.new(:locals), NodeField.new(:statements)] + when :range_node + [FlagsField.new(:flags, [:exclude_end?]), OptionalNodeField.new(:left), OptionalNodeField.new(:right), LocationField.new(:operator_loc)] + when :rational_node + [FlagsField.new(:flags, [:binary?, :decimal?, :octal?, :hexadecimal?]), IntegerField.new(:numerator), IntegerField.new(:denominator)] + when :redo_node + [] + when :regular_expression_node + [FlagsField.new(:flags, [:ignore_case?, :extended?, :multi_line?, :once?, :euc_jp?, :ascii_8bit?, :windows_31j?, :utf_8?, :forced_utf8_encoding?, :forced_binary_encoding?, :forced_us_ascii_encoding?]), LocationField.new(:opening_loc), LocationField.new(:content_loc), LocationField.new(:closing_loc), StringField.new(:unescaped)] + when :required_keyword_parameter_node + [FlagsField.new(:flags, [:repeated_parameter?]), ConstantField.new(:name), LocationField.new(:name_loc)] + when :required_parameter_node + [FlagsField.new(:flags, [:repeated_parameter?]), ConstantField.new(:name)] + when :rescue_modifier_node + [NodeField.new(:expression), LocationField.new(:keyword_loc), NodeField.new(:rescue_expression)] + when :rescue_node + [LocationField.new(:keyword_loc), NodeListField.new(:exceptions), OptionalLocationField.new(:operator_loc), OptionalNodeField.new(:reference), OptionalLocationField.new(:then_keyword_loc), OptionalNodeField.new(:statements), OptionalNodeField.new(:subsequent)] + when :rest_parameter_node + [FlagsField.new(:flags, [:repeated_parameter?]), OptionalConstantField.new(:name), OptionalLocationField.new(:name_loc), LocationField.new(:operator_loc)] + when :retry_node + [] + when :return_node + [LocationField.new(:keyword_loc), OptionalNodeField.new(:arguments)] + when :self_node + [] + when :shareable_constant_node + [FlagsField.new(:flags, [:literal?, :experimental_everything?, :experimental_copy?]), NodeField.new(:write)] + when :singleton_class_node + [ConstantListField.new(:locals), LocationField.new(:class_keyword_loc), LocationField.new(:operator_loc), NodeField.new(:expression), OptionalNodeField.new(:body), LocationField.new(:end_keyword_loc)] + when :source_encoding_node + [] + when :source_file_node + [FlagsField.new(:flags, [:forced_utf8_encoding?, :forced_binary_encoding?, :frozen?, :mutable?]), StringField.new(:filepath)] + when :source_line_node + [] + when :splat_node + [LocationField.new(:operator_loc), OptionalNodeField.new(:expression)] + when :statements_node + [NodeListField.new(:body)] + when :string_node + [FlagsField.new(:flags, [:forced_utf8_encoding?, :forced_binary_encoding?, :frozen?, :mutable?]), OptionalLocationField.new(:opening_loc), LocationField.new(:content_loc), OptionalLocationField.new(:closing_loc), StringField.new(:unescaped)] + when :super_node + [LocationField.new(:keyword_loc), OptionalLocationField.new(:lparen_loc), OptionalNodeField.new(:arguments), OptionalLocationField.new(:rparen_loc), OptionalNodeField.new(:block)] + when :symbol_node + [FlagsField.new(:flags, [:forced_utf8_encoding?, :forced_binary_encoding?, :forced_us_ascii_encoding?]), OptionalLocationField.new(:opening_loc), OptionalLocationField.new(:value_loc), OptionalLocationField.new(:closing_loc), StringField.new(:unescaped)] + when :true_node + [] + when :undef_node + [NodeListField.new(:names), LocationField.new(:keyword_loc)] + when :unless_node + [LocationField.new(:keyword_loc), NodeField.new(:predicate), OptionalLocationField.new(:then_keyword_loc), OptionalNodeField.new(:statements), OptionalNodeField.new(:else_clause), OptionalLocationField.new(:end_keyword_loc)] + when :until_node + [FlagsField.new(:flags, [:begin_modifier?]), LocationField.new(:keyword_loc), OptionalLocationField.new(:do_keyword_loc), OptionalLocationField.new(:closing_loc), NodeField.new(:predicate), OptionalNodeField.new(:statements)] + when :when_node + [LocationField.new(:keyword_loc), NodeListField.new(:conditions), OptionalLocationField.new(:then_keyword_loc), OptionalNodeField.new(:statements)] + when :while_node + [FlagsField.new(:flags, [:begin_modifier?]), LocationField.new(:keyword_loc), OptionalLocationField.new(:do_keyword_loc), OptionalLocationField.new(:closing_loc), NodeField.new(:predicate), OptionalNodeField.new(:statements)] + when :x_string_node + [FlagsField.new(:flags, [:forced_utf8_encoding?, :forced_binary_encoding?]), LocationField.new(:opening_loc), LocationField.new(:content_loc), LocationField.new(:closing_loc), StringField.new(:unescaped)] + when :yield_node + [LocationField.new(:keyword_loc), OptionalLocationField.new(:lparen_loc), OptionalNodeField.new(:arguments), OptionalLocationField.new(:rparen_loc)] + else + raise "Unknown node type: #{node.type.inspect}" + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/relocation.rb b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/relocation.rb new file mode 100644 index 0000000..3e9210a --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/relocation.rb @@ -0,0 +1,505 @@ +# frozen_string_literal: true +# :markup: markdown + +module Prism + # Prism parses deterministically for the same input. This provides a nice + # property that is exposed through the #node_id API on nodes. Effectively this + # means that for the same input, these values will remain consistent every + # time the source is parsed. This means we can reparse the source same with a + # #node_id value and find the exact same node again. + # + # The Relocation module provides an API around this property. It allows you to + # "save" nodes and locations using a minimal amount of memory (just the + # node_id and a field identifier) and then reify them later. + module Relocation + # An entry in a repository that will lazily reify its values when they are + # first accessed. + class Entry + # Raised if a value that could potentially be on an entry is missing + # because it was either not configured on the repository or it has not yet + # been fetched. + class MissingValueError < StandardError + end + + # Initialize a new entry with the given repository. + def initialize(repository) + @repository = repository + @values = nil + end + + # Fetch the filepath of the value. + def filepath + fetch_value(:filepath) + end + + # Fetch the start line of the value. + def start_line + fetch_value(:start_line) + end + + # Fetch the end line of the value. + def end_line + fetch_value(:end_line) + end + + # Fetch the start byte offset of the value. + def start_offset + fetch_value(:start_offset) + end + + # Fetch the end byte offset of the value. + def end_offset + fetch_value(:end_offset) + end + + # Fetch the start character offset of the value. + def start_character_offset + fetch_value(:start_character_offset) + end + + # Fetch the end character offset of the value. + def end_character_offset + fetch_value(:end_character_offset) + end + + # Fetch the start code units offset of the value, for the encoding that + # was configured on the repository. + def start_code_units_offset + fetch_value(:start_code_units_offset) + end + + # Fetch the end code units offset of the value, for the encoding that was + # configured on the repository. + def end_code_units_offset + fetch_value(:end_code_units_offset) + end + + # Fetch the start byte column of the value. + def start_column + fetch_value(:start_column) + end + + # Fetch the end byte column of the value. + def end_column + fetch_value(:end_column) + end + + # Fetch the start character column of the value. + def start_character_column + fetch_value(:start_character_column) + end + + # Fetch the end character column of the value. + def end_character_column + fetch_value(:end_character_column) + end + + # Fetch the start code units column of the value, for the encoding that + # was configured on the repository. + def start_code_units_column + fetch_value(:start_code_units_column) + end + + # Fetch the end code units column of the value, for the encoding that was + # configured on the repository. + def end_code_units_column + fetch_value(:end_code_units_column) + end + + # Fetch the leading comments of the value. + def leading_comments + fetch_value(:leading_comments) + end + + # Fetch the trailing comments of the value. + def trailing_comments + fetch_value(:trailing_comments) + end + + # Fetch the leading and trailing comments of the value. + def comments + leading_comments.concat(trailing_comments) + end + + # Reify the values on this entry with the given values. This is an + # internal-only API that is called from the repository when it is time to + # reify the values. + def reify!(values) # :nodoc: + @repository = nil + @values = values + end + + private + + # Fetch a value from the entry, raising an error if it is missing. + def fetch_value(name) + values.fetch(name) do + raise MissingValueError, "No value for #{name}, make sure the " \ + "repository has been properly configured" + end + end + + # Return the values from the repository, reifying them if necessary. + def values + @values || (@repository.reify!; @values) + end + end + + # Represents the source of a repository that will be reparsed. + class Source + # The value that will need to be reparsed. + attr_reader :value + + # Initialize the source with the given value. + def initialize(value) + @value = value + end + + # Reparse the value and return the parse result. + def result + raise NotImplementedError, "Subclasses must implement #result" + end + + # Create a code units cache for the given encoding. + def code_units_cache(encoding) + result.code_units_cache(encoding) + end + end + + # A source that is represented by a file path. + class SourceFilepath < Source + # Reparse the file and return the parse result. + def result + Prism.parse_file(value) + end + end + + # A source that is represented by a string. + class SourceString < Source + # Reparse the string and return the parse result. + def result + Prism.parse(value) + end + end + + # A field that represents the file path. + class FilepathField + # The file path that this field represents. + attr_reader :value + + # Initialize a new field with the given file path. + def initialize(value) + @value = value + end + + # Fetch the file path. + def fields(_value) + { filepath: value } + end + end + + # A field representing the start and end lines. + class LinesField + # Fetches the start and end line of a value. + def fields(value) + { start_line: value.start_line, end_line: value.end_line } + end + end + + # A field representing the start and end byte offsets. + class OffsetsField + # Fetches the start and end byte offset of a value. + def fields(value) + { start_offset: value.start_offset, end_offset: value.end_offset } + end + end + + # A field representing the start and end character offsets. + class CharacterOffsetsField + # Fetches the start and end character offset of a value. + def fields(value) + { + start_character_offset: value.start_character_offset, + end_character_offset: value.end_character_offset + } + end + end + + # A field representing the start and end code unit offsets. + class CodeUnitOffsetsField + # A pointer to the repository object that is used for lazily creating a + # code units cache. + attr_reader :repository + + # The associated encoding for the code units. + attr_reader :encoding + + # Initialize a new field with the associated repository and encoding. + def initialize(repository, encoding) + @repository = repository + @encoding = encoding + @cache = nil + end + + # Fetches the start and end code units offset of a value for a particular + # encoding. + def fields(value) + { + start_code_units_offset: value.cached_start_code_units_offset(cache), + end_code_units_offset: value.cached_end_code_units_offset(cache) + } + end + + private + + # Lazily create a code units cache for the associated encoding. + def cache + @cache ||= repository.code_units_cache(encoding) + end + end + + # A field representing the start and end byte columns. + class ColumnsField + # Fetches the start and end byte column of a value. + def fields(value) + { start_column: value.start_column, end_column: value.end_column } + end + end + + # A field representing the start and end character columns. + class CharacterColumnsField + # Fetches the start and end character column of a value. + def fields(value) + { + start_character_column: value.start_character_column, + end_character_column: value.end_character_column + } + end + end + + # A field representing the start and end code unit columns for a specific + # encoding. + class CodeUnitColumnsField + # The repository object that is used for lazily creating a code units + # cache. + attr_reader :repository + + # The associated encoding for the code units. + attr_reader :encoding + + # Initialize a new field with the associated repository and encoding. + def initialize(repository, encoding) + @repository = repository + @encoding = encoding + @cache = nil + end + + # Fetches the start and end code units column of a value for a particular + # encoding. + def fields(value) + { + start_code_units_column: value.cached_start_code_units_column(cache), + end_code_units_column: value.cached_end_code_units_column(cache) + } + end + + private + + # Lazily create a code units cache for the associated encoding. + def cache + @cache ||= repository.code_units_cache(encoding) + end + end + + # An abstract field used as the parent class of the two comments fields. + class CommentsField + # An object that represents a slice of a comment. + class Comment + # The slice of the comment. + attr_reader :slice + + # Initialize a new comment with the given slice. + def initialize(slice) + @slice = slice + end + end + + private + + # Create comment objects from the given values. + def comments(values) + values.map { |value| Comment.new(value.slice) } + end + end + + # A field representing the leading comments. + class LeadingCommentsField < CommentsField + # Fetches the leading comments of a value. + def fields(value) + { leading_comments: comments(value.leading_comments) } + end + end + + # A field representing the trailing comments. + class TrailingCommentsField < CommentsField + # Fetches the trailing comments of a value. + def fields(value) + { trailing_comments: comments(value.trailing_comments) } + end + end + + # A repository is a configured collection of fields and a set of entries + # that knows how to reparse a source and reify the values. + class Repository + # Raised when multiple fields of the same type are configured on the same + # repository. + class ConfigurationError < StandardError + end + + # The source associated with this repository. This will be either a + # SourceFilepath (the most common use case) or a SourceString. + attr_reader :source + + # The fields that have been configured on this repository. + attr_reader :fields + + # The entries that have been saved on this repository. + attr_reader :entries + + # Initialize a new repository with the given source. + def initialize(source) + @source = source + @fields = {} + @entries = Hash.new { |hash, node_id| hash[node_id] = {} } + end + + # Create a code units cache for the given encoding from the source. + def code_units_cache(encoding) + source.code_units_cache(encoding) + end + + # Configure the filepath field for this repository and return self. + def filepath + raise ConfigurationError, "Can only specify filepath for a filepath source" unless source.is_a?(SourceFilepath) + field(:filepath, FilepathField.new(source.value)) + end + + # Configure the lines field for this repository and return self. + def lines + field(:lines, LinesField.new) + end + + # Configure the offsets field for this repository and return self. + def offsets + field(:offsets, OffsetsField.new) + end + + # Configure the character offsets field for this repository and return + # self. + def character_offsets + field(:character_offsets, CharacterOffsetsField.new) + end + + # Configure the code unit offsets field for this repository for a specific + # encoding and return self. + def code_unit_offsets(encoding) + field(:code_unit_offsets, CodeUnitOffsetsField.new(self, encoding)) + end + + # Configure the columns field for this repository and return self. + def columns + field(:columns, ColumnsField.new) + end + + # Configure the character columns field for this repository and return + # self. + def character_columns + field(:character_columns, CharacterColumnsField.new) + end + + # Configure the code unit columns field for this repository for a specific + # encoding and return self. + def code_unit_columns(encoding) + field(:code_unit_columns, CodeUnitColumnsField.new(self, encoding)) + end + + # Configure the leading comments field for this repository and return + # self. + def leading_comments + field(:leading_comments, LeadingCommentsField.new) + end + + # Configure the trailing comments field for this repository and return + # self. + def trailing_comments + field(:trailing_comments, TrailingCommentsField.new) + end + + # Configure both the leading and trailing comment fields for this + # repository and return self. + def comments + leading_comments.trailing_comments + end + + # This method is called from nodes and locations when they want to enter + # themselves into the repository. It it internal-only and meant to be + # called from the #save* APIs. + def enter(node_id, field_name) # :nodoc: + entry = Entry.new(self) + @entries[node_id][field_name] = entry + entry + end + + # This method is called from the entries in the repository when they need + # to reify their values. It is internal-only and meant to be called from + # the various value APIs. + def reify! # :nodoc: + result = source.result + + # Attach the comments if they have been requested as part of the + # configuration of this repository. + if fields.key?(:leading_comments) || fields.key?(:trailing_comments) + result.attach_comments! + end + + queue = [result.value] #: Array[Prism::node] + while (node = queue.shift) + @entries[node.node_id].each do |field_name, entry| + value = node.public_send(field_name) + values = {} #: Hash[Symbol, untyped] + + fields.each_value do |field| + values.merge!(field.fields(value)) + end + + entry.reify!(values) + end + + queue.concat(node.compact_child_nodes) + end + + @entries.clear + end + + private + + # Append the given field to the repository and return the repository so + # that these calls can be chained. + def field(name, value) + raise ConfigurationError, "Cannot specify multiple #{name} fields" if @fields.key?(name) + @fields[name] = value + self + end + end + + # Create a new repository for the given filepath. + def self.filepath(value) + Repository.new(SourceFilepath.new(value)) + end + + # Create a new repository for the given string. + def self.string(value) + Repository.new(SourceString.new(value)) + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/serialize.rb b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/serialize.rb new file mode 100644 index 0000000..1e15d70 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/serialize.rb @@ -0,0 +1,2400 @@ +# frozen_string_literal: true +# :markup: markdown + +=begin +-- +This file is generated by the templates/template.rb script and should not be +modified manually. See templates/lib/prism/serialize.rb.erb +if you are looking to modify the template +++ +=end + +require "stringio" +require_relative "polyfill/unpack1" + +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 = 1 + + # The minor version of prism that we are expecting to find in the serialized + # strings. + MINOR_VERSION = 9 + + # The patch version of prism that we are expecting to find in the serialized + # strings. + PATCH_VERSION = 0 + + # Deserialize the dumped output from a request to parse or parse_file. + # + # The formatting of the source of this method is purposeful to illustrate + # the structure of the serialized data. + def self.load_parse(input, serialized, freeze) + input = input.dup + source = Source.for(input) + loader = Loader.new(source, serialized) + + loader.load_header + encoding = loader.load_encoding + start_line = loader.load_varsint + offsets = loader.load_line_offsets(freeze) + + source.replace_start_line(start_line) + source.replace_offsets(offsets) + + comments = loader.load_comments(freeze) + magic_comments = loader.load_magic_comments(freeze) + data_loc = loader.load_optional_location_object(freeze) + errors = loader.load_errors(encoding, freeze) + warnings = loader.load_warnings(encoding, freeze) + cpool_base = loader.load_uint32 + cpool_size = loader.load_varuint + + constant_pool = ConstantPool.new(input, serialized, cpool_base, cpool_size) + + node = loader.load_node(constant_pool, encoding, freeze) + loader.load_constant_pool(constant_pool) + raise unless loader.eof? + + result = ParseResult.new(node, comments, magic_comments, data_loc, errors, warnings, source) + result.freeze if freeze + + input.force_encoding(encoding) + + # This is an extremely niche use-case where the file was marked as binary + # but it contained UTF-8-encoded characters. In that case we will actually + # put it back to UTF-8 to give the location APIs the best chance of being + # correct. + if !input.ascii_only? && input.encoding == Encoding::BINARY + input.force_encoding(Encoding::UTF_8) + input.force_encoding(Encoding::BINARY) unless input.valid_encoding? + end + + if freeze + input.freeze + source.deep_freeze + end + + result + end + + # Deserialize the dumped output from a request to lex or lex_file. + # + # The formatting of the source of this method is purposeful to illustrate + # the structure of the serialized data. + def self.load_lex(input, serialized, freeze) + source = Source.for(input) + loader = Loader.new(source, serialized) + + tokens = loader.load_tokens + encoding = loader.load_encoding + start_line = loader.load_varsint + offsets = loader.load_line_offsets(freeze) + + source.replace_start_line(start_line) + source.replace_offsets(offsets) + + comments = loader.load_comments(freeze) + magic_comments = loader.load_magic_comments(freeze) + data_loc = loader.load_optional_location_object(freeze) + errors = loader.load_errors(encoding, freeze) + warnings = loader.load_warnings(encoding, freeze) + raise unless loader.eof? + + result = LexResult.new(tokens, comments, magic_comments, data_loc, errors, warnings, source) + + tokens.each do |token| + token[0].value.force_encoding(encoding) + + if freeze + token[0].deep_freeze + token.freeze + end + end + + if freeze + source.deep_freeze + tokens.freeze + result.freeze + end + + result + end + + # Deserialize the dumped output from a request to parse_comments or + # parse_file_comments. + # + # The formatting of the source of this method is purposeful to illustrate + # the structure of the serialized data. + def self.load_parse_comments(input, serialized, freeze) + source = Source.for(input) + loader = Loader.new(source, serialized) + + loader.load_header + loader.load_encoding + start_line = loader.load_varsint + + source.replace_start_line(start_line) + + result = loader.load_comments(freeze) + raise unless loader.eof? + + source.deep_freeze if freeze + result + end + + # Deserialize the dumped output from a request to parse_lex or + # parse_lex_file. + # + # The formatting of the source of this method is purposeful to illustrate + # the structure of the serialized data. + def self.load_parse_lex(input, serialized, freeze) + source = Source.for(input) + loader = Loader.new(source, serialized) + + tokens = loader.load_tokens + loader.load_header + encoding = loader.load_encoding + start_line = loader.load_varsint + offsets = loader.load_line_offsets(freeze) + + source.replace_start_line(start_line) + source.replace_offsets(offsets) + + comments = loader.load_comments(freeze) + magic_comments = loader.load_magic_comments(freeze) + data_loc = loader.load_optional_location_object(freeze) + errors = loader.load_errors(encoding, freeze) + warnings = loader.load_warnings(encoding, freeze) + cpool_base = loader.load_uint32 + cpool_size = loader.load_varuint + + constant_pool = ConstantPool.new(input, serialized, cpool_base, cpool_size) + + node = loader.load_node(constant_pool, encoding, freeze) + loader.load_constant_pool(constant_pool) + raise unless loader.eof? + + value = [node, tokens] + result = ParseLexResult.new(value, comments, magic_comments, data_loc, errors, warnings, source) + + tokens.each do |token| + token[0].value.force_encoding(encoding) + + if freeze + token[0].deep_freeze + token.freeze + end + end + + if freeze + source.deep_freeze + tokens.freeze + value.freeze + result.freeze + end + + result + end + + class ConstantPool # :nodoc: + attr_reader :size + + def initialize(input, serialized, base, size) + @input = input + @serialized = serialized + @base = base + @size = size + @pool = Array.new(size, nil) + end + + def get(index, encoding) + @pool[index] ||= + begin + offset = @base + index * 8 + start = @serialized.unpack1("L", offset: offset) + length = @serialized.unpack1("L", offset: offset + 4) + + 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 + end + end + end + + 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 # :nodoc: + end + + class Loader # :nodoc: + attr_reader :input, :io, :source + + def initialize(source, serialized) + @input = source.source.dup + raise unless serialized.encoding == Encoding::BINARY + @io = FastStringIO.new(serialized) + @source = source + define_load_node_lambdas if RUBY_ENGINE != "ruby" + end + + def eof? + io.getbyte + io.eof? + end + + def load_constant_pool(constant_pool) + trailer = 0 + + constant_pool.size.times do |index| + start, length = io.read(8).unpack("L2") + trailer += length if start.anybits?(1 << 31) + end + + io.read(trailer) + 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] + raise "Invalid serialization (location fields must be included but are not)" if io.getbyte != 0 + end + + def load_encoding + encoding = Encoding.find(io.read(load_varuint)) + @input = input.force_encoding(encoding).freeze + encoding + end + + def load_line_offsets(freeze) + offsets = Array.new(load_varuint) { load_varuint } + offsets.freeze if freeze + offsets + end + + def load_comments(freeze) + comments = + Array.new(load_varuint) do + comment = + case load_varuint + when 0 then InlineComment.new(load_location_object(freeze)) + when 1 then EmbDocComment.new(load_location_object(freeze)) + end + + comment.freeze if freeze + comment + end + + comments.freeze if freeze + comments + end + + def load_magic_comments(freeze) + magic_comments = + Array.new(load_varuint) do + magic_comment = + MagicComment.new( + load_location_object(freeze), + load_location_object(freeze) + ) + + magic_comment.freeze if freeze + magic_comment + end + + magic_comments.freeze if freeze + magic_comments + end + + DIAGNOSTIC_TYPES = [ + :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_conflict_ampersand, + :argument_conflict_star, + :argument_conflict_star_star, + :argument_formal_class, + :argument_formal_constant, + :argument_formal_global, + :argument_formal_ivar, + :argument_forwarding_unbound, + :argument_no_forwarding_ampersand, + :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_parameters, + :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_list, + :escape_invalid_unicode_literal, + :escape_invalid_unicode_long, + :escape_invalid_unicode_short, + :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_for_delimiter, + :expect_ident_req_parameter, + :expect_in_delimiter, + :expect_lparen_after_not_lparen, + :expect_lparen_after_not_other, + :expect_lparen_req_parameter, + :expect_message, + :expect_rbracket, + :expect_rparen, + :expect_rparen_after_multi, + :expect_rparen_req_parameter, + :expect_singleton_class_delimiter, + :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_numbered, + :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_identifier, + :heredoc_term, + :incomplete_question_mark, + :incomplete_variable_class, + :incomplete_variable_class_3_3, + :incomplete_variable_instance, + :incomplete_variable_instance_3_3, + :instance_variable_bare, + :invalid_block_exit, + :invalid_character, + :invalid_comma, + :invalid_encoding_magic_comment, + :invalid_escape_character, + :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_fraction, + :invalid_number_hexadecimal, + :invalid_number_octal, + :invalid_number_underscore_inner, + :invalid_number_underscore_trailing, + :invalid_percent, + :invalid_percent_eof, + :invalid_printable_character, + :invalid_retry_after_else, + :invalid_retry_after_ensure, + :invalid_retry_without_rescue, + :invalid_symbol, + :invalid_variable_global, + :invalid_variable_global_3_3, + :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, + :nesting_too_deep, + :no_local_variable, + :non_associative_operator, + :not_expression, + :number_literal_underscore, + :numbered_parameter_inner_block, + :numbered_parameter_it, + :numbered_parameter_ordinary, + :numbered_parameter_outer_block, + :operator_multi_assign, + :operator_write_arguments, + :operator_write_block, + :parameter_assoc_splat_multi, + :parameter_block_multi, + :parameter_circular, + :parameter_forwarding_after_rest, + :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_unexpected_no_kw, + :parameter_wild_loose_comma, + :pattern_array_multiple_rests, + :pattern_capture_duplicate, + :pattern_capture_in_alternative, + :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_find_missing_inner, + :pattern_hash_implicit, + :pattern_hash_key, + :pattern_hash_key_duplicate, + :pattern_hash_key_interpolated, + :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_parse_error, + :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_disallowed, + :unary_receiver, + :undef_argument, + :unexpected_block_argument, + :unexpected_index_block, + :unexpected_index_keywords, + :unexpected_label, + :unexpected_multi_write, + :unexpected_parameter_default_value, + :unexpected_range_operator, + :unexpected_safe_navigation, + :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, + :ambiguous_binary_operator, + :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, + :end_in_method, + :duplicated_hash_key, + :duplicated_when_clause, + :float_out_of_range, + :ignored_frozen_string_literal, + :indentation_mismatch, + :integer_in_flip_flop, + :invalid_character, + :invalid_magic_comment_value, + :invalid_numbered_reference, + :keyword_eol, + :literal_in_condition_default, + :literal_in_condition_verbose, + :shareable_constant_value_line, + :shebang_carriage_return, + :unexpected_carriage_return, + :unreachable_statement, + :unused_local_variable, + :void_statement, + ].freeze + + private_constant :DIAGNOSTIC_TYPES + + 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_errors(encoding, freeze) + errors = + Array.new(load_varuint) do + error = + ParseError.new( + DIAGNOSTIC_TYPES.fetch(load_varuint), + load_embedded_string(encoding), + load_location_object(freeze), + load_error_level + ) + + error.freeze if freeze + error + end + + errors.freeze if freeze + errors + end + + def load_warning_level + level = io.getbyte + + case level + when 0 + :default + when 1 + :verbose + else + raise "Unknown level: #{level}" + end + end + + def load_warnings(encoding, freeze) + warnings = + Array.new(load_varuint) do + warning = + ParseWarning.new( + DIAGNOSTIC_TYPES.fetch(load_varuint), + load_embedded_string(encoding), + load_location_object(freeze), + load_warning_level + ) + + warning.freeze if freeze + warning + end + + warnings.freeze if freeze + 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) + token = Token.new(@source, type, location.slice, location) + + tokens << [token, lex_state] + end + + tokens + end + + # 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(constant_pool, encoding, freeze) + if io.getbyte != 0 + io.pos -= 1 + load_node(constant_pool, encoding, freeze) + end + end + + def load_embedded_string(encoding) + io.read(load_varuint).force_encoding(encoding).freeze + end + + def load_string(encoding) + case (type = io.getbyte) + when 1 + input.byteslice(load_varuint, load_varuint).force_encoding(encoding).freeze + when 2 + load_embedded_string(encoding) + else + raise "Unknown serialized string type: #{type}" + end + end + + def load_location_object(freeze) + location = Location.new(source, load_varuint, load_varuint) + location.freeze if freeze + location + end + + def load_location(freeze) + return load_location_object(freeze) if freeze + (load_varuint << 32) | load_varuint + end + + def load_optional_location(freeze) + load_location(freeze) if io.getbyte != 0 + end + + def load_optional_location_object(freeze) + load_location_object(freeze) if io.getbyte != 0 + end + + def load_constant(constant_pool, encoding) + index = load_varuint + constant_pool.get(index - 1, encoding) + end + + def load_optional_constant(constant_pool, encoding) + index = load_varuint + constant_pool.get(index - 1, encoding) if index != 0 + end + + if RUBY_ENGINE == "ruby" + def load_node(constant_pool, encoding, freeze) + type = io.getbyte + node_id = load_varuint + location = load_location(freeze) + value = case type + when 1 then + AliasGlobalVariableNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze), load_node(constant_pool, encoding, freeze), load_location(freeze)) + when 2 then + AliasMethodNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze), load_node(constant_pool, encoding, freeze), load_location(freeze)) + when 3 then + AlternationPatternNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze), load_node(constant_pool, encoding, freeze), load_location(freeze)) + when 4 then + AndNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze), load_node(constant_pool, encoding, freeze), load_location(freeze)) + when 5 then + ArgumentsNode.new(source, node_id, location, load_varuint, Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }.tap { |nodes| nodes.freeze if freeze }) + when 6 then + ArrayNode.new(source, node_id, location, load_varuint, Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }.tap { |nodes| nodes.freeze if freeze }, load_optional_location(freeze), load_optional_location(freeze)) + when 7 then + ArrayPatternNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }.tap { |nodes| nodes.freeze if freeze }, load_optional_node(constant_pool, encoding, freeze), Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }.tap { |nodes| nodes.freeze if freeze }, load_optional_location(freeze), load_optional_location(freeze)) + when 8 then + AssocNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze), load_node(constant_pool, encoding, freeze), load_optional_location(freeze)) + when 9 then + AssocSplatNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), load_location(freeze)) + when 10 then + BackReferenceReadNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding)) + when 11 then + BeginNode.new(source, node_id, location, load_varuint, load_optional_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_optional_node(constant_pool, encoding, freeze), load_optional_node(constant_pool, encoding, freeze), load_optional_node(constant_pool, encoding, freeze), load_optional_location(freeze)) + when 12 then + BlockArgumentNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), load_location(freeze)) + when 13 then + BlockLocalVariableNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding)) + when 14 then + BlockNode.new(source, node_id, location, load_varuint, Array.new(load_varuint) { load_constant(constant_pool, encoding) }.tap { |constants| constants.freeze if freeze }, load_optional_node(constant_pool, encoding, freeze), load_optional_node(constant_pool, encoding, freeze), load_location(freeze), load_location(freeze)) + when 15 then + BlockParameterNode.new(source, node_id, location, load_varuint, load_optional_constant(constant_pool, encoding), load_optional_location(freeze), load_location(freeze)) + when 16 then + BlockParametersNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }.tap { |nodes| nodes.freeze if freeze }, load_optional_location(freeze), load_optional_location(freeze)) + when 17 then + BreakNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), load_location(freeze)) + when 18 then + CallAndWriteNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), load_optional_location(freeze), load_optional_location(freeze), load_constant(constant_pool, encoding), load_constant(constant_pool, encoding), load_location(freeze), load_node(constant_pool, encoding, freeze)) + when 19 then + CallNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), load_optional_location(freeze), load_constant(constant_pool, encoding), load_optional_location(freeze), load_optional_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_optional_location(freeze), load_optional_location(freeze), load_optional_node(constant_pool, encoding, freeze)) + when 20 then + CallOperatorWriteNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), load_optional_location(freeze), load_optional_location(freeze), load_constant(constant_pool, encoding), load_constant(constant_pool, encoding), load_constant(constant_pool, encoding), load_location(freeze), load_node(constant_pool, encoding, freeze)) + when 21 then + CallOrWriteNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), load_optional_location(freeze), load_optional_location(freeze), load_constant(constant_pool, encoding), load_constant(constant_pool, encoding), load_location(freeze), load_node(constant_pool, encoding, freeze)) + when 22 then + CallTargetNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze), load_location(freeze), load_constant(constant_pool, encoding), load_location(freeze)) + when 23 then + CapturePatternNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze), load_node(constant_pool, encoding, freeze), load_location(freeze)) + when 24 then + CaseMatchNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }.tap { |nodes| nodes.freeze if freeze }, load_optional_node(constant_pool, encoding, freeze), load_location(freeze), load_location(freeze)) + when 25 then + CaseNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }.tap { |nodes| nodes.freeze if freeze }, load_optional_node(constant_pool, encoding, freeze), load_location(freeze), load_location(freeze)) + when 26 then + ClassNode.new(source, node_id, location, load_varuint, Array.new(load_varuint) { load_constant(constant_pool, encoding) }.tap { |constants| constants.freeze if freeze }, load_location(freeze), load_node(constant_pool, encoding, freeze), load_optional_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_optional_node(constant_pool, encoding, freeze), load_location(freeze), load_constant(constant_pool, encoding)) + when 27 then + ClassVariableAndWriteNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_location(freeze), load_location(freeze), load_node(constant_pool, encoding, freeze)) + when 28 then + ClassVariableOperatorWriteNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_location(freeze), load_location(freeze), load_node(constant_pool, encoding, freeze), load_constant(constant_pool, encoding)) + when 29 then + ClassVariableOrWriteNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_location(freeze), load_location(freeze), load_node(constant_pool, encoding, freeze)) + when 30 then + ClassVariableReadNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding)) + when 31 then + ClassVariableTargetNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding)) + when 32 then + ClassVariableWriteNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_location(freeze), load_node(constant_pool, encoding, freeze), load_location(freeze)) + when 33 then + ConstantAndWriteNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_location(freeze), load_location(freeze), load_node(constant_pool, encoding, freeze)) + when 34 then + ConstantOperatorWriteNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_location(freeze), load_location(freeze), load_node(constant_pool, encoding, freeze), load_constant(constant_pool, encoding)) + when 35 then + ConstantOrWriteNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_location(freeze), load_location(freeze), load_node(constant_pool, encoding, freeze)) + when 36 then + ConstantPathAndWriteNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze), load_location(freeze), load_node(constant_pool, encoding, freeze)) + when 37 then + ConstantPathNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), load_optional_constant(constant_pool, encoding), load_location(freeze), load_location(freeze)) + when 38 then + ConstantPathOperatorWriteNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze), load_location(freeze), load_node(constant_pool, encoding, freeze), load_constant(constant_pool, encoding)) + when 39 then + ConstantPathOrWriteNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze), load_location(freeze), load_node(constant_pool, encoding, freeze)) + when 40 then + ConstantPathTargetNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), load_optional_constant(constant_pool, encoding), load_location(freeze), load_location(freeze)) + when 41 then + ConstantPathWriteNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze), load_location(freeze), load_node(constant_pool, encoding, freeze)) + when 42 then + ConstantReadNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding)) + when 43 then + ConstantTargetNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding)) + when 44 then + ConstantWriteNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_location(freeze), load_node(constant_pool, encoding, freeze), load_location(freeze)) + when 45 then + load_uint32 + DefNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_optional_node(constant_pool, encoding, freeze), load_optional_node(constant_pool, encoding, freeze), Array.new(load_varuint) { load_constant(constant_pool, encoding) }.tap { |constants| constants.freeze if freeze }, load_location(freeze), load_optional_location(freeze), load_optional_location(freeze), load_optional_location(freeze), load_optional_location(freeze), load_optional_location(freeze)) + when 46 then + DefinedNode.new(source, node_id, location, load_varuint, load_optional_location(freeze), load_node(constant_pool, encoding, freeze), load_optional_location(freeze), load_location(freeze)) + when 47 then + ElseNode.new(source, node_id, location, load_varuint, load_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_optional_location(freeze)) + when 48 then + EmbeddedStatementsNode.new(source, node_id, location, load_varuint, load_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_location(freeze)) + when 49 then + EmbeddedVariableNode.new(source, node_id, location, load_varuint, load_location(freeze), load_node(constant_pool, encoding, freeze)) + when 50 then + EnsureNode.new(source, node_id, location, load_varuint, load_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_location(freeze)) + when 51 then + FalseNode.new(source, node_id, location, load_varuint) + when 52 then + FindPatternNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), load_node(constant_pool, encoding, freeze), Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }.tap { |nodes| nodes.freeze if freeze }, load_node(constant_pool, encoding, freeze), load_optional_location(freeze), load_optional_location(freeze)) + when 53 then + FlipFlopNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), load_optional_node(constant_pool, encoding, freeze), load_location(freeze)) + when 54 then + FloatNode.new(source, node_id, location, load_varuint, load_double) + when 55 then + ForNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze), load_node(constant_pool, encoding, freeze), load_optional_node(constant_pool, encoding, freeze), load_location(freeze), load_location(freeze), load_optional_location(freeze), load_location(freeze)) + when 56 then + ForwardingArgumentsNode.new(source, node_id, location, load_varuint) + when 57 then + ForwardingParameterNode.new(source, node_id, location, load_varuint) + when 58 then + ForwardingSuperNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze)) + when 59 then + GlobalVariableAndWriteNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_location(freeze), load_location(freeze), load_node(constant_pool, encoding, freeze)) + when 60 then + GlobalVariableOperatorWriteNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_location(freeze), load_location(freeze), load_node(constant_pool, encoding, freeze), load_constant(constant_pool, encoding)) + when 61 then + GlobalVariableOrWriteNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_location(freeze), load_location(freeze), load_node(constant_pool, encoding, freeze)) + when 62 then + GlobalVariableReadNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding)) + when 63 then + GlobalVariableTargetNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding)) + when 64 then + GlobalVariableWriteNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_location(freeze), load_node(constant_pool, encoding, freeze), load_location(freeze)) + when 65 then + HashNode.new(source, node_id, location, load_varuint, load_location(freeze), Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }.tap { |nodes| nodes.freeze if freeze }, load_location(freeze)) + when 66 then + HashPatternNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }.tap { |nodes| nodes.freeze if freeze }, load_optional_node(constant_pool, encoding, freeze), load_optional_location(freeze), load_optional_location(freeze)) + when 67 then + IfNode.new(source, node_id, location, load_varuint, load_optional_location(freeze), load_node(constant_pool, encoding, freeze), load_optional_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_optional_node(constant_pool, encoding, freeze), load_optional_location(freeze)) + when 68 then + ImaginaryNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze)) + when 69 then + ImplicitNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze)) + when 70 then + ImplicitRestNode.new(source, node_id, location, load_varuint) + when 71 then + InNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze), load_optional_node(constant_pool, encoding, freeze), load_location(freeze), load_optional_location(freeze)) + when 72 then + IndexAndWriteNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), load_optional_location(freeze), load_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_location(freeze), load_node(constant_pool, encoding, freeze)) + when 73 then + IndexOperatorWriteNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), load_optional_location(freeze), load_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_constant(constant_pool, encoding), load_location(freeze), load_node(constant_pool, encoding, freeze)) + when 74 then + IndexOrWriteNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), load_optional_location(freeze), load_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_location(freeze), load_node(constant_pool, encoding, freeze)) + when 75 then + IndexTargetNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze), load_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_location(freeze), load_optional_node(constant_pool, encoding, freeze)) + when 76 then + InstanceVariableAndWriteNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_location(freeze), load_location(freeze), load_node(constant_pool, encoding, freeze)) + when 77 then + InstanceVariableOperatorWriteNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_location(freeze), load_location(freeze), load_node(constant_pool, encoding, freeze), load_constant(constant_pool, encoding)) + when 78 then + InstanceVariableOrWriteNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_location(freeze), load_location(freeze), load_node(constant_pool, encoding, freeze)) + when 79 then + InstanceVariableReadNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding)) + when 80 then + InstanceVariableTargetNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding)) + when 81 then + InstanceVariableWriteNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_location(freeze), load_node(constant_pool, encoding, freeze), load_location(freeze)) + when 82 then + IntegerNode.new(source, node_id, location, load_varuint, load_integer) + when 83 then + InterpolatedMatchLastLineNode.new(source, node_id, location, load_varuint, load_location(freeze), Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }.tap { |nodes| nodes.freeze if freeze }, load_location(freeze)) + when 84 then + InterpolatedRegularExpressionNode.new(source, node_id, location, load_varuint, load_location(freeze), Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }.tap { |nodes| nodes.freeze if freeze }, load_location(freeze)) + when 85 then + InterpolatedStringNode.new(source, node_id, location, load_varuint, load_optional_location(freeze), Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }.tap { |nodes| nodes.freeze if freeze }, load_optional_location(freeze)) + when 86 then + InterpolatedSymbolNode.new(source, node_id, location, load_varuint, load_optional_location(freeze), Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }.tap { |nodes| nodes.freeze if freeze }, load_optional_location(freeze)) + when 87 then + InterpolatedXStringNode.new(source, node_id, location, load_varuint, load_location(freeze), Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }.tap { |nodes| nodes.freeze if freeze }, load_location(freeze)) + when 88 then + ItLocalVariableReadNode.new(source, node_id, location, load_varuint) + when 89 then + ItParametersNode.new(source, node_id, location, load_varuint) + when 90 then + KeywordHashNode.new(source, node_id, location, load_varuint, Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }.tap { |nodes| nodes.freeze if freeze }) + when 91 then + KeywordRestParameterNode.new(source, node_id, location, load_varuint, load_optional_constant(constant_pool, encoding), load_optional_location(freeze), load_location(freeze)) + when 92 then + LambdaNode.new(source, node_id, location, load_varuint, Array.new(load_varuint) { load_constant(constant_pool, encoding) }.tap { |constants| constants.freeze if freeze }, load_location(freeze), load_location(freeze), load_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_optional_node(constant_pool, encoding, freeze)) + when 93 then + LocalVariableAndWriteNode.new(source, node_id, location, load_varuint, load_location(freeze), load_location(freeze), load_node(constant_pool, encoding, freeze), load_constant(constant_pool, encoding), load_varuint) + when 94 then + LocalVariableOperatorWriteNode.new(source, node_id, location, load_varuint, load_location(freeze), load_location(freeze), load_node(constant_pool, encoding, freeze), load_constant(constant_pool, encoding), load_constant(constant_pool, encoding), load_varuint) + when 95 then + LocalVariableOrWriteNode.new(source, node_id, location, load_varuint, load_location(freeze), load_location(freeze), load_node(constant_pool, encoding, freeze), load_constant(constant_pool, encoding), load_varuint) + when 96 then + LocalVariableReadNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_varuint) + when 97 then + LocalVariableTargetNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_varuint) + when 98 then + LocalVariableWriteNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_varuint, load_location(freeze), load_node(constant_pool, encoding, freeze), load_location(freeze)) + when 99 then + MatchLastLineNode.new(source, node_id, location, load_varuint, load_location(freeze), load_location(freeze), load_location(freeze), load_string(encoding)) + when 100 then + MatchPredicateNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze), load_node(constant_pool, encoding, freeze), load_location(freeze)) + when 101 then + MatchRequiredNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze), load_node(constant_pool, encoding, freeze), load_location(freeze)) + when 102 then + MatchWriteNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze), Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }.tap { |nodes| nodes.freeze if freeze }) + when 103 then + MissingNode.new(source, node_id, location, load_varuint) + when 104 then + ModuleNode.new(source, node_id, location, load_varuint, Array.new(load_varuint) { load_constant(constant_pool, encoding) }.tap { |constants| constants.freeze if freeze }, load_location(freeze), load_node(constant_pool, encoding, freeze), load_optional_node(constant_pool, encoding, freeze), load_location(freeze), load_constant(constant_pool, encoding)) + when 105 then + MultiTargetNode.new(source, node_id, location, load_varuint, Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }.tap { |nodes| nodes.freeze if freeze }, load_optional_node(constant_pool, encoding, freeze), Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }.tap { |nodes| nodes.freeze if freeze }, load_optional_location(freeze), load_optional_location(freeze)) + when 106 then + MultiWriteNode.new(source, node_id, location, load_varuint, Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }.tap { |nodes| nodes.freeze if freeze }, load_optional_node(constant_pool, encoding, freeze), Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }.tap { |nodes| nodes.freeze if freeze }, load_optional_location(freeze), load_optional_location(freeze), load_location(freeze), load_node(constant_pool, encoding, freeze)) + when 107 then + NextNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), load_location(freeze)) + when 108 then + NilNode.new(source, node_id, location, load_varuint) + when 109 then + NoKeywordsParameterNode.new(source, node_id, location, load_varuint, load_location(freeze), load_location(freeze)) + when 110 then + NumberedParametersNode.new(source, node_id, location, load_varuint, io.getbyte) + when 111 then + NumberedReferenceReadNode.new(source, node_id, location, load_varuint, load_varuint) + when 112 then + OptionalKeywordParameterNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_location(freeze), load_node(constant_pool, encoding, freeze)) + when 113 then + OptionalParameterNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_location(freeze), load_location(freeze), load_node(constant_pool, encoding, freeze)) + when 114 then + OrNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze), load_node(constant_pool, encoding, freeze), load_location(freeze)) + when 115 then + ParametersNode.new(source, node_id, location, load_varuint, Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }.tap { |nodes| nodes.freeze if freeze }, Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }.tap { |nodes| nodes.freeze if freeze }, load_optional_node(constant_pool, encoding, freeze), Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }.tap { |nodes| nodes.freeze if freeze }, Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }.tap { |nodes| nodes.freeze if freeze }, load_optional_node(constant_pool, encoding, freeze), load_optional_node(constant_pool, encoding, freeze)) + when 116 then + ParenthesesNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), load_location(freeze), load_location(freeze)) + when 117 then + PinnedExpressionNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze), load_location(freeze), load_location(freeze), load_location(freeze)) + when 118 then + PinnedVariableNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze), load_location(freeze)) + when 119 then + PostExecutionNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), load_location(freeze), load_location(freeze), load_location(freeze)) + when 120 then + PreExecutionNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), load_location(freeze), load_location(freeze), load_location(freeze)) + when 121 then + ProgramNode.new(source, node_id, location, load_varuint, Array.new(load_varuint) { load_constant(constant_pool, encoding) }.tap { |constants| constants.freeze if freeze }, load_node(constant_pool, encoding, freeze)) + when 122 then + RangeNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), load_optional_node(constant_pool, encoding, freeze), load_location(freeze)) + when 123 then + RationalNode.new(source, node_id, location, load_varuint, load_integer, load_integer) + when 124 then + RedoNode.new(source, node_id, location, load_varuint) + when 125 then + RegularExpressionNode.new(source, node_id, location, load_varuint, load_location(freeze), load_location(freeze), load_location(freeze), load_string(encoding)) + when 126 then + RequiredKeywordParameterNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_location(freeze)) + when 127 then + RequiredParameterNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding)) + when 128 then + RescueModifierNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze), load_location(freeze), load_node(constant_pool, encoding, freeze)) + when 129 then + RescueNode.new(source, node_id, location, load_varuint, load_location(freeze), Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }.tap { |nodes| nodes.freeze if freeze }, load_optional_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_optional_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_optional_node(constant_pool, encoding, freeze)) + when 130 then + RestParameterNode.new(source, node_id, location, load_varuint, load_optional_constant(constant_pool, encoding), load_optional_location(freeze), load_location(freeze)) + when 131 then + RetryNode.new(source, node_id, location, load_varuint) + when 132 then + ReturnNode.new(source, node_id, location, load_varuint, load_location(freeze), load_optional_node(constant_pool, encoding, freeze)) + when 133 then + SelfNode.new(source, node_id, location, load_varuint) + when 134 then + ShareableConstantNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze)) + when 135 then + SingletonClassNode.new(source, node_id, location, load_varuint, Array.new(load_varuint) { load_constant(constant_pool, encoding) }.tap { |constants| constants.freeze if freeze }, load_location(freeze), load_location(freeze), load_node(constant_pool, encoding, freeze), load_optional_node(constant_pool, encoding, freeze), load_location(freeze)) + when 136 then + SourceEncodingNode.new(source, node_id, location, load_varuint) + when 137 then + SourceFileNode.new(source, node_id, location, load_varuint, load_string(encoding)) + when 138 then + SourceLineNode.new(source, node_id, location, load_varuint) + when 139 then + SplatNode.new(source, node_id, location, load_varuint, load_location(freeze), load_optional_node(constant_pool, encoding, freeze)) + when 140 then + StatementsNode.new(source, node_id, location, load_varuint, Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }.tap { |nodes| nodes.freeze if freeze }) + when 141 then + StringNode.new(source, node_id, location, load_varuint, load_optional_location(freeze), load_location(freeze), load_optional_location(freeze), load_string(encoding)) + when 142 then + SuperNode.new(source, node_id, location, load_varuint, load_location(freeze), load_optional_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_optional_location(freeze), load_optional_node(constant_pool, encoding, freeze)) + when 143 then + SymbolNode.new(source, node_id, location, load_varuint, load_optional_location(freeze), load_optional_location(freeze), load_optional_location(freeze), load_string(encoding)) + when 144 then + TrueNode.new(source, node_id, location, load_varuint) + when 145 then + UndefNode.new(source, node_id, location, load_varuint, Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }.tap { |nodes| nodes.freeze if freeze }, load_location(freeze)) + when 146 then + UnlessNode.new(source, node_id, location, load_varuint, load_location(freeze), load_node(constant_pool, encoding, freeze), load_optional_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_optional_node(constant_pool, encoding, freeze), load_optional_location(freeze)) + when 147 then + UntilNode.new(source, node_id, location, load_varuint, load_location(freeze), load_optional_location(freeze), load_optional_location(freeze), load_node(constant_pool, encoding, freeze), load_optional_node(constant_pool, encoding, freeze)) + when 148 then + WhenNode.new(source, node_id, location, load_varuint, load_location(freeze), Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }.tap { |nodes| nodes.freeze if freeze }, load_optional_location(freeze), load_optional_node(constant_pool, encoding, freeze)) + when 149 then + WhileNode.new(source, node_id, location, load_varuint, load_location(freeze), load_optional_location(freeze), load_optional_location(freeze), load_node(constant_pool, encoding, freeze), load_optional_node(constant_pool, encoding, freeze)) + when 150 then + XStringNode.new(source, node_id, location, load_varuint, load_location(freeze), load_location(freeze), load_location(freeze), load_string(encoding)) + when 151 then + YieldNode.new(source, node_id, location, load_varuint, load_location(freeze), load_optional_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_optional_location(freeze)) + end + + value.freeze if freeze + value + end + else + def load_node(constant_pool, encoding, freeze) + @load_node_lambdas[io.getbyte].call(constant_pool, encoding, freeze) + end + + def define_load_node_lambdas + @load_node_lambdas = [ + nil, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = AliasGlobalVariableNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze), load_node(constant_pool, encoding, freeze), load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = AliasMethodNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze), load_node(constant_pool, encoding, freeze), load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = AlternationPatternNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze), load_node(constant_pool, encoding, freeze), load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = AndNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze), load_node(constant_pool, encoding, freeze), load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = ArgumentsNode.new(source, node_id, location, load_varuint, Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = ArrayNode.new(source, node_id, location, load_varuint, Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }, load_optional_location(freeze), load_optional_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = ArrayPatternNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }, load_optional_node(constant_pool, encoding, freeze), Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }, load_optional_location(freeze), load_optional_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = AssocNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze), load_node(constant_pool, encoding, freeze), load_optional_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = AssocSplatNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = BackReferenceReadNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = BeginNode.new(source, node_id, location, load_varuint, load_optional_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_optional_node(constant_pool, encoding, freeze), load_optional_node(constant_pool, encoding, freeze), load_optional_node(constant_pool, encoding, freeze), load_optional_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = BlockArgumentNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = BlockLocalVariableNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = BlockNode.new(source, node_id, location, load_varuint, Array.new(load_varuint) { load_constant(constant_pool, encoding) }, load_optional_node(constant_pool, encoding, freeze), load_optional_node(constant_pool, encoding, freeze), load_location(freeze), load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = BlockParameterNode.new(source, node_id, location, load_varuint, load_optional_constant(constant_pool, encoding), load_optional_location(freeze), load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = BlockParametersNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }, load_optional_location(freeze), load_optional_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = BreakNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = CallAndWriteNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), load_optional_location(freeze), load_optional_location(freeze), load_constant(constant_pool, encoding), load_constant(constant_pool, encoding), load_location(freeze), load_node(constant_pool, encoding, freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = CallNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), load_optional_location(freeze), load_constant(constant_pool, encoding), load_optional_location(freeze), load_optional_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_optional_location(freeze), load_optional_location(freeze), load_optional_node(constant_pool, encoding, freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = CallOperatorWriteNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), load_optional_location(freeze), load_optional_location(freeze), load_constant(constant_pool, encoding), load_constant(constant_pool, encoding), load_constant(constant_pool, encoding), load_location(freeze), load_node(constant_pool, encoding, freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = CallOrWriteNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), load_optional_location(freeze), load_optional_location(freeze), load_constant(constant_pool, encoding), load_constant(constant_pool, encoding), load_location(freeze), load_node(constant_pool, encoding, freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = CallTargetNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze), load_location(freeze), load_constant(constant_pool, encoding), load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = CapturePatternNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze), load_node(constant_pool, encoding, freeze), load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = CaseMatchNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }, load_optional_node(constant_pool, encoding, freeze), load_location(freeze), load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = CaseNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }, load_optional_node(constant_pool, encoding, freeze), load_location(freeze), load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = ClassNode.new(source, node_id, location, load_varuint, Array.new(load_varuint) { load_constant(constant_pool, encoding) }, load_location(freeze), load_node(constant_pool, encoding, freeze), load_optional_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_optional_node(constant_pool, encoding, freeze), load_location(freeze), load_constant(constant_pool, encoding)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = ClassVariableAndWriteNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_location(freeze), load_location(freeze), load_node(constant_pool, encoding, freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = ClassVariableOperatorWriteNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_location(freeze), load_location(freeze), load_node(constant_pool, encoding, freeze), load_constant(constant_pool, encoding)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = ClassVariableOrWriteNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_location(freeze), load_location(freeze), load_node(constant_pool, encoding, freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = ClassVariableReadNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = ClassVariableTargetNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = ClassVariableWriteNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_location(freeze), load_node(constant_pool, encoding, freeze), load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = ConstantAndWriteNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_location(freeze), load_location(freeze), load_node(constant_pool, encoding, freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = ConstantOperatorWriteNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_location(freeze), load_location(freeze), load_node(constant_pool, encoding, freeze), load_constant(constant_pool, encoding)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = ConstantOrWriteNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_location(freeze), load_location(freeze), load_node(constant_pool, encoding, freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = ConstantPathAndWriteNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze), load_location(freeze), load_node(constant_pool, encoding, freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = ConstantPathNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), load_optional_constant(constant_pool, encoding), load_location(freeze), load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = ConstantPathOperatorWriteNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze), load_location(freeze), load_node(constant_pool, encoding, freeze), load_constant(constant_pool, encoding)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = ConstantPathOrWriteNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze), load_location(freeze), load_node(constant_pool, encoding, freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = ConstantPathTargetNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), load_optional_constant(constant_pool, encoding), load_location(freeze), load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = ConstantPathWriteNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze), load_location(freeze), load_node(constant_pool, encoding, freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = ConstantReadNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = ConstantTargetNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = ConstantWriteNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_location(freeze), load_node(constant_pool, encoding, freeze), load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + load_uint32 + value = DefNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_optional_node(constant_pool, encoding, freeze), load_optional_node(constant_pool, encoding, freeze), Array.new(load_varuint) { load_constant(constant_pool, encoding) }, load_location(freeze), load_optional_location(freeze), load_optional_location(freeze), load_optional_location(freeze), load_optional_location(freeze), load_optional_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = DefinedNode.new(source, node_id, location, load_varuint, load_optional_location(freeze), load_node(constant_pool, encoding, freeze), load_optional_location(freeze), load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = ElseNode.new(source, node_id, location, load_varuint, load_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_optional_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = EmbeddedStatementsNode.new(source, node_id, location, load_varuint, load_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = EmbeddedVariableNode.new(source, node_id, location, load_varuint, load_location(freeze), load_node(constant_pool, encoding, freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = EnsureNode.new(source, node_id, location, load_varuint, load_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = FalseNode.new(source, node_id, location, load_varuint) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = FindPatternNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), load_node(constant_pool, encoding, freeze), Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }, load_node(constant_pool, encoding, freeze), load_optional_location(freeze), load_optional_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = FlipFlopNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), load_optional_node(constant_pool, encoding, freeze), load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = FloatNode.new(source, node_id, location, load_varuint, load_double) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = ForNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze), load_node(constant_pool, encoding, freeze), load_optional_node(constant_pool, encoding, freeze), load_location(freeze), load_location(freeze), load_optional_location(freeze), load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = ForwardingArgumentsNode.new(source, node_id, location, load_varuint) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = ForwardingParameterNode.new(source, node_id, location, load_varuint) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = ForwardingSuperNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = GlobalVariableAndWriteNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_location(freeze), load_location(freeze), load_node(constant_pool, encoding, freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = GlobalVariableOperatorWriteNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_location(freeze), load_location(freeze), load_node(constant_pool, encoding, freeze), load_constant(constant_pool, encoding)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = GlobalVariableOrWriteNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_location(freeze), load_location(freeze), load_node(constant_pool, encoding, freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = GlobalVariableReadNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = GlobalVariableTargetNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = GlobalVariableWriteNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_location(freeze), load_node(constant_pool, encoding, freeze), load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = HashNode.new(source, node_id, location, load_varuint, load_location(freeze), Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }, load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = HashPatternNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }, load_optional_node(constant_pool, encoding, freeze), load_optional_location(freeze), load_optional_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = IfNode.new(source, node_id, location, load_varuint, load_optional_location(freeze), load_node(constant_pool, encoding, freeze), load_optional_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_optional_node(constant_pool, encoding, freeze), load_optional_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = ImaginaryNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = ImplicitNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = ImplicitRestNode.new(source, node_id, location, load_varuint) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = InNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze), load_optional_node(constant_pool, encoding, freeze), load_location(freeze), load_optional_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = IndexAndWriteNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), load_optional_location(freeze), load_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_location(freeze), load_node(constant_pool, encoding, freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = IndexOperatorWriteNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), load_optional_location(freeze), load_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_constant(constant_pool, encoding), load_location(freeze), load_node(constant_pool, encoding, freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = IndexOrWriteNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), load_optional_location(freeze), load_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_location(freeze), load_node(constant_pool, encoding, freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = IndexTargetNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze), load_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_location(freeze), load_optional_node(constant_pool, encoding, freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = InstanceVariableAndWriteNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_location(freeze), load_location(freeze), load_node(constant_pool, encoding, freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = InstanceVariableOperatorWriteNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_location(freeze), load_location(freeze), load_node(constant_pool, encoding, freeze), load_constant(constant_pool, encoding)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = InstanceVariableOrWriteNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_location(freeze), load_location(freeze), load_node(constant_pool, encoding, freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = InstanceVariableReadNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = InstanceVariableTargetNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = InstanceVariableWriteNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_location(freeze), load_node(constant_pool, encoding, freeze), load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = IntegerNode.new(source, node_id, location, load_varuint, load_integer) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = InterpolatedMatchLastLineNode.new(source, node_id, location, load_varuint, load_location(freeze), Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }, load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = InterpolatedRegularExpressionNode.new(source, node_id, location, load_varuint, load_location(freeze), Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }, load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = InterpolatedStringNode.new(source, node_id, location, load_varuint, load_optional_location(freeze), Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }, load_optional_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = InterpolatedSymbolNode.new(source, node_id, location, load_varuint, load_optional_location(freeze), Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }, load_optional_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = InterpolatedXStringNode.new(source, node_id, location, load_varuint, load_location(freeze), Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }, load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = ItLocalVariableReadNode.new(source, node_id, location, load_varuint) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = ItParametersNode.new(source, node_id, location, load_varuint) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = KeywordHashNode.new(source, node_id, location, load_varuint, Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = KeywordRestParameterNode.new(source, node_id, location, load_varuint, load_optional_constant(constant_pool, encoding), load_optional_location(freeze), load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = LambdaNode.new(source, node_id, location, load_varuint, Array.new(load_varuint) { load_constant(constant_pool, encoding) }, load_location(freeze), load_location(freeze), load_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_optional_node(constant_pool, encoding, freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = LocalVariableAndWriteNode.new(source, node_id, location, load_varuint, load_location(freeze), load_location(freeze), load_node(constant_pool, encoding, freeze), load_constant(constant_pool, encoding), load_varuint) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = LocalVariableOperatorWriteNode.new(source, node_id, location, load_varuint, load_location(freeze), load_location(freeze), load_node(constant_pool, encoding, freeze), load_constant(constant_pool, encoding), load_constant(constant_pool, encoding), load_varuint) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = LocalVariableOrWriteNode.new(source, node_id, location, load_varuint, load_location(freeze), load_location(freeze), load_node(constant_pool, encoding, freeze), load_constant(constant_pool, encoding), load_varuint) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = LocalVariableReadNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_varuint) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = LocalVariableTargetNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_varuint) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = LocalVariableWriteNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_varuint, load_location(freeze), load_node(constant_pool, encoding, freeze), load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = MatchLastLineNode.new(source, node_id, location, load_varuint, load_location(freeze), load_location(freeze), load_location(freeze), load_string(encoding)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = MatchPredicateNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze), load_node(constant_pool, encoding, freeze), load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = MatchRequiredNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze), load_node(constant_pool, encoding, freeze), load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = MatchWriteNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze), Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = MissingNode.new(source, node_id, location, load_varuint) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = ModuleNode.new(source, node_id, location, load_varuint, Array.new(load_varuint) { load_constant(constant_pool, encoding) }, load_location(freeze), load_node(constant_pool, encoding, freeze), load_optional_node(constant_pool, encoding, freeze), load_location(freeze), load_constant(constant_pool, encoding)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = MultiTargetNode.new(source, node_id, location, load_varuint, Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }, load_optional_node(constant_pool, encoding, freeze), Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }, load_optional_location(freeze), load_optional_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = MultiWriteNode.new(source, node_id, location, load_varuint, Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }, load_optional_node(constant_pool, encoding, freeze), Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }, load_optional_location(freeze), load_optional_location(freeze), load_location(freeze), load_node(constant_pool, encoding, freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = NextNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = NilNode.new(source, node_id, location, load_varuint) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = NoKeywordsParameterNode.new(source, node_id, location, load_varuint, load_location(freeze), load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = NumberedParametersNode.new(source, node_id, location, load_varuint, io.getbyte) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = NumberedReferenceReadNode.new(source, node_id, location, load_varuint, load_varuint) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = OptionalKeywordParameterNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_location(freeze), load_node(constant_pool, encoding, freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = OptionalParameterNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_location(freeze), load_location(freeze), load_node(constant_pool, encoding, freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = OrNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze), load_node(constant_pool, encoding, freeze), load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = ParametersNode.new(source, node_id, location, load_varuint, Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }, Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }, load_optional_node(constant_pool, encoding, freeze), Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }, Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }, load_optional_node(constant_pool, encoding, freeze), load_optional_node(constant_pool, encoding, freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = ParenthesesNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), load_location(freeze), load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = PinnedExpressionNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze), load_location(freeze), load_location(freeze), load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = PinnedVariableNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze), load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = PostExecutionNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), load_location(freeze), load_location(freeze), load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = PreExecutionNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), load_location(freeze), load_location(freeze), load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = ProgramNode.new(source, node_id, location, load_varuint, Array.new(load_varuint) { load_constant(constant_pool, encoding) }, load_node(constant_pool, encoding, freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = RangeNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), load_optional_node(constant_pool, encoding, freeze), load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = RationalNode.new(source, node_id, location, load_varuint, load_integer, load_integer) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = RedoNode.new(source, node_id, location, load_varuint) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = RegularExpressionNode.new(source, node_id, location, load_varuint, load_location(freeze), load_location(freeze), load_location(freeze), load_string(encoding)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = RequiredKeywordParameterNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding), load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = RequiredParameterNode.new(source, node_id, location, load_varuint, load_constant(constant_pool, encoding)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = RescueModifierNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze), load_location(freeze), load_node(constant_pool, encoding, freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = RescueNode.new(source, node_id, location, load_varuint, load_location(freeze), Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }, load_optional_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_optional_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_optional_node(constant_pool, encoding, freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = RestParameterNode.new(source, node_id, location, load_varuint, load_optional_constant(constant_pool, encoding), load_optional_location(freeze), load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = RetryNode.new(source, node_id, location, load_varuint) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = ReturnNode.new(source, node_id, location, load_varuint, load_location(freeze), load_optional_node(constant_pool, encoding, freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = SelfNode.new(source, node_id, location, load_varuint) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = ShareableConstantNode.new(source, node_id, location, load_varuint, load_node(constant_pool, encoding, freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = SingletonClassNode.new(source, node_id, location, load_varuint, Array.new(load_varuint) { load_constant(constant_pool, encoding) }, load_location(freeze), load_location(freeze), load_node(constant_pool, encoding, freeze), load_optional_node(constant_pool, encoding, freeze), load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = SourceEncodingNode.new(source, node_id, location, load_varuint) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = SourceFileNode.new(source, node_id, location, load_varuint, load_string(encoding)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = SourceLineNode.new(source, node_id, location, load_varuint) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = SplatNode.new(source, node_id, location, load_varuint, load_location(freeze), load_optional_node(constant_pool, encoding, freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = StatementsNode.new(source, node_id, location, load_varuint, Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = StringNode.new(source, node_id, location, load_varuint, load_optional_location(freeze), load_location(freeze), load_optional_location(freeze), load_string(encoding)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = SuperNode.new(source, node_id, location, load_varuint, load_location(freeze), load_optional_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_optional_location(freeze), load_optional_node(constant_pool, encoding, freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = SymbolNode.new(source, node_id, location, load_varuint, load_optional_location(freeze), load_optional_location(freeze), load_optional_location(freeze), load_string(encoding)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = TrueNode.new(source, node_id, location, load_varuint) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = UndefNode.new(source, node_id, location, load_varuint, Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }, load_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = UnlessNode.new(source, node_id, location, load_varuint, load_location(freeze), load_node(constant_pool, encoding, freeze), load_optional_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_optional_node(constant_pool, encoding, freeze), load_optional_location(freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = UntilNode.new(source, node_id, location, load_varuint, load_location(freeze), load_optional_location(freeze), load_optional_location(freeze), load_node(constant_pool, encoding, freeze), load_optional_node(constant_pool, encoding, freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = WhenNode.new(source, node_id, location, load_varuint, load_location(freeze), Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }, load_optional_location(freeze), load_optional_node(constant_pool, encoding, freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = WhileNode.new(source, node_id, location, load_varuint, load_location(freeze), load_optional_location(freeze), load_optional_location(freeze), load_node(constant_pool, encoding, freeze), load_optional_node(constant_pool, encoding, freeze)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = XStringNode.new(source, node_id, location, load_varuint, load_location(freeze), load_location(freeze), load_location(freeze), load_string(encoding)) + value.freeze if freeze + value + }, + -> (constant_pool, encoding, freeze) { + node_id = load_varuint + location = load_location(freeze) + value = YieldNode.new(source, node_id, location, load_varuint, load_location(freeze), load_optional_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_optional_location(freeze)) + value.freeze if freeze + value + }, + ] + end + end + end + + # The token types that can be indexed by their enum values. + TOKEN_TYPES = [ + nil, + :EOF, + :BRACE_RIGHT, + :COMMA, + :EMBEXPR_END, + :KEYWORD_DO, + :KEYWORD_ELSE, + :KEYWORD_ELSIF, + :KEYWORD_END, + :KEYWORD_ENSURE, + :KEYWORD_IN, + :KEYWORD_RESCUE, + :KEYWORD_THEN, + :KEYWORD_WHEN, + :NEWLINE, + :PARENTHESIS_RIGHT, + :PIPE, + :SEMICOLON, + :AMPERSAND, + :AMPERSAND_AMPERSAND, + :AMPERSAND_AMPERSAND_EQUAL, + :AMPERSAND_DOT, + :AMPERSAND_EQUAL, + :BACKTICK, + :BACK_REFERENCE, + :BANG, + :BANG_EQUAL, + :BANG_TILDE, + :BRACE_LEFT, + :BRACKET_LEFT, + :BRACKET_LEFT_ARRAY, + :BRACKET_LEFT_RIGHT, + :BRACKET_LEFT_RIGHT_EQUAL, + :BRACKET_RIGHT, + :CARET, + :CARET_EQUAL, + :CHARACTER_LITERAL, + :CLASS_VARIABLE, + :COLON, + :COLON_COLON, + :COMMENT, + :CONSTANT, + :DOT, + :DOT_DOT, + :DOT_DOT_DOT, + :EMBDOC_BEGIN, + :EMBDOC_END, + :EMBDOC_LINE, + :EMBEXPR_BEGIN, + :EMBVAR, + :EQUAL, + :EQUAL_EQUAL, + :EQUAL_EQUAL_EQUAL, + :EQUAL_GREATER, + :EQUAL_TILDE, + :FLOAT, + :FLOAT_IMAGINARY, + :FLOAT_RATIONAL, + :FLOAT_RATIONAL_IMAGINARY, + :GLOBAL_VARIABLE, + :GREATER, + :GREATER_EQUAL, + :GREATER_GREATER, + :GREATER_GREATER_EQUAL, + :HEREDOC_END, + :HEREDOC_START, + :IDENTIFIER, + :IGNORED_NEWLINE, + :INSTANCE_VARIABLE, + :INTEGER, + :INTEGER_IMAGINARY, + :INTEGER_RATIONAL, + :INTEGER_RATIONAL_IMAGINARY, + :KEYWORD_ALIAS, + :KEYWORD_AND, + :KEYWORD_BEGIN, + :KEYWORD_BEGIN_UPCASE, + :KEYWORD_BREAK, + :KEYWORD_CASE, + :KEYWORD_CLASS, + :KEYWORD_DEF, + :KEYWORD_DEFINED, + :KEYWORD_DO_LOOP, + :KEYWORD_END_UPCASE, + :KEYWORD_FALSE, + :KEYWORD_FOR, + :KEYWORD_IF, + :KEYWORD_IF_MODIFIER, + :KEYWORD_MODULE, + :KEYWORD_NEXT, + :KEYWORD_NIL, + :KEYWORD_NOT, + :KEYWORD_OR, + :KEYWORD_REDO, + :KEYWORD_RESCUE_MODIFIER, + :KEYWORD_RETRY, + :KEYWORD_RETURN, + :KEYWORD_SELF, + :KEYWORD_SUPER, + :KEYWORD_TRUE, + :KEYWORD_UNDEF, + :KEYWORD_UNLESS, + :KEYWORD_UNLESS_MODIFIER, + :KEYWORD_UNTIL, + :KEYWORD_UNTIL_MODIFIER, + :KEYWORD_WHILE, + :KEYWORD_WHILE_MODIFIER, + :KEYWORD_YIELD, + :KEYWORD___ENCODING__, + :KEYWORD___FILE__, + :KEYWORD___LINE__, + :LABEL, + :LABEL_END, + :LAMBDA_BEGIN, + :LESS, + :LESS_EQUAL, + :LESS_EQUAL_GREATER, + :LESS_LESS, + :LESS_LESS_EQUAL, + :METHOD_NAME, + :MINUS, + :MINUS_EQUAL, + :MINUS_GREATER, + :NUMBERED_REFERENCE, + :PARENTHESIS_LEFT, + :PARENTHESIS_LEFT_PARENTHESES, + :PERCENT, + :PERCENT_EQUAL, + :PERCENT_LOWER_I, + :PERCENT_LOWER_W, + :PERCENT_LOWER_X, + :PERCENT_UPPER_I, + :PERCENT_UPPER_W, + :PIPE_EQUAL, + :PIPE_PIPE, + :PIPE_PIPE_EQUAL, + :PLUS, + :PLUS_EQUAL, + :QUESTION_MARK, + :REGEXP_BEGIN, + :REGEXP_END, + :SLASH, + :SLASH_EQUAL, + :STAR, + :STAR_EQUAL, + :STAR_STAR, + :STAR_STAR_EQUAL, + :STRING_BEGIN, + :STRING_CONTENT, + :STRING_END, + :SYMBOL_BEGIN, + :TILDE, + :UAMPERSAND, + :UCOLON_COLON, + :UDOT_DOT, + :UDOT_DOT_DOT, + :UMINUS, + :UMINUS_NUM, + :UPLUS, + :USTAR, + :USTAR_STAR, + :WORDS_SEP, + :__END__, + :MISSING, + :NOT_PROVIDED, + ].freeze + + private_constant :MAJOR_VERSION, :MINOR_VERSION, :PATCH_VERSION + private_constant :ConstantPool, :FastStringIO, :Loader, :TOKEN_TYPES + end + + private_constant :Serialize +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/string_query.rb b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/string_query.rb new file mode 100644 index 0000000..547f58d --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/string_query.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true +# :markup: markdown + +module Prism + # Query methods that allow categorizing strings based on their context for + # where they could be valid in a Ruby syntax tree. + class StringQuery + # The string that this query is wrapping. + attr_reader :string + + # Initialize a new query with the given string. + def initialize(string) + @string = string + end + + # Whether or not this string is a valid local variable name. + def local? + StringQuery.local?(string) + end + + # Whether or not this string is a valid constant name. + def constant? + StringQuery.constant?(string) + end + + # Whether or not this string is a valid method name. + def method_name? + StringQuery.method_name?(string) + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/translation.rb b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/translation.rb new file mode 100644 index 0000000..57b5713 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/translation.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true +# :markup: markdown + +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 :ParserCurrent, "prism/translation/parser_current" + autoload :Parser33, "prism/translation/parser_versions" + autoload :Parser34, "prism/translation/parser_versions" + autoload :Parser35, "prism/translation/parser_versions" + autoload :Parser40, "prism/translation/parser_versions" + autoload :Parser41, "prism/translation/parser_versions" + autoload :Ripper, "prism/translation/ripper" + autoload :RubyParser, "prism/translation/ruby_parser" + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/translation/parser.rb b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/translation/parser.rb new file mode 100644 index 0000000..fed4ac4 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/translation/parser.rb @@ -0,0 +1,376 @@ +# frozen_string_literal: true +# :markup: markdown + +begin + required_version = ">= 3.3.7.2" + gem "parser", required_version + require "parser" +rescue LoadError + warn(<<~MSG) + Error: Unable to load parser #{required_version}. \ + Add `gem "parser"` to your Gemfile or run `bundle update parser`. + MSG + exit(1) +end + +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. + # + # Note that this version of the parser always parses using the latest + # version of Ruby syntax supported by Prism. If you want specific version + # support, use one of the version-specific subclasses, such as + # `Prism::Translation::Parser34`. If you want to parse using the same + # version of Ruby syntax as the currently running version of Ruby, use + # `Prism::Translation::ParserCurrent`. + 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: + + # The `builder` argument is used to create the parser using our custom builder class by default. + # + # By using the `:parser` keyword argument, you can translate in a way that is compatible with + # the Parser gem using any parser. + # + # For example, in RuboCop for Ruby LSP, the following approach can be used to improve performance + # by reusing a pre-parsed `Prism::ParseLexResult`: + # + # class PrismPreparsed + # def initialize(prism_result) + # @prism_result = prism_result + # end + # + # def parse_lex(source, **options) + # @prism_result + # end + # end + # + # prism_preparsed = PrismPreparsed.new(prism_result) + # + # Prism::Translation::Ruby34.new(builder, parser: prism_preparsed) + # + # In an object passed to the `:parser` keyword argument, the `parse` and `parse_lex` methods + # should be implemented as needed. + # + def initialize(builder = Prism::Translation::Parser::Builder.new, parser: Prism) + if !builder.is_a?(Prism::Translation::Parser::Builder) + warn(<<~MSG, uplevel: 1, category: :deprecated) + [deprecation]: The builder passed to `Prism::Translation::Parser.new` is not a \ + `Prism::Translation::Parser::Builder` subclass. This will raise in the next major version. + MSG + end + @parser = parser + + super(builder) + end + + def version # :nodoc: + 41 + 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(@parser.parse(source, **prism_options), 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(@parser.parse(source, **prism_options), 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(@parser.parse_lex(source, **prism_options), 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 + 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 + 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 + 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 + + # Options for how prism should parse/lex the source. + def prism_options + options = { + filepath: @source_buffer.name, + version: convert_for_prism(version), + partial_script: true, + } + # The parser gem always encodes to UTF-8, unless it is binary. + # https://github.com/whitequark/parser/blob/v3.3.6.0/lib/parser/source/buffer.rb#L80-L107 + options[:encoding] = false if @source_buffer.source.encoding != Encoding::BINARY + + options + 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.1" + when 34 + "3.4.0" + when 35, 40 + "4.0.0" + when 41 + "4.1.0" + else + "latest" + end + end + + require_relative "parser/builder" + require_relative "parser/compiler" + require_relative "parser/lexer" + + private_constant :Compiler + private_constant :Lexer + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/translation/parser/builder.rb b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/translation/parser/builder.rb new file mode 100644 index 0000000..6b620c2 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/translation/parser/builder.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true +# :markup: markdown + +module Prism + module Translation + class Parser + # A builder that knows how to convert more modern Ruby syntax + # into whitequark/parser gem's syntax tree. + class Builder < ::Parser::Builders::Default + # It represents the `it` block argument, which is not yet implemented in the Parser gem. + def itarg + n(:itarg, [:it], nil) + end + + # The following three lines have been added to support the `it` block parameter syntax in the source code below. + # + # if args.type == :itarg + # block_type = :itblock + # args = :it + # + # https://github.com/whitequark/parser/blob/v3.3.7.1/lib/parser/builders/default.rb#L1122-L1155 + def block(method_call, begin_t, args, body, end_t) + _receiver, _selector, *call_args = *method_call + + if method_call.type == :yield + diagnostic :error, :block_given_to_yield, nil, method_call.loc.keyword, [loc(begin_t)] + end + + last_arg = call_args.last + if last_arg && (last_arg.type == :block_pass || last_arg.type == :forwarded_args) + diagnostic :error, :block_and_blockarg, nil, last_arg.loc.expression, [loc(begin_t)] + end + + if args.type == :itarg + block_type = :itblock + args = :it + elsif args.type == :numargs + block_type = :numblock + args = args.children[0] + else + block_type = :block + end + + if [:send, :csend, :index, :super, :zsuper, :lambda].include?(method_call.type) + n(block_type, [ method_call, args, body ], + block_map(method_call.loc.expression, begin_t, end_t)) + else + # Code like "return foo 1 do end" is reduced in a weird sequence. + # Here, method_call is actually (return). + actual_send, = *method_call + block = + n(block_type, [ actual_send, args, body ], + block_map(actual_send.loc.expression, begin_t, end_t)) + + n(method_call.type, [ block ], + method_call.loc.with_expression(join_exprs(method_call, block))) + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/translation/parser/compiler.rb b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/translation/parser/compiler.rb new file mode 100644 index 0000000..8805614 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/translation/parser/compiler.rb @@ -0,0 +1,2234 @@ +# frozen_string_literal: true +# :markup: markdown + +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) + if node.opening&.start_with?("%w", "%W", "%i", "%I") + elements = node.elements.flat_map do |element| + if element.is_a?(StringNode) + if element.content.include?("\n") + string_nodes_from_line_continuations(element.unescaped, element.content, element.content_loc.start_offset, node.opening) + else + [builder.string_internal([element.unescaped, srange(element.content_loc)])] + end + elsif element.is_a?(InterpolatedStringNode) + builder.string_compose( + token(element.opening_loc), + string_nodes_from_interpolation(element, node.opening), + token(element.closing_loc) + ) + else + [visit(element)] + end + end + else + elements = visit_all(node.elements) + end + + builder.array(token(node.opening_loc), 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 + if visited.empty? + builder.const_pattern(visit(node.constant), token(node.opening_loc), builder.array_pattern(token(node.opening_loc), visited, token(node.closing_loc)), token(node.closing_loc)) + else + builder.const_pattern(visit(node.constant), token(node.opening_loc), builder.array_pattern(nil, visited, nil), token(node.closing_loc)) + end + 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) + key = node.key + + if node.value.is_a?(ImplicitNode) + if in_pattern + if key.is_a?(SymbolNode) + if key.opening.nil? + builder.match_hash_var([key.unescaped, srange(key.location)]) + else + builder.match_hash_var_from_str(token(key.opening_loc), [builder.string_internal([key.unescaped, srange(key.value_loc)])], token(key.closing_loc)) + end + else + builder.match_hash_var_from_str(token(key.opening_loc), visit_all(key.parts), token(key.closing_loc)) + end + else + value = node.value.value + + implicit_value = if value.is_a?(CallNode) + builder.call_method(nil, nil, [value.name, srange(value.message_loc)]) + elsif value.is_a?(ConstantReadNode) + builder.const([value.name, srange(key.value_loc)]) + else + builder.ident([value.name, srange(key.value_loc)]).updated(:lvar) + end + + builder.pair_keyword([key.unescaped, srange(key)], implicit_value) + end + elsif node.operator_loc + builder.pair(visit(key), token(node.operator_loc), visit(node.value)) + elsif key.is_a?(SymbolNode) && key.opening_loc.nil? + builder.pair_keyword([key.unescaped, srange(key.location)], visit(node.value)) + else + parts = + if key.is_a?(SymbolNode) + [builder.string_internal([key.unescaped, srange(key.value_loc)])] + else + visit_all(key.parts) + end + + builder.pair_quoted(token(key.opening_loc), parts, token(key.closing_loc), visit(node.value)) + end + end + + # def foo(**); bar(**); end + # ^^ + # + # { **foo } + # ^^^^^ + def visit_assoc_splat_node(node) + if in_pattern + builder.match_rest(token(node.operator_loc), token(node.value&.location)) + elsif 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.subsequent&.location&.start_offset || + node.else_clause&.location&.start_offset || + node.ensure_clause&.location&.start_offset || + node.end_keyword_loc&.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_semicolon(find_start_offset, find_end_offset), + visit(rescue_clause.statements) + ) + end until (rescue_clause = rescue_clause.subsequent).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), + ), + token(node.equal_loc), + 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)), + token(node.equal_loc), + 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.binary_operator_loc.slice.chomp("="), srange(node.binary_operator_loc)], + visit(node.value) + ) + end + + # foo.bar &&= baz + # ^^^^^^^^^^^^^^^ + def visit_call_and_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 + # ^^^^^^^^^^^^^^^ + def visit_call_or_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, = 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.else_clause&.else_keyword_loc), + visit(node.else_clause), + 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.else_clause&.else_keyword_loc), + visit(node.else_clause), + 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.binary_operator_loc.slice.chomp("="), srange(node.binary_operator_loc)], + visit(node.value) + ) + end + + # @@foo &&= bar + # ^^^^^^^^^^^^^ + def visit_class_variable_and_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 + # ^^^^^^^^^^^^^ + def visit_class_variable_or_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 + # ^^^^^ + 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.binary_operator_loc.slice.chomp("="), srange(node.binary_operator_loc)], + visit(node.value) + ) + end + + # Foo &&= bar + # ^^^^^^^^^^^^ + def visit_constant_and_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 + # ^^^^^^^^^^^^ + def visit_constant_or_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 + # ^^^ + 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.name, srange(node.name_loc)] + ) + else + builder.const_fetch( + visit(node.parent), + token(node.delimiter_loc), + [node.name, srange(node.name_loc)] + ) + 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.binary_operator_loc.slice.chomp("="), srange(node.binary_operator_loc)], + visit(node.value) + ) + end + + # Foo::Bar &&= baz + # ^^^^^^^^^^^^^^^^ + def visit_constant_path_and_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 + # ^^^^^^^^^^^^^^^^ + def visit_constant_path_or_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 + # ^^^^^^^^ + 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) + # Very weird circumstances here where something like: + # + # defined? + # (1) + # + # gets parsed in Ruby as having only the `1` expression but in parser + # it gets parsed as having a begin. In this case we need to synthesize + # that begin to match parser's behavior. + if node.lparen_loc && node.keyword_loc.join(node.lparen_loc).slice.include?("\n") + builder.keyword_cmd( + :defined?, + token(node.keyword_loc), + nil, + [ + builder.begin( + token(node.lparen_loc), + visit(node.value), + token(node.rparen_loc) + ) + ], + nil + ) + else + builder.keyword_cmd( + :defined?, + token(node.keyword_loc), + token(node.lparen_loc), + [visit(node.value)], + token(node.rparen_loc) + ) + end + 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 (do_keyword_loc = node.do_keyword_loc) + token(do_keyword_loc) + else + srange_semicolon(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.binary_operator_loc.slice.chomp("="), srange(node.binary_operator_loc)], + visit(node.value) + ) + end + + # $foo &&= bar + # ^^^^^^^^^^^^ + def visit_global_variable_and_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 + # ^^^^^^^^^^^^ + def visit_global_variable_or_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 + # ^^^^ + 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.subsequent.else_keyword_loc), + visit(node.subsequent) + ) + elsif node.if_keyword_loc.start_offset == node.location.start_offset + builder.condition( + token(node.if_keyword_loc), + visit(node.predicate), + if (then_keyword_loc = node.then_keyword_loc) + token(then_keyword_loc) + else + srange_semicolon(node.predicate.location.end_offset, (node.statements&.location || node.subsequent&.location || node.end_keyword_loc).start_offset) + end, + visit(node.statements), + case node.subsequent + when IfNode + token(node.subsequent.if_keyword_loc) + when ElseNode + token(node.subsequent.else_keyword_loc) + end, + visit(node.subsequent), + if node.if_keyword != "elsif" + token(node.end_keyword_loc) + end + ) + else + builder.condition_mod( + visit(node.statements), + visit(node.subsequent), + token(node.if_keyword_loc), + visit(node.predicate) + ) + end + end + + # 1i + # ^^ + def visit_imaginary_node(node) + visit_numeric(node, builder.complex([Complex(0, node.numeric.value), 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, + if (then_loc = node.then_loc) + token(then_loc) + else + srange_semicolon(node.pattern.location.end_offset, node.statements&.location&.start_offset) + end, + 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.binary_operator_loc.slice.chomp("="), srange(node.binary_operator_loc)], + visit(node.value) + ) + end + + # foo[bar] &&= baz + # ^^^^^^^^^^^^^^^^ + def visit_index_and_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 + # ^^^^^^^^^^^^^^^^ + def visit_index_or_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], = 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.binary_operator_loc.slice.chomp("="), srange(node.binary_operator_loc)], + visit(node.value) + ) + end + + # @foo &&= bar + # ^^^^^^^^^^^^ + def visit_instance_variable_and_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 + # ^^^^^^^^^^^^ + def visit_instance_variable_or_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 + # ^^^^ + 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), + string_nodes_from_interpolation(node, node.opening), + [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? + return visit_heredoc(node) { |children, closing| builder.string_compose(token(node.opening_loc), children, closing) } + end + + builder.string_compose( + token(node.opening_loc), + string_nodes_from_interpolation(node, node.opening), + token(node.closing_loc) + ) + end + + # :"foo #{bar}" + # ^^^^^^^^^^^^^ + def visit_interpolated_symbol_node(node) + builder.symbol_compose( + token(node.opening_loc), + string_nodes_from_interpolation(node, node.opening), + token(node.closing_loc) + ) + end + + # `foo #{bar}` + # ^^^^^^^^^^^^ + def visit_interpolated_x_string_node(node) + if node.heredoc? + return visit_heredoc(node) { |children, closing| builder.xstring_compose(token(node.opening_loc), children, closing) } + end + + builder.xstring_compose( + token(node.opening_loc), + string_nodes_from_interpolation(node, node.opening), + token(node.closing_loc) + ) + end + + # -> { it } + # ^^ + def visit_it_local_variable_read_node(node) + builder.ident([:it, srange(node.location)]).updated(:lvar) + end + + # -> { it } + # ^^^^^^^^^ + def visit_it_parameters_node(node) + # FIXME: The builder _should_ always be a subclass of the prism builder. + # Currently RuboCop passes in its own builder that always inherits from the + # parser builder (which is lacking the `itarg` method). Once rubocop-ast + # opts in to use the custom prism builder a warning can be emitted when + # it is not the expected class, and eventually raise. + # https://github.com/rubocop/rubocop-ast/pull/354 + if builder.is_a?(Translation::Parser::Builder) + builder.itarg + else + builder.args(nil, [], nil, false) + end + 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, + visit(node.body), + [node.closing, srange(node.closing_loc)] + ) + end + + # foo + # ^^^ + def visit_local_variable_read_node(node) + builder.ident([node.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.binary_operator_loc.slice.chomp("="), srange(node.binary_operator_loc)], + visit(node.value) + ) + end + + # foo &&= bar + # ^^^^^^^^^^^ + def visit_local_variable_and_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 + # ^^^^^^^^^^^ + def visit_local_variable_or_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 + # ^^^ + 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)/ =~ 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) + builder.multi_lhs( + token(node.lparen_loc), + visit_all(multi_target_elements(node)), + token(node.rparen_loc) + ) + end + + # foo, bar = baz + # ^^^^^^^^^^^^^^ + def visit_multi_write_node(node) + elements = multi_target_elements(node) + + if elements.length == 1 && elements.first.is_a?(MultiTargetNode) && !node.rest + elements = multi_target_elements(elements.first) + end + + 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| + params << + if required.is_a?(RequiredParameterNode) + visit(required) + else + required.accept(copy_compiler(in_destructure: true)) + 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| + params << + if post.is_a?(RequiredParameterNode) + visit(post) + else + post.accept(copy_compiler(in_destructure: true)) + 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) + parts = node.expression.accept(copy_compiler(in_pattern: false)) # Don't treat * and similar as match_rest + expression = builder.begin(token(node.lparen_loc), parts, 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([node.value, 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) + parts = + if node.content == "" + [] + elsif node.content.include?("\n") + string_nodes_from_line_continuations(node.unescaped, node.content, node.content_loc.start_offset, node.opening) + else + [builder.string_internal([node.unescaped, srange(node.content_loc)])] + end + + builder.regexp_compose( + token(node.opening_loc), + 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/ 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? + visit_heredoc(node.to_interpolated) { |children, closing| builder.string_compose(token(node.opening_loc), children, closing) } + elsif node.opening == "?" + builder.character([node.unescaped, srange(node.location)]) + elsif node.opening&.start_with?("%") && node.unescaped.empty? + builder.string_compose(token(node.opening_loc), [], token(node.closing_loc)) + else + parts = + if node.content.include?("\n") + string_nodes_from_line_continuations(node.unescaped, node.content, node.content_loc.start_offset, node.opening) + else + [builder.string_internal([node.unescaped, srange(node.content_loc)])] + 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 == "" + [] + elsif node.value.include?("\n") + string_nodes_from_line_continuations(node.unescaped, node.value, node.value_loc.start_offset, node.opening) + else + [builder.string_internal([node.unescaped, srange(node.value_loc)])] + 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 (then_keyword_loc = node.then_keyword_loc) + token(then_keyword_loc) + else + srange_semicolon(node.predicate.location.end_offset, (node.statements&.location || node.else_clause&.location || node.end_keyword_loc).start_offset) + end, + visit(node.else_clause), + token(node.else_clause&.else_keyword_loc), + visit(node.statements), + token(node.end_keyword_loc) + ) + else + builder.condition_mod( + visit(node.else_clause), + 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), + if (do_keyword_loc = node.do_keyword_loc) + token(do_keyword_loc) + else + srange_semicolon(node.predicate.location.end_offset, (node.statements&.location || node.closing_loc).start_offset) + end, + 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 (then_keyword_loc = node.then_keyword_loc) + token(then_keyword_loc) + else + srange_semicolon(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), + if (do_keyword_loc = node.do_keyword_loc) + token(do_keyword_loc) + else + srange_semicolon(node.predicate.location.end_offset, (node.statements&.location || node.closing_loc).start_offset) + end, + 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? + return visit_heredoc(node.to_interpolated) { |children, closing| builder.xstring_compose(token(node.opening_loc), children, closing) } + end + + parts = + if node.content == "" + [] + elsif node.content.include?("\n") + string_nodes_from_line_continuations(node.unescaped, node.content, node.content_loc.start_offset, node.opening) + else + [builder.string_internal([node.unescaped, srange(node.content_loc)])] + end + + builder.xstring_compose( + token(node.opening_loc), + parts, + token(node.closing_loc) + ) + 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 + + # Returns the set of targets for a MultiTargetNode or a MultiWriteNode. + def multi_target_elements(node) + elements = [*node.lefts] + elements << node.rest if !node.rest.nil? && !node.rest.is_a?(ImplicitRestNode) + elements.concat(node.rights) + elements + 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 + receiver.copy(numerator: -receiver.numerator, location: message_loc.join(receiver.location)) + when :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 + + # 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 a semicolon between the given + # start offset and end offset. If the semicolon 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_semicolon(start_offset, end_offset) + if (match = source_buffer.source.byteslice(start_offset...end_offset)[/\A\s*;/]) + final_offset = start_offset + match.bytesize + [";", Range.new(source_buffer, offset_cache[final_offset - 1], offset_cache[final_offset])] + 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 + visited = parameter.is_a?(RequiredParameterNode) ? visit(parameter) : parameter.accept(copy_compiler(in_destructure: true)) + [builder.procarg0(visited)].concat(visit_all(parameters.locals)) + else + visit(parameters) + end, + token(parameters.closing_loc), + false + ) + end, + visit(block.body), + 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 + indented = false + + # If this is a dedenting heredoc, then we need to insert the opening + # content into the children as well. + if node.opening.start_with?("<<~") && node.parts.length > 0 && !node.parts.first.is_a?(StringNode) + location = node.parts.first.location + location = location.copy(start_offset: location.start_offset - location.start_line_slice.bytesize) + children << builder.string_internal(token(location)) + indented = true + end + + node.parts.each do |part| + pushing = + if part.is_a?(StringNode) && part.content.include?("\n") + string_nodes_from_line_continuations(part.unescaped, part.content, part.location.start_offset, node.opening) + 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") + appendee = children[-1] + + location = appendee.loc + location = location.with_expression(location.expression.join(child.loc.expression)) + + children[-1] = appendee.updated(:str, ["#{appendee.children.first}#{child.children.first}"], location: location) + 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))] + composed = yield children, closing_t + + composed = composed.updated(nil, children[1..-1]) if indented + composed + 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 + + # When the content of a string node is split across multiple lines, the + # parser gem creates individual string nodes for each line the content is part of. + def string_nodes_from_interpolation(node, opening) + node.parts.flat_map do |part| + if part.type == :string_node && part.content.include?("\n") && part.opening_loc.nil? + string_nodes_from_line_continuations(part.unescaped, part.content, part.content_loc.start_offset, opening) + else + visit(part) + end + end + end + + # Create parser string nodes from a single prism node. The parser gem + # "glues" strings together when a line continuation is encountered. + def string_nodes_from_line_continuations(unescaped, escaped, start_offset, opening) + unescaped = unescaped.lines + escaped = escaped.lines + percent_array = opening&.start_with?("%w", "%W", "%i", "%I") + regex = opening == "/" || opening&.start_with?("%r") + + # Non-interpolating strings + if opening&.end_with?("'") || opening&.start_with?("%q", "%s", "%w", "%i") + current_length = 0 + current_line = +"" + + escaped.filter_map.with_index do |escaped_line, index| + unescaped_line = unescaped.fetch(index, "") + current_length += escaped_line.bytesize + current_line << unescaped_line + + # Glue line continuations together. Only %w and %i arrays can contain these. + if percent_array && escaped_line[/(\\)*\n$/, 1]&.length&.odd? + next unless index == escaped.count - 1 + end + s = builder.string_internal([current_line, srange_offsets(start_offset, start_offset + current_length)]) + start_offset += escaped_line.bytesize + current_line = +"" + current_length = 0 + s + end + else + escaped_lengths = [] + normalized_lengths = [] + # Keeps track of where an unescaped line should start a new token. An unescaped + # \n would otherwise be indistinguishable from the actual newline at the end of + # of the line. The parser gem only emits a new string node at "real" newlines, + # line continuations don't start a new node as well. + do_next_tokens = [] + + escaped + .chunk_while { |before, after| before[/(\\*)\r?\n$/, 1]&.length&.odd? || false } + .each do |lines| + escaped_lengths << lines.sum(&:bytesize) + + unescaped_lines_count = + if regex + 0 # Will always be preserved as is + else + lines.sum do |line| + count = line.scan(/(\\*)n/).count { |(backslashes)| backslashes&.length&.odd? } + count -= 1 if !line.end_with?("\n") && count > 0 + count + end + end + + extra = 1 + extra = lines.count if percent_array # Account for line continuations in percent arrays + + normalized_lengths.concat(Array.new(unescaped_lines_count + extra, 0)) + normalized_lengths[-1] = lines.sum { |line| line.bytesize } + do_next_tokens.concat(Array.new(unescaped_lines_count + extra, false)) + do_next_tokens[-1] = true + end + + current_line = +"" + current_normalized_length = 0 + + emitted_count = 0 + unescaped.filter_map.with_index do |unescaped_line, index| + current_line << unescaped_line + current_normalized_length += normalized_lengths.fetch(index, 0) + + if do_next_tokens[index] + inner_part = builder.string_internal([current_line, srange_offsets(start_offset, start_offset + current_normalized_length)]) + start_offset += escaped_lengths.fetch(emitted_count, 0) + current_line = +"" + current_normalized_length = 0 + emitted_count += 1 + inner_part + else + nil + end + end + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/translation/parser/lexer.rb b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/translation/parser/lexer.rb new file mode 100644 index 0000000..75c48ef --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/translation/parser/lexer.rb @@ -0,0 +1,820 @@ +# frozen_string_literal: true +# :markup: markdown + +require "strscan" +require_relative "../../polyfill/append_as_bytes" +require_relative "../../polyfill/scan_byte" + +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 + # These tokens are always skipped + TYPES_ALWAYS_SKIP = Set.new(%i[IGNORED_NEWLINE __END__ EOF]) + private_constant :TYPES_ALWAYS_SKIP + + # The direct translating of types between the two lexers. + TYPES = { + # These tokens should never appear in the output of the lexer. + MISSING: nil, + NOT_PROVIDED: nil, + EMBDOC_END: nil, + EMBDOC_LINE: 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: :tLPAREN2, + 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: :tDSTAR, + 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: + + # It is used to determine whether `do` is of the token type `kDO` or `kDO_LAMBDA`. + # + # NOTE: In edge cases like `-> (foo = -> (bar) {}) do end`, please note that `kDO` is still returned + # instead of `kDO_LAMBDA`, which is expected: https://github.com/ruby/prism/pull/3046 + LAMBDA_TOKEN_TYPES = Set.new([:kDO_LAMBDA, :tLAMBDA, :tLAMBEG]) + + # The `PARENTHESIS_LEFT` token in Prism is classified as either `tLPAREN` or `tLPAREN2` in the Parser gem. + # The following token types are listed as those classified as `tLPAREN`. + LPAREN_CONVERSION_TOKEN_TYPES = Set.new([ + :kBREAK, :tCARET, :kCASE, :tDIVIDE, :kFOR, :kIF, :kNEXT, :kRETURN, :kUNTIL, :kWHILE, :tAMPER, :tANDOP, :tBANG, :tCOMMA, :tDOT2, :tDOT3, + :tEQL, :tLPAREN, :tLPAREN2, :tLPAREN_ARG, :tLSHFT, :tNL, :tOP_ASGN, :tOROP, :tPIPE, :tSEMI, :tSTRING_DBEG, :tUMINUS, :tUPLUS, :tLCURLY + ]) + + # Types of tokens that are allowed to continue a method call with comments in-between. + # For these, the parser gem doesn't emit a newline token after the last comment. + COMMENT_CONTINUATION_TYPES = Set.new([:COMMENT, :AMPERSAND_DOT, :DOT]) + private_constant :COMMENT_CONTINUATION_TYPES + + # Heredocs are complex and require us to keep track of a bit of info to refer to later + HeredocData = Struct.new(:identifier, :common_whitespace, keyword_init: true) + + private_constant :TYPES, :EXPR_BEG, :EXPR_LABEL, :LAMBDA_TOKEN_TYPES, :LPAREN_CONVERSION_TOKEN_TYPES, :HeredocData + + # 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_stack = [] + quote_stack = [] + + # The parser gem emits the newline tokens for comments out of order. This saves + # that token location to emit at a later time to properly line everything up. + # https://github.com/whitequark/parser/issues/1025 + comment_newline_location = nil + + while index < length + token, state = lexed[index] + index += 1 + next if TYPES_ALWAYS_SKIP.include?(token.type) + + type = TYPES.fetch(token.type) + value = token.value + location = range(token.location.start_offset, token.location.end_offset) + + case type + when :kDO + nearest_lambda_token = tokens.reverse_each.find do |token| + LAMBDA_TOKEN_TYPES.include?(token.first) + end + + if nearest_lambda_token&.first == :tLAMBDA + type = :kDO_LAMBDA + end + when :tCHARACTER + value.delete_prefix!("?") + # Character literals behave similar to double-quoted strings. We can use the same escaping mechanism. + value = unescape_string(value, "?") + when :tCOMMENT + if token.type == :EMBDOC_BEGIN + + while !((next_token = lexed[index]&.first) && next_token.type == :EMBDOC_END) && (index < length - 1) + value += next_token.value + index += 1 + end + + value += next_token.value + location = range(token.location.start_offset, next_token.location.end_offset) + index += 1 + else + is_at_eol = value.chomp!.nil? + location = range(token.location.start_offset, token.location.end_offset + (is_at_eol ? 0 : -1)) + + prev_token, _ = lexed[index - 2] if index - 2 >= 0 + next_token, _ = lexed[index] + + is_inline_comment = prev_token&.location&.start_line == token.location.start_line + if is_inline_comment && !is_at_eol && !COMMENT_CONTINUATION_TYPES.include?(next_token&.type) + tokens << [:tCOMMENT, [value, location]] + + nl_location = range(token.location.end_offset - 1, token.location.end_offset) + tokens << [:tNL, [nil, nl_location]] + next + elsif is_inline_comment && next_token&.type == :COMMENT + comment_newline_location = range(token.location.end_offset - 1, token.location.end_offset) + elsif comment_newline_location && !COMMENT_CONTINUATION_TYPES.include?(next_token&.type) + tokens << [:tCOMMENT, [value, location]] + tokens << [:tNL, [nil, comment_newline_location]] + comment_newline_location = nil + next + end + end + when :tNL + next_token, _ = lexed[index] + # Newlines after comments are emitted out of order. + if next_token&.type == :COMMENT + comment_newline_location = location + next + end + + value = nil + when :tFLOAT + value = parse_float(value) + when :tIMAGINARY + value = parse_complex(value) + when :tINTEGER + if value.start_with?("+") + tokens << [:tUNARY_NUM, ["+", range(token.location.start_offset, token.location.start_offset + 1)]] + location = range(token.location.start_offset + 1, 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 :tLPAREN2 + type = :tLPAREN if tokens.empty? || LPAREN_CONVERSION_TOKEN_TYPES.include?(tokens.dig(-1, 0)) + when :tNTH_REF + value = parse_integer(value.delete_prefix("$")) + when :tOP_ASGN + value.chomp!("=") + when :tRATIONAL + value = parse_rational(value) + when :tSPACE + location = range(token.location.start_offset, token.location.start_offset + percent_array_leading_whitespace(value)) + value = nil + when :tSTRING_BEG + next_token, _ = lexed[index] + next_next_token, _ = lexed[index + 1] + basic_quotes = value == '"' || value == "'" + + if basic_quotes && next_token&.type == :STRING_END + next_location = token.location.join(next_token.location) + type = :tSTRING + value = "" + location = range(next_location.start_offset, next_location.end_offset) + index += 1 + elsif value.start_with?("'", '"', "%") + if next_token&.type == :STRING_CONTENT && next_next_token&.type == :STRING_END + string_value = next_token.value + if simplify_string?(string_value, value) + next_location = token.location.join(next_next_token.location) + if percent_array?(value) + value = percent_array_unescape(string_value) + else + value = unescape_string(string_value, value) + end + type = :tSTRING + location = range(next_location.start_offset, next_location.end_offset) + index += 2 + tokens << [type, [value, location]] + + next + end + end + + quote_stack.push(value) + elsif token.type == :HEREDOC_START + quote = value[2] == "-" || value[2] == "~" ? value[3] : value[2] + heredoc_type = value[2] == "-" || value[2] == "~" ? value[2] : "" + heredoc = HeredocData.new( + identifier: value.match(/<<[-~]?["'`]?(?.*?)["'`]?\z/)[:heredoc_identifier], + common_whitespace: 0, + ) + + if quote == "`" + type = :tXSTRING_BEG + end + + # The parser gem trims whitespace from squiggly heredocs. We must record + # the most common whitespace to later remove. + if heredoc_type == "~" || heredoc_type == "`" + heredoc.common_whitespace = calculate_heredoc_whitespace(index) + end + + if quote == "'" || quote == '"' || quote == "`" + value = "<<#{quote}" + else + value = '<<"' + end + + heredoc_stack.push(heredoc) + quote_stack.push(value) + end + when :tSTRING_CONTENT + is_percent_array = percent_array?(quote_stack.last) + + if (lines = token.value.lines).one? + # Prism usually emits a single token for strings with line continuations. + # For squiggly heredocs they are not joined so we do that manually here. + current_string = +"" + current_length = 0 + start_offset = token.location.start_offset + while token.type == :STRING_CONTENT + current_length += token.value.bytesize + # Heredoc interpolation can have multiple STRING_CONTENT nodes on the same line. + prev_token, _ = lexed[index - 2] if index - 2 >= 0 + is_first_token_on_line = prev_token && token.location.start_line != prev_token.location.start_line + # The parser gem only removes indentation when the heredoc is not nested + not_nested = heredoc_stack.size == 1 + if is_percent_array + value = percent_array_unescape(token.value) + elsif is_first_token_on_line && not_nested && (current_heredoc = heredoc_stack.last).common_whitespace > 0 + value = trim_heredoc_whitespace(token.value, current_heredoc) + end + + current_string << unescape_string(value, quote_stack.last) + relevant_backslash_count = if quote_stack.last.start_with?("%W", "%I") + 0 # the last backslash escapes the newline + else + token.value[/(\\{1,})\n/, 1]&.length || 0 + end + if relevant_backslash_count.even? || !interpolation?(quote_stack.last) + tokens << [:tSTRING_CONTENT, [current_string, range(start_offset, start_offset + current_length)]] + break + end + token, _ = lexed[index] + index += 1 + end + else + # When the parser gem encounters a line continuation inside of a multiline string, + # it emits a single string node. The backslash (and remaining newline) is removed. + current_line = +"" + adjustment = 0 + start_offset = token.location.start_offset + emit = false + + lines.each.with_index do |line, index| + chomped_line = line.chomp + backslash_count = chomped_line[/\\{1,}\z/]&.length || 0 + is_interpolation = interpolation?(quote_stack.last) + + if backslash_count.odd? && (is_interpolation || is_percent_array) + if is_percent_array + current_line << percent_array_unescape(line) + adjustment += 1 + else + chomped_line.delete_suffix!("\\") + current_line << chomped_line + adjustment += 2 + end + # If the string ends with a line continuation emit the remainder + emit = index == lines.count - 1 + else + current_line << line + emit = true + end + + if emit + end_offset = start_offset + current_line.bytesize + adjustment + tokens << [:tSTRING_CONTENT, [unescape_string(current_line, quote_stack.last), range(start_offset, end_offset)]] + start_offset = end_offset + current_line = +"" + adjustment = 0 + end + end + end + next + 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_stack.pop.identifier + location = range(token.location.start_offset, token.location.end_offset - newline_length) + elsif token.type == :REGEXP_END + value = value[0] + location = range(token.location.start_offset, token.location.start_offset + 1) + end + + if percent_array?(quote_stack.pop) + prev_token, _ = lexed[index - 2] if index - 2 >= 0 + empty = %i[PERCENT_LOWER_I PERCENT_LOWER_W PERCENT_UPPER_I PERCENT_UPPER_W].include?(prev_token&.type) + ends_with_whitespace = prev_token&.type == :WORDS_SEP + # parser always emits a space token after content in a percent array, even if no actual whitespace is present. + if !empty && !ends_with_whitespace + tokens << [:tSPACE, [nil, range(token.location.start_offset, token.location.start_offset)]] + end + end + when :tSYMBEG + if (next_token = lexed[index]&.first) && next_token.type != :STRING_CONTENT && next_token.type != :EMBEXPR_BEGIN && next_token.type != :EMBVAR && next_token.type != :STRING_END + next_location = token.location.join(next_token.location) + type = :tSYMBOL + value = next_token.value + value = { "~@" => "~", "!@" => "!" }.fetch(value, value) + location = range(next_location.start_offset, next_location.end_offset) + index += 1 + else + quote_stack.push(value) + end + when :tFID + if !tokens.empty? && tokens.dig(-1, 0) == :kDEF + type = :tIDENTIFIER + end + when :tXSTRING_BEG + if (next_token = lexed[index]&.first) && !%i[STRING_CONTENT STRING_END EMBEXPR_BEGIN].include?(next_token.type) + # self.`() + type = :tBACK_REF2 + end + quote_stack.push(value) + when :tSYMBOLS_BEG, :tQSYMBOLS_BEG, :tWORDS_BEG, :tQWORDS_BEG + if (next_token = lexed[index]&.first) && next_token.type == :WORDS_SEP + index += 1 + end + + quote_stack.push(value) + when :tREGEXP_BEG + quote_stack.push(value) + end + + tokens << [type, [value, location]] + + if token.type == :REGEXP_END + tokens << [:tREGEXP_OPT, [token.value[1..], range(token.location.start_offset + 1, token.location.end_offset)]] + end + end + + tokens + end + + private + + # Creates a new parser range, taking prisms byte offsets into account + def range(start_offset, end_offset) + Range.new(source_buffer, offset_cache[start_offset], offset_cache[end_offset]) + end + + # 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 + + # Wonky heredoc tab/spaces rules. + # https://github.com/ruby/prism/blob/v1.3.0/src/prism.c#L10548-L10558 + def calculate_heredoc_whitespace(heredoc_token_index) + next_token_index = heredoc_token_index + nesting_level = 0 + previous_line = -1 + result = Float::MAX + + while (next_token = lexed[next_token_index]&.first) + next_token_index += 1 + next_next_token, _ = lexed[next_token_index] + first_token_on_line = next_token.location.start_column == 0 + + # String content inside nested heredocs and interpolation is ignored + if next_token.type == :HEREDOC_START || next_token.type == :EMBEXPR_BEGIN + # When interpolation is the first token of a line there is no string + # content to check against. There will be no common whitespace. + if nesting_level == 0 && first_token_on_line + result = 0 + end + nesting_level += 1 + elsif next_token.type == :HEREDOC_END || next_token.type == :EMBEXPR_END + nesting_level -= 1 + # When we encountered the matching heredoc end, we can exit + break if nesting_level == -1 + elsif next_token.type == :STRING_CONTENT && nesting_level == 0 && first_token_on_line + common_whitespace = 0 + next_token.value[/^\s*/].each_char do |char| + if char == "\t" + common_whitespace = (common_whitespace / 8 + 1) * 8; + else + common_whitespace += 1 + end + end + + is_first_token_on_line = next_token.location.start_line != previous_line + # Whitespace is significant if followed by interpolation + whitespace_only = common_whitespace == next_token.value.length && next_next_token&.location&.start_line != next_token.location.start_line + if is_first_token_on_line && !whitespace_only && common_whitespace < result + result = common_whitespace + previous_line = next_token.location.start_line + end + end + end + result + end + + # Wonky heredoc tab/spaces rules. + # https://github.com/ruby/prism/blob/v1.3.0/src/prism.c#L16528-L16545 + def trim_heredoc_whitespace(string, heredoc) + trimmed_whitespace = 0 + trimmed_characters = 0 + while (string[trimmed_characters] == "\t" || string[trimmed_characters] == " ") && trimmed_whitespace < heredoc.common_whitespace + if string[trimmed_characters] == "\t" + trimmed_whitespace = (trimmed_whitespace / 8 + 1) * 8; + break if trimmed_whitespace > heredoc.common_whitespace + else + trimmed_whitespace += 1 + end + trimmed_characters += 1 + end + + string[trimmed_characters..] + end + + # Escape sequences that have special and should appear unescaped in the resulting string. + ESCAPES = { + "a" => "\a", "b" => "\b", "e" => "\e", "f" => "\f", + "n" => "\n", "r" => "\r", "s" => "\s", "t" => "\t", + "v" => "\v", "\\" => "\\" + }.freeze + private_constant :ESCAPES + + # When one of these delimiters is encountered, then the other + # one is allowed to be escaped as well. + DELIMITER_SYMETRY = { "[" => "]", "(" => ")", "{" => "}", "<" => ">" }.freeze + private_constant :DELIMITER_SYMETRY + + + # https://github.com/whitequark/parser/blob/v3.3.6.0/lib/parser/lexer-strings.rl#L14 + REGEXP_META_CHARACTERS = ["\\", "$", "(", ")", "*", "+", ".", "<", ">", "?", "[", "]", "^", "{", "|", "}"] + private_constant :REGEXP_META_CHARACTERS + + # Apply Ruby string escaping rules + def unescape_string(string, quote) + # In single-quoted heredocs, everything is taken literally. + return string if quote == "<<'" + + # OPTIMIZATION: Assume that few strings need escaping to speed up the common case. + return string unless string.include?("\\") + + # Enclosing character for the string. `"` for `"foo"`, `{` for `%w{foo}`, etc. + delimiter = quote[-1] + + if regexp?(quote) + # Should be escaped handled to single-quoted heredocs. The only character that is + # allowed to be escaped is the delimiter, except when that also has special meaning + # in the regexp. Since all the symetry delimiters have special meaning, they don't need + # to be considered separately. + if REGEXP_META_CHARACTERS.include?(delimiter) + string + else + # There can never be an even amount of backslashes. It would be a syntax error. + string.gsub(/\\(#{Regexp.escape(delimiter)})/, '\1') + end + elsif interpolation?(quote) + # Appending individual escape sequences may force the string out of its intended + # encoding. Start out with binary and force it back later. + result = "".b + + scanner = StringScanner.new(string) + while (skipped = scanner.skip_until(/\\/)) + # Append what was just skipped over, excluding the found backslash. + result.append_as_bytes(string.byteslice(scanner.pos - skipped, skipped - 1)) + escape_read(result, scanner, false, false) + end + + # Add remaining chars + result.append_as_bytes(string.byteslice(scanner.pos..)) + result.force_encoding(source_buffer.source.encoding) + else + delimiters = Regexp.escape("#{delimiter}#{DELIMITER_SYMETRY[delimiter]}") + string.gsub(/\\([\\#{delimiters}])/, '\1') + end + end + + # Certain strings are merged into a single string token. + def simplify_string?(value, quote) + case quote + when "'" + # Only simplify 'foo' + !value.include?("\n") + when '"' + # Simplify when every line ends with a line continuation, or it is the last line + value.lines.all? do |line| + !line.end_with?("\n") || line[/(\\*)$/, 1]&.length&.odd? + end + else + # %q and similar are never simplified + false + end + end + + # Escape a byte value, given the control and meta flags. + def escape_build(value, control, meta) + value &= 0x9f if control + value |= 0x80 if meta + value + end + + # Read an escape out of the string scanner, given the control and meta + # flags, and push the unescaped value into the result. + def escape_read(result, scanner, control, meta) + if scanner.skip("\n") + # Line continuation + elsif (value = ESCAPES[scanner.peek(1)]) + # Simple single-character escape sequences like \n + result.append_as_bytes(value) + scanner.pos += 1 + elsif (value = scanner.scan(/[0-7]{1,3}/)) + # \nnn + result.append_as_bytes(escape_build(value.to_i(8), control, meta)) + elsif (value = scanner.scan(/x[0-9a-fA-F]{1,2}/)) + # \xnn + result.append_as_bytes(escape_build(value[1..].to_i(16), control, meta)) + elsif (value = scanner.scan(/u[0-9a-fA-F]{4}/)) + # \unnnn + result.append_as_bytes(value[1..].hex.chr(Encoding::UTF_8)) + elsif scanner.skip("u{}") + # https://github.com/whitequark/parser/issues/856 + elsif (value = scanner.scan(/u{.*?}/)) + # \u{nnnn ...} + value[2..-2].split.each do |unicode| + result.append_as_bytes(unicode.hex.chr(Encoding::UTF_8)) + end + elsif (value = scanner.scan(/c\\?(?=[[:print:]])|C-\\?(?=[[:print:]])/)) + # \cx or \C-x where x is an ASCII printable character + escape_read(result, scanner, true, meta) + elsif (value = scanner.scan(/M-\\?(?=[[:print:]])/)) + # \M-x where x is an ASCII printable character + escape_read(result, scanner, control, true) + elsif (byte = scanner.scan_byte) + # Something else after an escape. + if control && byte == 0x3f # ASCII '?' + result.append_as_bytes(escape_build(0x7f, false, meta)) + else + result.append_as_bytes(escape_build(byte, control, meta)) + end + end + end + + # In a percent array, certain whitespace can be preceeded with a backslash, + # causing the following characters to be part of the previous element. + def percent_array_unescape(string) + string.gsub(/(\\)+[ \f\n\r\t\v]/) do |full_match| + full_match.delete_prefix!("\\") if Regexp.last_match[1].length.odd? + full_match + end + end + + # For %-arrays whitespace, the parser gem only considers whitespace before the newline. + def percent_array_leading_whitespace(string) + return 1 if string.start_with?("\n") + + leading_whitespace = 0 + string.each_char do |c| + break if c == "\n" + leading_whitespace += 1 + end + leading_whitespace + end + + # Determine if characters preceeded by a backslash should be escaped or not + def interpolation?(quote) + !quote.end_with?("'") && !quote.start_with?("%q", "%w", "%i", "%s") + end + + # Regexp allow interpolation but are handled differently during unescaping + def regexp?(quote) + quote == "/" || quote.start_with?("%r") + end + + # Determine if the string is part of a %-style array. + def percent_array?(quote) + quote.start_with?("%w", "%W", "%i", "%I") + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/translation/parser_current.rb b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/translation/parser_current.rb new file mode 100644 index 0000000..f13eff6 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/translation/parser_current.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true +# :markup: markdown +# typed: ignore + +# +module Prism + module Translation + case RUBY_VERSION + when /^3\.3\./ + ParserCurrent = Parser33 + when /^3\.4\./ + ParserCurrent = Parser34 + when /^3\.5\./, /^4\.0\./ + ParserCurrent = Parser40 + when /^4\.1\./ + ParserCurrent = Parser41 + else + # Keep this in sync with released Ruby. + parser = Parser40 + major, minor, _patch = Gem::Version.new(RUBY_VERSION).segments + warn "warning: `Prism::Translation::Current` is loading #{parser.name}, " \ + "but you are running #{major}.#{minor}." + ParserCurrent = parser + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/translation/parser_versions.rb b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/translation/parser_versions.rb new file mode 100644 index 0000000..720c7d5 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/translation/parser_versions.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true +# :markup: markdown + +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 + + # This class is the entry-point for Ruby 3.4 of `Prism::Translation::Parser`. + class Parser34 < Parser + def version # :nodoc: + 34 + end + end + + # This class is the entry-point for Ruby 4.0 of `Prism::Translation::Parser`. + class Parser40 < Parser + def version # :nodoc: + 40 + end + end + + Parser35 = Parser40 # :nodoc: + + # This class is the entry-point for Ruby 4.1 of `Prism::Translation::Parser`. + class Parser41 < Parser + def version # :nodoc: + 41 + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/translation/ripper.rb b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/translation/ripper.rb new file mode 100644 index 0000000..735217d --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/translation/ripper.rb @@ -0,0 +1,3520 @@ +# frozen_string_literal: true +# :markup: markdown + +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 + # [[lineno, column], type, token, state]. + # 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, version: "current") + + if result.failure? && raise_errors + raise SyntaxError, result.errors.first.message + else + result.value + end + end + + # Tokenizes the Ruby program and returns an array of strings. + # The +filename+ and +lineno+ arguments are mostly ignored, since the + # return value is just the tokenized input. + # 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+. + # + # p Ripper.tokenize("def m(a) nil end") + # # => ["def", " ", "m", "(", "a", ")", " ", "nil", " ", "end"] + # + def self.tokenize(...) + lex(...).map(&:value) + 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 :Filter, "prism/translation/ripper/filter" + autoload :Lexer, "prism/translation/ripper/lexer" + autoload :SexpBuilder, "prism/translation/ripper/sexp" + autoload :SexpBuilderPP, "prism/translation/ripper/sexp" + + # :stopdoc: + # This is not part of the public API but used by some gems. + + # Ripper-internal bitflags. + LEX_STATE_NAMES = %i[ + BEG END ENDARG ENDFN ARG CMDARG MID FNAME DOT CLASS LABEL LABELED FITEM + ].map.with_index.to_h { |name, i| [2 ** i, name] }.freeze + private_constant :LEX_STATE_NAMES + + LEX_STATE_NAMES.each do |value, key| + const_set("EXPR_#{key}", value) + end + EXPR_NONE = 0 + 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 + + def self.lex_state_name(state) + LEX_STATE_NAMES.filter_map { |flag, name| name if state & flag != 0 }.join("|") + end + + # :startdoc: + + # 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, has_ripper_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 has_ripper_block + bounds(node.location) + on_method_add_block(call, block) + else + call + 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 :! + if node.message == "not" + receiver = + if !node.receiver.is_a?(ParenthesesNode) || !node.receiver.body.nil? + visit(node.receiver) + end + + bounds(node.location) + on_unary(:not, receiver) + else + receiver = visit(node.receiver) + + bounds(node.location) + on_unary(:!, receiver) + end + 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, has_ripper_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? && get_arguments_and_block(node.arguments, node.block).first.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 has_ripper_block + bounds(node.block.location) + on_method_add_block(call, block) + else + call + 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, has_ripper_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 has_ripper_block + bounds(node.block.location) + on_method_add_block(call, block) + else + call + end + end + end + end + + # Extract the arguments and block Ripper-style, which means if the block + # is like `&b` then it's moved to arguments. + private def get_arguments_and_block(arguments_node, block_node) + arguments = arguments_node&.arguments || [] + block = block_node + + if block.is_a?(BlockArgumentNode) + arguments += [block] + block = nil + end + + [arguments, block] + 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, block = get_arguments_and_block(arguments_node, block_node) + + [ + 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), + block != nil, + ] + 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.binary_operator_loc) + operator = on_op("#{node.binary_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.else_clause)) do |current, condition| + on_when(*visit(condition), current) + 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.else_clause)) do |current, condition| + on_in(*visit(condition), current) + 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.binary_operator_loc) + operator = on_op("#{node.binary_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.binary_operator_loc) + operator = on_op("#{node.binary_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.name_loc) + child = on_const(node.name.to_s) + + bounds(node.location) + on_top_const_ref(child) + else + parent = visit(node.parent) + + bounds(node.name_loc) + child = on_const(node.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.name_loc) + child = on_const(node.name.to_s) + + bounds(node.location) + on_top_const_field(child) + else + parent = visit(node.parent) + + bounds(node.name_loc) + child = on_const(node.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.binary_operator_loc) + operator = on_op("#{node.binary_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 + on_defs(receiver, operator, name, parameters, bodystmt) + else + on_def(name, parameters, bodystmt) + end + end + + # defined? a + # ^^^^^^^^^^ + # + # defined?(a) + # ^^^^^^^^^^^ + def visit_defined_node(node) + expression = visit(node.value) + + # Very weird circumstances here where something like: + # + # defined? + # (1) + # + # gets parsed in Ruby as having only the `1` expression but in Ripper it + # gets parsed as having a parentheses node. In this case we need to + # synthesize that node to match Ripper's behavior. + if node.lparen_loc && node.keyword_loc.join(node.lparen_loc).slice.include?("\n") + bounds(node.lparen_loc.join(node.rparen_loc)) + expression = on_paren(on_stmts_add(on_stmts_new, expression)) + end + + bounds(node.location) + on_defined(expression) + 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.binary_operator_loc) + operator = on_op("#{node.binary_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.subsequent.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 + subsequent = visit(node.subsequent) + + bounds(node.location) + if node.if_keyword == "if" + on_if(predicate, statements, subsequent) + else + on_elsif(predicate, statements, subsequent) + 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 subsequent. 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.binary_operator_loc) + operator = on_op("#{node.binary_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.binary_operator_loc) + operator = on_op("#{node.binary_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_local_variable_read_node(node) + bounds(node.location) + on_vcall(on_ident(node.slice)) + 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) + on_var_ref(on_ident(node.slice)) + 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.binary_operator_loc) + operator = on_op("#{node.binary_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)/ =~ 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 + + subsequent = visit(node.subsequent) + + bounds(node.location) + on_rescue(exceptions, reference, statements, subsequent) + 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, has_ripper_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 has_ripper_block + bounds(node.block.location) + on_method_add_block(call, block) + else + call + 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 + else_clause = visit(node.else_clause) + + bounds(node.location) + on_unless(predicate, statements, else_clause) + 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 subsequent. 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, partial_script: true, version: "current") + 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(arg); arg end + def _dispatch_2(arg, _); arg end + def _dispatch_3(arg, _, _); arg end + def _dispatch_4(arg, _, _, _); arg end + def _dispatch_5(arg, _, _, _, _); arg end + def _dispatch_7(arg, _, _, _, _, _, _); arg 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/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/translation/ripper/filter.rb b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/translation/ripper/filter.rb new file mode 100644 index 0000000..19deef2 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/translation/ripper/filter.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +module Prism + module Translation + class Ripper + class Filter # :nodoc: + # :stopdoc: + def initialize(src, filename = '-', lineno = 1) + @__lexer = Lexer.new(src, filename, lineno) + @__line = nil + @__col = nil + @__state = nil + end + + def filename + @__lexer.filename + end + + def lineno + @__line + end + + def column + @__col + end + + def state + @__state + end + + def parse(init = nil) + data = init + @__lexer.lex.each do |pos, event, tok, state| + @__line, @__col = *pos + @__state = state + data = if respond_to?(event, true) + then __send__(event, tok, data) + else on_default(event, tok, data) + end + end + data + end + + private + + def on_default(event, token, data) + data + end + # :startdoc: + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/translation/ripper/lexer.rb b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/translation/ripper/lexer.rb new file mode 100644 index 0000000..bed863a --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/translation/ripper/lexer.rb @@ -0,0 +1,135 @@ +# frozen_string_literal: true +# :markup: markdown + +require_relative "../ripper" + +module Prism + module Translation + class Ripper + class Lexer < Ripper # :nodoc: + # :stopdoc: + class State + + attr_reader :to_int, :to_s + + def initialize(i) + @to_int = i + @to_s = Ripper.lex_state_name(i) + freeze + end + + def [](index) + case index + when 0, :to_int + @to_int + when 1, :to_s + @to_s + else + nil + end + end + + alias to_i to_int + alias inspect to_s + def pretty_print(q) q.text(to_s) end + def ==(i) super or to_int == i end + def &(i) self.class.new(to_int & i) end + def |(i) self.class.new(to_int | i) end + def allbits?(i) to_int.allbits?(i) end + def anybits?(i) to_int.anybits?(i) end + def nobits?(i) to_int.nobits?(i) end + + # Instances are frozen and there are only a handful of them so we cache them here. + STATES = Hash.new { |h,k| h[k] = State.new(k) } + + def self.cached(i) + STATES[i] + end + end + + class Elem + attr_accessor :pos, :event, :tok, :state, :message + + def initialize(pos, event, tok, state, message = nil) + @pos = pos + @event = event + @tok = tok + @state = State.cached(state) + @message = message + end + + def [](index) + case index + when 0, :pos + @pos + when 1, :event + @event + when 2, :tok + @tok + when 3, :state + @state + when 4, :message + @message + else + nil + end + end + + def inspect + "#<#{self.class}: #{event}@#{pos[0]}:#{pos[1]}:#{state}: #{tok.inspect}#{": " if message}#{message}>" + end + + alias to_s inspect + + def pretty_print(q) + q.group(2, "#<#{self.class}:", ">") { + q.breakable + q.text("#{event}@#{pos[0]}:#{pos[1]}") + q.breakable + state.pretty_print(q) + q.breakable + q.text("token: ") + tok.pretty_print(q) + if message + q.breakable + q.text("message: ") + q.text(message) + end + } + end + + def to_a + if @message + [@pos, @event, @tok, @state, @message] + else + [@pos, @event, @tok, @state] + end + end + end + + # Pretty much just the same as Prism.lex_compat. + def lex(raise_errors: false) + Ripper.lex(@source, filename, lineno, raise_errors: raise_errors) + end + + # Returns the lex_compat result wrapped in `Elem`. Errors are omitted. + # Since ripper is a streaming parser, tokens are expected to be emitted in the order + # that the parser encounters them. This is not implemented. + def parse(...) + lex(...).map do |position, event, token, state| + Elem.new(position, event, token, state.to_int) + end + end + + # Similar to parse but ripper sorts the elements by position in the source. Also + # includes errors. Since prism does error recovery, in cases of syntax errors + # the result may differ greatly compared to ripper. + def scan(...) + parse(...) + end + + # :startdoc: + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/translation/ripper/sexp.rb b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/translation/ripper/sexp.rb new file mode 100644 index 0000000..8cfefc8 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/translation/ripper/sexp.rb @@ -0,0 +1,126 @@ +# frozen_string_literal: true +# :markup: markdown + +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/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/translation/ripper/shim.rb b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/translation/ripper/shim.rb new file mode 100644 index 0000000..10e21cd --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/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/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/translation/ruby_parser.rb b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/translation/ruby_parser.rb new file mode 100644 index 0000000..c026c4a --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/translation/ruby_parser.rb @@ -0,0 +1,1964 @@ +# frozen_string_literal: true +# :markup: markdown + +begin + require "sexp" +rescue LoadError + warn(%q{Error: Unable to load sexp. Add `gem "sexp_processor"` to your Gemfile.}) + exit(1) +end + +class RubyParser # :nodoc: + class SyntaxError < RuntimeError # :nodoc: + end +end + +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) + left = visit(node.left) + + if left[0] == :and + # ruby_parser has the and keyword as right-associative as opposed to + # prism which has it as left-associative. We reverse that + # associativity here. + nest = left + nest = nest[2] while nest[2][0] == :and + nest[2] = s(node, :and, nest[2], visit(node.right)) + left + else + s(node, :and, left, visit(node.right)) + end + 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.to_s.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.subsequent).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 + + case node.arguments.arguments.first + when RegularExpressionNode, InterpolatedRegularExpressionNode + return s(node, :match3, visit(node.arguments.arguments.first), visit(node.receiver)) + 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.binary_operator) + else + s(node, op_asgn_type(node, :op_asgn2), visit(node.receiver), node.write_name, node.binary_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.else_clause) + 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.else_clause) + 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 + + result = + 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 + + attach_comments(result, node) + result + 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.binary_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.binary_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.name) + else + s(node, :colon2, visit(node.parent), node.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.binary_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.name) + else + s(node, :colon2, visit(node.parent), node.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 + + attach_comments(result, node) + 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.binary_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.subsequent)) + 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.binary_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.binary_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 + visited << :space + elsif result[0] == :dstr + if !visited.empty? && part.parts[0].is_a?(StringNode) + # If we are in the middle of an implicitly concatenated string, + # we should not have a bare string as the first part. In this + # case we need to visit just that first part and then we can + # push the rest of the parts onto the visited array. + result[1] = visit(part.parts[0]) + end + visited.concat(result[1..-1]) + else + visited << result + end + end + + state = :beginning #: :beginning | :string_content | :interpolated_content + results = [] + + visited.each_with_index do |result, index| + 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 == :space + # continue + elsif result.is_a?(String) + results[0] = "#{results[0]}#{result}" + elsif result.is_a?(Array) && result[0] == :str + results[0] = "#{results[0]}#{result[1]}" + else + results << result + state = :interpolated_content + end + when :interpolated_content + if result == :space + # continue + elsif visited[index - 1] != :space && result.is_a?(Array) && result[0] == :str && results[-1][0] == :str && (results[-1].line_max == result.line) + results[-1][1] = "#{results[-1][1]}#{result[1]}" + results[-1].line_max = result.line_max + else + results << result + end + end + end + + results + end + + # ``` + # -> { it } + # ^^ + # ``` + def visit_it_local_variable_read_node(node) + s(node, :call, nil, :it) + 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, ItParametersNode, NumberedParametersNode + 0 + 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.binary_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)/ =~ 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 + + result = + 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 + + attach_comments(result, node) + result + 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) + left = visit(node.left) + + if left[0] == :or + # ruby_parser has the or keyword as right-associative as opposed to + # prism which has it as left-associative. We reverse that + # associativity here. + nest = left + nest = nest[2] while nest[2][0] == :or + nest[2] = s(node, :or, nest[2], visit(node.right)) + left + else + s(node, :or, left, visit(node.right)) + end + end + + # ``` + # def foo(bar, *baz); end + # ^^^^^^^^^ + # ``` + def visit_parameters_node(node) + children = + node.each_child_node.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) + unescaped = node.unescaped + + if node.forced_binary_encoding? + unescaped = unescaped.dup + unescaped.force_encoding(Encoding::BINARY) + end + + s(node, :str, 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.else_clause), 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 + + # Attach prism comments to the given sexp. + def attach_comments(sexp, node) + return unless node.comments + return if node.comments.empty? + + extra = node.location.start_line - node.comments.last.location.start_line + comments = node.comments.map(&:slice) + comments.concat([nil] * [0, extra].max) + sexp.comments = comments.join("\n") + end + + # 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, ItParametersNode, 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, partial_script: true), 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, partial_script: true), filepath) + end + + # Parse the give file and translate it into the + # seattlerb/ruby_parser gem's Sexp format. This method is + # provided for API compatibility to RubyParser and takes an + # optional +timeout+ argument. + def process(ruby, file = "(string)", timeout = nil) + Timeout.timeout(timeout) { parse(ruby, file) } + 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.attach_comments! + result.value.accept(Compiler.new(filepath)) + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/visitor.rb b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/visitor.rb new file mode 100644 index 0000000..c3d29f5 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/lib/prism/visitor.rb @@ -0,0 +1,813 @@ +# frozen_string_literal: true +# :markup: markdown + +=begin +-- +This file is generated by the templates/template.rb script and should not be +modified manually. See templates/lib/prism/visitor.rb.erb +if you are looking to modify the template +++ +=end + +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.each_child_node { |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 + # Visit a AliasGlobalVariableNode node + def visit_alias_global_variable_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a AliasMethodNode node + def visit_alias_method_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a AlternationPatternNode node + def visit_alternation_pattern_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a AndNode node + def visit_and_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a ArgumentsNode node + def visit_arguments_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a ArrayNode node + def visit_array_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a ArrayPatternNode node + def visit_array_pattern_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a AssocNode node + def visit_assoc_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a AssocSplatNode node + def visit_assoc_splat_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a BackReferenceReadNode node + def visit_back_reference_read_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a BeginNode node + def visit_begin_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a BlockArgumentNode node + def visit_block_argument_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a BlockLocalVariableNode node + def visit_block_local_variable_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a BlockNode node + def visit_block_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a BlockParameterNode node + def visit_block_parameter_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a BlockParametersNode node + def visit_block_parameters_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a BreakNode node + def visit_break_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a CallAndWriteNode node + def visit_call_and_write_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a CallNode node + def visit_call_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a CallOperatorWriteNode node + def visit_call_operator_write_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a CallOrWriteNode node + def visit_call_or_write_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a CallTargetNode node + def visit_call_target_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a CapturePatternNode node + def visit_capture_pattern_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a CaseMatchNode node + def visit_case_match_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a CaseNode node + def visit_case_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a ClassNode node + def visit_class_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a ClassVariableAndWriteNode node + def visit_class_variable_and_write_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a ClassVariableOperatorWriteNode node + def visit_class_variable_operator_write_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a ClassVariableOrWriteNode node + def visit_class_variable_or_write_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a ClassVariableReadNode node + def visit_class_variable_read_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a ClassVariableTargetNode node + def visit_class_variable_target_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a ClassVariableWriteNode node + def visit_class_variable_write_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a ConstantAndWriteNode node + def visit_constant_and_write_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a ConstantOperatorWriteNode node + def visit_constant_operator_write_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a ConstantOrWriteNode node + def visit_constant_or_write_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a ConstantPathAndWriteNode node + def visit_constant_path_and_write_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a ConstantPathNode node + def visit_constant_path_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a ConstantPathOperatorWriteNode node + def visit_constant_path_operator_write_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a ConstantPathOrWriteNode node + def visit_constant_path_or_write_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a ConstantPathTargetNode node + def visit_constant_path_target_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a ConstantPathWriteNode node + def visit_constant_path_write_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a ConstantReadNode node + def visit_constant_read_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a ConstantTargetNode node + def visit_constant_target_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a ConstantWriteNode node + def visit_constant_write_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a DefNode node + def visit_def_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a DefinedNode node + def visit_defined_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a ElseNode node + def visit_else_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a EmbeddedStatementsNode node + def visit_embedded_statements_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a EmbeddedVariableNode node + def visit_embedded_variable_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a EnsureNode node + def visit_ensure_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a FalseNode node + def visit_false_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a FindPatternNode node + def visit_find_pattern_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a FlipFlopNode node + def visit_flip_flop_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a FloatNode node + def visit_float_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a ForNode node + def visit_for_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a ForwardingArgumentsNode node + def visit_forwarding_arguments_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a ForwardingParameterNode node + def visit_forwarding_parameter_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a ForwardingSuperNode node + def visit_forwarding_super_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a GlobalVariableAndWriteNode node + def visit_global_variable_and_write_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a GlobalVariableOperatorWriteNode node + def visit_global_variable_operator_write_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a GlobalVariableOrWriteNode node + def visit_global_variable_or_write_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a GlobalVariableReadNode node + def visit_global_variable_read_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a GlobalVariableTargetNode node + def visit_global_variable_target_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a GlobalVariableWriteNode node + def visit_global_variable_write_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a HashNode node + def visit_hash_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a HashPatternNode node + def visit_hash_pattern_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a IfNode node + def visit_if_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a ImaginaryNode node + def visit_imaginary_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a ImplicitNode node + def visit_implicit_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a ImplicitRestNode node + def visit_implicit_rest_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a InNode node + def visit_in_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a IndexAndWriteNode node + def visit_index_and_write_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a IndexOperatorWriteNode node + def visit_index_operator_write_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a IndexOrWriteNode node + def visit_index_or_write_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a IndexTargetNode node + def visit_index_target_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a InstanceVariableAndWriteNode node + def visit_instance_variable_and_write_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a InstanceVariableOperatorWriteNode node + def visit_instance_variable_operator_write_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a InstanceVariableOrWriteNode node + def visit_instance_variable_or_write_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a InstanceVariableReadNode node + def visit_instance_variable_read_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a InstanceVariableTargetNode node + def visit_instance_variable_target_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a InstanceVariableWriteNode node + def visit_instance_variable_write_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a IntegerNode node + def visit_integer_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a InterpolatedMatchLastLineNode node + def visit_interpolated_match_last_line_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a InterpolatedRegularExpressionNode node + def visit_interpolated_regular_expression_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a InterpolatedStringNode node + def visit_interpolated_string_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a InterpolatedSymbolNode node + def visit_interpolated_symbol_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a InterpolatedXStringNode node + def visit_interpolated_x_string_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a ItLocalVariableReadNode node + def visit_it_local_variable_read_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a ItParametersNode node + def visit_it_parameters_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a KeywordHashNode node + def visit_keyword_hash_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a KeywordRestParameterNode node + def visit_keyword_rest_parameter_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a LambdaNode node + def visit_lambda_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a LocalVariableAndWriteNode node + def visit_local_variable_and_write_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a LocalVariableOperatorWriteNode node + def visit_local_variable_operator_write_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a LocalVariableOrWriteNode node + def visit_local_variable_or_write_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a LocalVariableReadNode node + def visit_local_variable_read_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a LocalVariableTargetNode node + def visit_local_variable_target_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a LocalVariableWriteNode node + def visit_local_variable_write_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a MatchLastLineNode node + def visit_match_last_line_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a MatchPredicateNode node + def visit_match_predicate_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a MatchRequiredNode node + def visit_match_required_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a MatchWriteNode node + def visit_match_write_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a MissingNode node + def visit_missing_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a ModuleNode node + def visit_module_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a MultiTargetNode node + def visit_multi_target_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a MultiWriteNode node + def visit_multi_write_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a NextNode node + def visit_next_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a NilNode node + def visit_nil_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a NoKeywordsParameterNode node + def visit_no_keywords_parameter_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a NumberedParametersNode node + def visit_numbered_parameters_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a NumberedReferenceReadNode node + def visit_numbered_reference_read_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a OptionalKeywordParameterNode node + def visit_optional_keyword_parameter_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a OptionalParameterNode node + def visit_optional_parameter_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a OrNode node + def visit_or_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a ParametersNode node + def visit_parameters_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a ParenthesesNode node + def visit_parentheses_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a PinnedExpressionNode node + def visit_pinned_expression_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a PinnedVariableNode node + def visit_pinned_variable_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a PostExecutionNode node + def visit_post_execution_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a PreExecutionNode node + def visit_pre_execution_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a ProgramNode node + def visit_program_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a RangeNode node + def visit_range_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a RationalNode node + def visit_rational_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a RedoNode node + def visit_redo_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a RegularExpressionNode node + def visit_regular_expression_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a RequiredKeywordParameterNode node + def visit_required_keyword_parameter_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a RequiredParameterNode node + def visit_required_parameter_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a RescueModifierNode node + def visit_rescue_modifier_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a RescueNode node + def visit_rescue_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a RestParameterNode node + def visit_rest_parameter_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a RetryNode node + def visit_retry_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a ReturnNode node + def visit_return_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a SelfNode node + def visit_self_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a ShareableConstantNode node + def visit_shareable_constant_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a SingletonClassNode node + def visit_singleton_class_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a SourceEncodingNode node + def visit_source_encoding_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a SourceFileNode node + def visit_source_file_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a SourceLineNode node + def visit_source_line_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a SplatNode node + def visit_splat_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a StatementsNode node + def visit_statements_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a StringNode node + def visit_string_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a SuperNode node + def visit_super_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a SymbolNode node + def visit_symbol_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a TrueNode node + def visit_true_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a UndefNode node + def visit_undef_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a UnlessNode node + def visit_unless_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a UntilNode node + def visit_until_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a WhenNode node + def visit_when_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a WhileNode node + def visit_while_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a XStringNode node + def visit_x_string_node(node) + node.each_child_node { |node| node.accept(self) } + end + + # Visit a YieldNode node + def visit_yield_node(node) + node.each_child_node { |node| node.accept(self) } + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/prism.gemspec b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/prism.gemspec new file mode 100644 index 0000000..20c66a5 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/prism.gemspec @@ -0,0 +1,172 @@ +# frozen_string_literal: true + +Gem::Specification.new do |spec| + spec.name = "prism" + spec.version = "1.9.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/relocation.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_strpbrk.h", + "include/prism/version.h", + "lib/prism.rb", + "lib/prism/compiler.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/errors.rb", + "lib/prism/parse_result/newlines.rb", + "lib/prism/pattern.rb", + "lib/prism/polyfill/append_as_bytes.rb", + "lib/prism/polyfill/byteindex.rb", + "lib/prism/polyfill/scan_byte.rb", + "lib/prism/polyfill/unpack1.rb", + "lib/prism/polyfill/warn.rb", + "lib/prism/reflection.rb", + "lib/prism/relocation.rb", + "lib/prism/serialize.rb", + "lib/prism/string_query.rb", + "lib/prism/translation.rb", + "lib/prism/translation/parser.rb", + "lib/prism/translation/parser_current.rb", + "lib/prism/translation/parser_versions.rb", + "lib/prism/translation/parser/builder.rb", + "lib/prism/translation/parser/compiler.rb", + "lib/prism/translation/parser/lexer.rb", + "lib/prism/translation/ripper.rb", + "lib/prism/translation/ripper/filter.rb", + "lib/prism/translation/ripper/lexer.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/dsl.rbi", + "rbi/prism/inspect_visitor.rbi", + "rbi/prism/node_ext.rbi", + "rbi/prism/node.rbi", + "rbi/prism/parse_result.rbi", + "rbi/prism/reflection.rbi", + "rbi/prism/string_query.rbi", + "rbi/prism/translation/parser.rbi", + "rbi/prism/translation/parser_versions.rbi", + "rbi/prism/translation/ripper.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/lex_compat.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/parse_result/comments.rbs", + "sig/prism/pattern.rbs", + "sig/prism/reflection.rbs", + "sig/prism/relocation.rbs", + "sig/prism/serialize.rbs", + "sig/prism/string_query.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.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/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/rbi/prism.rbi b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/rbi/prism.rbi new file mode 100644 index 0000000..b31adc6 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/rbi/prism.rbi @@ -0,0 +1,63 @@ +# typed: strict + +module Prism + sig { params(source: String, command_line: T.nilable(String), encoding: T.nilable(T.any(FalseClass, Encoding)), filepath: T.nilable(String), freeze: T.nilable(T::Boolean), frozen_string_literal: T.nilable(T::Boolean), line: T.nilable(Integer), main_script: T.nilable(T::Boolean), partial_script: T.nilable(T::Boolean), scopes: T.nilable(T::Array[T::Array[Symbol]]), version: T.nilable(String)).returns(String) } + def self.dump(source, command_line: nil, encoding: nil, filepath: nil, freeze: nil, frozen_string_literal: nil, line: nil, main_script: nil, partial_script: nil, scopes: nil, version: nil); end + + sig { params(filepath: String, command_line: T.nilable(String), encoding: T.nilable(T.any(FalseClass, Encoding)), freeze: T.nilable(T::Boolean), frozen_string_literal: T.nilable(T::Boolean), line: T.nilable(Integer), main_script: T.nilable(T::Boolean), partial_script: T.nilable(T::Boolean), scopes: T.nilable(T::Array[T::Array[Symbol]]), version: T.nilable(String)).returns(String) } + def self.dump_file(filepath, command_line: nil, encoding: nil, freeze: nil, frozen_string_literal: nil, line: nil, main_script: nil, partial_script: nil, scopes: nil, version: nil); end + + sig { params(source: String, command_line: T.nilable(String), encoding: T.nilable(T.any(FalseClass, Encoding)), filepath: T.nilable(String), freeze: T.nilable(T::Boolean), frozen_string_literal: T.nilable(T::Boolean), line: T.nilable(Integer), main_script: T.nilable(T::Boolean), partial_script: T.nilable(T::Boolean), scopes: T.nilable(T::Array[T::Array[Symbol]]), version: T.nilable(String)).returns(Prism::LexResult) } + def self.lex(source, command_line: nil, encoding: nil, filepath: nil, freeze: nil, frozen_string_literal: nil, line: nil, main_script: nil, partial_script: nil, scopes: nil, version: nil); end + + sig { params(filepath: String, command_line: T.nilable(String), encoding: T.nilable(T.any(FalseClass, Encoding)), freeze: T.nilable(T::Boolean), frozen_string_literal: T.nilable(T::Boolean), line: T.nilable(Integer), main_script: T.nilable(T::Boolean), partial_script: T.nilable(T::Boolean), scopes: T.nilable(T::Array[T::Array[Symbol]]), version: T.nilable(String)).returns(Prism::LexResult) } + def self.lex_file(filepath, command_line: nil, encoding: nil, freeze: nil, frozen_string_literal: nil, line: nil, main_script: nil, partial_script: nil, scopes: nil, version: nil); end + + sig { params(source: String, options: T::Hash[Symbol, T.untyped]).returns(Prism::LexCompat::Result) } + def self.lex_compat(source, **options); end + + sig { params(source: String, serialized: String, freeze: T.nilable(T::Boolean)).returns(Prism::ParseResult) } + def self.load(source, serialized, freeze = false); end + + sig { params(source: String, command_line: T.nilable(String), encoding: T.nilable(T.any(FalseClass, Encoding)), filepath: T.nilable(String), freeze: T.nilable(T::Boolean), frozen_string_literal: T.nilable(T::Boolean), line: T.nilable(Integer), main_script: T.nilable(T::Boolean), partial_script: T.nilable(T::Boolean), scopes: T.nilable(T::Array[T::Array[Symbol]]), version: T.nilable(String)).returns(Prism::ParseResult) } + def self.parse(source, command_line: nil, encoding: nil, filepath: nil, freeze: nil, frozen_string_literal: nil, line: nil, main_script: nil, partial_script: nil, scopes: nil, version: nil); end + + sig { params(filepath: String, command_line: T.nilable(String), encoding: T.nilable(T.any(FalseClass, Encoding)), freeze: T.nilable(T::Boolean), frozen_string_literal: T.nilable(T::Boolean), line: T.nilable(Integer), main_script: T.nilable(T::Boolean), partial_script: T.nilable(T::Boolean), scopes: T.nilable(T::Array[T::Array[Symbol]]), version: T.nilable(String)).returns(Prism::ParseResult) } + def self.parse_file(filepath, command_line: nil, encoding: nil, freeze: nil, frozen_string_literal: nil, line: nil, main_script: nil, partial_script: nil, scopes: nil, version: nil); end + + sig { params(source: String, command_line: T.nilable(String), encoding: T.nilable(T.any(FalseClass, Encoding)), filepath: T.nilable(String), freeze: T.nilable(T::Boolean), frozen_string_literal: T.nilable(T::Boolean), line: T.nilable(Integer), main_script: T.nilable(T::Boolean), partial_script: T.nilable(T::Boolean), scopes: T.nilable(T::Array[T::Array[Symbol]]), version: T.nilable(String)).void } + def self.profile(source, command_line: nil, encoding: nil, filepath: nil, freeze: nil, frozen_string_literal: nil, line: nil, main_script: nil, partial_script: nil, scopes: nil, version: nil); end + + sig { params(filepath: String, command_line: T.nilable(String), encoding: T.nilable(T.any(FalseClass, Encoding)), freeze: T.nilable(T::Boolean), frozen_string_literal: T.nilable(T::Boolean), line: T.nilable(Integer), main_script: T.nilable(T::Boolean), partial_script: T.nilable(T::Boolean), scopes: T.nilable(T::Array[T::Array[Symbol]]), version: T.nilable(String)).void } + def self.profile_file(filepath, command_line: nil, encoding: nil, freeze: nil, frozen_string_literal: nil, line: nil, main_script: nil, partial_script: nil, scopes: nil, version: nil); end + + sig { params(stream: T.any(IO, StringIO), command_line: T.nilable(String), encoding: T.nilable(T.any(FalseClass, Encoding)), filepath: T.nilable(String), freeze: T.nilable(T::Boolean), frozen_string_literal: T.nilable(T::Boolean), line: T.nilable(Integer), main_script: T.nilable(T::Boolean), partial_script: T.nilable(T::Boolean), scopes: T.nilable(T::Array[T::Array[Symbol]]), version: T.nilable(String)).returns(Prism::ParseResult) } + def self.parse_stream(stream, command_line: nil, encoding: nil, filepath: nil, freeze: nil, frozen_string_literal: nil, line: nil, main_script: nil, partial_script: nil, scopes: nil, version: nil); end + + sig { params(source: String, command_line: T.nilable(String), encoding: T.nilable(T.any(String, Encoding)), filepath: T.nilable(String), freeze: T.nilable(T::Boolean), frozen_string_literal: T.nilable(T::Boolean), line: T.nilable(Integer), main_script: T.nilable(T::Boolean), partial_script: T.nilable(T::Boolean), scopes: T.nilable(T::Array[T::Array[Symbol]]), version: T.nilable(String)).returns(T::Array[Prism::Comment]) } + def self.parse_comments(source, command_line: nil, encoding: nil, filepath: nil, freeze: nil, frozen_string_literal: nil, line: nil, main_script: nil, partial_script: nil, scopes: nil, version: nil); end + + sig { params(filepath: String, command_line: T.nilable(String), encoding: T.nilable(T.any(FalseClass, Encoding)), freeze: T.nilable(T::Boolean), frozen_string_literal: T.nilable(T::Boolean), line: T.nilable(Integer), main_script: T.nilable(T::Boolean), partial_script: T.nilable(T::Boolean), scopes: T.nilable(T::Array[T::Array[Symbol]]), version: T.nilable(String)).returns(T::Array[Prism::Comment]) } + def self.parse_file_comments(filepath, command_line: nil, encoding: nil, freeze: nil, frozen_string_literal: nil, line: nil, main_script: nil, partial_script: nil, scopes: nil, version: nil); end + + sig { params(source: String, command_line: T.nilable(String), encoding: T.nilable(T.any(FalseClass, Encoding)), filepath: T.nilable(String), freeze: T.nilable(T::Boolean), frozen_string_literal: T.nilable(T::Boolean), line: T.nilable(Integer), main_script: T.nilable(T::Boolean), partial_script: T.nilable(T::Boolean), scopes: T.nilable(T::Array[T::Array[Symbol]]), version: T.nilable(String)).returns(Prism::ParseLexResult) } + def self.parse_lex(source, command_line: nil, encoding: nil, filepath: nil, freeze: nil, frozen_string_literal: nil, line: nil, main_script: nil, partial_script: nil, scopes: nil, version: nil); end + + sig { params(filepath: String, command_line: T.nilable(String), encoding: T.nilable(T.any(FalseClass, Encoding)), freeze: T.nilable(T::Boolean), frozen_string_literal: T.nilable(T::Boolean), line: T.nilable(Integer), main_script: T.nilable(T::Boolean), partial_script: T.nilable(T::Boolean), scopes: T.nilable(T::Array[T::Array[Symbol]]), version: T.nilable(String)).returns(Prism::ParseLexResult) } + def self.parse_lex_file(filepath, command_line: nil, encoding: nil, freeze: nil, frozen_string_literal: nil, line: nil, main_script: nil, partial_script: nil, scopes: nil, version: nil); end + + sig { params(source: String, command_line: T.nilable(String), encoding: T.nilable(T.any(FalseClass, Encoding)), filepath: T.nilable(String), freeze: T.nilable(T::Boolean), frozen_string_literal: T.nilable(T::Boolean), line: T.nilable(Integer), main_script: T.nilable(T::Boolean), partial_script: T.nilable(T::Boolean), scopes: T.nilable(T::Array[T::Array[Symbol]]), version: T.nilable(String)).returns(T::Boolean) } + def self.parse_success?(source, command_line: nil, encoding: nil, filepath: nil, freeze: nil, frozen_string_literal: nil, line: nil, main_script: nil, partial_script: nil, scopes: nil, version: nil); end + + sig { params(source: String, command_line: T.nilable(String), encoding: T.nilable(T.any(FalseClass, Encoding)), filepath: T.nilable(String), freeze: T.nilable(T::Boolean), frozen_string_literal: T.nilable(T::Boolean), line: T.nilable(Integer), main_script: T.nilable(T::Boolean), partial_script: T.nilable(T::Boolean), scopes: T.nilable(T::Array[T::Array[Symbol]]), version: T.nilable(String)).returns(T::Boolean) } + def self.parse_failure?(source, command_line: nil, encoding: nil, filepath: nil, freeze: nil, frozen_string_literal: nil, line: nil, main_script: nil, partial_script: nil, scopes: nil, version: nil); end + + sig { params(filepath: String, command_line: T.nilable(String), encoding: T.nilable(T.any(FalseClass, Encoding)), freeze: T.nilable(T::Boolean), frozen_string_literal: T.nilable(T::Boolean), line: T.nilable(Integer), main_script: T.nilable(T::Boolean), partial_script: T.nilable(T::Boolean), scopes: T.nilable(T::Array[T::Array[Symbol]]), version: T.nilable(String)).returns(T::Boolean) } + def self.parse_file_success?(filepath, command_line: nil, encoding: nil, freeze: nil, frozen_string_literal: nil, line: nil, main_script: nil, partial_script: nil, scopes: nil, version: nil); end + + sig { params(filepath: String, command_line: T.nilable(String), encoding: T.nilable(T.any(FalseClass, Encoding)), freeze: T.nilable(T::Boolean), frozen_string_literal: T.nilable(T::Boolean), line: T.nilable(Integer), main_script: T.nilable(T::Boolean), partial_script: T.nilable(T::Boolean), scopes: T.nilable(T::Array[T::Array[Symbol]]), version: T.nilable(String)).returns(T::Boolean) } + def self.parse_file_failure?(filepath, command_line: nil, encoding: nil, freeze: nil, frozen_string_literal: nil, line: nil, main_script: nil, partial_script: nil, scopes: nil, version: nil); end + + sig { params(locals: T::Array[Symbol], forwarding: T::Array[Symbol]).returns(Prism::Scope) } + def self.scope(locals: [], forwarding: []); end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/rbi/prism/compiler.rbi b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/rbi/prism/compiler.rbi new file mode 100644 index 0000000..c738236 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/rbi/prism/compiler.rbi @@ -0,0 +1,12 @@ +# typed: strict + +class Prism::Compiler + sig { params(node: T.nilable(Prism::Node)).returns(T.untyped) } + def visit(node); end + + sig { params(nodes: T::Array[T.nilable(Prism::Node)]).returns(T::Array[T.untyped]) } + def visit_all(nodes); end + + sig { params(node: Prism::Node).returns(T::Array[T.untyped]) } + def visit_child_nodes(node); end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/rbi/prism/dsl.rbi b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/rbi/prism/dsl.rbi new file mode 100644 index 0000000..925a341 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/rbi/prism/dsl.rbi @@ -0,0 +1,524 @@ +# typed: strict + +=begin +This file is generated by the templates/template.rb script and should not be +modified manually. See templates/rbi/prism/dsl.rbi.erb +if you are looking to modify the template +=end + +module Prism::DSL + sig { params(string: String).returns(Prism::Source) } + def source(string); end + + sig { params(source: Prism::Source, start_offset: Integer, length: Integer).returns(Prism::Location) } + def location(source: default_source, start_offset: 0, length: 0); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, new_name: T.any(Prism::GlobalVariableReadNode, Prism::BackReferenceReadNode, Prism::NumberedReferenceReadNode), old_name: T.any(Prism::GlobalVariableReadNode, Prism::BackReferenceReadNode, Prism::NumberedReferenceReadNode, Prism::SymbolNode, Prism::MissingNode), keyword_loc: Prism::Location).returns(Prism::AliasGlobalVariableNode) } + def alias_global_variable_node(source: default_source, node_id: 0, location: default_location, flags: 0, new_name: global_variable_read_node(source: source), old_name: global_variable_read_node(source: source), keyword_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, new_name: T.any(Prism::SymbolNode, Prism::InterpolatedSymbolNode), old_name: T.any(Prism::SymbolNode, Prism::InterpolatedSymbolNode, Prism::GlobalVariableReadNode, Prism::MissingNode), keyword_loc: Prism::Location).returns(Prism::AliasMethodNode) } + def alias_method_node(source: default_source, node_id: 0, location: default_location, flags: 0, new_name: symbol_node(source: source), old_name: symbol_node(source: source), keyword_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, left: Prism::Node, right: Prism::Node, operator_loc: Prism::Location).returns(Prism::AlternationPatternNode) } + def alternation_pattern_node(source: default_source, node_id: 0, location: default_location, flags: 0, left: default_node(source, location), right: default_node(source, location), operator_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, left: Prism::Node, right: Prism::Node, operator_loc: Prism::Location).returns(Prism::AndNode) } + def and_node(source: default_source, node_id: 0, location: default_location, flags: 0, left: default_node(source, location), right: default_node(source, location), operator_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, arguments: T::Array[Prism::Node]).returns(Prism::ArgumentsNode) } + def arguments_node(source: default_source, node_id: 0, location: default_location, flags: 0, arguments: []); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, elements: T::Array[Prism::Node], opening_loc: T.nilable(Prism::Location), closing_loc: T.nilable(Prism::Location)).returns(Prism::ArrayNode) } + def array_node(source: default_source, node_id: 0, location: default_location, flags: 0, elements: [], opening_loc: nil, closing_loc: nil); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, constant: T.nilable(T.any(Prism::ConstantPathNode, Prism::ConstantReadNode)), requireds: T::Array[Prism::Node], rest: T.nilable(Prism::Node), posts: T::Array[Prism::Node], opening_loc: T.nilable(Prism::Location), closing_loc: T.nilable(Prism::Location)).returns(Prism::ArrayPatternNode) } + def array_pattern_node(source: default_source, node_id: 0, location: default_location, flags: 0, constant: nil, requireds: [], rest: nil, posts: [], opening_loc: nil, closing_loc: nil); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, key: Prism::Node, value: Prism::Node, operator_loc: T.nilable(Prism::Location)).returns(Prism::AssocNode) } + def assoc_node(source: default_source, node_id: 0, location: default_location, flags: 0, key: default_node(source, location), value: default_node(source, location), operator_loc: nil); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, value: T.nilable(Prism::Node), operator_loc: Prism::Location).returns(Prism::AssocSplatNode) } + def assoc_splat_node(source: default_source, node_id: 0, location: default_location, flags: 0, value: nil, operator_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol).returns(Prism::BackReferenceReadNode) } + def back_reference_read_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :""); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, begin_keyword_loc: T.nilable(Prism::Location), statements: T.nilable(Prism::StatementsNode), rescue_clause: T.nilable(Prism::RescueNode), else_clause: T.nilable(Prism::ElseNode), ensure_clause: T.nilable(Prism::EnsureNode), end_keyword_loc: T.nilable(Prism::Location)).returns(Prism::BeginNode) } + def begin_node(source: default_source, node_id: 0, location: default_location, flags: 0, begin_keyword_loc: nil, statements: nil, rescue_clause: nil, else_clause: nil, ensure_clause: nil, end_keyword_loc: nil); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, expression: T.nilable(Prism::Node), operator_loc: Prism::Location).returns(Prism::BlockArgumentNode) } + def block_argument_node(source: default_source, node_id: 0, location: default_location, flags: 0, expression: nil, operator_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol).returns(Prism::BlockLocalVariableNode) } + def block_local_variable_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :""); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, locals: T::Array[Symbol], parameters: T.nilable(T.any(Prism::BlockParametersNode, Prism::NumberedParametersNode, Prism::ItParametersNode)), body: T.nilable(T.any(Prism::StatementsNode, Prism::BeginNode)), opening_loc: Prism::Location, closing_loc: Prism::Location).returns(Prism::BlockNode) } + def block_node(source: default_source, node_id: 0, location: default_location, flags: 0, locals: [], parameters: nil, body: nil, opening_loc: location, closing_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: T.nilable(Symbol), name_loc: T.nilable(Prism::Location), operator_loc: Prism::Location).returns(Prism::BlockParameterNode) } + def block_parameter_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: nil, name_loc: nil, operator_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, parameters: T.nilable(Prism::ParametersNode), locals: T::Array[Prism::BlockLocalVariableNode], opening_loc: T.nilable(Prism::Location), closing_loc: T.nilable(Prism::Location)).returns(Prism::BlockParametersNode) } + def block_parameters_node(source: default_source, node_id: 0, location: default_location, flags: 0, parameters: nil, locals: [], opening_loc: nil, closing_loc: nil); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, arguments: T.nilable(Prism::ArgumentsNode), keyword_loc: Prism::Location).returns(Prism::BreakNode) } + def break_node(source: default_source, node_id: 0, location: default_location, flags: 0, arguments: nil, keyword_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, receiver: T.nilable(Prism::Node), call_operator_loc: T.nilable(Prism::Location), message_loc: T.nilable(Prism::Location), read_name: Symbol, write_name: Symbol, operator_loc: Prism::Location, value: Prism::Node).returns(Prism::CallAndWriteNode) } + def call_and_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, receiver: nil, call_operator_loc: nil, message_loc: nil, read_name: :"", write_name: :"", operator_loc: location, value: default_node(source, location)); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, receiver: T.nilable(Prism::Node), call_operator_loc: T.nilable(Prism::Location), name: Symbol, message_loc: T.nilable(Prism::Location), opening_loc: T.nilable(Prism::Location), arguments: T.nilable(Prism::ArgumentsNode), closing_loc: T.nilable(Prism::Location), equal_loc: T.nilable(Prism::Location), block: T.nilable(T.any(Prism::BlockNode, Prism::BlockArgumentNode))).returns(Prism::CallNode) } + def call_node(source: default_source, node_id: 0, location: default_location, flags: 0, receiver: nil, call_operator_loc: nil, name: :"", message_loc: nil, opening_loc: nil, arguments: nil, closing_loc: nil, equal_loc: nil, block: nil); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, receiver: T.nilable(Prism::Node), call_operator_loc: T.nilable(Prism::Location), message_loc: T.nilable(Prism::Location), read_name: Symbol, write_name: Symbol, binary_operator: Symbol, binary_operator_loc: Prism::Location, value: Prism::Node).returns(Prism::CallOperatorWriteNode) } + def call_operator_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, receiver: nil, call_operator_loc: nil, message_loc: nil, read_name: :"", write_name: :"", binary_operator: :"", binary_operator_loc: location, value: default_node(source, location)); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, receiver: T.nilable(Prism::Node), call_operator_loc: T.nilable(Prism::Location), message_loc: T.nilable(Prism::Location), read_name: Symbol, write_name: Symbol, operator_loc: Prism::Location, value: Prism::Node).returns(Prism::CallOrWriteNode) } + def call_or_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, receiver: nil, call_operator_loc: nil, message_loc: nil, read_name: :"", write_name: :"", operator_loc: location, value: default_node(source, location)); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, receiver: Prism::Node, call_operator_loc: Prism::Location, name: Symbol, message_loc: Prism::Location).returns(Prism::CallTargetNode) } + def call_target_node(source: default_source, node_id: 0, location: default_location, flags: 0, receiver: default_node(source, location), call_operator_loc: location, name: :"", message_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, value: Prism::Node, target: Prism::LocalVariableTargetNode, operator_loc: Prism::Location).returns(Prism::CapturePatternNode) } + def capture_pattern_node(source: default_source, node_id: 0, location: default_location, flags: 0, value: default_node(source, location), target: local_variable_target_node(source: source), operator_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, predicate: T.nilable(Prism::Node), conditions: T::Array[Prism::InNode], else_clause: T.nilable(Prism::ElseNode), case_keyword_loc: Prism::Location, end_keyword_loc: Prism::Location).returns(Prism::CaseMatchNode) } + def case_match_node(source: default_source, node_id: 0, location: default_location, flags: 0, predicate: nil, conditions: [], else_clause: nil, case_keyword_loc: location, end_keyword_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, predicate: T.nilable(Prism::Node), conditions: T::Array[Prism::WhenNode], else_clause: T.nilable(Prism::ElseNode), case_keyword_loc: Prism::Location, end_keyword_loc: Prism::Location).returns(Prism::CaseNode) } + def case_node(source: default_source, node_id: 0, location: default_location, flags: 0, predicate: nil, conditions: [], else_clause: nil, case_keyword_loc: location, end_keyword_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, locals: T::Array[Symbol], class_keyword_loc: Prism::Location, constant_path: T.any(Prism::ConstantReadNode, Prism::ConstantPathNode, Prism::CallNode), inheritance_operator_loc: T.nilable(Prism::Location), superclass: T.nilable(Prism::Node), body: T.nilable(T.any(Prism::StatementsNode, Prism::BeginNode)), end_keyword_loc: Prism::Location, name: Symbol).returns(Prism::ClassNode) } + def class_node(source: default_source, node_id: 0, location: default_location, flags: 0, locals: [], class_keyword_loc: location, constant_path: constant_read_node(source: source), inheritance_operator_loc: nil, superclass: nil, body: nil, end_keyword_loc: location, name: :""); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, operator_loc: Prism::Location, value: Prism::Node).returns(Prism::ClassVariableAndWriteNode) } + def class_variable_and_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", name_loc: location, operator_loc: location, value: default_node(source, location)); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, binary_operator_loc: Prism::Location, value: Prism::Node, binary_operator: Symbol).returns(Prism::ClassVariableOperatorWriteNode) } + def class_variable_operator_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", name_loc: location, binary_operator_loc: location, value: default_node(source, location), binary_operator: :""); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, operator_loc: Prism::Location, value: Prism::Node).returns(Prism::ClassVariableOrWriteNode) } + def class_variable_or_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", name_loc: location, operator_loc: location, value: default_node(source, location)); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol).returns(Prism::ClassVariableReadNode) } + def class_variable_read_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :""); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol).returns(Prism::ClassVariableTargetNode) } + def class_variable_target_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :""); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, value: Prism::Node, operator_loc: Prism::Location).returns(Prism::ClassVariableWriteNode) } + def class_variable_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", name_loc: location, value: default_node(source, location), operator_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, operator_loc: Prism::Location, value: Prism::Node).returns(Prism::ConstantAndWriteNode) } + def constant_and_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", name_loc: location, operator_loc: location, value: default_node(source, location)); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, binary_operator_loc: Prism::Location, value: Prism::Node, binary_operator: Symbol).returns(Prism::ConstantOperatorWriteNode) } + def constant_operator_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", name_loc: location, binary_operator_loc: location, value: default_node(source, location), binary_operator: :""); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, operator_loc: Prism::Location, value: Prism::Node).returns(Prism::ConstantOrWriteNode) } + def constant_or_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", name_loc: location, operator_loc: location, value: default_node(source, location)); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, target: Prism::ConstantPathNode, operator_loc: Prism::Location, value: Prism::Node).returns(Prism::ConstantPathAndWriteNode) } + def constant_path_and_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, target: constant_path_node(source: source), operator_loc: location, value: default_node(source, location)); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, parent: T.nilable(Prism::Node), name: T.nilable(Symbol), delimiter_loc: Prism::Location, name_loc: Prism::Location).returns(Prism::ConstantPathNode) } + def constant_path_node(source: default_source, node_id: 0, location: default_location, flags: 0, parent: nil, name: nil, delimiter_loc: location, name_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, target: Prism::ConstantPathNode, binary_operator_loc: Prism::Location, value: Prism::Node, binary_operator: Symbol).returns(Prism::ConstantPathOperatorWriteNode) } + def constant_path_operator_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, target: constant_path_node(source: source), binary_operator_loc: location, value: default_node(source, location), binary_operator: :""); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, target: Prism::ConstantPathNode, operator_loc: Prism::Location, value: Prism::Node).returns(Prism::ConstantPathOrWriteNode) } + def constant_path_or_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, target: constant_path_node(source: source), operator_loc: location, value: default_node(source, location)); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, parent: T.nilable(Prism::Node), name: T.nilable(Symbol), delimiter_loc: Prism::Location, name_loc: Prism::Location).returns(Prism::ConstantPathTargetNode) } + def constant_path_target_node(source: default_source, node_id: 0, location: default_location, flags: 0, parent: nil, name: nil, delimiter_loc: location, name_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, target: Prism::ConstantPathNode, operator_loc: Prism::Location, value: Prism::Node).returns(Prism::ConstantPathWriteNode) } + def constant_path_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, target: constant_path_node(source: source), operator_loc: location, value: default_node(source, location)); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol).returns(Prism::ConstantReadNode) } + def constant_read_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :""); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol).returns(Prism::ConstantTargetNode) } + def constant_target_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :""); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, value: Prism::Node, operator_loc: Prism::Location).returns(Prism::ConstantWriteNode) } + def constant_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", name_loc: location, value: default_node(source, location), operator_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, receiver: T.nilable(Prism::Node), parameters: T.nilable(Prism::ParametersNode), body: T.nilable(T.any(Prism::StatementsNode, Prism::BeginNode)), locals: T::Array[Symbol], def_keyword_loc: Prism::Location, operator_loc: T.nilable(Prism::Location), lparen_loc: T.nilable(Prism::Location), rparen_loc: T.nilable(Prism::Location), equal_loc: T.nilable(Prism::Location), end_keyword_loc: T.nilable(Prism::Location)).returns(Prism::DefNode) } + def def_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", name_loc: location, receiver: nil, parameters: nil, body: nil, locals: [], def_keyword_loc: location, operator_loc: nil, lparen_loc: nil, rparen_loc: nil, equal_loc: nil, end_keyword_loc: nil); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, lparen_loc: T.nilable(Prism::Location), value: Prism::Node, rparen_loc: T.nilable(Prism::Location), keyword_loc: Prism::Location).returns(Prism::DefinedNode) } + def defined_node(source: default_source, node_id: 0, location: default_location, flags: 0, lparen_loc: nil, value: default_node(source, location), rparen_loc: nil, keyword_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, else_keyword_loc: Prism::Location, statements: T.nilable(Prism::StatementsNode), end_keyword_loc: T.nilable(Prism::Location)).returns(Prism::ElseNode) } + def else_node(source: default_source, node_id: 0, location: default_location, flags: 0, else_keyword_loc: location, statements: nil, end_keyword_loc: nil); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, opening_loc: Prism::Location, statements: T.nilable(Prism::StatementsNode), closing_loc: Prism::Location).returns(Prism::EmbeddedStatementsNode) } + def embedded_statements_node(source: default_source, node_id: 0, location: default_location, flags: 0, opening_loc: location, statements: nil, closing_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, operator_loc: Prism::Location, variable: T.any(Prism::InstanceVariableReadNode, Prism::ClassVariableReadNode, Prism::GlobalVariableReadNode, Prism::BackReferenceReadNode, Prism::NumberedReferenceReadNode)).returns(Prism::EmbeddedVariableNode) } + def embedded_variable_node(source: default_source, node_id: 0, location: default_location, flags: 0, operator_loc: location, variable: instance_variable_read_node(source: source)); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, ensure_keyword_loc: Prism::Location, statements: T.nilable(Prism::StatementsNode), end_keyword_loc: Prism::Location).returns(Prism::EnsureNode) } + def ensure_node(source: default_source, node_id: 0, location: default_location, flags: 0, ensure_keyword_loc: location, statements: nil, end_keyword_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer).returns(Prism::FalseNode) } + def false_node(source: default_source, node_id: 0, location: default_location, flags: 0); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, constant: T.nilable(T.any(Prism::ConstantPathNode, Prism::ConstantReadNode)), left: Prism::SplatNode, requireds: T::Array[Prism::Node], right: T.any(Prism::SplatNode, Prism::MissingNode), opening_loc: T.nilable(Prism::Location), closing_loc: T.nilable(Prism::Location)).returns(Prism::FindPatternNode) } + def find_pattern_node(source: default_source, node_id: 0, location: default_location, flags: 0, constant: nil, left: splat_node(source: source), requireds: [], right: splat_node(source: source), opening_loc: nil, closing_loc: nil); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, left: T.nilable(Prism::Node), right: T.nilable(Prism::Node), operator_loc: Prism::Location).returns(Prism::FlipFlopNode) } + def flip_flop_node(source: default_source, node_id: 0, location: default_location, flags: 0, left: nil, right: nil, operator_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, value: Float).returns(Prism::FloatNode) } + def float_node(source: default_source, node_id: 0, location: default_location, flags: 0, value: 0.0); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, index: T.any(Prism::LocalVariableTargetNode, Prism::InstanceVariableTargetNode, Prism::ClassVariableTargetNode, Prism::GlobalVariableTargetNode, Prism::ConstantTargetNode, Prism::ConstantPathTargetNode, Prism::CallTargetNode, Prism::IndexTargetNode, Prism::MultiTargetNode, Prism::BackReferenceReadNode, Prism::NumberedReferenceReadNode, Prism::MissingNode), collection: Prism::Node, statements: T.nilable(Prism::StatementsNode), for_keyword_loc: Prism::Location, in_keyword_loc: Prism::Location, do_keyword_loc: T.nilable(Prism::Location), end_keyword_loc: Prism::Location).returns(Prism::ForNode) } + def for_node(source: default_source, node_id: 0, location: default_location, flags: 0, index: local_variable_target_node(source: source), collection: default_node(source, location), statements: nil, for_keyword_loc: location, in_keyword_loc: location, do_keyword_loc: nil, end_keyword_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer).returns(Prism::ForwardingArgumentsNode) } + def forwarding_arguments_node(source: default_source, node_id: 0, location: default_location, flags: 0); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer).returns(Prism::ForwardingParameterNode) } + def forwarding_parameter_node(source: default_source, node_id: 0, location: default_location, flags: 0); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, block: T.nilable(Prism::BlockNode)).returns(Prism::ForwardingSuperNode) } + def forwarding_super_node(source: default_source, node_id: 0, location: default_location, flags: 0, block: nil); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, operator_loc: Prism::Location, value: Prism::Node).returns(Prism::GlobalVariableAndWriteNode) } + def global_variable_and_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", name_loc: location, operator_loc: location, value: default_node(source, location)); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, binary_operator_loc: Prism::Location, value: Prism::Node, binary_operator: Symbol).returns(Prism::GlobalVariableOperatorWriteNode) } + def global_variable_operator_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", name_loc: location, binary_operator_loc: location, value: default_node(source, location), binary_operator: :""); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, operator_loc: Prism::Location, value: Prism::Node).returns(Prism::GlobalVariableOrWriteNode) } + def global_variable_or_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", name_loc: location, operator_loc: location, value: default_node(source, location)); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol).returns(Prism::GlobalVariableReadNode) } + def global_variable_read_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :""); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol).returns(Prism::GlobalVariableTargetNode) } + def global_variable_target_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :""); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, value: Prism::Node, operator_loc: Prism::Location).returns(Prism::GlobalVariableWriteNode) } + def global_variable_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", name_loc: location, value: default_node(source, location), operator_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, opening_loc: Prism::Location, elements: T::Array[T.any(Prism::AssocNode, Prism::AssocSplatNode)], closing_loc: Prism::Location).returns(Prism::HashNode) } + def hash_node(source: default_source, node_id: 0, location: default_location, flags: 0, opening_loc: location, elements: [], closing_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, constant: T.nilable(T.any(Prism::ConstantPathNode, Prism::ConstantReadNode)), elements: T::Array[Prism::AssocNode], rest: T.nilable(T.any(Prism::AssocSplatNode, Prism::NoKeywordsParameterNode)), opening_loc: T.nilable(Prism::Location), closing_loc: T.nilable(Prism::Location)).returns(Prism::HashPatternNode) } + def hash_pattern_node(source: default_source, node_id: 0, location: default_location, flags: 0, constant: nil, elements: [], rest: nil, opening_loc: nil, closing_loc: nil); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, if_keyword_loc: T.nilable(Prism::Location), predicate: Prism::Node, then_keyword_loc: T.nilable(Prism::Location), statements: T.nilable(Prism::StatementsNode), subsequent: T.nilable(T.any(Prism::ElseNode, Prism::IfNode)), end_keyword_loc: T.nilable(Prism::Location)).returns(Prism::IfNode) } + def if_node(source: default_source, node_id: 0, location: default_location, flags: 0, if_keyword_loc: nil, predicate: default_node(source, location), then_keyword_loc: nil, statements: nil, subsequent: nil, end_keyword_loc: nil); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, numeric: T.any(Prism::FloatNode, Prism::IntegerNode, Prism::RationalNode)).returns(Prism::ImaginaryNode) } + def imaginary_node(source: default_source, node_id: 0, location: default_location, flags: 0, numeric: float_node(source: source)); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, value: T.any(Prism::LocalVariableReadNode, Prism::CallNode, Prism::ConstantReadNode, Prism::LocalVariableTargetNode)).returns(Prism::ImplicitNode) } + def implicit_node(source: default_source, node_id: 0, location: default_location, flags: 0, value: local_variable_read_node(source: source)); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer).returns(Prism::ImplicitRestNode) } + def implicit_rest_node(source: default_source, node_id: 0, location: default_location, flags: 0); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, pattern: Prism::Node, statements: T.nilable(Prism::StatementsNode), in_loc: Prism::Location, then_loc: T.nilable(Prism::Location)).returns(Prism::InNode) } + def in_node(source: default_source, node_id: 0, location: default_location, flags: 0, pattern: default_node(source, location), statements: nil, in_loc: location, then_loc: nil); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, receiver: T.nilable(Prism::Node), call_operator_loc: T.nilable(Prism::Location), opening_loc: Prism::Location, arguments: T.nilable(Prism::ArgumentsNode), closing_loc: Prism::Location, block: T.nilable(Prism::BlockArgumentNode), operator_loc: Prism::Location, value: Prism::Node).returns(Prism::IndexAndWriteNode) } + def index_and_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, receiver: nil, call_operator_loc: nil, opening_loc: location, arguments: nil, closing_loc: location, block: nil, operator_loc: location, value: default_node(source, location)); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, receiver: T.nilable(Prism::Node), call_operator_loc: T.nilable(Prism::Location), opening_loc: Prism::Location, arguments: T.nilable(Prism::ArgumentsNode), closing_loc: Prism::Location, block: T.nilable(Prism::BlockArgumentNode), binary_operator: Symbol, binary_operator_loc: Prism::Location, value: Prism::Node).returns(Prism::IndexOperatorWriteNode) } + def index_operator_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, receiver: nil, call_operator_loc: nil, opening_loc: location, arguments: nil, closing_loc: location, block: nil, binary_operator: :"", binary_operator_loc: location, value: default_node(source, location)); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, receiver: T.nilable(Prism::Node), call_operator_loc: T.nilable(Prism::Location), opening_loc: Prism::Location, arguments: T.nilable(Prism::ArgumentsNode), closing_loc: Prism::Location, block: T.nilable(Prism::BlockArgumentNode), operator_loc: Prism::Location, value: Prism::Node).returns(Prism::IndexOrWriteNode) } + def index_or_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, receiver: nil, call_operator_loc: nil, opening_loc: location, arguments: nil, closing_loc: location, block: nil, operator_loc: location, value: default_node(source, location)); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, receiver: Prism::Node, opening_loc: Prism::Location, arguments: T.nilable(Prism::ArgumentsNode), closing_loc: Prism::Location, block: T.nilable(Prism::BlockArgumentNode)).returns(Prism::IndexTargetNode) } + def index_target_node(source: default_source, node_id: 0, location: default_location, flags: 0, receiver: default_node(source, location), opening_loc: location, arguments: nil, closing_loc: location, block: nil); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, operator_loc: Prism::Location, value: Prism::Node).returns(Prism::InstanceVariableAndWriteNode) } + def instance_variable_and_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", name_loc: location, operator_loc: location, value: default_node(source, location)); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, binary_operator_loc: Prism::Location, value: Prism::Node, binary_operator: Symbol).returns(Prism::InstanceVariableOperatorWriteNode) } + def instance_variable_operator_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", name_loc: location, binary_operator_loc: location, value: default_node(source, location), binary_operator: :""); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, operator_loc: Prism::Location, value: Prism::Node).returns(Prism::InstanceVariableOrWriteNode) } + def instance_variable_or_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", name_loc: location, operator_loc: location, value: default_node(source, location)); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol).returns(Prism::InstanceVariableReadNode) } + def instance_variable_read_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :""); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol).returns(Prism::InstanceVariableTargetNode) } + def instance_variable_target_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :""); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, value: Prism::Node, operator_loc: Prism::Location).returns(Prism::InstanceVariableWriteNode) } + def instance_variable_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", name_loc: location, value: default_node(source, location), operator_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, value: Integer).returns(Prism::IntegerNode) } + def integer_node(source: default_source, node_id: 0, location: default_location, flags: 0, value: 0); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, opening_loc: Prism::Location, parts: T::Array[T.any(Prism::StringNode, Prism::EmbeddedStatementsNode, Prism::EmbeddedVariableNode)], closing_loc: Prism::Location).returns(Prism::InterpolatedMatchLastLineNode) } + def interpolated_match_last_line_node(source: default_source, node_id: 0, location: default_location, flags: 0, opening_loc: location, parts: [], closing_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, opening_loc: Prism::Location, parts: T::Array[T.any(Prism::StringNode, Prism::EmbeddedStatementsNode, Prism::EmbeddedVariableNode)], closing_loc: Prism::Location).returns(Prism::InterpolatedRegularExpressionNode) } + def interpolated_regular_expression_node(source: default_source, node_id: 0, location: default_location, flags: 0, opening_loc: location, parts: [], closing_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, opening_loc: T.nilable(Prism::Location), parts: T::Array[T.any(Prism::StringNode, Prism::EmbeddedStatementsNode, Prism::EmbeddedVariableNode, Prism::InterpolatedStringNode, Prism::XStringNode, Prism::InterpolatedXStringNode, Prism::SymbolNode, Prism::InterpolatedSymbolNode)], closing_loc: T.nilable(Prism::Location)).returns(Prism::InterpolatedStringNode) } + def interpolated_string_node(source: default_source, node_id: 0, location: default_location, flags: 0, opening_loc: nil, parts: [], closing_loc: nil); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, opening_loc: T.nilable(Prism::Location), parts: T::Array[T.any(Prism::StringNode, Prism::EmbeddedStatementsNode, Prism::EmbeddedVariableNode)], closing_loc: T.nilable(Prism::Location)).returns(Prism::InterpolatedSymbolNode) } + def interpolated_symbol_node(source: default_source, node_id: 0, location: default_location, flags: 0, opening_loc: nil, parts: [], closing_loc: nil); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, opening_loc: Prism::Location, parts: T::Array[T.any(Prism::StringNode, Prism::EmbeddedStatementsNode, Prism::EmbeddedVariableNode)], closing_loc: Prism::Location).returns(Prism::InterpolatedXStringNode) } + def interpolated_x_string_node(source: default_source, node_id: 0, location: default_location, flags: 0, opening_loc: location, parts: [], closing_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer).returns(Prism::ItLocalVariableReadNode) } + def it_local_variable_read_node(source: default_source, node_id: 0, location: default_location, flags: 0); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer).returns(Prism::ItParametersNode) } + def it_parameters_node(source: default_source, node_id: 0, location: default_location, flags: 0); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, elements: T::Array[T.any(Prism::AssocNode, Prism::AssocSplatNode)]).returns(Prism::KeywordHashNode) } + def keyword_hash_node(source: default_source, node_id: 0, location: default_location, flags: 0, elements: []); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: T.nilable(Symbol), name_loc: T.nilable(Prism::Location), operator_loc: Prism::Location).returns(Prism::KeywordRestParameterNode) } + def keyword_rest_parameter_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: nil, name_loc: nil, operator_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, locals: T::Array[Symbol], operator_loc: Prism::Location, opening_loc: Prism::Location, closing_loc: Prism::Location, parameters: T.nilable(T.any(Prism::BlockParametersNode, Prism::NumberedParametersNode, Prism::ItParametersNode)), body: T.nilable(T.any(Prism::StatementsNode, Prism::BeginNode))).returns(Prism::LambdaNode) } + def lambda_node(source: default_source, node_id: 0, location: default_location, flags: 0, locals: [], operator_loc: location, opening_loc: location, closing_loc: location, parameters: nil, body: nil); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name_loc: Prism::Location, operator_loc: Prism::Location, value: Prism::Node, name: Symbol, depth: Integer).returns(Prism::LocalVariableAndWriteNode) } + def local_variable_and_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, name_loc: location, operator_loc: location, value: default_node(source, location), name: :"", depth: 0); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name_loc: Prism::Location, binary_operator_loc: Prism::Location, value: Prism::Node, name: Symbol, binary_operator: Symbol, depth: Integer).returns(Prism::LocalVariableOperatorWriteNode) } + def local_variable_operator_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, name_loc: location, binary_operator_loc: location, value: default_node(source, location), name: :"", binary_operator: :"", depth: 0); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name_loc: Prism::Location, operator_loc: Prism::Location, value: Prism::Node, name: Symbol, depth: Integer).returns(Prism::LocalVariableOrWriteNode) } + def local_variable_or_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, name_loc: location, operator_loc: location, value: default_node(source, location), name: :"", depth: 0); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, depth: Integer).returns(Prism::LocalVariableReadNode) } + def local_variable_read_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", depth: 0); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, depth: Integer).returns(Prism::LocalVariableTargetNode) } + def local_variable_target_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", depth: 0); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, depth: Integer, name_loc: Prism::Location, value: Prism::Node, operator_loc: Prism::Location).returns(Prism::LocalVariableWriteNode) } + def local_variable_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", depth: 0, name_loc: location, value: default_node(source, location), operator_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, opening_loc: Prism::Location, content_loc: Prism::Location, closing_loc: Prism::Location, unescaped: String).returns(Prism::MatchLastLineNode) } + def match_last_line_node(source: default_source, node_id: 0, location: default_location, flags: 0, opening_loc: location, content_loc: location, closing_loc: location, unescaped: ""); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, value: Prism::Node, pattern: Prism::Node, operator_loc: Prism::Location).returns(Prism::MatchPredicateNode) } + def match_predicate_node(source: default_source, node_id: 0, location: default_location, flags: 0, value: default_node(source, location), pattern: default_node(source, location), operator_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, value: Prism::Node, pattern: Prism::Node, operator_loc: Prism::Location).returns(Prism::MatchRequiredNode) } + def match_required_node(source: default_source, node_id: 0, location: default_location, flags: 0, value: default_node(source, location), pattern: default_node(source, location), operator_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, call: Prism::CallNode, targets: T::Array[Prism::LocalVariableTargetNode]).returns(Prism::MatchWriteNode) } + def match_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, call: call_node(source: source), targets: []); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer).returns(Prism::MissingNode) } + def missing_node(source: default_source, node_id: 0, location: default_location, flags: 0); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, locals: T::Array[Symbol], module_keyword_loc: Prism::Location, constant_path: T.any(Prism::ConstantReadNode, Prism::ConstantPathNode, Prism::MissingNode), body: T.nilable(T.any(Prism::StatementsNode, Prism::BeginNode)), end_keyword_loc: Prism::Location, name: Symbol).returns(Prism::ModuleNode) } + def module_node(source: default_source, node_id: 0, location: default_location, flags: 0, locals: [], module_keyword_loc: location, constant_path: constant_read_node(source: source), body: nil, end_keyword_loc: location, name: :""); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, lefts: T::Array[T.any(Prism::LocalVariableTargetNode, Prism::InstanceVariableTargetNode, Prism::ClassVariableTargetNode, Prism::GlobalVariableTargetNode, Prism::ConstantTargetNode, Prism::ConstantPathTargetNode, Prism::CallTargetNode, Prism::IndexTargetNode, Prism::MultiTargetNode, Prism::RequiredParameterNode, Prism::BackReferenceReadNode, Prism::NumberedReferenceReadNode)], rest: T.nilable(T.any(Prism::ImplicitRestNode, Prism::SplatNode)), rights: T::Array[T.any(Prism::LocalVariableTargetNode, Prism::InstanceVariableTargetNode, Prism::ClassVariableTargetNode, Prism::GlobalVariableTargetNode, Prism::ConstantTargetNode, Prism::ConstantPathTargetNode, Prism::CallTargetNode, Prism::IndexTargetNode, Prism::MultiTargetNode, Prism::RequiredParameterNode, Prism::BackReferenceReadNode, Prism::NumberedReferenceReadNode)], lparen_loc: T.nilable(Prism::Location), rparen_loc: T.nilable(Prism::Location)).returns(Prism::MultiTargetNode) } + def multi_target_node(source: default_source, node_id: 0, location: default_location, flags: 0, lefts: [], rest: nil, rights: [], lparen_loc: nil, rparen_loc: nil); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, lefts: T::Array[T.any(Prism::LocalVariableTargetNode, Prism::InstanceVariableTargetNode, Prism::ClassVariableTargetNode, Prism::GlobalVariableTargetNode, Prism::ConstantTargetNode, Prism::ConstantPathTargetNode, Prism::CallTargetNode, Prism::IndexTargetNode, Prism::MultiTargetNode, Prism::BackReferenceReadNode, Prism::NumberedReferenceReadNode)], rest: T.nilable(T.any(Prism::ImplicitRestNode, Prism::SplatNode)), rights: T::Array[T.any(Prism::LocalVariableTargetNode, Prism::InstanceVariableTargetNode, Prism::ClassVariableTargetNode, Prism::GlobalVariableTargetNode, Prism::ConstantTargetNode, Prism::ConstantPathTargetNode, Prism::CallTargetNode, Prism::IndexTargetNode, Prism::MultiTargetNode, Prism::BackReferenceReadNode, Prism::NumberedReferenceReadNode)], lparen_loc: T.nilable(Prism::Location), rparen_loc: T.nilable(Prism::Location), operator_loc: Prism::Location, value: Prism::Node).returns(Prism::MultiWriteNode) } + def multi_write_node(source: default_source, node_id: 0, location: default_location, flags: 0, lefts: [], rest: nil, rights: [], lparen_loc: nil, rparen_loc: nil, operator_loc: location, value: default_node(source, location)); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, arguments: T.nilable(Prism::ArgumentsNode), keyword_loc: Prism::Location).returns(Prism::NextNode) } + def next_node(source: default_source, node_id: 0, location: default_location, flags: 0, arguments: nil, keyword_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer).returns(Prism::NilNode) } + def nil_node(source: default_source, node_id: 0, location: default_location, flags: 0); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, operator_loc: Prism::Location, keyword_loc: Prism::Location).returns(Prism::NoKeywordsParameterNode) } + def no_keywords_parameter_node(source: default_source, node_id: 0, location: default_location, flags: 0, operator_loc: location, keyword_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, maximum: Integer).returns(Prism::NumberedParametersNode) } + def numbered_parameters_node(source: default_source, node_id: 0, location: default_location, flags: 0, maximum: 0); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, number: Integer).returns(Prism::NumberedReferenceReadNode) } + def numbered_reference_read_node(source: default_source, node_id: 0, location: default_location, flags: 0, number: 0); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, value: Prism::Node).returns(Prism::OptionalKeywordParameterNode) } + def optional_keyword_parameter_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", name_loc: location, value: default_node(source, location)); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, operator_loc: Prism::Location, value: Prism::Node).returns(Prism::OptionalParameterNode) } + def optional_parameter_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", name_loc: location, operator_loc: location, value: default_node(source, location)); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, left: Prism::Node, right: Prism::Node, operator_loc: Prism::Location).returns(Prism::OrNode) } + def or_node(source: default_source, node_id: 0, location: default_location, flags: 0, left: default_node(source, location), right: default_node(source, location), operator_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, requireds: T::Array[T.any(Prism::RequiredParameterNode, Prism::MultiTargetNode)], optionals: T::Array[Prism::OptionalParameterNode], rest: T.nilable(T.any(Prism::RestParameterNode, Prism::ImplicitRestNode)), posts: T::Array[T.any(Prism::RequiredParameterNode, Prism::MultiTargetNode, Prism::KeywordRestParameterNode, Prism::NoKeywordsParameterNode, Prism::ForwardingParameterNode)], keywords: T::Array[T.any(Prism::RequiredKeywordParameterNode, Prism::OptionalKeywordParameterNode)], keyword_rest: T.nilable(T.any(Prism::KeywordRestParameterNode, Prism::ForwardingParameterNode, Prism::NoKeywordsParameterNode)), block: T.nilable(Prism::BlockParameterNode)).returns(Prism::ParametersNode) } + def parameters_node(source: default_source, node_id: 0, location: default_location, flags: 0, requireds: [], optionals: [], rest: nil, posts: [], keywords: [], keyword_rest: nil, block: nil); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, body: T.nilable(Prism::Node), opening_loc: Prism::Location, closing_loc: Prism::Location).returns(Prism::ParenthesesNode) } + def parentheses_node(source: default_source, node_id: 0, location: default_location, flags: 0, body: nil, opening_loc: location, closing_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, expression: Prism::Node, operator_loc: Prism::Location, lparen_loc: Prism::Location, rparen_loc: Prism::Location).returns(Prism::PinnedExpressionNode) } + def pinned_expression_node(source: default_source, node_id: 0, location: default_location, flags: 0, expression: default_node(source, location), operator_loc: location, lparen_loc: location, rparen_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, variable: T.any(Prism::LocalVariableReadNode, Prism::InstanceVariableReadNode, Prism::ClassVariableReadNode, Prism::GlobalVariableReadNode, Prism::BackReferenceReadNode, Prism::NumberedReferenceReadNode, Prism::ItLocalVariableReadNode, Prism::MissingNode), operator_loc: Prism::Location).returns(Prism::PinnedVariableNode) } + def pinned_variable_node(source: default_source, node_id: 0, location: default_location, flags: 0, variable: local_variable_read_node(source: source), operator_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, statements: T.nilable(Prism::StatementsNode), keyword_loc: Prism::Location, opening_loc: Prism::Location, closing_loc: Prism::Location).returns(Prism::PostExecutionNode) } + def post_execution_node(source: default_source, node_id: 0, location: default_location, flags: 0, statements: nil, keyword_loc: location, opening_loc: location, closing_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, statements: T.nilable(Prism::StatementsNode), keyword_loc: Prism::Location, opening_loc: Prism::Location, closing_loc: Prism::Location).returns(Prism::PreExecutionNode) } + def pre_execution_node(source: default_source, node_id: 0, location: default_location, flags: 0, statements: nil, keyword_loc: location, opening_loc: location, closing_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, locals: T::Array[Symbol], statements: Prism::StatementsNode).returns(Prism::ProgramNode) } + def program_node(source: default_source, node_id: 0, location: default_location, flags: 0, locals: [], statements: statements_node(source: source)); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, left: T.nilable(Prism::Node), right: T.nilable(Prism::Node), operator_loc: Prism::Location).returns(Prism::RangeNode) } + def range_node(source: default_source, node_id: 0, location: default_location, flags: 0, left: nil, right: nil, operator_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, numerator: Integer, denominator: Integer).returns(Prism::RationalNode) } + def rational_node(source: default_source, node_id: 0, location: default_location, flags: 0, numerator: 0, denominator: 0); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer).returns(Prism::RedoNode) } + def redo_node(source: default_source, node_id: 0, location: default_location, flags: 0); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, opening_loc: Prism::Location, content_loc: Prism::Location, closing_loc: Prism::Location, unescaped: String).returns(Prism::RegularExpressionNode) } + def regular_expression_node(source: default_source, node_id: 0, location: default_location, flags: 0, opening_loc: location, content_loc: location, closing_loc: location, unescaped: ""); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location).returns(Prism::RequiredKeywordParameterNode) } + def required_keyword_parameter_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :"", name_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol).returns(Prism::RequiredParameterNode) } + def required_parameter_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: :""); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, expression: Prism::Node, keyword_loc: Prism::Location, rescue_expression: Prism::Node).returns(Prism::RescueModifierNode) } + def rescue_modifier_node(source: default_source, node_id: 0, location: default_location, flags: 0, expression: default_node(source, location), keyword_loc: location, rescue_expression: default_node(source, location)); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, keyword_loc: Prism::Location, exceptions: T::Array[Prism::Node], operator_loc: T.nilable(Prism::Location), reference: T.nilable(T.any(Prism::LocalVariableTargetNode, Prism::InstanceVariableTargetNode, Prism::ClassVariableTargetNode, Prism::GlobalVariableTargetNode, Prism::ConstantTargetNode, Prism::ConstantPathTargetNode, Prism::CallTargetNode, Prism::IndexTargetNode, Prism::BackReferenceReadNode, Prism::NumberedReferenceReadNode, Prism::MissingNode)), then_keyword_loc: T.nilable(Prism::Location), statements: T.nilable(Prism::StatementsNode), subsequent: T.nilable(Prism::RescueNode)).returns(Prism::RescueNode) } + def rescue_node(source: default_source, node_id: 0, location: default_location, flags: 0, keyword_loc: location, exceptions: [], operator_loc: nil, reference: nil, then_keyword_loc: nil, statements: nil, subsequent: nil); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: T.nilable(Symbol), name_loc: T.nilable(Prism::Location), operator_loc: Prism::Location).returns(Prism::RestParameterNode) } + def rest_parameter_node(source: default_source, node_id: 0, location: default_location, flags: 0, name: nil, name_loc: nil, operator_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer).returns(Prism::RetryNode) } + def retry_node(source: default_source, node_id: 0, location: default_location, flags: 0); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, keyword_loc: Prism::Location, arguments: T.nilable(Prism::ArgumentsNode)).returns(Prism::ReturnNode) } + def return_node(source: default_source, node_id: 0, location: default_location, flags: 0, keyword_loc: location, arguments: nil); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer).returns(Prism::SelfNode) } + def self_node(source: default_source, node_id: 0, location: default_location, flags: 0); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, write: T.any(Prism::ConstantWriteNode, Prism::ConstantAndWriteNode, Prism::ConstantOrWriteNode, Prism::ConstantOperatorWriteNode, Prism::ConstantPathWriteNode, Prism::ConstantPathAndWriteNode, Prism::ConstantPathOrWriteNode, Prism::ConstantPathOperatorWriteNode)).returns(Prism::ShareableConstantNode) } + def shareable_constant_node(source: default_source, node_id: 0, location: default_location, flags: 0, write: constant_write_node(source: source)); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, locals: T::Array[Symbol], class_keyword_loc: Prism::Location, operator_loc: Prism::Location, expression: Prism::Node, body: T.nilable(T.any(Prism::StatementsNode, Prism::BeginNode)), end_keyword_loc: Prism::Location).returns(Prism::SingletonClassNode) } + def singleton_class_node(source: default_source, node_id: 0, location: default_location, flags: 0, locals: [], class_keyword_loc: location, operator_loc: location, expression: default_node(source, location), body: nil, end_keyword_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer).returns(Prism::SourceEncodingNode) } + def source_encoding_node(source: default_source, node_id: 0, location: default_location, flags: 0); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, filepath: String).returns(Prism::SourceFileNode) } + def source_file_node(source: default_source, node_id: 0, location: default_location, flags: 0, filepath: ""); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer).returns(Prism::SourceLineNode) } + def source_line_node(source: default_source, node_id: 0, location: default_location, flags: 0); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, operator_loc: Prism::Location, expression: T.nilable(Prism::Node)).returns(Prism::SplatNode) } + def splat_node(source: default_source, node_id: 0, location: default_location, flags: 0, operator_loc: location, expression: nil); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, body: T::Array[Prism::Node]).returns(Prism::StatementsNode) } + def statements_node(source: default_source, node_id: 0, location: default_location, flags: 0, body: []); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, opening_loc: T.nilable(Prism::Location), content_loc: Prism::Location, closing_loc: T.nilable(Prism::Location), unescaped: String).returns(Prism::StringNode) } + def string_node(source: default_source, node_id: 0, location: default_location, flags: 0, opening_loc: nil, content_loc: location, closing_loc: nil, unescaped: ""); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, keyword_loc: Prism::Location, lparen_loc: T.nilable(Prism::Location), arguments: T.nilable(Prism::ArgumentsNode), rparen_loc: T.nilable(Prism::Location), block: T.nilable(T.any(Prism::BlockNode, Prism::BlockArgumentNode))).returns(Prism::SuperNode) } + def super_node(source: default_source, node_id: 0, location: default_location, flags: 0, keyword_loc: location, lparen_loc: nil, arguments: nil, rparen_loc: nil, block: nil); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, opening_loc: T.nilable(Prism::Location), value_loc: T.nilable(Prism::Location), closing_loc: T.nilable(Prism::Location), unescaped: String).returns(Prism::SymbolNode) } + def symbol_node(source: default_source, node_id: 0, location: default_location, flags: 0, opening_loc: nil, value_loc: nil, closing_loc: nil, unescaped: ""); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer).returns(Prism::TrueNode) } + def true_node(source: default_source, node_id: 0, location: default_location, flags: 0); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, names: T::Array[T.any(Prism::SymbolNode, Prism::InterpolatedSymbolNode)], keyword_loc: Prism::Location).returns(Prism::UndefNode) } + def undef_node(source: default_source, node_id: 0, location: default_location, flags: 0, names: [], keyword_loc: location); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, keyword_loc: Prism::Location, predicate: Prism::Node, then_keyword_loc: T.nilable(Prism::Location), statements: T.nilable(Prism::StatementsNode), else_clause: T.nilable(Prism::ElseNode), end_keyword_loc: T.nilable(Prism::Location)).returns(Prism::UnlessNode) } + def unless_node(source: default_source, node_id: 0, location: default_location, flags: 0, keyword_loc: location, predicate: default_node(source, location), then_keyword_loc: nil, statements: nil, else_clause: nil, end_keyword_loc: nil); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, keyword_loc: Prism::Location, do_keyword_loc: T.nilable(Prism::Location), closing_loc: T.nilable(Prism::Location), predicate: Prism::Node, statements: T.nilable(Prism::StatementsNode)).returns(Prism::UntilNode) } + def until_node(source: default_source, node_id: 0, location: default_location, flags: 0, keyword_loc: location, do_keyword_loc: nil, closing_loc: nil, predicate: default_node(source, location), statements: nil); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, keyword_loc: Prism::Location, conditions: T::Array[Prism::Node], then_keyword_loc: T.nilable(Prism::Location), statements: T.nilable(Prism::StatementsNode)).returns(Prism::WhenNode) } + def when_node(source: default_source, node_id: 0, location: default_location, flags: 0, keyword_loc: location, conditions: [], then_keyword_loc: nil, statements: nil); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, keyword_loc: Prism::Location, do_keyword_loc: T.nilable(Prism::Location), closing_loc: T.nilable(Prism::Location), predicate: Prism::Node, statements: T.nilable(Prism::StatementsNode)).returns(Prism::WhileNode) } + def while_node(source: default_source, node_id: 0, location: default_location, flags: 0, keyword_loc: location, do_keyword_loc: nil, closing_loc: nil, predicate: default_node(source, location), statements: nil); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, opening_loc: Prism::Location, content_loc: Prism::Location, closing_loc: Prism::Location, unescaped: String).returns(Prism::XStringNode) } + def x_string_node(source: default_source, node_id: 0, location: default_location, flags: 0, opening_loc: location, content_loc: location, closing_loc: location, unescaped: ""); end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, keyword_loc: Prism::Location, lparen_loc: T.nilable(Prism::Location), arguments: T.nilable(Prism::ArgumentsNode), rparen_loc: T.nilable(Prism::Location)).returns(Prism::YieldNode) } + def yield_node(source: default_source, node_id: 0, location: default_location, flags: 0, keyword_loc: location, lparen_loc: nil, arguments: nil, rparen_loc: nil); end + + sig { params(name: Symbol).returns(Integer) } + def arguments_node_flag(name); end + + sig { params(name: Symbol).returns(Integer) } + def array_node_flag(name); end + + sig { params(name: Symbol).returns(Integer) } + def call_node_flag(name); end + + sig { params(name: Symbol).returns(Integer) } + def encoding_flag(name); end + + sig { params(name: Symbol).returns(Integer) } + def integer_base_flag(name); end + + sig { params(name: Symbol).returns(Integer) } + def interpolated_string_node_flag(name); end + + sig { params(name: Symbol).returns(Integer) } + def keyword_hash_node_flag(name); end + + sig { params(name: Symbol).returns(Integer) } + def loop_flag(name); end + + sig { params(name: Symbol).returns(Integer) } + def parameter_flag(name); end + + sig { params(name: Symbol).returns(Integer) } + def parentheses_node_flag(name); end + + sig { params(name: Symbol).returns(Integer) } + def range_flag(name); end + + sig { params(name: Symbol).returns(Integer) } + def regular_expression_flag(name); end + + sig { params(name: Symbol).returns(Integer) } + def shareable_constant_node_flag(name); end + + sig { params(name: Symbol).returns(Integer) } + def string_flag(name); end + + sig { params(name: Symbol).returns(Integer) } + def symbol_flag(name); end + + private + + sig { returns(Prism::Source) } + def default_source; end + + sig { returns(Prism::Location) } + def default_location; end + + sig { params(source: Prism::Source, location: Prism::Location).returns(Prism::Node) } + def default_node(source, location); end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/rbi/prism/inspect_visitor.rbi b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/rbi/prism/inspect_visitor.rbi new file mode 100644 index 0000000..bb0c224 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/rbi/prism/inspect_visitor.rbi @@ -0,0 +1,12 @@ +# typed: strict + +class Prism::InspectVisitor < Prism::Visitor + sig { params(indent: String).void } + def initialize(indent = ""); end + + sig { params(node: Prism::Node).returns(String) } + def self.compose(node); end + + sig { returns(String) } + def compose; end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/rbi/prism/node.rbi b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/rbi/prism/node.rbi new file mode 100644 index 0000000..5404ff4 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/rbi/prism/node.rbi @@ -0,0 +1,8750 @@ +# typed: strict + +=begin +This file is generated by the templates/template.rb script and should not be +modified manually. See templates/rbi/prism/node.rbi.erb +if you are looking to modify the template +=end + +class Prism::Node + abstract! + + sig { returns(Prism::Source) } + def source; end + + sig { returns(Integer) } + def node_id; end + + sig { returns(Prism::Location) } + def location; end + + sig{ returns(Integer) } + def flags; end + + sig { returns(T::Boolean) } + def newline?; end + + sig { returns(T::Boolean) } + def static_literal?; end + + sig { returns(Integer) } + def start_offset; end + + sig { returns(Integer) } + def end_offset; end + + sig { returns(T::Array[String]) } + def source_lines; end + + sig { returns(T::Array[String]) } + def script_lines; end + + sig { returns(String) } + def slice; end + + sig { returns(String) } + def slice_lines; end + + sig { params(q: T.untyped).void } + def pretty_print(q); end + + sig { returns(String) } + def to_dot; end + + sig { params(line: Integer, column: Integer).returns(T::Array[Prism::Node]) } + def tunnel(line, column); end + + sig { params(block: T.proc.params(node: Prism::Node).returns(T::Boolean)).returns(T.nilable(Prism::Node)) } + def breadth_first_search(&block); end + + sig { params(block: T.proc.params(node: Prism::Node).returns(T::Boolean)).returns(T::Array[Prism::Node]) } + def breadth_first_search_all(&block); end + + sig { abstract.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { abstract.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { abstract.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { abstract.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { abstract.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { abstract.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { abstract.returns(Symbol) } + def type; end + + sig { abstract.returns(String) } + def inspect; end +end + +# Represents the use of the `alias` keyword to alias a global variable. +# +# alias $foo $bar +# ^^^^^^^^^^^^^^^ +class Prism::AliasGlobalVariableNode < Prism::Node + sig { returns(T.any(Prism::GlobalVariableReadNode, Prism::BackReferenceReadNode, Prism::NumberedReferenceReadNode)) } + def new_name; end + + sig { returns(T.any(Prism::GlobalVariableReadNode, Prism::BackReferenceReadNode, Prism::NumberedReferenceReadNode, Prism::SymbolNode, Prism::MissingNode)) } + def old_name; end + + sig { returns(Prism::Location) } + def keyword_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, new_name: T.any(Prism::GlobalVariableReadNode, Prism::BackReferenceReadNode, Prism::NumberedReferenceReadNode), old_name: T.any(Prism::GlobalVariableReadNode, Prism::BackReferenceReadNode, Prism::NumberedReferenceReadNode, Prism::SymbolNode, Prism::MissingNode), keyword_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, new_name, old_name, keyword_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, new_name: T.any(Prism::GlobalVariableReadNode, Prism::BackReferenceReadNode, Prism::NumberedReferenceReadNode), old_name: T.any(Prism::GlobalVariableReadNode, Prism::BackReferenceReadNode, Prism::NumberedReferenceReadNode, Prism::SymbolNode, Prism::MissingNode), keyword_loc: Prism::Location).returns(Prism::AliasGlobalVariableNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, new_name: self.new_name, old_name: self.old_name, keyword_loc: self.keyword_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def keyword; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `alias` keyword to alias a method. +# +# alias foo bar +# ^^^^^^^^^^^^^ +class Prism::AliasMethodNode < Prism::Node + sig { returns(T.any(Prism::SymbolNode, Prism::InterpolatedSymbolNode)) } + def new_name; end + + sig { returns(T.any(Prism::SymbolNode, Prism::InterpolatedSymbolNode, Prism::GlobalVariableReadNode, Prism::MissingNode)) } + def old_name; end + + sig { returns(Prism::Location) } + def keyword_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, new_name: T.any(Prism::SymbolNode, Prism::InterpolatedSymbolNode), old_name: T.any(Prism::SymbolNode, Prism::InterpolatedSymbolNode, Prism::GlobalVariableReadNode, Prism::MissingNode), keyword_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, new_name, old_name, keyword_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, new_name: T.any(Prism::SymbolNode, Prism::InterpolatedSymbolNode), old_name: T.any(Prism::SymbolNode, Prism::InterpolatedSymbolNode, Prism::GlobalVariableReadNode, Prism::MissingNode), keyword_loc: Prism::Location).returns(Prism::AliasMethodNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, new_name: self.new_name, old_name: self.old_name, keyword_loc: self.keyword_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def keyword; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents an alternation pattern in pattern matching. +# +# foo => bar | baz +# ^^^^^^^^^ +class Prism::AlternationPatternNode < Prism::Node + sig { returns(Prism::Node) } + def left; end + + sig { returns(Prism::Node) } + def right; end + + sig { returns(Prism::Location) } + def operator_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, left: Prism::Node, right: Prism::Node, operator_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, left, right, operator_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, left: Prism::Node, right: Prism::Node, operator_loc: Prism::Location).returns(Prism::AlternationPatternNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, left: self.left, right: self.right, operator_loc: self.operator_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def operator; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `&&` operator or the `and` keyword. +# +# left and right +# ^^^^^^^^^^^^^^ +class Prism::AndNode < Prism::Node + sig { returns(Prism::Node) } + def left; end + + sig { returns(Prism::Node) } + def right; end + + sig { returns(Prism::Location) } + def operator_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, left: Prism::Node, right: Prism::Node, operator_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, left, right, operator_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, left: Prism::Node, right: Prism::Node, operator_loc: Prism::Location).returns(Prism::AndNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, left: self.left, right: self.right, operator_loc: self.operator_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def operator; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents a set of arguments to a method or a keyword. +# +# return foo, bar, baz +# ^^^^^^^^^^^^^ +class Prism::ArgumentsNode < Prism::Node + sig { returns(T::Boolean) } + def contains_forwarding?; end + + sig { returns(T::Boolean) } + def contains_keywords?; end + + sig { returns(T::Boolean) } + def contains_keyword_splat?; end + + sig { returns(T::Boolean) } + def contains_splat?; end + + sig { returns(T::Boolean) } + def contains_multiple_splats?; end + + sig { returns(T::Array[Prism::Node]) } + def arguments; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, arguments: T::Array[Prism::Node]).void } + def initialize(source, node_id, location, flags, arguments); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, arguments: T::Array[Prism::Node]).returns(Prism::ArgumentsNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, arguments: self.arguments); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents an array literal. This can be a regular array using brackets or a special array using % like %w or %i. +# +# [1, 2, 3] +# ^^^^^^^^^ +class Prism::ArrayNode < Prism::Node + sig { returns(T::Boolean) } + def contains_splat?; end + + sig { returns(T::Array[Prism::Node]) } + def elements; end + + sig { returns(T.nilable(Prism::Location)) } + def opening_loc; end + + sig { returns(T.nilable(Prism::Location)) } + def closing_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, elements: T::Array[Prism::Node], opening_loc: T.nilable(Prism::Location), closing_loc: T.nilable(Prism::Location)).void } + def initialize(source, node_id, location, flags, elements, opening_loc, closing_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, elements: T::Array[Prism::Node], opening_loc: T.nilable(Prism::Location), closing_loc: T.nilable(Prism::Location)).returns(Prism::ArrayNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, elements: self.elements, opening_loc: self.opening_loc, closing_loc: self.closing_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(T.nilable(String)) } + def opening; end + + sig { returns(T.nilable(String)) } + def closing; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents an array pattern in pattern matching. +# +# foo in 1, 2 +# ^^^^^^^^^^^ +# +# foo in [1, 2] +# ^^^^^^^^^^^^^ +# +# foo in *bar +# ^^^^^^^^^^^ +# +# foo in Bar[] +# ^^^^^^^^^^^^ +# +# foo in Bar[1, 2, 3] +# ^^^^^^^^^^^^^^^^^^^ +class Prism::ArrayPatternNode < Prism::Node + sig { returns(T.nilable(T.any(Prism::ConstantPathNode, Prism::ConstantReadNode))) } + def constant; end + + sig { returns(T::Array[Prism::Node]) } + def requireds; end + + sig { returns(T.nilable(Prism::Node)) } + def rest; end + + sig { returns(T::Array[Prism::Node]) } + def posts; end + + sig { returns(T.nilable(Prism::Location)) } + def opening_loc; end + + sig { returns(T.nilable(Prism::Location)) } + def closing_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, constant: T.nilable(T.any(Prism::ConstantPathNode, Prism::ConstantReadNode)), requireds: T::Array[Prism::Node], rest: T.nilable(Prism::Node), posts: T::Array[Prism::Node], opening_loc: T.nilable(Prism::Location), closing_loc: T.nilable(Prism::Location)).void } + def initialize(source, node_id, location, flags, constant, requireds, rest, posts, opening_loc, closing_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, constant: T.nilable(T.any(Prism::ConstantPathNode, Prism::ConstantReadNode)), requireds: T::Array[Prism::Node], rest: T.nilable(Prism::Node), posts: T::Array[Prism::Node], opening_loc: T.nilable(Prism::Location), closing_loc: T.nilable(Prism::Location)).returns(Prism::ArrayPatternNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, constant: self.constant, requireds: self.requireds, rest: self.rest, posts: self.posts, opening_loc: self.opening_loc, closing_loc: self.closing_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(T.nilable(String)) } + def opening; end + + sig { returns(T.nilable(String)) } + def closing; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents a hash key/value pair. +# +# { a => b } +# ^^^^^^ +class Prism::AssocNode < Prism::Node + sig { returns(Prism::Node) } + def key; end + + sig { returns(Prism::Node) } + def value; end + + sig { returns(T.nilable(Prism::Location)) } + def operator_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, key: Prism::Node, value: Prism::Node, operator_loc: T.nilable(Prism::Location)).void } + def initialize(source, node_id, location, flags, key, value, operator_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, key: Prism::Node, value: Prism::Node, operator_loc: T.nilable(Prism::Location)).returns(Prism::AssocNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, key: self.key, value: self.value, operator_loc: self.operator_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(T.nilable(String)) } + def operator; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents a splat in a hash literal. +# +# { **foo } +# ^^^^^ +class Prism::AssocSplatNode < Prism::Node + sig { returns(T.nilable(Prism::Node)) } + def value; end + + sig { returns(Prism::Location) } + def operator_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, value: T.nilable(Prism::Node), operator_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, value, operator_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, value: T.nilable(Prism::Node), operator_loc: Prism::Location).returns(Prism::AssocSplatNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, value: self.value, operator_loc: self.operator_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def operator; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents reading a reference to a field in the previous match. +# +# $' +# ^^ +class Prism::BackReferenceReadNode < Prism::Node + sig { returns(Symbol) } + def name; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol).void } + def initialize(source, node_id, location, flags, name); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol).returns(Prism::BackReferenceReadNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents a begin statement. +# +# begin +# foo +# end +# ^^^^^ +class Prism::BeginNode < Prism::Node + sig { returns(T.nilable(Prism::Location)) } + def begin_keyword_loc; end + + sig { returns(T.nilable(Prism::StatementsNode)) } + def statements; end + + sig { returns(T.nilable(Prism::RescueNode)) } + def rescue_clause; end + + sig { returns(T.nilable(Prism::ElseNode)) } + def else_clause; end + + sig { returns(T.nilable(Prism::EnsureNode)) } + def ensure_clause; end + + sig { returns(T.nilable(Prism::Location)) } + def end_keyword_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, begin_keyword_loc: T.nilable(Prism::Location), statements: T.nilable(Prism::StatementsNode), rescue_clause: T.nilable(Prism::RescueNode), else_clause: T.nilable(Prism::ElseNode), ensure_clause: T.nilable(Prism::EnsureNode), end_keyword_loc: T.nilable(Prism::Location)).void } + def initialize(source, node_id, location, flags, begin_keyword_loc, statements, rescue_clause, else_clause, ensure_clause, end_keyword_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, begin_keyword_loc: T.nilable(Prism::Location), statements: T.nilable(Prism::StatementsNode), rescue_clause: T.nilable(Prism::RescueNode), else_clause: T.nilable(Prism::ElseNode), ensure_clause: T.nilable(Prism::EnsureNode), end_keyword_loc: T.nilable(Prism::Location)).returns(Prism::BeginNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, begin_keyword_loc: self.begin_keyword_loc, statements: self.statements, rescue_clause: self.rescue_clause, else_clause: self.else_clause, ensure_clause: self.ensure_clause, end_keyword_loc: self.end_keyword_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(T.nilable(String)) } + def begin_keyword; end + + sig { returns(T.nilable(String)) } + def end_keyword; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents a block argument using `&`. +# +# bar(&args) +# ^^^^^^^^^^ +class Prism::BlockArgumentNode < Prism::Node + sig { returns(T.nilable(Prism::Node)) } + def expression; end + + sig { returns(Prism::Location) } + def operator_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, expression: T.nilable(Prism::Node), operator_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, expression, operator_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, expression: T.nilable(Prism::Node), operator_loc: Prism::Location).returns(Prism::BlockArgumentNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, expression: self.expression, operator_loc: self.operator_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def operator; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents a block local variable. +# +# a { |; b| } +# ^ +class Prism::BlockLocalVariableNode < Prism::Node + sig { returns(T::Boolean) } + def repeated_parameter?; end + + sig { returns(Symbol) } + def name; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol).void } + def initialize(source, node_id, location, flags, name); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol).returns(Prism::BlockLocalVariableNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents a block of ruby code. +# +# [1, 2, 3].each { |i| puts x } +# ^^^^^^^^^^^^^^ +class Prism::BlockNode < Prism::Node + sig { returns(T::Array[Symbol]) } + def locals; end + + sig { returns(T.nilable(T.any(Prism::BlockParametersNode, Prism::NumberedParametersNode, Prism::ItParametersNode))) } + def parameters; end + + sig { returns(T.nilable(T.any(Prism::StatementsNode, Prism::BeginNode))) } + def body; end + + sig { returns(Prism::Location) } + def opening_loc; end + + sig { returns(Prism::Location) } + def closing_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, locals: T::Array[Symbol], parameters: T.nilable(T.any(Prism::BlockParametersNode, Prism::NumberedParametersNode, Prism::ItParametersNode)), body: T.nilable(T.any(Prism::StatementsNode, Prism::BeginNode)), opening_loc: Prism::Location, closing_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, locals, parameters, body, opening_loc, closing_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, locals: T::Array[Symbol], parameters: T.nilable(T.any(Prism::BlockParametersNode, Prism::NumberedParametersNode, Prism::ItParametersNode)), body: T.nilable(T.any(Prism::StatementsNode, Prism::BeginNode)), opening_loc: Prism::Location, closing_loc: Prism::Location).returns(Prism::BlockNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, locals: self.locals, parameters: self.parameters, body: self.body, opening_loc: self.opening_loc, closing_loc: self.closing_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def opening; end + + sig { returns(String) } + def closing; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents a block parameter of a method, block, or lambda definition. +# +# def a(&b) +# ^^ +# end +class Prism::BlockParameterNode < Prism::Node + sig { returns(T::Boolean) } + def repeated_parameter?; end + + sig { returns(T.nilable(Symbol)) } + def name; end + + sig { returns(T.nilable(Prism::Location)) } + def name_loc; end + + sig { returns(Prism::Location) } + def operator_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: T.nilable(Symbol), name_loc: T.nilable(Prism::Location), operator_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, name, name_loc, operator_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, name: T.nilable(Symbol), name_loc: T.nilable(Prism::Location), operator_loc: Prism::Location).returns(Prism::BlockParameterNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc, operator_loc: self.operator_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def operator; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents a block's parameters declaration. +# +# -> (a, b = 1; local) { } +# ^^^^^^^^^^^^^^^^^ +# +# foo do |a, b = 1; local| +# ^^^^^^^^^^^^^^^^^ +# end +class Prism::BlockParametersNode < Prism::Node + sig { returns(T.nilable(Prism::ParametersNode)) } + def parameters; end + + sig { returns(T::Array[Prism::BlockLocalVariableNode]) } + def locals; end + + sig { returns(T.nilable(Prism::Location)) } + def opening_loc; end + + sig { returns(T.nilable(Prism::Location)) } + def closing_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, parameters: T.nilable(Prism::ParametersNode), locals: T::Array[Prism::BlockLocalVariableNode], opening_loc: T.nilable(Prism::Location), closing_loc: T.nilable(Prism::Location)).void } + def initialize(source, node_id, location, flags, parameters, locals, opening_loc, closing_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, parameters: T.nilable(Prism::ParametersNode), locals: T::Array[Prism::BlockLocalVariableNode], opening_loc: T.nilable(Prism::Location), closing_loc: T.nilable(Prism::Location)).returns(Prism::BlockParametersNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, parameters: self.parameters, locals: self.locals, opening_loc: self.opening_loc, closing_loc: self.closing_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(T.nilable(String)) } + def opening; end + + sig { returns(T.nilable(String)) } + def closing; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `break` keyword. +# +# break foo +# ^^^^^^^^^ +class Prism::BreakNode < Prism::Node + sig { returns(T.nilable(Prism::ArgumentsNode)) } + def arguments; end + + sig { returns(Prism::Location) } + def keyword_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, arguments: T.nilable(Prism::ArgumentsNode), keyword_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, arguments, keyword_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, arguments: T.nilable(Prism::ArgumentsNode), keyword_loc: Prism::Location).returns(Prism::BreakNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, arguments: self.arguments, keyword_loc: self.keyword_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def keyword; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `&&=` operator on a call. +# +# foo.bar &&= value +# ^^^^^^^^^^^^^^^^^ +class Prism::CallAndWriteNode < Prism::Node + sig { returns(T::Boolean) } + def safe_navigation?; end + + sig { returns(T::Boolean) } + def variable_call?; end + + sig { returns(T::Boolean) } + def attribute_write?; end + + sig { returns(T::Boolean) } + def ignore_visibility?; end + + sig { returns(T.nilable(Prism::Node)) } + def receiver; end + + sig { returns(T.nilable(Prism::Location)) } + def call_operator_loc; end + + sig { returns(T.nilable(Prism::Location)) } + def message_loc; end + + sig { returns(Symbol) } + def read_name; end + + sig { returns(Symbol) } + def write_name; end + + sig { returns(Prism::Location) } + def operator_loc; end + + sig { returns(Prism::Node) } + def value; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, receiver: T.nilable(Prism::Node), call_operator_loc: T.nilable(Prism::Location), message_loc: T.nilable(Prism::Location), read_name: Symbol, write_name: Symbol, operator_loc: Prism::Location, value: Prism::Node).void } + def initialize(source, node_id, location, flags, receiver, call_operator_loc, message_loc, read_name, write_name, operator_loc, value); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, receiver: T.nilable(Prism::Node), call_operator_loc: T.nilable(Prism::Location), message_loc: T.nilable(Prism::Location), read_name: Symbol, write_name: Symbol, operator_loc: Prism::Location, value: Prism::Node).returns(Prism::CallAndWriteNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, receiver: self.receiver, call_operator_loc: self.call_operator_loc, message_loc: self.message_loc, read_name: self.read_name, write_name: self.write_name, operator_loc: self.operator_loc, value: self.value); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(T.nilable(String)) } + def call_operator; end + + sig { returns(T.nilable(String)) } + def message; end + + sig { returns(String) } + def operator; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents a method call, in all of the various forms that can take. +# +# foo +# ^^^ +# +# foo() +# ^^^^^ +# +# +foo +# ^^^^ +# +# foo + bar +# ^^^^^^^^^ +# +# foo.bar +# ^^^^^^^ +# +# foo&.bar +# ^^^^^^^^ +class Prism::CallNode < Prism::Node + sig { returns(T::Boolean) } + def safe_navigation?; end + + sig { returns(T::Boolean) } + def variable_call?; end + + sig { returns(T::Boolean) } + def attribute_write?; end + + sig { returns(T::Boolean) } + def ignore_visibility?; end + + sig { returns(T.nilable(Prism::Node)) } + def receiver; end + + sig { returns(T.nilable(Prism::Location)) } + def call_operator_loc; end + + sig { returns(Symbol) } + def name; end + + sig { returns(T.nilable(Prism::Location)) } + def message_loc; end + + sig { returns(T.nilable(Prism::Location)) } + def opening_loc; end + + sig { returns(T.nilable(Prism::ArgumentsNode)) } + def arguments; end + + sig { returns(T.nilable(Prism::Location)) } + def closing_loc; end + + sig { returns(T.nilable(Prism::Location)) } + def equal_loc; end + + sig { returns(T.nilable(T.any(Prism::BlockNode, Prism::BlockArgumentNode))) } + def block; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, receiver: T.nilable(Prism::Node), call_operator_loc: T.nilable(Prism::Location), name: Symbol, message_loc: T.nilable(Prism::Location), opening_loc: T.nilable(Prism::Location), arguments: T.nilable(Prism::ArgumentsNode), closing_loc: T.nilable(Prism::Location), equal_loc: T.nilable(Prism::Location), block: T.nilable(T.any(Prism::BlockNode, Prism::BlockArgumentNode))).void } + def initialize(source, node_id, location, flags, receiver, call_operator_loc, name, message_loc, opening_loc, arguments, closing_loc, equal_loc, block); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, receiver: T.nilable(Prism::Node), call_operator_loc: T.nilable(Prism::Location), name: Symbol, message_loc: T.nilable(Prism::Location), opening_loc: T.nilable(Prism::Location), arguments: T.nilable(Prism::ArgumentsNode), closing_loc: T.nilable(Prism::Location), equal_loc: T.nilable(Prism::Location), block: T.nilable(T.any(Prism::BlockNode, Prism::BlockArgumentNode))).returns(Prism::CallNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, receiver: self.receiver, call_operator_loc: self.call_operator_loc, name: self.name, message_loc: self.message_loc, opening_loc: self.opening_loc, arguments: self.arguments, closing_loc: self.closing_loc, equal_loc: self.equal_loc, block: self.block); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(T.nilable(String)) } + def call_operator; end + + sig { returns(T.nilable(String)) } + def message; end + + sig { returns(T.nilable(String)) } + def opening; end + + sig { returns(T.nilable(String)) } + def closing; end + + sig { returns(T.nilable(String)) } + def equal; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of an assignment operator on a call. +# +# foo.bar += baz +# ^^^^^^^^^^^^^^ +class Prism::CallOperatorWriteNode < Prism::Node + sig { returns(T::Boolean) } + def safe_navigation?; end + + sig { returns(T::Boolean) } + def variable_call?; end + + sig { returns(T::Boolean) } + def attribute_write?; end + + sig { returns(T::Boolean) } + def ignore_visibility?; end + + sig { returns(T.nilable(Prism::Node)) } + def receiver; end + + sig { returns(T.nilable(Prism::Location)) } + def call_operator_loc; end + + sig { returns(T.nilable(Prism::Location)) } + def message_loc; end + + sig { returns(Symbol) } + def read_name; end + + sig { returns(Symbol) } + def write_name; end + + sig { returns(Symbol) } + def binary_operator; end + + sig { returns(Prism::Location) } + def binary_operator_loc; end + + sig { returns(Prism::Node) } + def value; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, receiver: T.nilable(Prism::Node), call_operator_loc: T.nilable(Prism::Location), message_loc: T.nilable(Prism::Location), read_name: Symbol, write_name: Symbol, binary_operator: Symbol, binary_operator_loc: Prism::Location, value: Prism::Node).void } + def initialize(source, node_id, location, flags, receiver, call_operator_loc, message_loc, read_name, write_name, binary_operator, binary_operator_loc, value); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, receiver: T.nilable(Prism::Node), call_operator_loc: T.nilable(Prism::Location), message_loc: T.nilable(Prism::Location), read_name: Symbol, write_name: Symbol, binary_operator: Symbol, binary_operator_loc: Prism::Location, value: Prism::Node).returns(Prism::CallOperatorWriteNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, receiver: self.receiver, call_operator_loc: self.call_operator_loc, message_loc: self.message_loc, read_name: self.read_name, write_name: self.write_name, binary_operator: self.binary_operator, binary_operator_loc: self.binary_operator_loc, value: self.value); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(T.nilable(String)) } + def call_operator; end + + sig { returns(T.nilable(String)) } + def message; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `||=` operator on a call. +# +# foo.bar ||= value +# ^^^^^^^^^^^^^^^^^ +class Prism::CallOrWriteNode < Prism::Node + sig { returns(T::Boolean) } + def safe_navigation?; end + + sig { returns(T::Boolean) } + def variable_call?; end + + sig { returns(T::Boolean) } + def attribute_write?; end + + sig { returns(T::Boolean) } + def ignore_visibility?; end + + sig { returns(T.nilable(Prism::Node)) } + def receiver; end + + sig { returns(T.nilable(Prism::Location)) } + def call_operator_loc; end + + sig { returns(T.nilable(Prism::Location)) } + def message_loc; end + + sig { returns(Symbol) } + def read_name; end + + sig { returns(Symbol) } + def write_name; end + + sig { returns(Prism::Location) } + def operator_loc; end + + sig { returns(Prism::Node) } + def value; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, receiver: T.nilable(Prism::Node), call_operator_loc: T.nilable(Prism::Location), message_loc: T.nilable(Prism::Location), read_name: Symbol, write_name: Symbol, operator_loc: Prism::Location, value: Prism::Node).void } + def initialize(source, node_id, location, flags, receiver, call_operator_loc, message_loc, read_name, write_name, operator_loc, value); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, receiver: T.nilable(Prism::Node), call_operator_loc: T.nilable(Prism::Location), message_loc: T.nilable(Prism::Location), read_name: Symbol, write_name: Symbol, operator_loc: Prism::Location, value: Prism::Node).returns(Prism::CallOrWriteNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, receiver: self.receiver, call_operator_loc: self.call_operator_loc, message_loc: self.message_loc, read_name: self.read_name, write_name: self.write_name, operator_loc: self.operator_loc, value: self.value); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(T.nilable(String)) } + def call_operator; end + + sig { returns(T.nilable(String)) } + def message; end + + sig { returns(String) } + def operator; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents assigning to a method call. +# +# foo.bar, = 1 +# ^^^^^^^ +# +# begin +# rescue => foo.bar +# ^^^^^^^ +# end +# +# for foo.bar in baz do end +# ^^^^^^^ +class Prism::CallTargetNode < Prism::Node + sig { returns(T::Boolean) } + def safe_navigation?; end + + sig { returns(T::Boolean) } + def variable_call?; end + + sig { returns(T::Boolean) } + def attribute_write?; end + + sig { returns(T::Boolean) } + def ignore_visibility?; end + + sig { returns(Prism::Node) } + def receiver; end + + sig { returns(Prism::Location) } + def call_operator_loc; end + + sig { returns(Symbol) } + def name; end + + sig { returns(Prism::Location) } + def message_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, receiver: Prism::Node, call_operator_loc: Prism::Location, name: Symbol, message_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, receiver, call_operator_loc, name, message_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, receiver: Prism::Node, call_operator_loc: Prism::Location, name: Symbol, message_loc: Prism::Location).returns(Prism::CallTargetNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, receiver: self.receiver, call_operator_loc: self.call_operator_loc, name: self.name, message_loc: self.message_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def call_operator; end + + sig { returns(String) } + def message; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents assigning to a local variable in pattern matching. +# +# foo => [bar => baz] +# ^^^^^^^^^^^^ +class Prism::CapturePatternNode < Prism::Node + sig { returns(Prism::Node) } + def value; end + + sig { returns(Prism::LocalVariableTargetNode) } + def target; end + + sig { returns(Prism::Location) } + def operator_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, value: Prism::Node, target: Prism::LocalVariableTargetNode, operator_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, value, target, operator_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, value: Prism::Node, target: Prism::LocalVariableTargetNode, operator_loc: Prism::Location).returns(Prism::CapturePatternNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, value: self.value, target: self.target, operator_loc: self.operator_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def operator; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of a case statement for pattern matching. +# +# case true +# in false +# end +# ^^^^^^^^^ +class Prism::CaseMatchNode < Prism::Node + sig { returns(T.nilable(Prism::Node)) } + def predicate; end + + sig { returns(T::Array[Prism::InNode]) } + def conditions; end + + sig { returns(T.nilable(Prism::ElseNode)) } + def else_clause; end + + sig { returns(Prism::Location) } + def case_keyword_loc; end + + sig { returns(Prism::Location) } + def end_keyword_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, predicate: T.nilable(Prism::Node), conditions: T::Array[Prism::InNode], else_clause: T.nilable(Prism::ElseNode), case_keyword_loc: Prism::Location, end_keyword_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, predicate, conditions, else_clause, case_keyword_loc, end_keyword_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, predicate: T.nilable(Prism::Node), conditions: T::Array[Prism::InNode], else_clause: T.nilable(Prism::ElseNode), case_keyword_loc: Prism::Location, end_keyword_loc: Prism::Location).returns(Prism::CaseMatchNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, predicate: self.predicate, conditions: self.conditions, else_clause: self.else_clause, case_keyword_loc: self.case_keyword_loc, end_keyword_loc: self.end_keyword_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def case_keyword; end + + sig { returns(String) } + def end_keyword; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of a case statement. +# +# case true +# when false +# end +# ^^^^^^^^^^ +class Prism::CaseNode < Prism::Node + sig { returns(T.nilable(Prism::Node)) } + def predicate; end + + sig { returns(T::Array[Prism::WhenNode]) } + def conditions; end + + sig { returns(T.nilable(Prism::ElseNode)) } + def else_clause; end + + sig { returns(Prism::Location) } + def case_keyword_loc; end + + sig { returns(Prism::Location) } + def end_keyword_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, predicate: T.nilable(Prism::Node), conditions: T::Array[Prism::WhenNode], else_clause: T.nilable(Prism::ElseNode), case_keyword_loc: Prism::Location, end_keyword_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, predicate, conditions, else_clause, case_keyword_loc, end_keyword_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, predicate: T.nilable(Prism::Node), conditions: T::Array[Prism::WhenNode], else_clause: T.nilable(Prism::ElseNode), case_keyword_loc: Prism::Location, end_keyword_loc: Prism::Location).returns(Prism::CaseNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, predicate: self.predicate, conditions: self.conditions, else_clause: self.else_clause, case_keyword_loc: self.case_keyword_loc, end_keyword_loc: self.end_keyword_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def case_keyword; end + + sig { returns(String) } + def end_keyword; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents a class declaration involving the `class` keyword. +# +# class Foo end +# ^^^^^^^^^^^^^ +class Prism::ClassNode < Prism::Node + sig { returns(T::Array[Symbol]) } + def locals; end + + sig { returns(Prism::Location) } + def class_keyword_loc; end + + sig { returns(T.any(Prism::ConstantReadNode, Prism::ConstantPathNode, Prism::CallNode)) } + def constant_path; end + + sig { returns(T.nilable(Prism::Location)) } + def inheritance_operator_loc; end + + sig { returns(T.nilable(Prism::Node)) } + def superclass; end + + sig { returns(T.nilable(T.any(Prism::StatementsNode, Prism::BeginNode))) } + def body; end + + sig { returns(Prism::Location) } + def end_keyword_loc; end + + sig { returns(Symbol) } + def name; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, locals: T::Array[Symbol], class_keyword_loc: Prism::Location, constant_path: T.any(Prism::ConstantReadNode, Prism::ConstantPathNode, Prism::CallNode), inheritance_operator_loc: T.nilable(Prism::Location), superclass: T.nilable(Prism::Node), body: T.nilable(T.any(Prism::StatementsNode, Prism::BeginNode)), end_keyword_loc: Prism::Location, name: Symbol).void } + def initialize(source, node_id, location, flags, locals, class_keyword_loc, constant_path, inheritance_operator_loc, superclass, body, end_keyword_loc, name); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, locals: T::Array[Symbol], class_keyword_loc: Prism::Location, constant_path: T.any(Prism::ConstantReadNode, Prism::ConstantPathNode, Prism::CallNode), inheritance_operator_loc: T.nilable(Prism::Location), superclass: T.nilable(Prism::Node), body: T.nilable(T.any(Prism::StatementsNode, Prism::BeginNode)), end_keyword_loc: Prism::Location, name: Symbol).returns(Prism::ClassNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, locals: self.locals, class_keyword_loc: self.class_keyword_loc, constant_path: self.constant_path, inheritance_operator_loc: self.inheritance_operator_loc, superclass: self.superclass, body: self.body, end_keyword_loc: self.end_keyword_loc, name: self.name); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def class_keyword; end + + sig { returns(T.nilable(String)) } + def inheritance_operator; end + + sig { returns(String) } + def end_keyword; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `&&=` operator for assignment to a class variable. +# +# @@target &&= value +# ^^^^^^^^^^^^^^^^^^ +class Prism::ClassVariableAndWriteNode < Prism::Node + sig { returns(Symbol) } + def name; end + + sig { returns(Prism::Location) } + def name_loc; end + + sig { returns(Prism::Location) } + def operator_loc; end + + sig { returns(Prism::Node) } + def value; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, operator_loc: Prism::Location, value: Prism::Node).void } + def initialize(source, node_id, location, flags, name, name_loc, operator_loc, value); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, operator_loc: Prism::Location, value: Prism::Node).returns(Prism::ClassVariableAndWriteNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc, operator_loc: self.operator_loc, value: self.value); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def operator; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents assigning to a class variable using an operator that isn't `=`. +# +# @@target += value +# ^^^^^^^^^^^^^^^^^ +class Prism::ClassVariableOperatorWriteNode < Prism::Node + sig { returns(Symbol) } + def name; end + + sig { returns(Prism::Location) } + def name_loc; end + + sig { returns(Prism::Location) } + def binary_operator_loc; end + + sig { returns(Prism::Node) } + def value; end + + sig { returns(Symbol) } + def binary_operator; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, binary_operator_loc: Prism::Location, value: Prism::Node, binary_operator: Symbol).void } + def initialize(source, node_id, location, flags, name, name_loc, binary_operator_loc, value, binary_operator); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, binary_operator_loc: Prism::Location, value: Prism::Node, binary_operator: Symbol).returns(Prism::ClassVariableOperatorWriteNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc, binary_operator_loc: self.binary_operator_loc, value: self.value, binary_operator: self.binary_operator); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `||=` operator for assignment to a class variable. +# +# @@target ||= value +# ^^^^^^^^^^^^^^^^^^ +class Prism::ClassVariableOrWriteNode < Prism::Node + sig { returns(Symbol) } + def name; end + + sig { returns(Prism::Location) } + def name_loc; end + + sig { returns(Prism::Location) } + def operator_loc; end + + sig { returns(Prism::Node) } + def value; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, operator_loc: Prism::Location, value: Prism::Node).void } + def initialize(source, node_id, location, flags, name, name_loc, operator_loc, value); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, operator_loc: Prism::Location, value: Prism::Node).returns(Prism::ClassVariableOrWriteNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc, operator_loc: self.operator_loc, value: self.value); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def operator; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents referencing a class variable. +# +# @@foo +# ^^^^^ +class Prism::ClassVariableReadNode < Prism::Node + sig { returns(Symbol) } + def name; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol).void } + def initialize(source, node_id, location, flags, name); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol).returns(Prism::ClassVariableReadNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents writing to a class variable in a context that doesn't have an explicit value. +# +# @@foo, @@bar = baz +# ^^^^^ ^^^^^ +class Prism::ClassVariableTargetNode < Prism::Node + sig { returns(Symbol) } + def name; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol).void } + def initialize(source, node_id, location, flags, name); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol).returns(Prism::ClassVariableTargetNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents writing to a class variable. +# +# @@foo = 1 +# ^^^^^^^^^ +class Prism::ClassVariableWriteNode < Prism::Node + sig { returns(Symbol) } + def name; end + + sig { returns(Prism::Location) } + def name_loc; end + + sig { returns(Prism::Node) } + def value; end + + sig { returns(Prism::Location) } + def operator_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, value: Prism::Node, operator_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, name, name_loc, value, operator_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, value: Prism::Node, operator_loc: Prism::Location).returns(Prism::ClassVariableWriteNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc, value: self.value, operator_loc: self.operator_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def operator; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `&&=` operator for assignment to a constant. +# +# Target &&= value +# ^^^^^^^^^^^^^^^^ +class Prism::ConstantAndWriteNode < Prism::Node + sig { returns(Symbol) } + def name; end + + sig { returns(Prism::Location) } + def name_loc; end + + sig { returns(Prism::Location) } + def operator_loc; end + + sig { returns(Prism::Node) } + def value; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, operator_loc: Prism::Location, value: Prism::Node).void } + def initialize(source, node_id, location, flags, name, name_loc, operator_loc, value); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, operator_loc: Prism::Location, value: Prism::Node).returns(Prism::ConstantAndWriteNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc, operator_loc: self.operator_loc, value: self.value); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def operator; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents assigning to a constant using an operator that isn't `=`. +# +# Target += value +# ^^^^^^^^^^^^^^^ +class Prism::ConstantOperatorWriteNode < Prism::Node + sig { returns(Symbol) } + def name; end + + sig { returns(Prism::Location) } + def name_loc; end + + sig { returns(Prism::Location) } + def binary_operator_loc; end + + sig { returns(Prism::Node) } + def value; end + + sig { returns(Symbol) } + def binary_operator; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, binary_operator_loc: Prism::Location, value: Prism::Node, binary_operator: Symbol).void } + def initialize(source, node_id, location, flags, name, name_loc, binary_operator_loc, value, binary_operator); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, binary_operator_loc: Prism::Location, value: Prism::Node, binary_operator: Symbol).returns(Prism::ConstantOperatorWriteNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc, binary_operator_loc: self.binary_operator_loc, value: self.value, binary_operator: self.binary_operator); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `||=` operator for assignment to a constant. +# +# Target ||= value +# ^^^^^^^^^^^^^^^^ +class Prism::ConstantOrWriteNode < Prism::Node + sig { returns(Symbol) } + def name; end + + sig { returns(Prism::Location) } + def name_loc; end + + sig { returns(Prism::Location) } + def operator_loc; end + + sig { returns(Prism::Node) } + def value; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, operator_loc: Prism::Location, value: Prism::Node).void } + def initialize(source, node_id, location, flags, name, name_loc, operator_loc, value); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, operator_loc: Prism::Location, value: Prism::Node).returns(Prism::ConstantOrWriteNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc, operator_loc: self.operator_loc, value: self.value); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def operator; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `&&=` operator for assignment to a constant path. +# +# Parent::Child &&= value +# ^^^^^^^^^^^^^^^^^^^^^^^ +class Prism::ConstantPathAndWriteNode < Prism::Node + sig { returns(Prism::ConstantPathNode) } + def target; end + + sig { returns(Prism::Location) } + def operator_loc; end + + sig { returns(Prism::Node) } + def value; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, target: Prism::ConstantPathNode, operator_loc: Prism::Location, value: Prism::Node).void } + def initialize(source, node_id, location, flags, target, operator_loc, value); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, target: Prism::ConstantPathNode, operator_loc: Prism::Location, value: Prism::Node).returns(Prism::ConstantPathAndWriteNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, target: self.target, operator_loc: self.operator_loc, value: self.value); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def operator; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents accessing a constant through a path of `::` operators. +# +# Foo::Bar +# ^^^^^^^^ +class Prism::ConstantPathNode < Prism::Node + sig { returns(T.nilable(Prism::Node)) } + def parent; end + + sig { returns(T.nilable(Symbol)) } + def name; end + + sig { returns(Prism::Location) } + def delimiter_loc; end + + sig { returns(Prism::Location) } + def name_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, parent: T.nilable(Prism::Node), name: T.nilable(Symbol), delimiter_loc: Prism::Location, name_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, parent, name, delimiter_loc, name_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, parent: T.nilable(Prism::Node), name: T.nilable(Symbol), delimiter_loc: Prism::Location, name_loc: Prism::Location).returns(Prism::ConstantPathNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, parent: self.parent, name: self.name, delimiter_loc: self.delimiter_loc, name_loc: self.name_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def delimiter; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents assigning to a constant path using an operator that isn't `=`. +# +# Parent::Child += value +# ^^^^^^^^^^^^^^^^^^^^^^ +class Prism::ConstantPathOperatorWriteNode < Prism::Node + sig { returns(Prism::ConstantPathNode) } + def target; end + + sig { returns(Prism::Location) } + def binary_operator_loc; end + + sig { returns(Prism::Node) } + def value; end + + sig { returns(Symbol) } + def binary_operator; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, target: Prism::ConstantPathNode, binary_operator_loc: Prism::Location, value: Prism::Node, binary_operator: Symbol).void } + def initialize(source, node_id, location, flags, target, binary_operator_loc, value, binary_operator); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, target: Prism::ConstantPathNode, binary_operator_loc: Prism::Location, value: Prism::Node, binary_operator: Symbol).returns(Prism::ConstantPathOperatorWriteNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, target: self.target, binary_operator_loc: self.binary_operator_loc, value: self.value, binary_operator: self.binary_operator); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `||=` operator for assignment to a constant path. +# +# Parent::Child ||= value +# ^^^^^^^^^^^^^^^^^^^^^^^ +class Prism::ConstantPathOrWriteNode < Prism::Node + sig { returns(Prism::ConstantPathNode) } + def target; end + + sig { returns(Prism::Location) } + def operator_loc; end + + sig { returns(Prism::Node) } + def value; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, target: Prism::ConstantPathNode, operator_loc: Prism::Location, value: Prism::Node).void } + def initialize(source, node_id, location, flags, target, operator_loc, value); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, target: Prism::ConstantPathNode, operator_loc: Prism::Location, value: Prism::Node).returns(Prism::ConstantPathOrWriteNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, target: self.target, operator_loc: self.operator_loc, value: self.value); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def operator; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents writing to a constant path in a context that doesn't have an explicit value. +# +# Foo::Foo, Bar::Bar = baz +# ^^^^^^^^ ^^^^^^^^ +class Prism::ConstantPathTargetNode < Prism::Node + sig { returns(T.nilable(Prism::Node)) } + def parent; end + + sig { returns(T.nilable(Symbol)) } + def name; end + + sig { returns(Prism::Location) } + def delimiter_loc; end + + sig { returns(Prism::Location) } + def name_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, parent: T.nilable(Prism::Node), name: T.nilable(Symbol), delimiter_loc: Prism::Location, name_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, parent, name, delimiter_loc, name_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, parent: T.nilable(Prism::Node), name: T.nilable(Symbol), delimiter_loc: Prism::Location, name_loc: Prism::Location).returns(Prism::ConstantPathTargetNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, parent: self.parent, name: self.name, delimiter_loc: self.delimiter_loc, name_loc: self.name_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def delimiter; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents writing to a constant path. +# +# ::Foo = 1 +# ^^^^^^^^^ +# +# Foo::Bar = 1 +# ^^^^^^^^^^^^ +# +# ::Foo::Bar = 1 +# ^^^^^^^^^^^^^^ +class Prism::ConstantPathWriteNode < Prism::Node + sig { returns(Prism::ConstantPathNode) } + def target; end + + sig { returns(Prism::Location) } + def operator_loc; end + + sig { returns(Prism::Node) } + def value; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, target: Prism::ConstantPathNode, operator_loc: Prism::Location, value: Prism::Node).void } + def initialize(source, node_id, location, flags, target, operator_loc, value); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, target: Prism::ConstantPathNode, operator_loc: Prism::Location, value: Prism::Node).returns(Prism::ConstantPathWriteNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, target: self.target, operator_loc: self.operator_loc, value: self.value); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def operator; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents referencing a constant. +# +# Foo +# ^^^ +class Prism::ConstantReadNode < Prism::Node + sig { returns(Symbol) } + def name; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol).void } + def initialize(source, node_id, location, flags, name); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol).returns(Prism::ConstantReadNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents writing to a constant in a context that doesn't have an explicit value. +# +# Foo, Bar = baz +# ^^^ ^^^ +class Prism::ConstantTargetNode < Prism::Node + sig { returns(Symbol) } + def name; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol).void } + def initialize(source, node_id, location, flags, name); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol).returns(Prism::ConstantTargetNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents writing to a constant. +# +# Foo = 1 +# ^^^^^^^ +class Prism::ConstantWriteNode < Prism::Node + sig { returns(Symbol) } + def name; end + + sig { returns(Prism::Location) } + def name_loc; end + + sig { returns(Prism::Node) } + def value; end + + sig { returns(Prism::Location) } + def operator_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, value: Prism::Node, operator_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, name, name_loc, value, operator_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, value: Prism::Node, operator_loc: Prism::Location).returns(Prism::ConstantWriteNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc, value: self.value, operator_loc: self.operator_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def operator; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents a method definition. +# +# def method +# end +# ^^^^^^^^^^ +class Prism::DefNode < Prism::Node + sig { returns(Symbol) } + def name; end + + sig { returns(Prism::Location) } + def name_loc; end + + sig { returns(T.nilable(Prism::Node)) } + def receiver; end + + sig { returns(T.nilable(Prism::ParametersNode)) } + def parameters; end + + sig { returns(T.nilable(T.any(Prism::StatementsNode, Prism::BeginNode))) } + def body; end + + sig { returns(T::Array[Symbol]) } + def locals; end + + sig { returns(Prism::Location) } + def def_keyword_loc; end + + sig { returns(T.nilable(Prism::Location)) } + def operator_loc; end + + sig { returns(T.nilable(Prism::Location)) } + def lparen_loc; end + + sig { returns(T.nilable(Prism::Location)) } + def rparen_loc; end + + sig { returns(T.nilable(Prism::Location)) } + def equal_loc; end + + sig { returns(T.nilable(Prism::Location)) } + def end_keyword_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, receiver: T.nilable(Prism::Node), parameters: T.nilable(Prism::ParametersNode), body: T.nilable(T.any(Prism::StatementsNode, Prism::BeginNode)), locals: T::Array[Symbol], def_keyword_loc: Prism::Location, operator_loc: T.nilable(Prism::Location), lparen_loc: T.nilable(Prism::Location), rparen_loc: T.nilable(Prism::Location), equal_loc: T.nilable(Prism::Location), end_keyword_loc: T.nilable(Prism::Location)).void } + def initialize(source, node_id, location, flags, name, name_loc, receiver, parameters, body, locals, def_keyword_loc, operator_loc, lparen_loc, rparen_loc, equal_loc, end_keyword_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, receiver: T.nilable(Prism::Node), parameters: T.nilable(Prism::ParametersNode), body: T.nilable(T.any(Prism::StatementsNode, Prism::BeginNode)), locals: T::Array[Symbol], def_keyword_loc: Prism::Location, operator_loc: T.nilable(Prism::Location), lparen_loc: T.nilable(Prism::Location), rparen_loc: T.nilable(Prism::Location), equal_loc: T.nilable(Prism::Location), end_keyword_loc: T.nilable(Prism::Location)).returns(Prism::DefNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc, receiver: self.receiver, parameters: self.parameters, body: self.body, locals: self.locals, def_keyword_loc: self.def_keyword_loc, operator_loc: self.operator_loc, lparen_loc: self.lparen_loc, rparen_loc: self.rparen_loc, equal_loc: self.equal_loc, end_keyword_loc: self.end_keyword_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def def_keyword; end + + sig { returns(T.nilable(String)) } + def operator; end + + sig { returns(T.nilable(String)) } + def lparen; end + + sig { returns(T.nilable(String)) } + def rparen; end + + sig { returns(T.nilable(String)) } + def equal; end + + sig { returns(T.nilable(String)) } + def end_keyword; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `defined?` keyword. +# +# defined?(a) +# ^^^^^^^^^^^ +class Prism::DefinedNode < Prism::Node + sig { returns(T.nilable(Prism::Location)) } + def lparen_loc; end + + sig { returns(Prism::Node) } + def value; end + + sig { returns(T.nilable(Prism::Location)) } + def rparen_loc; end + + sig { returns(Prism::Location) } + def keyword_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, lparen_loc: T.nilable(Prism::Location), value: Prism::Node, rparen_loc: T.nilable(Prism::Location), keyword_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, lparen_loc, value, rparen_loc, keyword_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, lparen_loc: T.nilable(Prism::Location), value: Prism::Node, rparen_loc: T.nilable(Prism::Location), keyword_loc: Prism::Location).returns(Prism::DefinedNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, lparen_loc: self.lparen_loc, value: self.value, rparen_loc: self.rparen_loc, keyword_loc: self.keyword_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(T.nilable(String)) } + def lparen; end + + sig { returns(T.nilable(String)) } + def rparen; end + + sig { returns(String) } + def keyword; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents an `else` clause in a `case`, `if`, or `unless` statement. +# +# if a then b else c end +# ^^^^^^^^^^ +class Prism::ElseNode < Prism::Node + sig { returns(Prism::Location) } + def else_keyword_loc; end + + sig { returns(T.nilable(Prism::StatementsNode)) } + def statements; end + + sig { returns(T.nilable(Prism::Location)) } + def end_keyword_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, else_keyword_loc: Prism::Location, statements: T.nilable(Prism::StatementsNode), end_keyword_loc: T.nilable(Prism::Location)).void } + def initialize(source, node_id, location, flags, else_keyword_loc, statements, end_keyword_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, else_keyword_loc: Prism::Location, statements: T.nilable(Prism::StatementsNode), end_keyword_loc: T.nilable(Prism::Location)).returns(Prism::ElseNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, else_keyword_loc: self.else_keyword_loc, statements: self.statements, end_keyword_loc: self.end_keyword_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def else_keyword; end + + sig { returns(T.nilable(String)) } + def end_keyword; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents an interpolated set of statements. +# +# "foo #{bar}" +# ^^^^^^ +class Prism::EmbeddedStatementsNode < Prism::Node + sig { returns(Prism::Location) } + def opening_loc; end + + sig { returns(T.nilable(Prism::StatementsNode)) } + def statements; end + + sig { returns(Prism::Location) } + def closing_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, opening_loc: Prism::Location, statements: T.nilable(Prism::StatementsNode), closing_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, opening_loc, statements, closing_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, opening_loc: Prism::Location, statements: T.nilable(Prism::StatementsNode), closing_loc: Prism::Location).returns(Prism::EmbeddedStatementsNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, opening_loc: self.opening_loc, statements: self.statements, closing_loc: self.closing_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def opening; end + + sig { returns(String) } + def closing; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents an interpolated variable. +# +# "foo #@bar" +# ^^^^^ +class Prism::EmbeddedVariableNode < Prism::Node + sig { returns(Prism::Location) } + def operator_loc; end + + sig { returns(T.any(Prism::InstanceVariableReadNode, Prism::ClassVariableReadNode, Prism::GlobalVariableReadNode, Prism::BackReferenceReadNode, Prism::NumberedReferenceReadNode)) } + def variable; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, operator_loc: Prism::Location, variable: T.any(Prism::InstanceVariableReadNode, Prism::ClassVariableReadNode, Prism::GlobalVariableReadNode, Prism::BackReferenceReadNode, Prism::NumberedReferenceReadNode)).void } + def initialize(source, node_id, location, flags, operator_loc, variable); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, operator_loc: Prism::Location, variable: T.any(Prism::InstanceVariableReadNode, Prism::ClassVariableReadNode, Prism::GlobalVariableReadNode, Prism::BackReferenceReadNode, Prism::NumberedReferenceReadNode)).returns(Prism::EmbeddedVariableNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, operator_loc: self.operator_loc, variable: self.variable); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def operator; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents an `ensure` clause in a `begin` statement. +# +# begin +# foo +# ensure +# ^^^^^^ +# bar +# end +class Prism::EnsureNode < Prism::Node + sig { returns(Prism::Location) } + def ensure_keyword_loc; end + + sig { returns(T.nilable(Prism::StatementsNode)) } + def statements; end + + sig { returns(Prism::Location) } + def end_keyword_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, ensure_keyword_loc: Prism::Location, statements: T.nilable(Prism::StatementsNode), end_keyword_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, ensure_keyword_loc, statements, end_keyword_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, ensure_keyword_loc: Prism::Location, statements: T.nilable(Prism::StatementsNode), end_keyword_loc: Prism::Location).returns(Prism::EnsureNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, ensure_keyword_loc: self.ensure_keyword_loc, statements: self.statements, end_keyword_loc: self.end_keyword_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def ensure_keyword; end + + sig { returns(String) } + def end_keyword; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the literal `false` keyword. +# +# false +# ^^^^^ +class Prism::FalseNode < Prism::Node + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer).void } + def initialize(source, node_id, location, flags); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer).returns(Prism::FalseNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents a find pattern in pattern matching. +# +# foo in *bar, baz, *qux +# ^^^^^^^^^^^^^^^ +# +# foo in [*bar, baz, *qux] +# ^^^^^^^^^^^^^^^^^ +# +# foo in Foo(*bar, baz, *qux) +# ^^^^^^^^^^^^^^^^^^^^ +# +# foo => *bar, baz, *qux +# ^^^^^^^^^^^^^^^ +class Prism::FindPatternNode < Prism::Node + sig { returns(T.nilable(T.any(Prism::ConstantPathNode, Prism::ConstantReadNode))) } + def constant; end + + sig { returns(Prism::SplatNode) } + def left; end + + sig { returns(T::Array[Prism::Node]) } + def requireds; end + + sig { returns(T.any(Prism::SplatNode, Prism::MissingNode)) } + def right; end + + sig { returns(T.nilable(Prism::Location)) } + def opening_loc; end + + sig { returns(T.nilable(Prism::Location)) } + def closing_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, constant: T.nilable(T.any(Prism::ConstantPathNode, Prism::ConstantReadNode)), left: Prism::SplatNode, requireds: T::Array[Prism::Node], right: T.any(Prism::SplatNode, Prism::MissingNode), opening_loc: T.nilable(Prism::Location), closing_loc: T.nilable(Prism::Location)).void } + def initialize(source, node_id, location, flags, constant, left, requireds, right, opening_loc, closing_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, constant: T.nilable(T.any(Prism::ConstantPathNode, Prism::ConstantReadNode)), left: Prism::SplatNode, requireds: T::Array[Prism::Node], right: T.any(Prism::SplatNode, Prism::MissingNode), opening_loc: T.nilable(Prism::Location), closing_loc: T.nilable(Prism::Location)).returns(Prism::FindPatternNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, constant: self.constant, left: self.left, requireds: self.requireds, right: self.right, opening_loc: self.opening_loc, closing_loc: self.closing_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(T.nilable(String)) } + def opening; end + + sig { returns(T.nilable(String)) } + def closing; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `..` or `...` operators to create flip flops. +# +# baz if foo .. bar +# ^^^^^^^^^^ +class Prism::FlipFlopNode < Prism::Node + sig { returns(T::Boolean) } + def exclude_end?; end + + sig { returns(T.nilable(Prism::Node)) } + def left; end + + sig { returns(T.nilable(Prism::Node)) } + def right; end + + sig { returns(Prism::Location) } + def operator_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, left: T.nilable(Prism::Node), right: T.nilable(Prism::Node), operator_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, left, right, operator_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, left: T.nilable(Prism::Node), right: T.nilable(Prism::Node), operator_loc: Prism::Location).returns(Prism::FlipFlopNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, left: self.left, right: self.right, operator_loc: self.operator_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def operator; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents a floating point number literal. +# +# 1.0 +# ^^^ +class Prism::FloatNode < Prism::Node + sig { returns(Float) } + def value; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, value: Float).void } + def initialize(source, node_id, location, flags, value); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, value: Float).returns(Prism::FloatNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, value: self.value); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `for` keyword. +# +# for i in a end +# ^^^^^^^^^^^^^^ +class Prism::ForNode < Prism::Node + sig { returns(T.any(Prism::LocalVariableTargetNode, Prism::InstanceVariableTargetNode, Prism::ClassVariableTargetNode, Prism::GlobalVariableTargetNode, Prism::ConstantTargetNode, Prism::ConstantPathTargetNode, Prism::CallTargetNode, Prism::IndexTargetNode, Prism::MultiTargetNode, Prism::BackReferenceReadNode, Prism::NumberedReferenceReadNode, Prism::MissingNode)) } + def index; end + + sig { returns(Prism::Node) } + def collection; end + + sig { returns(T.nilable(Prism::StatementsNode)) } + def statements; end + + sig { returns(Prism::Location) } + def for_keyword_loc; end + + sig { returns(Prism::Location) } + def in_keyword_loc; end + + sig { returns(T.nilable(Prism::Location)) } + def do_keyword_loc; end + + sig { returns(Prism::Location) } + def end_keyword_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, index: T.any(Prism::LocalVariableTargetNode, Prism::InstanceVariableTargetNode, Prism::ClassVariableTargetNode, Prism::GlobalVariableTargetNode, Prism::ConstantTargetNode, Prism::ConstantPathTargetNode, Prism::CallTargetNode, Prism::IndexTargetNode, Prism::MultiTargetNode, Prism::BackReferenceReadNode, Prism::NumberedReferenceReadNode, Prism::MissingNode), collection: Prism::Node, statements: T.nilable(Prism::StatementsNode), for_keyword_loc: Prism::Location, in_keyword_loc: Prism::Location, do_keyword_loc: T.nilable(Prism::Location), end_keyword_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, index, collection, statements, for_keyword_loc, in_keyword_loc, do_keyword_loc, end_keyword_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, index: T.any(Prism::LocalVariableTargetNode, Prism::InstanceVariableTargetNode, Prism::ClassVariableTargetNode, Prism::GlobalVariableTargetNode, Prism::ConstantTargetNode, Prism::ConstantPathTargetNode, Prism::CallTargetNode, Prism::IndexTargetNode, Prism::MultiTargetNode, Prism::BackReferenceReadNode, Prism::NumberedReferenceReadNode, Prism::MissingNode), collection: Prism::Node, statements: T.nilable(Prism::StatementsNode), for_keyword_loc: Prism::Location, in_keyword_loc: Prism::Location, do_keyword_loc: T.nilable(Prism::Location), end_keyword_loc: Prism::Location).returns(Prism::ForNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, index: self.index, collection: self.collection, statements: self.statements, for_keyword_loc: self.for_keyword_loc, in_keyword_loc: self.in_keyword_loc, do_keyword_loc: self.do_keyword_loc, end_keyword_loc: self.end_keyword_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def for_keyword; end + + sig { returns(String) } + def in_keyword; end + + sig { returns(T.nilable(String)) } + def do_keyword; end + + sig { returns(String) } + def end_keyword; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents forwarding all arguments to this method to another method. +# +# def foo(...) +# bar(...) +# ^^^ +# end +class Prism::ForwardingArgumentsNode < Prism::Node + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer).void } + def initialize(source, node_id, location, flags); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer).returns(Prism::ForwardingArgumentsNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the forwarding parameter in a method, block, or lambda declaration. +# +# def foo(...) +# ^^^ +# end +class Prism::ForwardingParameterNode < Prism::Node + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer).void } + def initialize(source, node_id, location, flags); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer).returns(Prism::ForwardingParameterNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `super` keyword without parentheses or arguments, but which might have a block. +# +# super +# ^^^^^ +# +# super { 123 } +# ^^^^^^^^^^^^^ +# +# If it has any other arguments, it would be a `SuperNode` instead. +class Prism::ForwardingSuperNode < Prism::Node + sig { returns(T.nilable(Prism::BlockNode)) } + def block; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, block: T.nilable(Prism::BlockNode)).void } + def initialize(source, node_id, location, flags, block); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, block: T.nilable(Prism::BlockNode)).returns(Prism::ForwardingSuperNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, block: self.block); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `&&=` operator for assignment to a global variable. +# +# $target &&= value +# ^^^^^^^^^^^^^^^^^ +class Prism::GlobalVariableAndWriteNode < Prism::Node + sig { returns(Symbol) } + def name; end + + sig { returns(Prism::Location) } + def name_loc; end + + sig { returns(Prism::Location) } + def operator_loc; end + + sig { returns(Prism::Node) } + def value; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, operator_loc: Prism::Location, value: Prism::Node).void } + def initialize(source, node_id, location, flags, name, name_loc, operator_loc, value); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, operator_loc: Prism::Location, value: Prism::Node).returns(Prism::GlobalVariableAndWriteNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc, operator_loc: self.operator_loc, value: self.value); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def operator; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents assigning to a global variable using an operator that isn't `=`. +# +# $target += value +# ^^^^^^^^^^^^^^^^ +class Prism::GlobalVariableOperatorWriteNode < Prism::Node + sig { returns(Symbol) } + def name; end + + sig { returns(Prism::Location) } + def name_loc; end + + sig { returns(Prism::Location) } + def binary_operator_loc; end + + sig { returns(Prism::Node) } + def value; end + + sig { returns(Symbol) } + def binary_operator; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, binary_operator_loc: Prism::Location, value: Prism::Node, binary_operator: Symbol).void } + def initialize(source, node_id, location, flags, name, name_loc, binary_operator_loc, value, binary_operator); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, binary_operator_loc: Prism::Location, value: Prism::Node, binary_operator: Symbol).returns(Prism::GlobalVariableOperatorWriteNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc, binary_operator_loc: self.binary_operator_loc, value: self.value, binary_operator: self.binary_operator); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `||=` operator for assignment to a global variable. +# +# $target ||= value +# ^^^^^^^^^^^^^^^^^ +class Prism::GlobalVariableOrWriteNode < Prism::Node + sig { returns(Symbol) } + def name; end + + sig { returns(Prism::Location) } + def name_loc; end + + sig { returns(Prism::Location) } + def operator_loc; end + + sig { returns(Prism::Node) } + def value; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, operator_loc: Prism::Location, value: Prism::Node).void } + def initialize(source, node_id, location, flags, name, name_loc, operator_loc, value); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, operator_loc: Prism::Location, value: Prism::Node).returns(Prism::GlobalVariableOrWriteNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc, operator_loc: self.operator_loc, value: self.value); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def operator; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents referencing a global variable. +# +# $foo +# ^^^^ +class Prism::GlobalVariableReadNode < Prism::Node + sig { returns(Symbol) } + def name; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol).void } + def initialize(source, node_id, location, flags, name); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol).returns(Prism::GlobalVariableReadNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents writing to a global variable in a context that doesn't have an explicit value. +# +# $foo, $bar = baz +# ^^^^ ^^^^ +class Prism::GlobalVariableTargetNode < Prism::Node + sig { returns(Symbol) } + def name; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol).void } + def initialize(source, node_id, location, flags, name); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol).returns(Prism::GlobalVariableTargetNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents writing to a global variable. +# +# $foo = 1 +# ^^^^^^^^ +class Prism::GlobalVariableWriteNode < Prism::Node + sig { returns(Symbol) } + def name; end + + sig { returns(Prism::Location) } + def name_loc; end + + sig { returns(Prism::Node) } + def value; end + + sig { returns(Prism::Location) } + def operator_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, value: Prism::Node, operator_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, name, name_loc, value, operator_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, value: Prism::Node, operator_loc: Prism::Location).returns(Prism::GlobalVariableWriteNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc, value: self.value, operator_loc: self.operator_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def operator; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents a hash literal. +# +# { a => b } +# ^^^^^^^^^^ +class Prism::HashNode < Prism::Node + sig { returns(Prism::Location) } + def opening_loc; end + + sig { returns(T::Array[T.any(Prism::AssocNode, Prism::AssocSplatNode)]) } + def elements; end + + sig { returns(Prism::Location) } + def closing_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, opening_loc: Prism::Location, elements: T::Array[T.any(Prism::AssocNode, Prism::AssocSplatNode)], closing_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, opening_loc, elements, closing_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, opening_loc: Prism::Location, elements: T::Array[T.any(Prism::AssocNode, Prism::AssocSplatNode)], closing_loc: Prism::Location).returns(Prism::HashNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, opening_loc: self.opening_loc, elements: self.elements, closing_loc: self.closing_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def opening; end + + sig { returns(String) } + def closing; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents a hash pattern in pattern matching. +# +# foo => { a: 1, b: 2 } +# ^^^^^^^^^^^^^^ +# +# foo => { a: 1, b: 2, **c } +# ^^^^^^^^^^^^^^^^^^^ +# +# foo => Bar[a: 1, b: 2] +# ^^^^^^^^^^^^^^^ +# +# foo in { a: 1, b: 2 } +# ^^^^^^^^^^^^^^ +class Prism::HashPatternNode < Prism::Node + sig { returns(T.nilable(T.any(Prism::ConstantPathNode, Prism::ConstantReadNode))) } + def constant; end + + sig { returns(T::Array[Prism::AssocNode]) } + def elements; end + + sig { returns(T.nilable(T.any(Prism::AssocSplatNode, Prism::NoKeywordsParameterNode))) } + def rest; end + + sig { returns(T.nilable(Prism::Location)) } + def opening_loc; end + + sig { returns(T.nilable(Prism::Location)) } + def closing_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, constant: T.nilable(T.any(Prism::ConstantPathNode, Prism::ConstantReadNode)), elements: T::Array[Prism::AssocNode], rest: T.nilable(T.any(Prism::AssocSplatNode, Prism::NoKeywordsParameterNode)), opening_loc: T.nilable(Prism::Location), closing_loc: T.nilable(Prism::Location)).void } + def initialize(source, node_id, location, flags, constant, elements, rest, opening_loc, closing_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, constant: T.nilable(T.any(Prism::ConstantPathNode, Prism::ConstantReadNode)), elements: T::Array[Prism::AssocNode], rest: T.nilable(T.any(Prism::AssocSplatNode, Prism::NoKeywordsParameterNode)), opening_loc: T.nilable(Prism::Location), closing_loc: T.nilable(Prism::Location)).returns(Prism::HashPatternNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, constant: self.constant, elements: self.elements, rest: self.rest, opening_loc: self.opening_loc, closing_loc: self.closing_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(T.nilable(String)) } + def opening; end + + sig { returns(T.nilable(String)) } + def closing; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# 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 +# ^^^^^^^^^^^^^^^ +class Prism::IfNode < Prism::Node + sig { returns(T.nilable(Prism::Location)) } + def if_keyword_loc; end + + sig { returns(Prism::Node) } + def predicate; end + + sig { returns(T.nilable(Prism::Location)) } + def then_keyword_loc; end + + sig { returns(T.nilable(Prism::StatementsNode)) } + def statements; end + + sig { returns(T.nilable(T.any(Prism::ElseNode, Prism::IfNode))) } + def subsequent; end + + sig { returns(T.nilable(Prism::Location)) } + def end_keyword_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, if_keyword_loc: T.nilable(Prism::Location), predicate: Prism::Node, then_keyword_loc: T.nilable(Prism::Location), statements: T.nilable(Prism::StatementsNode), subsequent: T.nilable(T.any(Prism::ElseNode, Prism::IfNode)), end_keyword_loc: T.nilable(Prism::Location)).void } + def initialize(source, node_id, location, flags, if_keyword_loc, predicate, then_keyword_loc, statements, subsequent, end_keyword_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, if_keyword_loc: T.nilable(Prism::Location), predicate: Prism::Node, then_keyword_loc: T.nilable(Prism::Location), statements: T.nilable(Prism::StatementsNode), subsequent: T.nilable(T.any(Prism::ElseNode, Prism::IfNode)), end_keyword_loc: T.nilable(Prism::Location)).returns(Prism::IfNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, if_keyword_loc: self.if_keyword_loc, predicate: self.predicate, then_keyword_loc: self.then_keyword_loc, statements: self.statements, subsequent: self.subsequent, end_keyword_loc: self.end_keyword_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(T.nilable(String)) } + def if_keyword; end + + sig { returns(T.nilable(String)) } + def then_keyword; end + + sig { returns(T.nilable(String)) } + def end_keyword; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents an imaginary number literal. +# +# 1.0i +# ^^^^ +class Prism::ImaginaryNode < Prism::Node + sig { returns(T.any(Prism::FloatNode, Prism::IntegerNode, Prism::RationalNode)) } + def numeric; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, numeric: T.any(Prism::FloatNode, Prism::IntegerNode, Prism::RationalNode)).void } + def initialize(source, node_id, location, flags, numeric); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, numeric: T.any(Prism::FloatNode, Prism::IntegerNode, Prism::RationalNode)).returns(Prism::ImaginaryNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, numeric: self.numeric); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# 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: } +# ^^^^ +class Prism::ImplicitNode < Prism::Node + sig { returns(T.any(Prism::LocalVariableReadNode, Prism::CallNode, Prism::ConstantReadNode, Prism::LocalVariableTargetNode)) } + def value; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, value: T.any(Prism::LocalVariableReadNode, Prism::CallNode, Prism::ConstantReadNode, Prism::LocalVariableTargetNode)).void } + def initialize(source, node_id, location, flags, value); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, value: T.any(Prism::LocalVariableReadNode, Prism::CallNode, Prism::ConstantReadNode, Prism::LocalVariableTargetNode)).returns(Prism::ImplicitNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, value: self.value); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents using a trailing comma to indicate an implicit rest parameter. +# +# foo { |bar,| } +# ^ +# +# foo in [bar,] +# ^ +# +# for foo, in bar do end +# ^ +# +# foo, = bar +# ^ +class Prism::ImplicitRestNode < Prism::Node + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer).void } + def initialize(source, node_id, location, flags); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer).returns(Prism::ImplicitRestNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `in` keyword in a case statement. +# +# case a; in b then c end +# ^^^^^^^^^^^ +class Prism::InNode < Prism::Node + sig { returns(Prism::Node) } + def pattern; end + + sig { returns(T.nilable(Prism::StatementsNode)) } + def statements; end + + sig { returns(Prism::Location) } + def in_loc; end + + sig { returns(T.nilable(Prism::Location)) } + def then_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, pattern: Prism::Node, statements: T.nilable(Prism::StatementsNode), in_loc: Prism::Location, then_loc: T.nilable(Prism::Location)).void } + def initialize(source, node_id, location, flags, pattern, statements, in_loc, then_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, pattern: Prism::Node, statements: T.nilable(Prism::StatementsNode), in_loc: Prism::Location, then_loc: T.nilable(Prism::Location)).returns(Prism::InNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, pattern: self.pattern, statements: self.statements, in_loc: self.in_loc, then_loc: self.then_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def in; end + + sig { returns(T.nilable(String)) } + def then; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `&&=` operator on a call to the `[]` method. +# +# foo.bar[baz] &&= value +# ^^^^^^^^^^^^^^^^^^^^^^ +class Prism::IndexAndWriteNode < Prism::Node + sig { returns(T::Boolean) } + def safe_navigation?; end + + sig { returns(T::Boolean) } + def variable_call?; end + + sig { returns(T::Boolean) } + def attribute_write?; end + + sig { returns(T::Boolean) } + def ignore_visibility?; end + + sig { returns(T.nilable(Prism::Node)) } + def receiver; end + + sig { returns(T.nilable(Prism::Location)) } + def call_operator_loc; end + + sig { returns(Prism::Location) } + def opening_loc; end + + sig { returns(T.nilable(Prism::ArgumentsNode)) } + def arguments; end + + sig { returns(Prism::Location) } + def closing_loc; end + + sig { returns(T.nilable(Prism::BlockArgumentNode)) } + def block; end + + sig { returns(Prism::Location) } + def operator_loc; end + + sig { returns(Prism::Node) } + def value; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, receiver: T.nilable(Prism::Node), call_operator_loc: T.nilable(Prism::Location), opening_loc: Prism::Location, arguments: T.nilable(Prism::ArgumentsNode), closing_loc: Prism::Location, block: T.nilable(Prism::BlockArgumentNode), operator_loc: Prism::Location, value: Prism::Node).void } + def initialize(source, node_id, location, flags, receiver, call_operator_loc, opening_loc, arguments, closing_loc, block, operator_loc, value); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, receiver: T.nilable(Prism::Node), call_operator_loc: T.nilable(Prism::Location), opening_loc: Prism::Location, arguments: T.nilable(Prism::ArgumentsNode), closing_loc: Prism::Location, block: T.nilable(Prism::BlockArgumentNode), operator_loc: Prism::Location, value: Prism::Node).returns(Prism::IndexAndWriteNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, receiver: self.receiver, call_operator_loc: self.call_operator_loc, opening_loc: self.opening_loc, arguments: self.arguments, closing_loc: self.closing_loc, block: self.block, operator_loc: self.operator_loc, value: self.value); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(T.nilable(String)) } + def call_operator; end + + sig { returns(String) } + def opening; end + + sig { returns(String) } + def closing; end + + sig { returns(String) } + def operator; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of an assignment operator on a call to `[]`. +# +# foo.bar[baz] += value +# ^^^^^^^^^^^^^^^^^^^^^ +class Prism::IndexOperatorWriteNode < Prism::Node + sig { returns(T::Boolean) } + def safe_navigation?; end + + sig { returns(T::Boolean) } + def variable_call?; end + + sig { returns(T::Boolean) } + def attribute_write?; end + + sig { returns(T::Boolean) } + def ignore_visibility?; end + + sig { returns(T.nilable(Prism::Node)) } + def receiver; end + + sig { returns(T.nilable(Prism::Location)) } + def call_operator_loc; end + + sig { returns(Prism::Location) } + def opening_loc; end + + sig { returns(T.nilable(Prism::ArgumentsNode)) } + def arguments; end + + sig { returns(Prism::Location) } + def closing_loc; end + + sig { returns(T.nilable(Prism::BlockArgumentNode)) } + def block; end + + sig { returns(Symbol) } + def binary_operator; end + + sig { returns(Prism::Location) } + def binary_operator_loc; end + + sig { returns(Prism::Node) } + def value; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, receiver: T.nilable(Prism::Node), call_operator_loc: T.nilable(Prism::Location), opening_loc: Prism::Location, arguments: T.nilable(Prism::ArgumentsNode), closing_loc: Prism::Location, block: T.nilable(Prism::BlockArgumentNode), binary_operator: Symbol, binary_operator_loc: Prism::Location, value: Prism::Node).void } + def initialize(source, node_id, location, flags, receiver, call_operator_loc, opening_loc, arguments, closing_loc, block, binary_operator, binary_operator_loc, value); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, receiver: T.nilable(Prism::Node), call_operator_loc: T.nilable(Prism::Location), opening_loc: Prism::Location, arguments: T.nilable(Prism::ArgumentsNode), closing_loc: Prism::Location, block: T.nilable(Prism::BlockArgumentNode), binary_operator: Symbol, binary_operator_loc: Prism::Location, value: Prism::Node).returns(Prism::IndexOperatorWriteNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, receiver: self.receiver, call_operator_loc: self.call_operator_loc, opening_loc: self.opening_loc, arguments: self.arguments, closing_loc: self.closing_loc, block: self.block, binary_operator: self.binary_operator, binary_operator_loc: self.binary_operator_loc, value: self.value); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(T.nilable(String)) } + def call_operator; end + + sig { returns(String) } + def opening; end + + sig { returns(String) } + def closing; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `||=` operator on a call to `[]`. +# +# foo.bar[baz] ||= value +# ^^^^^^^^^^^^^^^^^^^^^^ +class Prism::IndexOrWriteNode < Prism::Node + sig { returns(T::Boolean) } + def safe_navigation?; end + + sig { returns(T::Boolean) } + def variable_call?; end + + sig { returns(T::Boolean) } + def attribute_write?; end + + sig { returns(T::Boolean) } + def ignore_visibility?; end + + sig { returns(T.nilable(Prism::Node)) } + def receiver; end + + sig { returns(T.nilable(Prism::Location)) } + def call_operator_loc; end + + sig { returns(Prism::Location) } + def opening_loc; end + + sig { returns(T.nilable(Prism::ArgumentsNode)) } + def arguments; end + + sig { returns(Prism::Location) } + def closing_loc; end + + sig { returns(T.nilable(Prism::BlockArgumentNode)) } + def block; end + + sig { returns(Prism::Location) } + def operator_loc; end + + sig { returns(Prism::Node) } + def value; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, receiver: T.nilable(Prism::Node), call_operator_loc: T.nilable(Prism::Location), opening_loc: Prism::Location, arguments: T.nilable(Prism::ArgumentsNode), closing_loc: Prism::Location, block: T.nilable(Prism::BlockArgumentNode), operator_loc: Prism::Location, value: Prism::Node).void } + def initialize(source, node_id, location, flags, receiver, call_operator_loc, opening_loc, arguments, closing_loc, block, operator_loc, value); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, receiver: T.nilable(Prism::Node), call_operator_loc: T.nilable(Prism::Location), opening_loc: Prism::Location, arguments: T.nilable(Prism::ArgumentsNode), closing_loc: Prism::Location, block: T.nilable(Prism::BlockArgumentNode), operator_loc: Prism::Location, value: Prism::Node).returns(Prism::IndexOrWriteNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, receiver: self.receiver, call_operator_loc: self.call_operator_loc, opening_loc: self.opening_loc, arguments: self.arguments, closing_loc: self.closing_loc, block: self.block, operator_loc: self.operator_loc, value: self.value); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(T.nilable(String)) } + def call_operator; end + + sig { returns(String) } + def opening; end + + sig { returns(String) } + def closing; end + + sig { returns(String) } + def operator; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents assigning to an index. +# +# foo[bar], = 1 +# ^^^^^^^^ +# +# begin +# rescue => foo[bar] +# ^^^^^^^^ +# end +# +# for foo[bar] in baz do end +# ^^^^^^^^ +class Prism::IndexTargetNode < Prism::Node + sig { returns(T::Boolean) } + def safe_navigation?; end + + sig { returns(T::Boolean) } + def variable_call?; end + + sig { returns(T::Boolean) } + def attribute_write?; end + + sig { returns(T::Boolean) } + def ignore_visibility?; end + + sig { returns(Prism::Node) } + def receiver; end + + sig { returns(Prism::Location) } + def opening_loc; end + + sig { returns(T.nilable(Prism::ArgumentsNode)) } + def arguments; end + + sig { returns(Prism::Location) } + def closing_loc; end + + sig { returns(T.nilable(Prism::BlockArgumentNode)) } + def block; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, receiver: Prism::Node, opening_loc: Prism::Location, arguments: T.nilable(Prism::ArgumentsNode), closing_loc: Prism::Location, block: T.nilable(Prism::BlockArgumentNode)).void } + def initialize(source, node_id, location, flags, receiver, opening_loc, arguments, closing_loc, block); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, receiver: Prism::Node, opening_loc: Prism::Location, arguments: T.nilable(Prism::ArgumentsNode), closing_loc: Prism::Location, block: T.nilable(Prism::BlockArgumentNode)).returns(Prism::IndexTargetNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, receiver: self.receiver, opening_loc: self.opening_loc, arguments: self.arguments, closing_loc: self.closing_loc, block: self.block); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def opening; end + + sig { returns(String) } + def closing; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `&&=` operator for assignment to an instance variable. +# +# @target &&= value +# ^^^^^^^^^^^^^^^^^ +class Prism::InstanceVariableAndWriteNode < Prism::Node + sig { returns(Symbol) } + def name; end + + sig { returns(Prism::Location) } + def name_loc; end + + sig { returns(Prism::Location) } + def operator_loc; end + + sig { returns(Prism::Node) } + def value; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, operator_loc: Prism::Location, value: Prism::Node).void } + def initialize(source, node_id, location, flags, name, name_loc, operator_loc, value); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, operator_loc: Prism::Location, value: Prism::Node).returns(Prism::InstanceVariableAndWriteNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc, operator_loc: self.operator_loc, value: self.value); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def operator; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents assigning to an instance variable using an operator that isn't `=`. +# +# @target += value +# ^^^^^^^^^^^^^^^^ +class Prism::InstanceVariableOperatorWriteNode < Prism::Node + sig { returns(Symbol) } + def name; end + + sig { returns(Prism::Location) } + def name_loc; end + + sig { returns(Prism::Location) } + def binary_operator_loc; end + + sig { returns(Prism::Node) } + def value; end + + sig { returns(Symbol) } + def binary_operator; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, binary_operator_loc: Prism::Location, value: Prism::Node, binary_operator: Symbol).void } + def initialize(source, node_id, location, flags, name, name_loc, binary_operator_loc, value, binary_operator); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, binary_operator_loc: Prism::Location, value: Prism::Node, binary_operator: Symbol).returns(Prism::InstanceVariableOperatorWriteNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc, binary_operator_loc: self.binary_operator_loc, value: self.value, binary_operator: self.binary_operator); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `||=` operator for assignment to an instance variable. +# +# @target ||= value +# ^^^^^^^^^^^^^^^^^ +class Prism::InstanceVariableOrWriteNode < Prism::Node + sig { returns(Symbol) } + def name; end + + sig { returns(Prism::Location) } + def name_loc; end + + sig { returns(Prism::Location) } + def operator_loc; end + + sig { returns(Prism::Node) } + def value; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, operator_loc: Prism::Location, value: Prism::Node).void } + def initialize(source, node_id, location, flags, name, name_loc, operator_loc, value); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, operator_loc: Prism::Location, value: Prism::Node).returns(Prism::InstanceVariableOrWriteNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc, operator_loc: self.operator_loc, value: self.value); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def operator; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents referencing an instance variable. +# +# @foo +# ^^^^ +class Prism::InstanceVariableReadNode < Prism::Node + sig { returns(Symbol) } + def name; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol).void } + def initialize(source, node_id, location, flags, name); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol).returns(Prism::InstanceVariableReadNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents writing to an instance variable in a context that doesn't have an explicit value. +# +# @foo, @bar = baz +# ^^^^ ^^^^ +class Prism::InstanceVariableTargetNode < Prism::Node + sig { returns(Symbol) } + def name; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol).void } + def initialize(source, node_id, location, flags, name); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol).returns(Prism::InstanceVariableTargetNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents writing to an instance variable. +# +# @foo = 1 +# ^^^^^^^^ +class Prism::InstanceVariableWriteNode < Prism::Node + sig { returns(Symbol) } + def name; end + + sig { returns(Prism::Location) } + def name_loc; end + + sig { returns(Prism::Node) } + def value; end + + sig { returns(Prism::Location) } + def operator_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, value: Prism::Node, operator_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, name, name_loc, value, operator_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, value: Prism::Node, operator_loc: Prism::Location).returns(Prism::InstanceVariableWriteNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc, value: self.value, operator_loc: self.operator_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def operator; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents an integer number literal. +# +# 1 +# ^ +class Prism::IntegerNode < Prism::Node + sig { returns(T::Boolean) } + def binary?; end + + sig { returns(T::Boolean) } + def decimal?; end + + sig { returns(T::Boolean) } + def octal?; end + + sig { returns(T::Boolean) } + def hexadecimal?; end + + sig { returns(Integer) } + def value; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, value: Integer).void } + def initialize(source, node_id, location, flags, value); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, value: Integer).returns(Prism::IntegerNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, value: self.value); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# 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 +# ^^^^^^^^^^^^^^^^ +class Prism::InterpolatedMatchLastLineNode < Prism::Node + sig { returns(T::Boolean) } + def ignore_case?; end + + sig { returns(T::Boolean) } + def extended?; end + + sig { returns(T::Boolean) } + def multi_line?; end + + sig { returns(T::Boolean) } + def once?; end + + sig { returns(T::Boolean) } + def euc_jp?; end + + sig { returns(T::Boolean) } + def ascii_8bit?; end + + sig { returns(T::Boolean) } + def windows_31j?; end + + sig { returns(T::Boolean) } + def utf_8?; end + + sig { returns(T::Boolean) } + def forced_utf8_encoding?; end + + sig { returns(T::Boolean) } + def forced_binary_encoding?; end + + sig { returns(T::Boolean) } + def forced_us_ascii_encoding?; end + + sig { returns(Prism::Location) } + def opening_loc; end + + sig { returns(T::Array[T.any(Prism::StringNode, Prism::EmbeddedStatementsNode, Prism::EmbeddedVariableNode)]) } + def parts; end + + sig { returns(Prism::Location) } + def closing_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, opening_loc: Prism::Location, parts: T::Array[T.any(Prism::StringNode, Prism::EmbeddedStatementsNode, Prism::EmbeddedVariableNode)], closing_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, opening_loc, parts, closing_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, opening_loc: Prism::Location, parts: T::Array[T.any(Prism::StringNode, Prism::EmbeddedStatementsNode, Prism::EmbeddedVariableNode)], closing_loc: Prism::Location).returns(Prism::InterpolatedMatchLastLineNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, opening_loc: self.opening_loc, parts: self.parts, closing_loc: self.closing_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def opening; end + + sig { returns(String) } + def closing; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents a regular expression literal that contains interpolation. +# +# /foo #{bar} baz/ +# ^^^^^^^^^^^^^^^^ +class Prism::InterpolatedRegularExpressionNode < Prism::Node + sig { returns(T::Boolean) } + def ignore_case?; end + + sig { returns(T::Boolean) } + def extended?; end + + sig { returns(T::Boolean) } + def multi_line?; end + + sig { returns(T::Boolean) } + def once?; end + + sig { returns(T::Boolean) } + def euc_jp?; end + + sig { returns(T::Boolean) } + def ascii_8bit?; end + + sig { returns(T::Boolean) } + def windows_31j?; end + + sig { returns(T::Boolean) } + def utf_8?; end + + sig { returns(T::Boolean) } + def forced_utf8_encoding?; end + + sig { returns(T::Boolean) } + def forced_binary_encoding?; end + + sig { returns(T::Boolean) } + def forced_us_ascii_encoding?; end + + sig { returns(Prism::Location) } + def opening_loc; end + + sig { returns(T::Array[T.any(Prism::StringNode, Prism::EmbeddedStatementsNode, Prism::EmbeddedVariableNode)]) } + def parts; end + + sig { returns(Prism::Location) } + def closing_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, opening_loc: Prism::Location, parts: T::Array[T.any(Prism::StringNode, Prism::EmbeddedStatementsNode, Prism::EmbeddedVariableNode)], closing_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, opening_loc, parts, closing_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, opening_loc: Prism::Location, parts: T::Array[T.any(Prism::StringNode, Prism::EmbeddedStatementsNode, Prism::EmbeddedVariableNode)], closing_loc: Prism::Location).returns(Prism::InterpolatedRegularExpressionNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, opening_loc: self.opening_loc, parts: self.parts, closing_loc: self.closing_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def opening; end + + sig { returns(String) } + def closing; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents a string literal that contains interpolation. +# +# "foo #{bar} baz" +# ^^^^^^^^^^^^^^^^ +class Prism::InterpolatedStringNode < Prism::Node + sig { returns(T::Boolean) } + def frozen?; end + + sig { returns(T::Boolean) } + def mutable?; end + + sig { returns(T.nilable(Prism::Location)) } + def opening_loc; end + + sig { returns(T::Array[T.any(Prism::StringNode, Prism::EmbeddedStatementsNode, Prism::EmbeddedVariableNode, Prism::InterpolatedStringNode, Prism::XStringNode, Prism::InterpolatedXStringNode, Prism::SymbolNode, Prism::InterpolatedSymbolNode)]) } + def parts; end + + sig { returns(T.nilable(Prism::Location)) } + def closing_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, opening_loc: T.nilable(Prism::Location), parts: T::Array[T.any(Prism::StringNode, Prism::EmbeddedStatementsNode, Prism::EmbeddedVariableNode, Prism::InterpolatedStringNode, Prism::XStringNode, Prism::InterpolatedXStringNode, Prism::SymbolNode, Prism::InterpolatedSymbolNode)], closing_loc: T.nilable(Prism::Location)).void } + def initialize(source, node_id, location, flags, opening_loc, parts, closing_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, opening_loc: T.nilable(Prism::Location), parts: T::Array[T.any(Prism::StringNode, Prism::EmbeddedStatementsNode, Prism::EmbeddedVariableNode, Prism::InterpolatedStringNode, Prism::XStringNode, Prism::InterpolatedXStringNode, Prism::SymbolNode, Prism::InterpolatedSymbolNode)], closing_loc: T.nilable(Prism::Location)).returns(Prism::InterpolatedStringNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, opening_loc: self.opening_loc, parts: self.parts, closing_loc: self.closing_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(T.nilable(String)) } + def opening; end + + sig { returns(T.nilable(String)) } + def closing; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents a symbol literal that contains interpolation. +# +# :"foo #{bar} baz" +# ^^^^^^^^^^^^^^^^^ +class Prism::InterpolatedSymbolNode < Prism::Node + sig { returns(T.nilable(Prism::Location)) } + def opening_loc; end + + sig { returns(T::Array[T.any(Prism::StringNode, Prism::EmbeddedStatementsNode, Prism::EmbeddedVariableNode)]) } + def parts; end + + sig { returns(T.nilable(Prism::Location)) } + def closing_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, opening_loc: T.nilable(Prism::Location), parts: T::Array[T.any(Prism::StringNode, Prism::EmbeddedStatementsNode, Prism::EmbeddedVariableNode)], closing_loc: T.nilable(Prism::Location)).void } + def initialize(source, node_id, location, flags, opening_loc, parts, closing_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, opening_loc: T.nilable(Prism::Location), parts: T::Array[T.any(Prism::StringNode, Prism::EmbeddedStatementsNode, Prism::EmbeddedVariableNode)], closing_loc: T.nilable(Prism::Location)).returns(Prism::InterpolatedSymbolNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, opening_loc: self.opening_loc, parts: self.parts, closing_loc: self.closing_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(T.nilable(String)) } + def opening; end + + sig { returns(T.nilable(String)) } + def closing; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents an xstring literal that contains interpolation. +# +# `foo #{bar} baz` +# ^^^^^^^^^^^^^^^^ +class Prism::InterpolatedXStringNode < Prism::Node + sig { returns(Prism::Location) } + def opening_loc; end + + sig { returns(T::Array[T.any(Prism::StringNode, Prism::EmbeddedStatementsNode, Prism::EmbeddedVariableNode)]) } + def parts; end + + sig { returns(Prism::Location) } + def closing_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, opening_loc: Prism::Location, parts: T::Array[T.any(Prism::StringNode, Prism::EmbeddedStatementsNode, Prism::EmbeddedVariableNode)], closing_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, opening_loc, parts, closing_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, opening_loc: Prism::Location, parts: T::Array[T.any(Prism::StringNode, Prism::EmbeddedStatementsNode, Prism::EmbeddedVariableNode)], closing_loc: Prism::Location).returns(Prism::InterpolatedXStringNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, opening_loc: self.opening_loc, parts: self.parts, closing_loc: self.closing_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def opening; end + + sig { returns(String) } + def closing; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents reading from the implicit `it` local variable. +# +# -> { it } +# ^^ +class Prism::ItLocalVariableReadNode < Prism::Node + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer).void } + def initialize(source, node_id, location, flags); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer).returns(Prism::ItLocalVariableReadNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents an implicit set of parameters through the use of the `it` keyword within a block or lambda. +# +# -> { it + it } +# ^^^^^^^^^^^^^^ +class Prism::ItParametersNode < Prism::Node + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer).void } + def initialize(source, node_id, location, flags); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer).returns(Prism::ItParametersNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents a hash literal without opening and closing braces. +# +# foo(a: b) +# ^^^^ +class Prism::KeywordHashNode < Prism::Node + sig { returns(T::Boolean) } + def symbol_keys?; end + + sig { returns(T::Array[T.any(Prism::AssocNode, Prism::AssocSplatNode)]) } + def elements; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, elements: T::Array[T.any(Prism::AssocNode, Prism::AssocSplatNode)]).void } + def initialize(source, node_id, location, flags, elements); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, elements: T::Array[T.any(Prism::AssocNode, Prism::AssocSplatNode)]).returns(Prism::KeywordHashNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, elements: self.elements); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents a keyword rest parameter to a method, block, or lambda definition. +# +# def a(**b) +# ^^^ +# end +class Prism::KeywordRestParameterNode < Prism::Node + sig { returns(T::Boolean) } + def repeated_parameter?; end + + sig { returns(T.nilable(Symbol)) } + def name; end + + sig { returns(T.nilable(Prism::Location)) } + def name_loc; end + + sig { returns(Prism::Location) } + def operator_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: T.nilable(Symbol), name_loc: T.nilable(Prism::Location), operator_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, name, name_loc, operator_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, name: T.nilable(Symbol), name_loc: T.nilable(Prism::Location), operator_loc: Prism::Location).returns(Prism::KeywordRestParameterNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc, operator_loc: self.operator_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def operator; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents using a lambda literal (not the lambda method call). +# +# ->(value) { value * 2 } +# ^^^^^^^^^^^^^^^^^^^^^^^ +class Prism::LambdaNode < Prism::Node + sig { returns(T::Array[Symbol]) } + def locals; end + + sig { returns(Prism::Location) } + def operator_loc; end + + sig { returns(Prism::Location) } + def opening_loc; end + + sig { returns(Prism::Location) } + def closing_loc; end + + sig { returns(T.nilable(T.any(Prism::BlockParametersNode, Prism::NumberedParametersNode, Prism::ItParametersNode))) } + def parameters; end + + sig { returns(T.nilable(T.any(Prism::StatementsNode, Prism::BeginNode))) } + def body; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, locals: T::Array[Symbol], operator_loc: Prism::Location, opening_loc: Prism::Location, closing_loc: Prism::Location, parameters: T.nilable(T.any(Prism::BlockParametersNode, Prism::NumberedParametersNode, Prism::ItParametersNode)), body: T.nilable(T.any(Prism::StatementsNode, Prism::BeginNode))).void } + def initialize(source, node_id, location, flags, locals, operator_loc, opening_loc, closing_loc, parameters, body); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, locals: T::Array[Symbol], operator_loc: Prism::Location, opening_loc: Prism::Location, closing_loc: Prism::Location, parameters: T.nilable(T.any(Prism::BlockParametersNode, Prism::NumberedParametersNode, Prism::ItParametersNode)), body: T.nilable(T.any(Prism::StatementsNode, Prism::BeginNode))).returns(Prism::LambdaNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, locals: self.locals, operator_loc: self.operator_loc, opening_loc: self.opening_loc, closing_loc: self.closing_loc, parameters: self.parameters, body: self.body); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def operator; end + + sig { returns(String) } + def opening; end + + sig { returns(String) } + def closing; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `&&=` operator for assignment to a local variable. +# +# target &&= value +# ^^^^^^^^^^^^^^^^ +class Prism::LocalVariableAndWriteNode < Prism::Node + sig { returns(Prism::Location) } + def name_loc; end + + sig { returns(Prism::Location) } + def operator_loc; end + + sig { returns(Prism::Node) } + def value; end + + sig { returns(Symbol) } + def name; end + + sig { returns(Integer) } + def depth; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name_loc: Prism::Location, operator_loc: Prism::Location, value: Prism::Node, name: Symbol, depth: Integer).void } + def initialize(source, node_id, location, flags, name_loc, operator_loc, value, name, depth); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, name_loc: Prism::Location, operator_loc: Prism::Location, value: Prism::Node, name: Symbol, depth: Integer).returns(Prism::LocalVariableAndWriteNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name_loc: self.name_loc, operator_loc: self.operator_loc, value: self.value, name: self.name, depth: self.depth); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def operator; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents assigning to a local variable using an operator that isn't `=`. +# +# target += value +# ^^^^^^^^^^^^^^^ +class Prism::LocalVariableOperatorWriteNode < Prism::Node + sig { returns(Prism::Location) } + def name_loc; end + + sig { returns(Prism::Location) } + def binary_operator_loc; end + + sig { returns(Prism::Node) } + def value; end + + sig { returns(Symbol) } + def name; end + + sig { returns(Symbol) } + def binary_operator; end + + sig { returns(Integer) } + def depth; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name_loc: Prism::Location, binary_operator_loc: Prism::Location, value: Prism::Node, name: Symbol, binary_operator: Symbol, depth: Integer).void } + def initialize(source, node_id, location, flags, name_loc, binary_operator_loc, value, name, binary_operator, depth); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, name_loc: Prism::Location, binary_operator_loc: Prism::Location, value: Prism::Node, name: Symbol, binary_operator: Symbol, depth: Integer).returns(Prism::LocalVariableOperatorWriteNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name_loc: self.name_loc, binary_operator_loc: self.binary_operator_loc, value: self.value, name: self.name, binary_operator: self.binary_operator, depth: self.depth); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `||=` operator for assignment to a local variable. +# +# target ||= value +# ^^^^^^^^^^^^^^^^ +class Prism::LocalVariableOrWriteNode < Prism::Node + sig { returns(Prism::Location) } + def name_loc; end + + sig { returns(Prism::Location) } + def operator_loc; end + + sig { returns(Prism::Node) } + def value; end + + sig { returns(Symbol) } + def name; end + + sig { returns(Integer) } + def depth; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name_loc: Prism::Location, operator_loc: Prism::Location, value: Prism::Node, name: Symbol, depth: Integer).void } + def initialize(source, node_id, location, flags, name_loc, operator_loc, value, name, depth); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, name_loc: Prism::Location, operator_loc: Prism::Location, value: Prism::Node, name: Symbol, depth: Integer).returns(Prism::LocalVariableOrWriteNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name_loc: self.name_loc, operator_loc: self.operator_loc, value: self.value, name: self.name, depth: self.depth); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def operator; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# 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 +# ^^^ +class Prism::LocalVariableReadNode < Prism::Node + sig { returns(Symbol) } + def name; end + + sig { returns(Integer) } + def depth; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, depth: Integer).void } + def initialize(source, node_id, location, flags, name, depth); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, depth: Integer).returns(Prism::LocalVariableReadNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, depth: self.depth); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents writing to a local variable in a context that doesn't have an explicit value. +# +# foo, bar = baz +# ^^^ ^^^ +# +# foo => baz +# ^^^ +class Prism::LocalVariableTargetNode < Prism::Node + sig { returns(Symbol) } + def name; end + + sig { returns(Integer) } + def depth; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, depth: Integer).void } + def initialize(source, node_id, location, flags, name, depth); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, depth: Integer).returns(Prism::LocalVariableTargetNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, depth: self.depth); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents writing to a local variable. +# +# foo = 1 +# ^^^^^^^ +class Prism::LocalVariableWriteNode < Prism::Node + sig { returns(Symbol) } + def name; end + + sig { returns(Integer) } + def depth; end + + sig { returns(Prism::Location) } + def name_loc; end + + sig { returns(Prism::Node) } + def value; end + + sig { returns(Prism::Location) } + def operator_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, depth: Integer, name_loc: Prism::Location, value: Prism::Node, operator_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, name, depth, name_loc, value, operator_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, depth: Integer, name_loc: Prism::Location, value: Prism::Node, operator_loc: Prism::Location).returns(Prism::LocalVariableWriteNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, depth: self.depth, name_loc: self.name_loc, value: self.value, operator_loc: self.operator_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def operator; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# 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 +# ^^^^^^ +class Prism::MatchLastLineNode < Prism::Node + sig { returns(T::Boolean) } + def ignore_case?; end + + sig { returns(T::Boolean) } + def extended?; end + + sig { returns(T::Boolean) } + def multi_line?; end + + sig { returns(T::Boolean) } + def once?; end + + sig { returns(T::Boolean) } + def euc_jp?; end + + sig { returns(T::Boolean) } + def ascii_8bit?; end + + sig { returns(T::Boolean) } + def windows_31j?; end + + sig { returns(T::Boolean) } + def utf_8?; end + + sig { returns(T::Boolean) } + def forced_utf8_encoding?; end + + sig { returns(T::Boolean) } + def forced_binary_encoding?; end + + sig { returns(T::Boolean) } + def forced_us_ascii_encoding?; end + + sig { returns(Prism::Location) } + def opening_loc; end + + sig { returns(Prism::Location) } + def content_loc; end + + sig { returns(Prism::Location) } + def closing_loc; end + + sig { returns(String) } + def unescaped; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, opening_loc: Prism::Location, content_loc: Prism::Location, closing_loc: Prism::Location, unescaped: String).void } + def initialize(source, node_id, location, flags, opening_loc, content_loc, closing_loc, unescaped); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, opening_loc: Prism::Location, content_loc: Prism::Location, closing_loc: Prism::Location, unescaped: String).returns(Prism::MatchLastLineNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, opening_loc: self.opening_loc, content_loc: self.content_loc, closing_loc: self.closing_loc, unescaped: self.unescaped); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def opening; end + + sig { returns(String) } + def content; end + + sig { returns(String) } + def closing; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the modifier `in` operator. +# +# foo in bar +# ^^^^^^^^^^ +class Prism::MatchPredicateNode < Prism::Node + sig { returns(Prism::Node) } + def value; end + + sig { returns(Prism::Node) } + def pattern; end + + sig { returns(Prism::Location) } + def operator_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, value: Prism::Node, pattern: Prism::Node, operator_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, value, pattern, operator_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, value: Prism::Node, pattern: Prism::Node, operator_loc: Prism::Location).returns(Prism::MatchPredicateNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, value: self.value, pattern: self.pattern, operator_loc: self.operator_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def operator; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `=>` operator. +# +# foo => bar +# ^^^^^^^^^^ +class Prism::MatchRequiredNode < Prism::Node + sig { returns(Prism::Node) } + def value; end + + sig { returns(Prism::Node) } + def pattern; end + + sig { returns(Prism::Location) } + def operator_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, value: Prism::Node, pattern: Prism::Node, operator_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, value, pattern, operator_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, value: Prism::Node, pattern: Prism::Node, operator_loc: Prism::Location).returns(Prism::MatchRequiredNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, value: self.value, pattern: self.pattern, operator_loc: self.operator_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def operator; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents writing local variables using a regular expression match with named capture groups. +# +# /(?bar)/ =~ baz +# ^^^^^^^^^^^^^^^^^^^^ +class Prism::MatchWriteNode < Prism::Node + sig { returns(Prism::CallNode) } + def call; end + + sig { returns(T::Array[Prism::LocalVariableTargetNode]) } + def targets; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, call: Prism::CallNode, targets: T::Array[Prism::LocalVariableTargetNode]).void } + def initialize(source, node_id, location, flags, call, targets); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, call: Prism::CallNode, targets: T::Array[Prism::LocalVariableTargetNode]).returns(Prism::MatchWriteNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, call: self.call, targets: self.targets); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents a node that is missing from the source and results in a syntax error. +class Prism::MissingNode < Prism::Node + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer).void } + def initialize(source, node_id, location, flags); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer).returns(Prism::MissingNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents a module declaration involving the `module` keyword. +# +# module Foo end +# ^^^^^^^^^^^^^^ +class Prism::ModuleNode < Prism::Node + sig { returns(T::Array[Symbol]) } + def locals; end + + sig { returns(Prism::Location) } + def module_keyword_loc; end + + sig { returns(T.any(Prism::ConstantReadNode, Prism::ConstantPathNode, Prism::MissingNode)) } + def constant_path; end + + sig { returns(T.nilable(T.any(Prism::StatementsNode, Prism::BeginNode))) } + def body; end + + sig { returns(Prism::Location) } + def end_keyword_loc; end + + sig { returns(Symbol) } + def name; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, locals: T::Array[Symbol], module_keyword_loc: Prism::Location, constant_path: T.any(Prism::ConstantReadNode, Prism::ConstantPathNode, Prism::MissingNode), body: T.nilable(T.any(Prism::StatementsNode, Prism::BeginNode)), end_keyword_loc: Prism::Location, name: Symbol).void } + def initialize(source, node_id, location, flags, locals, module_keyword_loc, constant_path, body, end_keyword_loc, name); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, locals: T::Array[Symbol], module_keyword_loc: Prism::Location, constant_path: T.any(Prism::ConstantReadNode, Prism::ConstantPathNode, Prism::MissingNode), body: T.nilable(T.any(Prism::StatementsNode, Prism::BeginNode)), end_keyword_loc: Prism::Location, name: Symbol).returns(Prism::ModuleNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, locals: self.locals, module_keyword_loc: self.module_keyword_loc, constant_path: self.constant_path, body: self.body, end_keyword_loc: self.end_keyword_loc, name: self.name); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def module_keyword; end + + sig { returns(String) } + def end_keyword; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents a multi-target expression. +# +# a, (b, c) = 1, 2, 3 +# ^^^^^^ +# +# This can be a part of `MultiWriteNode` as above, or the target of a `for` loop +# +# for a, b in [[1, 2], [3, 4]] +# ^^^^ +class Prism::MultiTargetNode < Prism::Node + sig { returns(T::Array[T.any(Prism::LocalVariableTargetNode, Prism::InstanceVariableTargetNode, Prism::ClassVariableTargetNode, Prism::GlobalVariableTargetNode, Prism::ConstantTargetNode, Prism::ConstantPathTargetNode, Prism::CallTargetNode, Prism::IndexTargetNode, Prism::MultiTargetNode, Prism::RequiredParameterNode, Prism::BackReferenceReadNode, Prism::NumberedReferenceReadNode)]) } + def lefts; end + + sig { returns(T.nilable(T.any(Prism::ImplicitRestNode, Prism::SplatNode))) } + def rest; end + + sig { returns(T::Array[T.any(Prism::LocalVariableTargetNode, Prism::InstanceVariableTargetNode, Prism::ClassVariableTargetNode, Prism::GlobalVariableTargetNode, Prism::ConstantTargetNode, Prism::ConstantPathTargetNode, Prism::CallTargetNode, Prism::IndexTargetNode, Prism::MultiTargetNode, Prism::RequiredParameterNode, Prism::BackReferenceReadNode, Prism::NumberedReferenceReadNode)]) } + def rights; end + + sig { returns(T.nilable(Prism::Location)) } + def lparen_loc; end + + sig { returns(T.nilable(Prism::Location)) } + def rparen_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, lefts: T::Array[T.any(Prism::LocalVariableTargetNode, Prism::InstanceVariableTargetNode, Prism::ClassVariableTargetNode, Prism::GlobalVariableTargetNode, Prism::ConstantTargetNode, Prism::ConstantPathTargetNode, Prism::CallTargetNode, Prism::IndexTargetNode, Prism::MultiTargetNode, Prism::RequiredParameterNode, Prism::BackReferenceReadNode, Prism::NumberedReferenceReadNode)], rest: T.nilable(T.any(Prism::ImplicitRestNode, Prism::SplatNode)), rights: T::Array[T.any(Prism::LocalVariableTargetNode, Prism::InstanceVariableTargetNode, Prism::ClassVariableTargetNode, Prism::GlobalVariableTargetNode, Prism::ConstantTargetNode, Prism::ConstantPathTargetNode, Prism::CallTargetNode, Prism::IndexTargetNode, Prism::MultiTargetNode, Prism::RequiredParameterNode, Prism::BackReferenceReadNode, Prism::NumberedReferenceReadNode)], lparen_loc: T.nilable(Prism::Location), rparen_loc: T.nilable(Prism::Location)).void } + def initialize(source, node_id, location, flags, lefts, rest, rights, lparen_loc, rparen_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, lefts: T::Array[T.any(Prism::LocalVariableTargetNode, Prism::InstanceVariableTargetNode, Prism::ClassVariableTargetNode, Prism::GlobalVariableTargetNode, Prism::ConstantTargetNode, Prism::ConstantPathTargetNode, Prism::CallTargetNode, Prism::IndexTargetNode, Prism::MultiTargetNode, Prism::RequiredParameterNode, Prism::BackReferenceReadNode, Prism::NumberedReferenceReadNode)], rest: T.nilable(T.any(Prism::ImplicitRestNode, Prism::SplatNode)), rights: T::Array[T.any(Prism::LocalVariableTargetNode, Prism::InstanceVariableTargetNode, Prism::ClassVariableTargetNode, Prism::GlobalVariableTargetNode, Prism::ConstantTargetNode, Prism::ConstantPathTargetNode, Prism::CallTargetNode, Prism::IndexTargetNode, Prism::MultiTargetNode, Prism::RequiredParameterNode, Prism::BackReferenceReadNode, Prism::NumberedReferenceReadNode)], lparen_loc: T.nilable(Prism::Location), rparen_loc: T.nilable(Prism::Location)).returns(Prism::MultiTargetNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, lefts: self.lefts, rest: self.rest, rights: self.rights, lparen_loc: self.lparen_loc, rparen_loc: self.rparen_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(T.nilable(String)) } + def lparen; end + + sig { returns(T.nilable(String)) } + def rparen; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents a write to a multi-target expression. +# +# a, b, c = 1, 2, 3 +# ^^^^^^^^^^^^^^^^^ +class Prism::MultiWriteNode < Prism::Node + sig { returns(T::Array[T.any(Prism::LocalVariableTargetNode, Prism::InstanceVariableTargetNode, Prism::ClassVariableTargetNode, Prism::GlobalVariableTargetNode, Prism::ConstantTargetNode, Prism::ConstantPathTargetNode, Prism::CallTargetNode, Prism::IndexTargetNode, Prism::MultiTargetNode, Prism::BackReferenceReadNode, Prism::NumberedReferenceReadNode)]) } + def lefts; end + + sig { returns(T.nilable(T.any(Prism::ImplicitRestNode, Prism::SplatNode))) } + def rest; end + + sig { returns(T::Array[T.any(Prism::LocalVariableTargetNode, Prism::InstanceVariableTargetNode, Prism::ClassVariableTargetNode, Prism::GlobalVariableTargetNode, Prism::ConstantTargetNode, Prism::ConstantPathTargetNode, Prism::CallTargetNode, Prism::IndexTargetNode, Prism::MultiTargetNode, Prism::BackReferenceReadNode, Prism::NumberedReferenceReadNode)]) } + def rights; end + + sig { returns(T.nilable(Prism::Location)) } + def lparen_loc; end + + sig { returns(T.nilable(Prism::Location)) } + def rparen_loc; end + + sig { returns(Prism::Location) } + def operator_loc; end + + sig { returns(Prism::Node) } + def value; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, lefts: T::Array[T.any(Prism::LocalVariableTargetNode, Prism::InstanceVariableTargetNode, Prism::ClassVariableTargetNode, Prism::GlobalVariableTargetNode, Prism::ConstantTargetNode, Prism::ConstantPathTargetNode, Prism::CallTargetNode, Prism::IndexTargetNode, Prism::MultiTargetNode, Prism::BackReferenceReadNode, Prism::NumberedReferenceReadNode)], rest: T.nilable(T.any(Prism::ImplicitRestNode, Prism::SplatNode)), rights: T::Array[T.any(Prism::LocalVariableTargetNode, Prism::InstanceVariableTargetNode, Prism::ClassVariableTargetNode, Prism::GlobalVariableTargetNode, Prism::ConstantTargetNode, Prism::ConstantPathTargetNode, Prism::CallTargetNode, Prism::IndexTargetNode, Prism::MultiTargetNode, Prism::BackReferenceReadNode, Prism::NumberedReferenceReadNode)], lparen_loc: T.nilable(Prism::Location), rparen_loc: T.nilable(Prism::Location), operator_loc: Prism::Location, value: Prism::Node).void } + def initialize(source, node_id, location, flags, lefts, rest, rights, lparen_loc, rparen_loc, operator_loc, value); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, lefts: T::Array[T.any(Prism::LocalVariableTargetNode, Prism::InstanceVariableTargetNode, Prism::ClassVariableTargetNode, Prism::GlobalVariableTargetNode, Prism::ConstantTargetNode, Prism::ConstantPathTargetNode, Prism::CallTargetNode, Prism::IndexTargetNode, Prism::MultiTargetNode, Prism::BackReferenceReadNode, Prism::NumberedReferenceReadNode)], rest: T.nilable(T.any(Prism::ImplicitRestNode, Prism::SplatNode)), rights: T::Array[T.any(Prism::LocalVariableTargetNode, Prism::InstanceVariableTargetNode, Prism::ClassVariableTargetNode, Prism::GlobalVariableTargetNode, Prism::ConstantTargetNode, Prism::ConstantPathTargetNode, Prism::CallTargetNode, Prism::IndexTargetNode, Prism::MultiTargetNode, Prism::BackReferenceReadNode, Prism::NumberedReferenceReadNode)], lparen_loc: T.nilable(Prism::Location), rparen_loc: T.nilable(Prism::Location), operator_loc: Prism::Location, value: Prism::Node).returns(Prism::MultiWriteNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, lefts: self.lefts, rest: self.rest, rights: self.rights, lparen_loc: self.lparen_loc, rparen_loc: self.rparen_loc, operator_loc: self.operator_loc, value: self.value); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(T.nilable(String)) } + def lparen; end + + sig { returns(T.nilable(String)) } + def rparen; end + + sig { returns(String) } + def operator; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `next` keyword. +# +# next 1 +# ^^^^^^ +class Prism::NextNode < Prism::Node + sig { returns(T.nilable(Prism::ArgumentsNode)) } + def arguments; end + + sig { returns(Prism::Location) } + def keyword_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, arguments: T.nilable(Prism::ArgumentsNode), keyword_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, arguments, keyword_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, arguments: T.nilable(Prism::ArgumentsNode), keyword_loc: Prism::Location).returns(Prism::NextNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, arguments: self.arguments, keyword_loc: self.keyword_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def keyword; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `nil` keyword. +# +# nil +# ^^^ +class Prism::NilNode < Prism::Node + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer).void } + def initialize(source, node_id, location, flags); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer).returns(Prism::NilNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of `**nil` inside method arguments. +# +# def a(**nil) +# ^^^^^ +# end +class Prism::NoKeywordsParameterNode < Prism::Node + sig { returns(Prism::Location) } + def operator_loc; end + + sig { returns(Prism::Location) } + def keyword_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, operator_loc: Prism::Location, keyword_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, operator_loc, keyword_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, operator_loc: Prism::Location, keyword_loc: Prism::Location).returns(Prism::NoKeywordsParameterNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, operator_loc: self.operator_loc, keyword_loc: self.keyword_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def operator; end + + sig { returns(String) } + def keyword; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents an implicit set of parameters through the use of numbered parameters within a block or lambda. +# +# -> { _1 + _2 } +# ^^^^^^^^^^^^^^ +class Prism::NumberedParametersNode < Prism::Node + sig { returns(Integer) } + def maximum; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, maximum: Integer).void } + def initialize(source, node_id, location, flags, maximum); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, maximum: Integer).returns(Prism::NumberedParametersNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, maximum: self.maximum); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents reading a numbered reference to a capture in the previous match. +# +# $1 +# ^^ +class Prism::NumberedReferenceReadNode < Prism::Node + sig { returns(Integer) } + def number; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, number: Integer).void } + def initialize(source, node_id, location, flags, number); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, number: Integer).returns(Prism::NumberedReferenceReadNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, number: self.number); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents an optional keyword parameter to a method, block, or lambda definition. +# +# def a(b: 1) +# ^^^^ +# end +class Prism::OptionalKeywordParameterNode < Prism::Node + sig { returns(T::Boolean) } + def repeated_parameter?; end + + sig { returns(Symbol) } + def name; end + + sig { returns(Prism::Location) } + def name_loc; end + + sig { returns(Prism::Node) } + def value; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, value: Prism::Node).void } + def initialize(source, node_id, location, flags, name, name_loc, value); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, value: Prism::Node).returns(Prism::OptionalKeywordParameterNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc, value: self.value); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents an optional parameter to a method, block, or lambda definition. +# +# def a(b = 1) +# ^^^^^ +# end +class Prism::OptionalParameterNode < Prism::Node + sig { returns(T::Boolean) } + def repeated_parameter?; end + + sig { returns(Symbol) } + def name; end + + sig { returns(Prism::Location) } + def name_loc; end + + sig { returns(Prism::Location) } + def operator_loc; end + + sig { returns(Prism::Node) } + def value; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, operator_loc: Prism::Location, value: Prism::Node).void } + def initialize(source, node_id, location, flags, name, name_loc, operator_loc, value); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location, operator_loc: Prism::Location, value: Prism::Node).returns(Prism::OptionalParameterNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc, operator_loc: self.operator_loc, value: self.value); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def operator; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `||` operator or the `or` keyword. +# +# left or right +# ^^^^^^^^^^^^^ +class Prism::OrNode < Prism::Node + sig { returns(Prism::Node) } + def left; end + + sig { returns(Prism::Node) } + def right; end + + sig { returns(Prism::Location) } + def operator_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, left: Prism::Node, right: Prism::Node, operator_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, left, right, operator_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, left: Prism::Node, right: Prism::Node, operator_loc: Prism::Location).returns(Prism::OrNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, left: self.left, right: self.right, operator_loc: self.operator_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def operator; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the list of parameters on a method, block, or lambda definition. +# +# def a(b, c, d) +# ^^^^^^^ +# end +class Prism::ParametersNode < Prism::Node + sig { returns(T::Array[T.any(Prism::RequiredParameterNode, Prism::MultiTargetNode)]) } + def requireds; end + + sig { returns(T::Array[Prism::OptionalParameterNode]) } + def optionals; end + + sig { returns(T.nilable(T.any(Prism::RestParameterNode, Prism::ImplicitRestNode))) } + def rest; end + + sig { returns(T::Array[T.any(Prism::RequiredParameterNode, Prism::MultiTargetNode, Prism::KeywordRestParameterNode, Prism::NoKeywordsParameterNode, Prism::ForwardingParameterNode)]) } + def posts; end + + sig { returns(T::Array[T.any(Prism::RequiredKeywordParameterNode, Prism::OptionalKeywordParameterNode)]) } + def keywords; end + + sig { returns(T.nilable(T.any(Prism::KeywordRestParameterNode, Prism::ForwardingParameterNode, Prism::NoKeywordsParameterNode))) } + def keyword_rest; end + + sig { returns(T.nilable(Prism::BlockParameterNode)) } + def block; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, requireds: T::Array[T.any(Prism::RequiredParameterNode, Prism::MultiTargetNode)], optionals: T::Array[Prism::OptionalParameterNode], rest: T.nilable(T.any(Prism::RestParameterNode, Prism::ImplicitRestNode)), posts: T::Array[T.any(Prism::RequiredParameterNode, Prism::MultiTargetNode, Prism::KeywordRestParameterNode, Prism::NoKeywordsParameterNode, Prism::ForwardingParameterNode)], keywords: T::Array[T.any(Prism::RequiredKeywordParameterNode, Prism::OptionalKeywordParameterNode)], keyword_rest: T.nilable(T.any(Prism::KeywordRestParameterNode, Prism::ForwardingParameterNode, Prism::NoKeywordsParameterNode)), block: T.nilable(Prism::BlockParameterNode)).void } + def initialize(source, node_id, location, flags, requireds, optionals, rest, posts, keywords, keyword_rest, block); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, requireds: T::Array[T.any(Prism::RequiredParameterNode, Prism::MultiTargetNode)], optionals: T::Array[Prism::OptionalParameterNode], rest: T.nilable(T.any(Prism::RestParameterNode, Prism::ImplicitRestNode)), posts: T::Array[T.any(Prism::RequiredParameterNode, Prism::MultiTargetNode, Prism::KeywordRestParameterNode, Prism::NoKeywordsParameterNode, Prism::ForwardingParameterNode)], keywords: T::Array[T.any(Prism::RequiredKeywordParameterNode, Prism::OptionalKeywordParameterNode)], keyword_rest: T.nilable(T.any(Prism::KeywordRestParameterNode, Prism::ForwardingParameterNode, Prism::NoKeywordsParameterNode)), block: T.nilable(Prism::BlockParameterNode)).returns(Prism::ParametersNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, requireds: self.requireds, optionals: self.optionals, rest: self.rest, posts: self.posts, keywords: self.keywords, keyword_rest: self.keyword_rest, block: self.block); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents a parenthesized expression +# +# (10 + 34) +# ^^^^^^^^^ +class Prism::ParenthesesNode < Prism::Node + sig { returns(T::Boolean) } + def multiple_statements?; end + + sig { returns(T.nilable(Prism::Node)) } + def body; end + + sig { returns(Prism::Location) } + def opening_loc; end + + sig { returns(Prism::Location) } + def closing_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, body: T.nilable(Prism::Node), opening_loc: Prism::Location, closing_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, body, opening_loc, closing_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, body: T.nilable(Prism::Node), opening_loc: Prism::Location, closing_loc: Prism::Location).returns(Prism::ParenthesesNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, body: self.body, opening_loc: self.opening_loc, closing_loc: self.closing_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def opening; end + + sig { returns(String) } + def closing; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `^` operator for pinning an expression in a pattern matching expression. +# +# foo in ^(bar) +# ^^^^^^ +class Prism::PinnedExpressionNode < Prism::Node + sig { returns(Prism::Node) } + def expression; end + + sig { returns(Prism::Location) } + def operator_loc; end + + sig { returns(Prism::Location) } + def lparen_loc; end + + sig { returns(Prism::Location) } + def rparen_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, expression: Prism::Node, operator_loc: Prism::Location, lparen_loc: Prism::Location, rparen_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, expression, operator_loc, lparen_loc, rparen_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, expression: Prism::Node, operator_loc: Prism::Location, lparen_loc: Prism::Location, rparen_loc: Prism::Location).returns(Prism::PinnedExpressionNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, expression: self.expression, operator_loc: self.operator_loc, lparen_loc: self.lparen_loc, rparen_loc: self.rparen_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def operator; end + + sig { returns(String) } + def lparen; end + + sig { returns(String) } + def rparen; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `^` operator for pinning a variable in a pattern matching expression. +# +# foo in ^bar +# ^^^^ +class Prism::PinnedVariableNode < Prism::Node + sig { returns(T.any(Prism::LocalVariableReadNode, Prism::InstanceVariableReadNode, Prism::ClassVariableReadNode, Prism::GlobalVariableReadNode, Prism::BackReferenceReadNode, Prism::NumberedReferenceReadNode, Prism::ItLocalVariableReadNode, Prism::MissingNode)) } + def variable; end + + sig { returns(Prism::Location) } + def operator_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, variable: T.any(Prism::LocalVariableReadNode, Prism::InstanceVariableReadNode, Prism::ClassVariableReadNode, Prism::GlobalVariableReadNode, Prism::BackReferenceReadNode, Prism::NumberedReferenceReadNode, Prism::ItLocalVariableReadNode, Prism::MissingNode), operator_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, variable, operator_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, variable: T.any(Prism::LocalVariableReadNode, Prism::InstanceVariableReadNode, Prism::ClassVariableReadNode, Prism::GlobalVariableReadNode, Prism::BackReferenceReadNode, Prism::NumberedReferenceReadNode, Prism::ItLocalVariableReadNode, Prism::MissingNode), operator_loc: Prism::Location).returns(Prism::PinnedVariableNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, variable: self.variable, operator_loc: self.operator_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def operator; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `END` keyword. +# +# END { foo } +# ^^^^^^^^^^^ +class Prism::PostExecutionNode < Prism::Node + sig { returns(T.nilable(Prism::StatementsNode)) } + def statements; end + + sig { returns(Prism::Location) } + def keyword_loc; end + + sig { returns(Prism::Location) } + def opening_loc; end + + sig { returns(Prism::Location) } + def closing_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, statements: T.nilable(Prism::StatementsNode), keyword_loc: Prism::Location, opening_loc: Prism::Location, closing_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, statements, keyword_loc, opening_loc, closing_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, statements: T.nilable(Prism::StatementsNode), keyword_loc: Prism::Location, opening_loc: Prism::Location, closing_loc: Prism::Location).returns(Prism::PostExecutionNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, statements: self.statements, keyword_loc: self.keyword_loc, opening_loc: self.opening_loc, closing_loc: self.closing_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def keyword; end + + sig { returns(String) } + def opening; end + + sig { returns(String) } + def closing; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `BEGIN` keyword. +# +# BEGIN { foo } +# ^^^^^^^^^^^^^ +class Prism::PreExecutionNode < Prism::Node + sig { returns(T.nilable(Prism::StatementsNode)) } + def statements; end + + sig { returns(Prism::Location) } + def keyword_loc; end + + sig { returns(Prism::Location) } + def opening_loc; end + + sig { returns(Prism::Location) } + def closing_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, statements: T.nilable(Prism::StatementsNode), keyword_loc: Prism::Location, opening_loc: Prism::Location, closing_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, statements, keyword_loc, opening_loc, closing_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, statements: T.nilable(Prism::StatementsNode), keyword_loc: Prism::Location, opening_loc: Prism::Location, closing_loc: Prism::Location).returns(Prism::PreExecutionNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, statements: self.statements, keyword_loc: self.keyword_loc, opening_loc: self.opening_loc, closing_loc: self.closing_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def keyword; end + + sig { returns(String) } + def opening; end + + sig { returns(String) } + def closing; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# The top level node of any parse tree. +class Prism::ProgramNode < Prism::Node + sig { returns(T::Array[Symbol]) } + def locals; end + + sig { returns(Prism::StatementsNode) } + def statements; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, locals: T::Array[Symbol], statements: Prism::StatementsNode).void } + def initialize(source, node_id, location, flags, locals, statements); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, locals: T::Array[Symbol], statements: Prism::StatementsNode).returns(Prism::ProgramNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, locals: self.locals, statements: self.statements); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `..` or `...` operators. +# +# 1..2 +# ^^^^ +# +# c if a =~ /left/ ... b =~ /right/ +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +class Prism::RangeNode < Prism::Node + sig { returns(T::Boolean) } + def exclude_end?; end + + sig { returns(T.nilable(Prism::Node)) } + def left; end + + sig { returns(T.nilable(Prism::Node)) } + def right; end + + sig { returns(Prism::Location) } + def operator_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, left: T.nilable(Prism::Node), right: T.nilable(Prism::Node), operator_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, left, right, operator_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, left: T.nilable(Prism::Node), right: T.nilable(Prism::Node), operator_loc: Prism::Location).returns(Prism::RangeNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, left: self.left, right: self.right, operator_loc: self.operator_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def operator; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents a rational number literal. +# +# 1.0r +# ^^^^ +class Prism::RationalNode < Prism::Node + sig { returns(T::Boolean) } + def binary?; end + + sig { returns(T::Boolean) } + def decimal?; end + + sig { returns(T::Boolean) } + def octal?; end + + sig { returns(T::Boolean) } + def hexadecimal?; end + + sig { returns(Integer) } + def numerator; end + + sig { returns(Integer) } + def denominator; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, numerator: Integer, denominator: Integer).void } + def initialize(source, node_id, location, flags, numerator, denominator); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, numerator: Integer, denominator: Integer).returns(Prism::RationalNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, numerator: self.numerator, denominator: self.denominator); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `redo` keyword. +# +# redo +# ^^^^ +class Prism::RedoNode < Prism::Node + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer).void } + def initialize(source, node_id, location, flags); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer).returns(Prism::RedoNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents a regular expression literal with no interpolation. +# +# /foo/i +# ^^^^^^ +class Prism::RegularExpressionNode < Prism::Node + sig { returns(T::Boolean) } + def ignore_case?; end + + sig { returns(T::Boolean) } + def extended?; end + + sig { returns(T::Boolean) } + def multi_line?; end + + sig { returns(T::Boolean) } + def once?; end + + sig { returns(T::Boolean) } + def euc_jp?; end + + sig { returns(T::Boolean) } + def ascii_8bit?; end + + sig { returns(T::Boolean) } + def windows_31j?; end + + sig { returns(T::Boolean) } + def utf_8?; end + + sig { returns(T::Boolean) } + def forced_utf8_encoding?; end + + sig { returns(T::Boolean) } + def forced_binary_encoding?; end + + sig { returns(T::Boolean) } + def forced_us_ascii_encoding?; end + + sig { returns(Prism::Location) } + def opening_loc; end + + sig { returns(Prism::Location) } + def content_loc; end + + sig { returns(Prism::Location) } + def closing_loc; end + + sig { returns(String) } + def unescaped; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, opening_loc: Prism::Location, content_loc: Prism::Location, closing_loc: Prism::Location, unescaped: String).void } + def initialize(source, node_id, location, flags, opening_loc, content_loc, closing_loc, unescaped); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, opening_loc: Prism::Location, content_loc: Prism::Location, closing_loc: Prism::Location, unescaped: String).returns(Prism::RegularExpressionNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, opening_loc: self.opening_loc, content_loc: self.content_loc, closing_loc: self.closing_loc, unescaped: self.unescaped); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def opening; end + + sig { returns(String) } + def content; end + + sig { returns(String) } + def closing; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents a required keyword parameter to a method, block, or lambda definition. +# +# def a(b: ) +# ^^ +# end +class Prism::RequiredKeywordParameterNode < Prism::Node + sig { returns(T::Boolean) } + def repeated_parameter?; end + + sig { returns(Symbol) } + def name; end + + sig { returns(Prism::Location) } + def name_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, name, name_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol, name_loc: Prism::Location).returns(Prism::RequiredKeywordParameterNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents a required parameter to a method, block, or lambda definition. +# +# def a(b) +# ^ +# end +class Prism::RequiredParameterNode < Prism::Node + sig { returns(T::Boolean) } + def repeated_parameter?; end + + sig { returns(Symbol) } + def name; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol).void } + def initialize(source, node_id, location, flags, name); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, name: Symbol).returns(Prism::RequiredParameterNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents an expression modified with a rescue. +# +# foo rescue nil +# ^^^^^^^^^^^^^^ +class Prism::RescueModifierNode < Prism::Node + sig { returns(Prism::Node) } + def expression; end + + sig { returns(Prism::Location) } + def keyword_loc; end + + sig { returns(Prism::Node) } + def rescue_expression; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, expression: Prism::Node, keyword_loc: Prism::Location, rescue_expression: Prism::Node).void } + def initialize(source, node_id, location, flags, expression, keyword_loc, rescue_expression); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, expression: Prism::Node, keyword_loc: Prism::Location, rescue_expression: Prism::Node).returns(Prism::RescueModifierNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, expression: self.expression, keyword_loc: self.keyword_loc, rescue_expression: self.rescue_expression); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def keyword; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents a rescue statement. +# +# begin +# rescue Foo, *splat, Bar => ex +# foo +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +# end +# +# `Foo, *splat, Bar` are in the `exceptions` field. `ex` is in the `reference` field. +class Prism::RescueNode < Prism::Node + sig { returns(Prism::Location) } + def keyword_loc; end + + sig { returns(T::Array[Prism::Node]) } + def exceptions; end + + sig { returns(T.nilable(Prism::Location)) } + def operator_loc; end + + sig { returns(T.nilable(T.any(Prism::LocalVariableTargetNode, Prism::InstanceVariableTargetNode, Prism::ClassVariableTargetNode, Prism::GlobalVariableTargetNode, Prism::ConstantTargetNode, Prism::ConstantPathTargetNode, Prism::CallTargetNode, Prism::IndexTargetNode, Prism::BackReferenceReadNode, Prism::NumberedReferenceReadNode, Prism::MissingNode))) } + def reference; end + + sig { returns(T.nilable(Prism::Location)) } + def then_keyword_loc; end + + sig { returns(T.nilable(Prism::StatementsNode)) } + def statements; end + + sig { returns(T.nilable(Prism::RescueNode)) } + def subsequent; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, keyword_loc: Prism::Location, exceptions: T::Array[Prism::Node], operator_loc: T.nilable(Prism::Location), reference: T.nilable(T.any(Prism::LocalVariableTargetNode, Prism::InstanceVariableTargetNode, Prism::ClassVariableTargetNode, Prism::GlobalVariableTargetNode, Prism::ConstantTargetNode, Prism::ConstantPathTargetNode, Prism::CallTargetNode, Prism::IndexTargetNode, Prism::BackReferenceReadNode, Prism::NumberedReferenceReadNode, Prism::MissingNode)), then_keyword_loc: T.nilable(Prism::Location), statements: T.nilable(Prism::StatementsNode), subsequent: T.nilable(Prism::RescueNode)).void } + def initialize(source, node_id, location, flags, keyword_loc, exceptions, operator_loc, reference, then_keyword_loc, statements, subsequent); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, keyword_loc: Prism::Location, exceptions: T::Array[Prism::Node], operator_loc: T.nilable(Prism::Location), reference: T.nilable(T.any(Prism::LocalVariableTargetNode, Prism::InstanceVariableTargetNode, Prism::ClassVariableTargetNode, Prism::GlobalVariableTargetNode, Prism::ConstantTargetNode, Prism::ConstantPathTargetNode, Prism::CallTargetNode, Prism::IndexTargetNode, Prism::BackReferenceReadNode, Prism::NumberedReferenceReadNode, Prism::MissingNode)), then_keyword_loc: T.nilable(Prism::Location), statements: T.nilable(Prism::StatementsNode), subsequent: T.nilable(Prism::RescueNode)).returns(Prism::RescueNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, keyword_loc: self.keyword_loc, exceptions: self.exceptions, operator_loc: self.operator_loc, reference: self.reference, then_keyword_loc: self.then_keyword_loc, statements: self.statements, subsequent: self.subsequent); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def keyword; end + + sig { returns(T.nilable(String)) } + def operator; end + + sig { returns(T.nilable(String)) } + def then_keyword; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents a rest parameter to a method, block, or lambda definition. +# +# def a(*b) +# ^^ +# end +class Prism::RestParameterNode < Prism::Node + sig { returns(T::Boolean) } + def repeated_parameter?; end + + sig { returns(T.nilable(Symbol)) } + def name; end + + sig { returns(T.nilable(Prism::Location)) } + def name_loc; end + + sig { returns(Prism::Location) } + def operator_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, name: T.nilable(Symbol), name_loc: T.nilable(Prism::Location), operator_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, name, name_loc, operator_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, name: T.nilable(Symbol), name_loc: T.nilable(Prism::Location), operator_loc: Prism::Location).returns(Prism::RestParameterNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, name: self.name, name_loc: self.name_loc, operator_loc: self.operator_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def operator; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `retry` keyword. +# +# retry +# ^^^^^ +class Prism::RetryNode < Prism::Node + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer).void } + def initialize(source, node_id, location, flags); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer).returns(Prism::RetryNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `return` keyword. +# +# return 1 +# ^^^^^^^^ +class Prism::ReturnNode < Prism::Node + sig { returns(Prism::Location) } + def keyword_loc; end + + sig { returns(T.nilable(Prism::ArgumentsNode)) } + def arguments; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, keyword_loc: Prism::Location, arguments: T.nilable(Prism::ArgumentsNode)).void } + def initialize(source, node_id, location, flags, keyword_loc, arguments); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, keyword_loc: Prism::Location, arguments: T.nilable(Prism::ArgumentsNode)).returns(Prism::ReturnNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, keyword_loc: self.keyword_loc, arguments: self.arguments); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def keyword; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the `self` keyword. +# +# self +# ^^^^ +class Prism::SelfNode < Prism::Node + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer).void } + def initialize(source, node_id, location, flags); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer).returns(Prism::SelfNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# 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 } +# ^^^^^^^^^^^^ +class Prism::ShareableConstantNode < Prism::Node + sig { returns(T::Boolean) } + def literal?; end + + sig { returns(T::Boolean) } + def experimental_everything?; end + + sig { returns(T::Boolean) } + def experimental_copy?; end + + sig { returns(T.any(Prism::ConstantWriteNode, Prism::ConstantAndWriteNode, Prism::ConstantOrWriteNode, Prism::ConstantOperatorWriteNode, Prism::ConstantPathWriteNode, Prism::ConstantPathAndWriteNode, Prism::ConstantPathOrWriteNode, Prism::ConstantPathOperatorWriteNode)) } + def write; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, write: T.any(Prism::ConstantWriteNode, Prism::ConstantAndWriteNode, Prism::ConstantOrWriteNode, Prism::ConstantOperatorWriteNode, Prism::ConstantPathWriteNode, Prism::ConstantPathAndWriteNode, Prism::ConstantPathOrWriteNode, Prism::ConstantPathOperatorWriteNode)).void } + def initialize(source, node_id, location, flags, write); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, write: T.any(Prism::ConstantWriteNode, Prism::ConstantAndWriteNode, Prism::ConstantOrWriteNode, Prism::ConstantOperatorWriteNode, Prism::ConstantPathWriteNode, Prism::ConstantPathAndWriteNode, Prism::ConstantPathOrWriteNode, Prism::ConstantPathOperatorWriteNode)).returns(Prism::ShareableConstantNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, write: self.write); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents a singleton class declaration involving the `class` keyword. +# +# class << self end +# ^^^^^^^^^^^^^^^^^ +class Prism::SingletonClassNode < Prism::Node + sig { returns(T::Array[Symbol]) } + def locals; end + + sig { returns(Prism::Location) } + def class_keyword_loc; end + + sig { returns(Prism::Location) } + def operator_loc; end + + sig { returns(Prism::Node) } + def expression; end + + sig { returns(T.nilable(T.any(Prism::StatementsNode, Prism::BeginNode))) } + def body; end + + sig { returns(Prism::Location) } + def end_keyword_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, locals: T::Array[Symbol], class_keyword_loc: Prism::Location, operator_loc: Prism::Location, expression: Prism::Node, body: T.nilable(T.any(Prism::StatementsNode, Prism::BeginNode)), end_keyword_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, locals, class_keyword_loc, operator_loc, expression, body, end_keyword_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, locals: T::Array[Symbol], class_keyword_loc: Prism::Location, operator_loc: Prism::Location, expression: Prism::Node, body: T.nilable(T.any(Prism::StatementsNode, Prism::BeginNode)), end_keyword_loc: Prism::Location).returns(Prism::SingletonClassNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, locals: self.locals, class_keyword_loc: self.class_keyword_loc, operator_loc: self.operator_loc, expression: self.expression, body: self.body, end_keyword_loc: self.end_keyword_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def class_keyword; end + + sig { returns(String) } + def operator; end + + sig { returns(String) } + def end_keyword; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `__ENCODING__` keyword. +# +# __ENCODING__ +# ^^^^^^^^^^^^ +class Prism::SourceEncodingNode < Prism::Node + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer).void } + def initialize(source, node_id, location, flags); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer).returns(Prism::SourceEncodingNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `__FILE__` keyword. +# +# __FILE__ +# ^^^^^^^^ +class Prism::SourceFileNode < Prism::Node + sig { returns(T::Boolean) } + def forced_utf8_encoding?; end + + sig { returns(T::Boolean) } + def forced_binary_encoding?; end + + sig { returns(T::Boolean) } + def frozen?; end + + sig { returns(T::Boolean) } + def mutable?; end + + sig { returns(String) } + def filepath; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, filepath: String).void } + def initialize(source, node_id, location, flags, filepath); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, filepath: String).returns(Prism::SourceFileNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, filepath: self.filepath); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `__LINE__` keyword. +# +# __LINE__ +# ^^^^^^^^ +class Prism::SourceLineNode < Prism::Node + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer).void } + def initialize(source, node_id, location, flags); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer).returns(Prism::SourceLineNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the splat operator. +# +# [*a] +# ^^ +class Prism::SplatNode < Prism::Node + sig { returns(Prism::Location) } + def operator_loc; end + + sig { returns(T.nilable(Prism::Node)) } + def expression; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, operator_loc: Prism::Location, expression: T.nilable(Prism::Node)).void } + def initialize(source, node_id, location, flags, operator_loc, expression); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, operator_loc: Prism::Location, expression: T.nilable(Prism::Node)).returns(Prism::SplatNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, operator_loc: self.operator_loc, expression: self.expression); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def operator; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents a set of statements contained within some scope. +# +# foo; bar; baz +# ^^^^^^^^^^^^^ +class Prism::StatementsNode < Prism::Node + sig { returns(T::Array[Prism::Node]) } + def body; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, body: T::Array[Prism::Node]).void } + def initialize(source, node_id, location, flags, body); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, body: T::Array[Prism::Node]).returns(Prism::StatementsNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, body: self.body); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# 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" +# ^^^^ ^^^^ +class Prism::StringNode < Prism::Node + sig { returns(T::Boolean) } + def forced_utf8_encoding?; end + + sig { returns(T::Boolean) } + def forced_binary_encoding?; end + + sig { returns(T::Boolean) } + def frozen?; end + + sig { returns(T::Boolean) } + def mutable?; end + + sig { returns(T.nilable(Prism::Location)) } + def opening_loc; end + + sig { returns(Prism::Location) } + def content_loc; end + + sig { returns(T.nilable(Prism::Location)) } + def closing_loc; end + + sig { returns(String) } + def unescaped; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, opening_loc: T.nilable(Prism::Location), content_loc: Prism::Location, closing_loc: T.nilable(Prism::Location), unescaped: String).void } + def initialize(source, node_id, location, flags, opening_loc, content_loc, closing_loc, unescaped); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, opening_loc: T.nilable(Prism::Location), content_loc: Prism::Location, closing_loc: T.nilable(Prism::Location), unescaped: String).returns(Prism::StringNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, opening_loc: self.opening_loc, content_loc: self.content_loc, closing_loc: self.closing_loc, unescaped: self.unescaped); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(T.nilable(String)) } + def opening; end + + sig { returns(String) } + def content; end + + sig { returns(T.nilable(String)) } + def closing; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `super` keyword with parentheses or arguments. +# +# super() +# ^^^^^^^ +# +# super foo, bar +# ^^^^^^^^^^^^^^ +# +# If no arguments are provided (except for a block), it would be a `ForwardingSuperNode` instead. +class Prism::SuperNode < Prism::Node + sig { returns(Prism::Location) } + def keyword_loc; end + + sig { returns(T.nilable(Prism::Location)) } + def lparen_loc; end + + sig { returns(T.nilable(Prism::ArgumentsNode)) } + def arguments; end + + sig { returns(T.nilable(Prism::Location)) } + def rparen_loc; end + + sig { returns(T.nilable(T.any(Prism::BlockNode, Prism::BlockArgumentNode))) } + def block; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, keyword_loc: Prism::Location, lparen_loc: T.nilable(Prism::Location), arguments: T.nilable(Prism::ArgumentsNode), rparen_loc: T.nilable(Prism::Location), block: T.nilable(T.any(Prism::BlockNode, Prism::BlockArgumentNode))).void } + def initialize(source, node_id, location, flags, keyword_loc, lparen_loc, arguments, rparen_loc, block); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, keyword_loc: Prism::Location, lparen_loc: T.nilable(Prism::Location), arguments: T.nilable(Prism::ArgumentsNode), rparen_loc: T.nilable(Prism::Location), block: T.nilable(T.any(Prism::BlockNode, Prism::BlockArgumentNode))).returns(Prism::SuperNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, keyword_loc: self.keyword_loc, lparen_loc: self.lparen_loc, arguments: self.arguments, rparen_loc: self.rparen_loc, block: self.block); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def keyword; end + + sig { returns(T.nilable(String)) } + def lparen; end + + sig { returns(T.nilable(String)) } + def rparen; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents a symbol literal or a symbol contained within a `%i` list. +# +# :foo +# ^^^^ +# +# %i[foo] +# ^^^ +class Prism::SymbolNode < Prism::Node + sig { returns(T::Boolean) } + def forced_utf8_encoding?; end + + sig { returns(T::Boolean) } + def forced_binary_encoding?; end + + sig { returns(T::Boolean) } + def forced_us_ascii_encoding?; end + + sig { returns(T.nilable(Prism::Location)) } + def opening_loc; end + + sig { returns(T.nilable(Prism::Location)) } + def value_loc; end + + sig { returns(T.nilable(Prism::Location)) } + def closing_loc; end + + sig { returns(String) } + def unescaped; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, opening_loc: T.nilable(Prism::Location), value_loc: T.nilable(Prism::Location), closing_loc: T.nilable(Prism::Location), unescaped: String).void } + def initialize(source, node_id, location, flags, opening_loc, value_loc, closing_loc, unescaped); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, opening_loc: T.nilable(Prism::Location), value_loc: T.nilable(Prism::Location), closing_loc: T.nilable(Prism::Location), unescaped: String).returns(Prism::SymbolNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, opening_loc: self.opening_loc, value_loc: self.value_loc, closing_loc: self.closing_loc, unescaped: self.unescaped); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(T.nilable(String)) } + def opening; end + + sig { returns(T.nilable(String)) } + def value; end + + sig { returns(T.nilable(String)) } + def closing; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the literal `true` keyword. +# +# true +# ^^^^ +class Prism::TrueNode < Prism::Node + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer).void } + def initialize(source, node_id, location, flags); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer).returns(Prism::TrueNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `undef` keyword. +# +# undef :foo, :bar, :baz +# ^^^^^^^^^^^^^^^^^^^^^^ +class Prism::UndefNode < Prism::Node + sig { returns(T::Array[T.any(Prism::SymbolNode, Prism::InterpolatedSymbolNode)]) } + def names; end + + sig { returns(Prism::Location) } + def keyword_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, names: T::Array[T.any(Prism::SymbolNode, Prism::InterpolatedSymbolNode)], keyword_loc: Prism::Location).void } + def initialize(source, node_id, location, flags, names, keyword_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, names: T::Array[T.any(Prism::SymbolNode, Prism::InterpolatedSymbolNode)], keyword_loc: Prism::Location).returns(Prism::UndefNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, names: self.names, keyword_loc: self.keyword_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def keyword; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `unless` keyword, either in the block form or the modifier form. +# +# bar unless foo +# ^^^^^^^^^^^^^^ +# +# unless foo then bar end +# ^^^^^^^^^^^^^^^^^^^^^^^ +class Prism::UnlessNode < Prism::Node + sig { returns(Prism::Location) } + def keyword_loc; end + + sig { returns(Prism::Node) } + def predicate; end + + sig { returns(T.nilable(Prism::Location)) } + def then_keyword_loc; end + + sig { returns(T.nilable(Prism::StatementsNode)) } + def statements; end + + sig { returns(T.nilable(Prism::ElseNode)) } + def else_clause; end + + sig { returns(T.nilable(Prism::Location)) } + def end_keyword_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, keyword_loc: Prism::Location, predicate: Prism::Node, then_keyword_loc: T.nilable(Prism::Location), statements: T.nilable(Prism::StatementsNode), else_clause: T.nilable(Prism::ElseNode), end_keyword_loc: T.nilable(Prism::Location)).void } + def initialize(source, node_id, location, flags, keyword_loc, predicate, then_keyword_loc, statements, else_clause, end_keyword_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, keyword_loc: Prism::Location, predicate: Prism::Node, then_keyword_loc: T.nilable(Prism::Location), statements: T.nilable(Prism::StatementsNode), else_clause: T.nilable(Prism::ElseNode), end_keyword_loc: T.nilable(Prism::Location)).returns(Prism::UnlessNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, keyword_loc: self.keyword_loc, predicate: self.predicate, then_keyword_loc: self.then_keyword_loc, statements: self.statements, else_clause: self.else_clause, end_keyword_loc: self.end_keyword_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def keyword; end + + sig { returns(T.nilable(String)) } + def then_keyword; end + + sig { returns(T.nilable(String)) } + def end_keyword; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `until` keyword, either in the block form or the modifier form. +# +# bar until foo +# ^^^^^^^^^^^^^ +# +# until foo do bar end +# ^^^^^^^^^^^^^^^^^^^^ +class Prism::UntilNode < Prism::Node + sig { returns(T::Boolean) } + def begin_modifier?; end + + sig { returns(Prism::Location) } + def keyword_loc; end + + sig { returns(T.nilable(Prism::Location)) } + def do_keyword_loc; end + + sig { returns(T.nilable(Prism::Location)) } + def closing_loc; end + + sig { returns(Prism::Node) } + def predicate; end + + sig { returns(T.nilable(Prism::StatementsNode)) } + def statements; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, keyword_loc: Prism::Location, do_keyword_loc: T.nilable(Prism::Location), closing_loc: T.nilable(Prism::Location), predicate: Prism::Node, statements: T.nilable(Prism::StatementsNode)).void } + def initialize(source, node_id, location, flags, keyword_loc, do_keyword_loc, closing_loc, predicate, statements); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, keyword_loc: Prism::Location, do_keyword_loc: T.nilable(Prism::Location), closing_loc: T.nilable(Prism::Location), predicate: Prism::Node, statements: T.nilable(Prism::StatementsNode)).returns(Prism::UntilNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, keyword_loc: self.keyword_loc, do_keyword_loc: self.do_keyword_loc, closing_loc: self.closing_loc, predicate: self.predicate, statements: self.statements); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def keyword; end + + sig { returns(T.nilable(String)) } + def do_keyword; end + + sig { returns(T.nilable(String)) } + def closing; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `when` keyword within a case statement. +# +# case true +# when true +# ^^^^^^^^^ +# end +class Prism::WhenNode < Prism::Node + sig { returns(Prism::Location) } + def keyword_loc; end + + sig { returns(T::Array[Prism::Node]) } + def conditions; end + + sig { returns(T.nilable(Prism::Location)) } + def then_keyword_loc; end + + sig { returns(T.nilable(Prism::StatementsNode)) } + def statements; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, keyword_loc: Prism::Location, conditions: T::Array[Prism::Node], then_keyword_loc: T.nilable(Prism::Location), statements: T.nilable(Prism::StatementsNode)).void } + def initialize(source, node_id, location, flags, keyword_loc, conditions, then_keyword_loc, statements); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, keyword_loc: Prism::Location, conditions: T::Array[Prism::Node], then_keyword_loc: T.nilable(Prism::Location), statements: T.nilable(Prism::StatementsNode)).returns(Prism::WhenNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, keyword_loc: self.keyword_loc, conditions: self.conditions, then_keyword_loc: self.then_keyword_loc, statements: self.statements); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def keyword; end + + sig { returns(T.nilable(String)) } + def then_keyword; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `while` keyword, either in the block form or the modifier form. +# +# bar while foo +# ^^^^^^^^^^^^^ +# +# while foo do bar end +# ^^^^^^^^^^^^^^^^^^^^ +class Prism::WhileNode < Prism::Node + sig { returns(T::Boolean) } + def begin_modifier?; end + + sig { returns(Prism::Location) } + def keyword_loc; end + + sig { returns(T.nilable(Prism::Location)) } + def do_keyword_loc; end + + sig { returns(T.nilable(Prism::Location)) } + def closing_loc; end + + sig { returns(Prism::Node) } + def predicate; end + + sig { returns(T.nilable(Prism::StatementsNode)) } + def statements; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, keyword_loc: Prism::Location, do_keyword_loc: T.nilable(Prism::Location), closing_loc: T.nilable(Prism::Location), predicate: Prism::Node, statements: T.nilable(Prism::StatementsNode)).void } + def initialize(source, node_id, location, flags, keyword_loc, do_keyword_loc, closing_loc, predicate, statements); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, keyword_loc: Prism::Location, do_keyword_loc: T.nilable(Prism::Location), closing_loc: T.nilable(Prism::Location), predicate: Prism::Node, statements: T.nilable(Prism::StatementsNode)).returns(Prism::WhileNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, keyword_loc: self.keyword_loc, do_keyword_loc: self.do_keyword_loc, closing_loc: self.closing_loc, predicate: self.predicate, statements: self.statements); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def keyword; end + + sig { returns(T.nilable(String)) } + def do_keyword; end + + sig { returns(T.nilable(String)) } + def closing; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents an xstring literal with no interpolation. +# +# `foo` +# ^^^^^ +class Prism::XStringNode < Prism::Node + sig { returns(T::Boolean) } + def forced_utf8_encoding?; end + + sig { returns(T::Boolean) } + def forced_binary_encoding?; end + + sig { returns(Prism::Location) } + def opening_loc; end + + sig { returns(Prism::Location) } + def content_loc; end + + sig { returns(Prism::Location) } + def closing_loc; end + + sig { returns(String) } + def unescaped; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, opening_loc: Prism::Location, content_loc: Prism::Location, closing_loc: Prism::Location, unescaped: String).void } + def initialize(source, node_id, location, flags, opening_loc, content_loc, closing_loc, unescaped); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, opening_loc: Prism::Location, content_loc: Prism::Location, closing_loc: Prism::Location, unescaped: String).returns(Prism::XStringNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, opening_loc: self.opening_loc, content_loc: self.content_loc, closing_loc: self.closing_loc, unescaped: self.unescaped); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def opening; end + + sig { returns(String) } + def content; end + + sig { returns(String) } + def closing; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Represents the use of the `yield` keyword. +# +# yield 1 +# ^^^^^^^ +class Prism::YieldNode < Prism::Node + sig { returns(Prism::Location) } + def keyword_loc; end + + sig { returns(T.nilable(Prism::Location)) } + def lparen_loc; end + + sig { returns(T.nilable(Prism::ArgumentsNode)) } + def arguments; end + + sig { returns(T.nilable(Prism::Location)) } + def rparen_loc; end + + sig { params(source: Prism::Source, node_id: Integer, location: Prism::Location, flags: Integer, keyword_loc: Prism::Location, lparen_loc: T.nilable(Prism::Location), arguments: T.nilable(Prism::ArgumentsNode), rparen_loc: T.nilable(Prism::Location)).void } + def initialize(source, node_id, location, flags, keyword_loc, lparen_loc, arguments, rparen_loc); end + + sig { override.params(visitor: Prism::Visitor).returns(T.untyped) } + def accept(visitor); end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def child_nodes; end + + sig { override.returns(T::Array[T.nilable(Prism::Node)]) } + def deconstruct; end + + sig { override.returns(T::Array[Prism::Node]) } + def compact_child_nodes; end + + sig { override.returns(T::Array[T.any(Prism::Node, Prism::Location)]) } + def comment_targets; end + + sig { params(node_id: Integer, location: Prism::Location, flags: Integer, keyword_loc: Prism::Location, lparen_loc: T.nilable(Prism::Location), arguments: T.nilable(Prism::ArgumentsNode), rparen_loc: T.nilable(Prism::Location)).returns(Prism::YieldNode) } + def copy(node_id: self.node_id, location: self.location, flags: self.flags, keyword_loc: self.keyword_loc, lparen_loc: self.lparen_loc, arguments: self.arguments, rparen_loc: self.rparen_loc); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def keyword; end + + sig { returns(T.nilable(String)) } + def lparen; end + + sig { returns(T.nilable(String)) } + def rparen; end + + sig { override.returns(T::Array[Prism::Reflection::Field]) } + def fields; end + + sig { override.returns(String) } + def inspect; end + + sig { override.returns(Symbol) } + def type; end +end + +# Flags for arguments nodes. +module Prism::ArgumentsNodeFlags + # if the arguments contain forwarding + CONTAINS_FORWARDING = T.let(1 << 2, Integer) + # if the arguments contain keywords + CONTAINS_KEYWORDS = T.let(1 << 3, Integer) + # if the arguments contain a keyword splat + CONTAINS_KEYWORD_SPLAT = T.let(1 << 4, Integer) + # if the arguments contain a splat + CONTAINS_SPLAT = T.let(1 << 5, Integer) + # if the arguments contain multiple splats + CONTAINS_MULTIPLE_SPLATS = T.let(1 << 6, Integer) +end + +# Flags for array nodes. +module Prism::ArrayNodeFlags + # if array contains splat nodes + CONTAINS_SPLAT = T.let(1 << 2, Integer) +end + +# Flags for call nodes. +module Prism::CallNodeFlags + # &. operator + SAFE_NAVIGATION = T.let(1 << 2, Integer) + # a call that could have been a local variable + VARIABLE_CALL = T.let(1 << 3, Integer) + # a call that is an attribute write, so the value being written should be returned + ATTRIBUTE_WRITE = T.let(1 << 4, Integer) + # a call that ignores method visibility + IGNORE_VISIBILITY = T.let(1 << 5, Integer) +end + +# Flags for nodes that have unescaped content. +module Prism::EncodingFlags + # internal bytes forced the encoding to UTF-8 + FORCED_UTF8_ENCODING = T.let(1 << 2, Integer) + # internal bytes forced the encoding to binary + FORCED_BINARY_ENCODING = T.let(1 << 3, Integer) +end + +# Flags for integer nodes that correspond to the base of the integer. +module Prism::IntegerBaseFlags + # 0b prefix + BINARY = T.let(1 << 2, Integer) + # 0d or no prefix + DECIMAL = T.let(1 << 3, Integer) + # 0o or 0 prefix + OCTAL = T.let(1 << 4, Integer) + # 0x prefix + HEXADECIMAL = T.let(1 << 5, Integer) +end + +# Flags for interpolated string nodes that indicated mutability if they are also marked as literals. +module Prism::InterpolatedStringNodeFlags + # frozen by virtue of a `frozen_string_literal: true` comment or `--enable-frozen-string-literal`; only for adjacent string literals like `'a' 'b'` + FROZEN = T.let(1 << 2, Integer) + # mutable by virtue of a `frozen_string_literal: false` comment or `--disable-frozen-string-literal`; only for adjacent string literals like `'a' 'b'` + MUTABLE = T.let(1 << 3, Integer) +end + +# Flags for keyword hash nodes. +module Prism::KeywordHashNodeFlags + # a keyword hash which only has `AssocNode` elements all with symbol keys, which means the elements can be treated as keyword arguments + SYMBOL_KEYS = T.let(1 << 2, Integer) +end + +# Flags for while and until loop nodes. +module Prism::LoopFlags + # a loop after a begin statement, so the body is executed first before the condition + BEGIN_MODIFIER = T.let(1 << 2, Integer) +end + +# Flags for parameter nodes. +module Prism::ParameterFlags + # a parameter name that has been repeated in the method signature + REPEATED_PARAMETER = T.let(1 << 2, Integer) +end + +# Flags for parentheses nodes. +module Prism::ParenthesesNodeFlags + # parentheses that contain multiple potentially void statements + MULTIPLE_STATEMENTS = T.let(1 << 2, Integer) +end + +# Flags for range and flip-flop nodes. +module Prism::RangeFlags + # ... operator + EXCLUDE_END = T.let(1 << 2, Integer) +end + +# Flags for regular expression and match last line nodes. +module Prism::RegularExpressionFlags + # i - ignores the case of characters when matching + IGNORE_CASE = T.let(1 << 2, Integer) + # x - ignores whitespace and allows comments in regular expressions + EXTENDED = T.let(1 << 3, Integer) + # m - allows $ to match the end of lines within strings + MULTI_LINE = T.let(1 << 4, Integer) + # o - only interpolates values into the regular expression once + ONCE = T.let(1 << 5, Integer) + # e - forces the EUC-JP encoding + EUC_JP = T.let(1 << 6, Integer) + # n - forces the ASCII-8BIT encoding + ASCII_8BIT = T.let(1 << 7, Integer) + # s - forces the Windows-31J encoding + WINDOWS_31J = T.let(1 << 8, Integer) + # u - forces the UTF-8 encoding + UTF_8 = T.let(1 << 9, Integer) + # internal bytes forced the encoding to UTF-8 + FORCED_UTF8_ENCODING = T.let(1 << 10, Integer) + # internal bytes forced the encoding to binary + FORCED_BINARY_ENCODING = T.let(1 << 11, Integer) + # internal bytes forced the encoding to US-ASCII + FORCED_US_ASCII_ENCODING = T.let(1 << 12, Integer) +end + +# Flags for shareable constant nodes. +module Prism::ShareableConstantNodeFlags + # constant writes that should be modified with shareable constant value literal + LITERAL = T.let(1 << 2, Integer) + # constant writes that should be modified with shareable constant value experimental everything + EXPERIMENTAL_EVERYTHING = T.let(1 << 3, Integer) + # constant writes that should be modified with shareable constant value experimental copy + EXPERIMENTAL_COPY = T.let(1 << 4, Integer) +end + +# Flags for string nodes. +module Prism::StringFlags + # internal bytes forced the encoding to UTF-8 + FORCED_UTF8_ENCODING = T.let(1 << 2, Integer) + # internal bytes forced the encoding to binary + FORCED_BINARY_ENCODING = T.let(1 << 3, Integer) + # frozen by virtue of a `frozen_string_literal: true` comment or `--enable-frozen-string-literal` + FROZEN = T.let(1 << 4, Integer) + # mutable by virtue of a `frozen_string_literal: false` comment or `--disable-frozen-string-literal` + MUTABLE = T.let(1 << 5, Integer) +end + +# Flags for symbol nodes. +module Prism::SymbolFlags + # internal bytes forced the encoding to UTF-8 + FORCED_UTF8_ENCODING = T.let(1 << 2, Integer) + # internal bytes forced the encoding to binary + FORCED_BINARY_ENCODING = T.let(1 << 3, Integer) + # internal bytes forced the encoding to US-ASCII + FORCED_US_ASCII_ENCODING = T.let(1 << 4, Integer) +end + +# The flags that are common to all nodes. +module Prism::NodeFlags + # A flag to indicate that the node is a candidate to emit a :line event + # through tracepoint when compiled. + NEWLINE = T.let(1, Integer) + + # A flag to indicate that the value that the node represents is a value that + # can be determined at parse-time. + STATIC_LITERAL = T.let(2, Integer) +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/rbi/prism/node_ext.rbi b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/rbi/prism/node_ext.rbi new file mode 100644 index 0000000..e5cf737 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/rbi/prism/node_ext.rbi @@ -0,0 +1,107 @@ +# typed: strict + +class Prism::InterpolatedMatchLastLineNode < Prism::Node + sig { returns(Integer) } + def options; end +end + +class Prism::InterpolatedRegularExpressionNode < Prism::Node + sig { returns(Integer) } + def options; end +end + +class Prism::MatchLastLineNode < Prism::Node + sig { returns(Integer) } + def options; end +end + +class Prism::RegularExpressionNode < Prism::Node + sig { returns(Integer) } + def options; end +end + +class Prism::InterpolatedStringNode < Prism::Node + sig { returns(T::Boolean) } + def heredoc?; end +end + +class Prism::InterpolatedXStringNode < Prism::Node + sig { returns(T::Boolean) } + def heredoc?; end +end + +class Prism::StringNode < Prism::Node + sig { returns(T::Boolean) } + def heredoc?; end + + sig { returns(Prism::InterpolatedStringNode) } + def to_interpolated; end +end + +class Prism::XStringNode < Prism::Node + sig { returns(T::Boolean) } + def heredoc?; end + + sig { returns(Prism::InterpolatedXStringNode) } + def to_interpolated; end +end + +class Prism::ImaginaryNode < Prism::Node + sig { returns(Complex) } + def value; end +end + +class Prism::RationalNode < Prism::Node + sig { returns(Rational) } + def value; end +end + +class Prism::ConstantReadNode < Prism::Node + sig { returns(T::Array[Symbol]) } + def full_name_parts; end + + sig { returns(String) } + def full_name; end +end + +class Prism::ConstantWriteNode < Prism::Node + sig { returns(T::Array[Symbol]) } + def full_name_parts; end + + sig { returns(String) } + def full_name; end +end + +class Prism::ConstantPathNode < Prism::Node + sig { returns(T::Array[Symbol]) } + def full_name_parts; end + + sig { returns(String) } + def full_name; end +end + +class Prism::ConstantPathTargetNode < Prism::Node + sig { returns(T::Array[Symbol]) } + def full_name_parts; end + + sig { returns(String) } + def full_name; end +end + +class Prism::ConstantTargetNode < Prism::Node + sig { returns(T::Array[Symbol]) } + def full_name_parts; end + + sig { returns(String) } + def full_name; end +end + +class Prism::ParametersNode < Prism::Node + sig { returns(T::Array[T.any([Symbol, Symbol], [Symbol])]) } + def signature; end +end + +class Prism::CallNode < Prism::Node + sig { returns(T.nilable(Prism::Location)) } + def full_message_loc; end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/rbi/prism/parse_result.rbi b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/rbi/prism/parse_result.rbi new file mode 100644 index 0000000..6f2bbb6 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/rbi/prism/parse_result.rbi @@ -0,0 +1,404 @@ +# typed: strict + +class Prism::Source + sig { returns(String) } + def source; end + + sig { returns(Integer) } + def start_line; end + + sig { returns(T::Array[Integer]) } + def offsets; end + + sig { params(source: String, start_line: Integer, offsets: T::Array[Integer]).void } + def initialize(source, start_line = 1, offsets = []); end + + sig { params(start_line: Integer).void } + def replace_start_line(start_line); end + + sig { params(offsets: T::Array[Integer]).void } + def replace_offsets(offsets); end + + sig { returns(Encoding) } + def encoding; end + + sig { returns(T::Array[String]) } + def lines; end + + sig { params(byte_offset: Integer, length: Integer).returns(String) } + def slice(byte_offset, length); end + + sig { params(byte_offset: Integer).returns(Integer) } + def line(byte_offset); end + + sig { params(byte_offset: Integer).returns(Integer) } + def line_start(byte_offset); end + + sig { params(byte_offset: Integer).returns(Integer) } + def column(byte_offset); end + + sig { params(byte_offset: Integer).returns(Integer) } + def character_offset(byte_offset); end + + sig { params(byte_offset: Integer).returns(Integer) } + def character_column(byte_offset); end + + sig { params(byte_offset: Integer, encoding: Encoding).returns(Integer) } + def code_units_offset(byte_offset, encoding); end + + sig { params(encoding: Encoding).returns(T.any(Prism::CodeUnitsCache, T.proc.params(byte_offset: Integer).returns(Integer))) } + def code_units_cache(encoding); end + + sig { params(byte_offset: Integer, encoding: Encoding).returns(Integer) } + def code_units_column(byte_offset, encoding); end +end + +class Prism::CodeUnitsCache + sig { params(source: String, encoding: Encoding).void } + def initialize(source, encoding); end + + sig { params(byte_offset: Integer).returns(Integer) } + def [](byte_offset); end +end + +class Prism::ASCIISource < Prism::Source + sig { params(byte_offset: Integer).returns(Integer) } + def character_offset(byte_offset); end + + sig { params(byte_offset: Integer).returns(Integer) } + def character_column(byte_offset); end + + sig { params(byte_offset: Integer, encoding: Encoding).returns(Integer) } + def code_units_offset(byte_offset, encoding); end + + sig { params(encoding: Encoding).returns(T.any(Prism::CodeUnitsCache, T.proc.params(byte_offset: Integer).returns(Integer))) } + def code_units_cache(encoding); end + + sig { params(byte_offset: Integer, encoding: Encoding).returns(Integer) } + def code_units_column(byte_offset, encoding); end +end + +class Prism::Location + sig { returns(Prism::Source) } + def source; end + + sig { returns(Integer) } + def start_offset; end + + sig { returns(Integer) } + def length; end + + sig { params(source: Prism::Source, start_offset: Integer, length: Integer).void } + def initialize(source, start_offset, length); end + + sig { returns(T::Array[Prism::Comment]) } + def leading_comments; end + + sig { params(comment: Prism::Comment).void } + def leading_comment(comment); end + + sig { returns(T::Array[Prism::Comment]) } + def trailing_comments; end + + sig { params(comment: Prism::Comment).void } + def trailing_comment(comment); end + + sig { returns(T::Array[Prism::Comment]) } + def comments; end + + sig { params(source: Prism::Source, start_offset: Integer, length: Integer).returns(Prism::Location) } + def copy(source: self.source, start_offset: self.start_offset, length: self.length); end + + sig { returns(Prism::Location) } + def chop; end + + sig { returns(String) } + def inspect; end + + sig { returns(T::Array[String]) } + def source_lines; end + + sig { returns(String) } + def slice; end + + sig { returns(Integer) } + def start_character_offset; end + + sig { params(encoding: Encoding).returns(Integer) } + def start_code_units_offset(encoding = Encoding::UTF_16LE); end + + sig { params(cache: T.any(Prism::CodeUnitsCache, T.proc.params(byte_offset: Integer).returns(Integer))).returns(Integer) } + def cached_start_code_units_offset(cache); end + + sig { returns(Integer) } + def end_offset; end + + sig { returns(Integer) } + def end_character_offset; end + + sig { params(encoding: Encoding).returns(Integer) } + def end_code_units_offset(encoding = Encoding::UTF_16LE); end + + sig { params(cache: T.any(Prism::CodeUnitsCache, T.proc.params(byte_offset: Integer).returns(Integer))).returns(Integer) } + def cached_end_code_units_offset(cache); end + + sig { returns(Integer) } + def start_line; end + + sig { returns(String) } + def start_line_slice; end + + sig { returns(Integer) } + def end_line; end + + sig { returns(Integer) } + def start_column; end + + sig { returns(Integer) } + def start_character_column; end + + sig { params(encoding: Encoding).returns(Integer) } + def start_code_units_column(encoding = Encoding::UTF_16LE); end + + sig { params(cache: T.any(Prism::CodeUnitsCache, T.proc.params(byte_offset: Integer).returns(Integer))).returns(Integer) } + def cached_start_code_units_column(cache); end + + sig { returns(Integer) } + def end_column; end + + sig { returns(Integer) } + def end_character_column; end + + sig { params(encoding: Encoding).returns(Integer) } + def end_code_units_column(encoding = Encoding::UTF_16LE); end + + sig { params(cache: T.any(Prism::CodeUnitsCache, T.proc.params(byte_offset: Integer).returns(Integer))).returns(Integer) } + def cached_end_code_units_column(cache); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { params(q: T.untyped).void } + def pretty_print(q); end + + sig { params(other: T.untyped).returns(T::Boolean) } + def ==(other); end + + sig { params(other: Prism::Location).returns(Prism::Location) } + def join(other); end + + sig { params(string: String).returns(Prism::Location) } + def adjoin(string); end +end + +class Prism::Comment + abstract! + + sig { returns(Prism::Location) } + def location; end + + sig { params(location: Prism::Location).void } + def initialize(location); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def slice; end + + sig { abstract.returns(T::Boolean) } + def trailing?; end +end + +class Prism::InlineComment < Prism::Comment + sig { override.returns(T::Boolean) } + def trailing?; end + + sig { returns(String) } + def inspect; end +end + +class Prism::EmbDocComment < Prism::Comment + sig { override.returns(T::Boolean) } + def trailing?; end + + sig { returns(String) } + def inspect; end +end + +class Prism::MagicComment + sig { returns(Prism::Location) } + def key_loc; end + + sig { returns(Prism::Location) } + def value_loc; end + + sig { params(key_loc: Prism::Location, value_loc: Prism::Location).void } + def initialize(key_loc, value_loc); end + + sig { returns(String) } + def key; end + + sig { returns(String) } + def value; end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def inspect; end +end + +class Prism::ParseError + sig { returns(Symbol) } + def type; end + + sig { returns(String) } + def message; end + + sig { returns(Prism::Location) } + def location; end + + sig { returns(Symbol) } + def level; end + + sig { params(type: Symbol, message: String, location: Prism::Location, level: Symbol).void } + def initialize(type, message, location, level); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def inspect; end +end + +class Prism::ParseWarning + sig { returns(Symbol) } + def type; end + + sig { returns(String) } + def message; end + + sig { returns(Prism::Location) } + def location; end + + sig { returns(Symbol) } + def level; end + + sig { params(type: Symbol, message: String, location: Prism::Location, level: Symbol).void } + def initialize(type, message, location, level); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(String) } + def inspect; end +end + +class Prism::Result + sig { params(comments: T::Array[Prism::Comment], magic_comments: T::Array[Prism::MagicComment], data_loc: T.nilable(Prism::Location), errors: T::Array[Prism::ParseError], warnings: T::Array[Prism::ParseWarning], source: Prism::Source).void } + def initialize(comments, magic_comments, data_loc, errors, warnings, source); end + + sig { returns(T::Array[Prism::Comment]) } + def comments; end + + sig { returns(T::Array[Prism::MagicComment]) } + def magic_comments; end + + sig { returns(T.nilable(Prism::Location)) } + def data_loc; end + + sig { returns(T::Array[Prism::ParseError]) } + def errors; end + + sig { returns(T::Array[Prism::ParseWarning]) } + def warnings; end + + sig { returns(Prism::Source) } + def source; end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(Encoding) } + def encoding; end + + sig { returns(T::Boolean) } + def success?; end + + sig { returns(T::Boolean) } + def failure?; end + + sig { params(encoding: Encoding).returns(T.any(Prism::CodeUnitsCache, T.proc.params(byte_offset: Integer).returns(Integer))) } + def code_units_cache(encoding); end +end + +class Prism::ParseResult < Prism::Result + sig { params(value: Prism::ProgramNode, comments: T::Array[Prism::Comment], magic_comments: T::Array[Prism::MagicComment], data_loc: T.nilable(Prism::Location), errors: T::Array[Prism::ParseError], warnings: T::Array[Prism::ParseWarning], source: Prism::Source).void } + def initialize(value, comments, magic_comments, data_loc, errors, warnings, source); end + + sig { returns(Prism::ProgramNode) } + def value; end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end +end + +class Prism::LexResult < Prism::Result + sig { params(value: T::Array[T.untyped], comments: T::Array[Prism::Comment], magic_comments: T::Array[Prism::MagicComment], data_loc: T.nilable(Prism::Location), errors: T::Array[Prism::ParseError], warnings: T::Array[Prism::ParseWarning], source: Prism::Source).void } + def initialize(value, comments, magic_comments, data_loc, errors, warnings, source); end + + sig { returns(T::Array[T.untyped]) } + def value; end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end +end + +class Prism::ParseLexResult < Prism::Result + sig { params(value: [Prism::ProgramNode, T::Array[T.untyped]], comments: T::Array[Prism::Comment], magic_comments: T::Array[Prism::MagicComment], data_loc: T.nilable(Prism::Location), errors: T::Array[Prism::ParseError], warnings: T::Array[Prism::ParseWarning], source: Prism::Source).void } + def initialize(value, comments, magic_comments, data_loc, errors, warnings, source); end + + sig { returns([Prism::ProgramNode, T::Array[T.untyped]]) } + def value; end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end +end + +class Prism::Token + sig { returns(Prism::Source) } + def source; end + + sig { returns(Symbol) } + def type; end + + sig { returns(String) } + def value; end + + sig { params(source: Prism::Source, type: Symbol, value: String, location: T.any(Integer, Prism::Location)).void } + def initialize(source, type, value, location); end + + sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } + def deconstruct_keys(keys); end + + sig { returns(Prism::Location) } + def location; end + + sig { params(q: T.untyped).void } + def pretty_print(q); end + + sig { params(other: T.untyped).returns(T::Boolean) } + def ==(other); end +end + +class Prism::Scope + sig { returns(T::Array[Symbol]) } + def locals; end + + sig { returns(T::Array[Symbol]) } + def forwarding; end + + sig { params(locals: T::Array[Symbol], forwarding: T::Array[Symbol]).void } + def initialize(locals, forwarding); end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/rbi/prism/reflection.rbi b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/rbi/prism/reflection.rbi new file mode 100644 index 0000000..b21e9b5 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/rbi/prism/reflection.rbi @@ -0,0 +1,58 @@ +# typed: strict + +module Prism::Reflection +end + +class Prism::Reflection::Field + sig { params(name: Symbol).void } + def initialize(name); end + + sig { returns(Symbol) } + def name; end +end + +class Prism::Reflection::NodeField < Prism::Reflection::Field +end + +class Prism::Reflection::OptionalNodeField < Prism::Reflection::Field +end + +class Prism::Reflection::NodeListField < Prism::Reflection::Field +end + +class Prism::Reflection::ConstantField < Prism::Reflection::Field +end + +class Prism::Reflection::OptionalConstantField < Prism::Reflection::Field +end + +class Prism::Reflection::ConstantListField < Prism::Reflection::Field +end + +class Prism::Reflection::StringField < Prism::Reflection::Field +end + +class Prism::Reflection::LocationField < Prism::Reflection::Field +end + +class Prism::Reflection::OptionalLocationField < Prism::Reflection::Field +end + +class Prism::Reflection::IntegerField < Prism::Reflection::Field +end + +class Prism::Reflection::FloatField < Prism::Reflection::Field +end + +class Prism::Reflection::FlagsField < Prism::Reflection::Field + sig { params(name: Symbol, flags: T::Array[Symbol]).void } + def initialize(name, flags); end + + sig { returns(T::Array[Symbol]) } + def flags; end +end + +module Prism::Reflection + sig { params(node: T.class_of(Prism::Node)).returns(T::Array[Prism::Reflection::Field]) } + def self.fields_for(node); end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/rbi/prism/string_query.rbi b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/rbi/prism/string_query.rbi new file mode 100644 index 0000000..3e5bae1 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/rbi/prism/string_query.rbi @@ -0,0 +1,12 @@ +# typed: strict + +class Prism::StringQuery + sig { params(string: String).returns(T::Boolean) } + def self.local?(string); end + + sig { params(string: String).returns(T::Boolean) } + def self.constant?(string); end + + sig { params(string: String).returns(T::Boolean) } + def self.method_name?(string); end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/rbi/prism/translation/parser.rbi b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/rbi/prism/translation/parser.rbi new file mode 100644 index 0000000..bd60a5a --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/rbi/prism/translation/parser.rbi @@ -0,0 +1,11 @@ +# typed: strict + +# We keep these shims in here because our client libraries might not have parser +# in their bundle. +module Parser; end +class Parser::Base; end + +class Prism::Translation::Parser < Parser::Base + sig { overridable.returns(Integer) } + def version; end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/rbi/prism/translation/parser_versions.rbi b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/rbi/prism/translation/parser_versions.rbi new file mode 100644 index 0000000..8a7297b --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/rbi/prism/translation/parser_versions.rbi @@ -0,0 +1,23 @@ +# typed: strict + +class Prism::Translation::Parser33 < Prism::Translation::Parser + sig { override.returns(Integer) } + def version; end +end + +class Prism::Translation::Parser34 < Prism::Translation::Parser + sig { override.returns(Integer) } + def version; end +end + +class Prism::Translation::Parser40 < Prism::Translation::Parser + sig { override.returns(Integer) } + def version; end +end + +Prism::Translation::Parser35 = Prism::Translation::Parser40 + +class Prism::Translation::Parser40 < Prism::Translation::Parser + sig { override.returns(Integer) } + def version; end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/rbi/prism/translation/ripper.rbi b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/rbi/prism/translation/ripper.rbi new file mode 100644 index 0000000..4d23970 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/rbi/prism/translation/ripper.rbi @@ -0,0 +1,15 @@ +# typed: strict + +class Prism::Translation::Ripper < Prism::Compiler + sig { returns(T::Boolean) } + def error?; end + + sig { returns(T.untyped) } + def parse; end + + sig { params(source: String, filename: String, lineno: Integer, raise_errors: T.untyped).returns(T.untyped) } + def self.sexp_raw(source, filename = "-", lineno = 1, raise_errors: false); end + + sig { params(source: String, filename: String, lineno: Integer, raise_errors: T.untyped).returns(T.untyped) } + def self.sexp(source, filename = "-", lineno = 1, raise_errors: false); end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/rbi/prism/visitor.rbi b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/rbi/prism/visitor.rbi new file mode 100644 index 0000000..3f074e1 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/rbi/prism/visitor.rbi @@ -0,0 +1,473 @@ +# typed: strict + +=begin +This file is generated by the templates/template.rb script and should not be +modified manually. See templates/rbi/prism/visitor.rbi.erb +if you are looking to modify the template +=end + +class Prism::BasicVisitor + sig { params(node: T.nilable(Prism::Node)).void } + def visit(node); end + + sig { params(nodes: T::Array[T.nilable(Prism::Node)]).void } + def visit_all(nodes); end + + sig { params(node: Prism::Node).void } + def visit_child_nodes(node); end +end + +class Prism::Visitor < Prism::BasicVisitor + sig { params(node: Prism::AliasGlobalVariableNode).void } + def visit_alias_global_variable_node(node); end + + sig { params(node: Prism::AliasMethodNode).void } + def visit_alias_method_node(node); end + + sig { params(node: Prism::AlternationPatternNode).void } + def visit_alternation_pattern_node(node); end + + sig { params(node: Prism::AndNode).void } + def visit_and_node(node); end + + sig { params(node: Prism::ArgumentsNode).void } + def visit_arguments_node(node); end + + sig { params(node: Prism::ArrayNode).void } + def visit_array_node(node); end + + sig { params(node: Prism::ArrayPatternNode).void } + def visit_array_pattern_node(node); end + + sig { params(node: Prism::AssocNode).void } + def visit_assoc_node(node); end + + sig { params(node: Prism::AssocSplatNode).void } + def visit_assoc_splat_node(node); end + + sig { params(node: Prism::BackReferenceReadNode).void } + def visit_back_reference_read_node(node); end + + sig { params(node: Prism::BeginNode).void } + def visit_begin_node(node); end + + sig { params(node: Prism::BlockArgumentNode).void } + def visit_block_argument_node(node); end + + sig { params(node: Prism::BlockLocalVariableNode).void } + def visit_block_local_variable_node(node); end + + sig { params(node: Prism::BlockNode).void } + def visit_block_node(node); end + + sig { params(node: Prism::BlockParameterNode).void } + def visit_block_parameter_node(node); end + + sig { params(node: Prism::BlockParametersNode).void } + def visit_block_parameters_node(node); end + + sig { params(node: Prism::BreakNode).void } + def visit_break_node(node); end + + sig { params(node: Prism::CallAndWriteNode).void } + def visit_call_and_write_node(node); end + + sig { params(node: Prism::CallNode).void } + def visit_call_node(node); end + + sig { params(node: Prism::CallOperatorWriteNode).void } + def visit_call_operator_write_node(node); end + + sig { params(node: Prism::CallOrWriteNode).void } + def visit_call_or_write_node(node); end + + sig { params(node: Prism::CallTargetNode).void } + def visit_call_target_node(node); end + + sig { params(node: Prism::CapturePatternNode).void } + def visit_capture_pattern_node(node); end + + sig { params(node: Prism::CaseMatchNode).void } + def visit_case_match_node(node); end + + sig { params(node: Prism::CaseNode).void } + def visit_case_node(node); end + + sig { params(node: Prism::ClassNode).void } + def visit_class_node(node); end + + sig { params(node: Prism::ClassVariableAndWriteNode).void } + def visit_class_variable_and_write_node(node); end + + sig { params(node: Prism::ClassVariableOperatorWriteNode).void } + def visit_class_variable_operator_write_node(node); end + + sig { params(node: Prism::ClassVariableOrWriteNode).void } + def visit_class_variable_or_write_node(node); end + + sig { params(node: Prism::ClassVariableReadNode).void } + def visit_class_variable_read_node(node); end + + sig { params(node: Prism::ClassVariableTargetNode).void } + def visit_class_variable_target_node(node); end + + sig { params(node: Prism::ClassVariableWriteNode).void } + def visit_class_variable_write_node(node); end + + sig { params(node: Prism::ConstantAndWriteNode).void } + def visit_constant_and_write_node(node); end + + sig { params(node: Prism::ConstantOperatorWriteNode).void } + def visit_constant_operator_write_node(node); end + + sig { params(node: Prism::ConstantOrWriteNode).void } + def visit_constant_or_write_node(node); end + + sig { params(node: Prism::ConstantPathAndWriteNode).void } + def visit_constant_path_and_write_node(node); end + + sig { params(node: Prism::ConstantPathNode).void } + def visit_constant_path_node(node); end + + sig { params(node: Prism::ConstantPathOperatorWriteNode).void } + def visit_constant_path_operator_write_node(node); end + + sig { params(node: Prism::ConstantPathOrWriteNode).void } + def visit_constant_path_or_write_node(node); end + + sig { params(node: Prism::ConstantPathTargetNode).void } + def visit_constant_path_target_node(node); end + + sig { params(node: Prism::ConstantPathWriteNode).void } + def visit_constant_path_write_node(node); end + + sig { params(node: Prism::ConstantReadNode).void } + def visit_constant_read_node(node); end + + sig { params(node: Prism::ConstantTargetNode).void } + def visit_constant_target_node(node); end + + sig { params(node: Prism::ConstantWriteNode).void } + def visit_constant_write_node(node); end + + sig { params(node: Prism::DefNode).void } + def visit_def_node(node); end + + sig { params(node: Prism::DefinedNode).void } + def visit_defined_node(node); end + + sig { params(node: Prism::ElseNode).void } + def visit_else_node(node); end + + sig { params(node: Prism::EmbeddedStatementsNode).void } + def visit_embedded_statements_node(node); end + + sig { params(node: Prism::EmbeddedVariableNode).void } + def visit_embedded_variable_node(node); end + + sig { params(node: Prism::EnsureNode).void } + def visit_ensure_node(node); end + + sig { params(node: Prism::FalseNode).void } + def visit_false_node(node); end + + sig { params(node: Prism::FindPatternNode).void } + def visit_find_pattern_node(node); end + + sig { params(node: Prism::FlipFlopNode).void } + def visit_flip_flop_node(node); end + + sig { params(node: Prism::FloatNode).void } + def visit_float_node(node); end + + sig { params(node: Prism::ForNode).void } + def visit_for_node(node); end + + sig { params(node: Prism::ForwardingArgumentsNode).void } + def visit_forwarding_arguments_node(node); end + + sig { params(node: Prism::ForwardingParameterNode).void } + def visit_forwarding_parameter_node(node); end + + sig { params(node: Prism::ForwardingSuperNode).void } + def visit_forwarding_super_node(node); end + + sig { params(node: Prism::GlobalVariableAndWriteNode).void } + def visit_global_variable_and_write_node(node); end + + sig { params(node: Prism::GlobalVariableOperatorWriteNode).void } + def visit_global_variable_operator_write_node(node); end + + sig { params(node: Prism::GlobalVariableOrWriteNode).void } + def visit_global_variable_or_write_node(node); end + + sig { params(node: Prism::GlobalVariableReadNode).void } + def visit_global_variable_read_node(node); end + + sig { params(node: Prism::GlobalVariableTargetNode).void } + def visit_global_variable_target_node(node); end + + sig { params(node: Prism::GlobalVariableWriteNode).void } + def visit_global_variable_write_node(node); end + + sig { params(node: Prism::HashNode).void } + def visit_hash_node(node); end + + sig { params(node: Prism::HashPatternNode).void } + def visit_hash_pattern_node(node); end + + sig { params(node: Prism::IfNode).void } + def visit_if_node(node); end + + sig { params(node: Prism::ImaginaryNode).void } + def visit_imaginary_node(node); end + + sig { params(node: Prism::ImplicitNode).void } + def visit_implicit_node(node); end + + sig { params(node: Prism::ImplicitRestNode).void } + def visit_implicit_rest_node(node); end + + sig { params(node: Prism::InNode).void } + def visit_in_node(node); end + + sig { params(node: Prism::IndexAndWriteNode).void } + def visit_index_and_write_node(node); end + + sig { params(node: Prism::IndexOperatorWriteNode).void } + def visit_index_operator_write_node(node); end + + sig { params(node: Prism::IndexOrWriteNode).void } + def visit_index_or_write_node(node); end + + sig { params(node: Prism::IndexTargetNode).void } + def visit_index_target_node(node); end + + sig { params(node: Prism::InstanceVariableAndWriteNode).void } + def visit_instance_variable_and_write_node(node); end + + sig { params(node: Prism::InstanceVariableOperatorWriteNode).void } + def visit_instance_variable_operator_write_node(node); end + + sig { params(node: Prism::InstanceVariableOrWriteNode).void } + def visit_instance_variable_or_write_node(node); end + + sig { params(node: Prism::InstanceVariableReadNode).void } + def visit_instance_variable_read_node(node); end + + sig { params(node: Prism::InstanceVariableTargetNode).void } + def visit_instance_variable_target_node(node); end + + sig { params(node: Prism::InstanceVariableWriteNode).void } + def visit_instance_variable_write_node(node); end + + sig { params(node: Prism::IntegerNode).void } + def visit_integer_node(node); end + + sig { params(node: Prism::InterpolatedMatchLastLineNode).void } + def visit_interpolated_match_last_line_node(node); end + + sig { params(node: Prism::InterpolatedRegularExpressionNode).void } + def visit_interpolated_regular_expression_node(node); end + + sig { params(node: Prism::InterpolatedStringNode).void } + def visit_interpolated_string_node(node); end + + sig { params(node: Prism::InterpolatedSymbolNode).void } + def visit_interpolated_symbol_node(node); end + + sig { params(node: Prism::InterpolatedXStringNode).void } + def visit_interpolated_x_string_node(node); end + + sig { params(node: Prism::ItLocalVariableReadNode).void } + def visit_it_local_variable_read_node(node); end + + sig { params(node: Prism::ItParametersNode).void } + def visit_it_parameters_node(node); end + + sig { params(node: Prism::KeywordHashNode).void } + def visit_keyword_hash_node(node); end + + sig { params(node: Prism::KeywordRestParameterNode).void } + def visit_keyword_rest_parameter_node(node); end + + sig { params(node: Prism::LambdaNode).void } + def visit_lambda_node(node); end + + sig { params(node: Prism::LocalVariableAndWriteNode).void } + def visit_local_variable_and_write_node(node); end + + sig { params(node: Prism::LocalVariableOperatorWriteNode).void } + def visit_local_variable_operator_write_node(node); end + + sig { params(node: Prism::LocalVariableOrWriteNode).void } + def visit_local_variable_or_write_node(node); end + + sig { params(node: Prism::LocalVariableReadNode).void } + def visit_local_variable_read_node(node); end + + sig { params(node: Prism::LocalVariableTargetNode).void } + def visit_local_variable_target_node(node); end + + sig { params(node: Prism::LocalVariableWriteNode).void } + def visit_local_variable_write_node(node); end + + sig { params(node: Prism::MatchLastLineNode).void } + def visit_match_last_line_node(node); end + + sig { params(node: Prism::MatchPredicateNode).void } + def visit_match_predicate_node(node); end + + sig { params(node: Prism::MatchRequiredNode).void } + def visit_match_required_node(node); end + + sig { params(node: Prism::MatchWriteNode).void } + def visit_match_write_node(node); end + + sig { params(node: Prism::MissingNode).void } + def visit_missing_node(node); end + + sig { params(node: Prism::ModuleNode).void } + def visit_module_node(node); end + + sig { params(node: Prism::MultiTargetNode).void } + def visit_multi_target_node(node); end + + sig { params(node: Prism::MultiWriteNode).void } + def visit_multi_write_node(node); end + + sig { params(node: Prism::NextNode).void } + def visit_next_node(node); end + + sig { params(node: Prism::NilNode).void } + def visit_nil_node(node); end + + sig { params(node: Prism::NoKeywordsParameterNode).void } + def visit_no_keywords_parameter_node(node); end + + sig { params(node: Prism::NumberedParametersNode).void } + def visit_numbered_parameters_node(node); end + + sig { params(node: Prism::NumberedReferenceReadNode).void } + def visit_numbered_reference_read_node(node); end + + sig { params(node: Prism::OptionalKeywordParameterNode).void } + def visit_optional_keyword_parameter_node(node); end + + sig { params(node: Prism::OptionalParameterNode).void } + def visit_optional_parameter_node(node); end + + sig { params(node: Prism::OrNode).void } + def visit_or_node(node); end + + sig { params(node: Prism::ParametersNode).void } + def visit_parameters_node(node); end + + sig { params(node: Prism::ParenthesesNode).void } + def visit_parentheses_node(node); end + + sig { params(node: Prism::PinnedExpressionNode).void } + def visit_pinned_expression_node(node); end + + sig { params(node: Prism::PinnedVariableNode).void } + def visit_pinned_variable_node(node); end + + sig { params(node: Prism::PostExecutionNode).void } + def visit_post_execution_node(node); end + + sig { params(node: Prism::PreExecutionNode).void } + def visit_pre_execution_node(node); end + + sig { params(node: Prism::ProgramNode).void } + def visit_program_node(node); end + + sig { params(node: Prism::RangeNode).void } + def visit_range_node(node); end + + sig { params(node: Prism::RationalNode).void } + def visit_rational_node(node); end + + sig { params(node: Prism::RedoNode).void } + def visit_redo_node(node); end + + sig { params(node: Prism::RegularExpressionNode).void } + def visit_regular_expression_node(node); end + + sig { params(node: Prism::RequiredKeywordParameterNode).void } + def visit_required_keyword_parameter_node(node); end + + sig { params(node: Prism::RequiredParameterNode).void } + def visit_required_parameter_node(node); end + + sig { params(node: Prism::RescueModifierNode).void } + def visit_rescue_modifier_node(node); end + + sig { params(node: Prism::RescueNode).void } + def visit_rescue_node(node); end + + sig { params(node: Prism::RestParameterNode).void } + def visit_rest_parameter_node(node); end + + sig { params(node: Prism::RetryNode).void } + def visit_retry_node(node); end + + sig { params(node: Prism::ReturnNode).void } + def visit_return_node(node); end + + sig { params(node: Prism::SelfNode).void } + def visit_self_node(node); end + + sig { params(node: Prism::ShareableConstantNode).void } + def visit_shareable_constant_node(node); end + + sig { params(node: Prism::SingletonClassNode).void } + def visit_singleton_class_node(node); end + + sig { params(node: Prism::SourceEncodingNode).void } + def visit_source_encoding_node(node); end + + sig { params(node: Prism::SourceFileNode).void } + def visit_source_file_node(node); end + + sig { params(node: Prism::SourceLineNode).void } + def visit_source_line_node(node); end + + sig { params(node: Prism::SplatNode).void } + def visit_splat_node(node); end + + sig { params(node: Prism::StatementsNode).void } + def visit_statements_node(node); end + + sig { params(node: Prism::StringNode).void } + def visit_string_node(node); end + + sig { params(node: Prism::SuperNode).void } + def visit_super_node(node); end + + sig { params(node: Prism::SymbolNode).void } + def visit_symbol_node(node); end + + sig { params(node: Prism::TrueNode).void } + def visit_true_node(node); end + + sig { params(node: Prism::UndefNode).void } + def visit_undef_node(node); end + + sig { params(node: Prism::UnlessNode).void } + def visit_unless_node(node); end + + sig { params(node: Prism::UntilNode).void } + def visit_until_node(node); end + + sig { params(node: Prism::WhenNode).void } + def visit_when_node(node); end + + sig { params(node: Prism::WhileNode).void } + def visit_while_node(node); end + + sig { params(node: Prism::XStringNode).void } + def visit_x_string_node(node); end + + sig { params(node: Prism::YieldNode).void } + def visit_yield_node(node); end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism.rbs b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism.rbs new file mode 100644 index 0000000..444f6c2 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism.rbs @@ -0,0 +1,272 @@ +# This file is generated by the templates/template.rb script and should not be +# modified manually. See templates/sig/prism.rbs.erb +# if you are looking to modify the template + +module Prism + BACKEND: :CEXT | :FFI + VERSION: String + + class CurrentVersionError < ArgumentError + def initialize: (String version) -> void + end + + # Methods taking a Ruby source code string: + + def self.parse: ( + String source, + ?command_line: String, + ?encoding: Encoding | false, + ?filepath: String, + ?freeze: bool, + ?frozen_string_literal: bool, + ?line: Integer, + ?main_script: bool, + ?partial_script: bool, + ?scopes: Array[Array[Symbol]], + ?version: String + ) -> ParseResult + + def self.profile: ( + String source, + ?command_line: String, + ?encoding: Encoding | false, + ?filepath: String, + ?freeze: bool, + ?frozen_string_literal: bool, + ?line: Integer, + ?main_script: bool, + ?partial_script: bool, + ?scopes: Array[Array[Symbol]], + ?version: String + ) -> nil + + def self.lex: ( + String source, + ?command_line: String, + ?encoding: Encoding | false, + ?filepath: String, + ?freeze: bool, + ?frozen_string_literal: bool, + ?line: Integer, + ?main_script: bool, + ?partial_script: bool, + ?scopes: Array[Array[Symbol]], + ?version: String + ) -> LexResult + + def self.lex_compat: ( + String source, + ?command_line: String, + ?encoding: Encoding | false, + ?filepath: String, + ?freeze: bool, + ?frozen_string_literal: bool, + ?line: Integer, + ?main_script: bool, + ?partial_script: bool, + ?scopes: Array[Array[Symbol]], + ?version: String + ) -> LexCompat::Result + + def self.parse_lex: ( + String source, + ?command_line: String, + ?encoding: Encoding | false, + ?filepath: String, + ?freeze: bool, + ?frozen_string_literal: bool, + ?line: Integer, + ?main_script: bool, + ?partial_script: bool, + ?scopes: Array[Array[Symbol]], + ?version: String + ) -> ParseLexResult + + def self.dump: ( + String source, + ?command_line: String, + ?encoding: Encoding | false, + ?filepath: String, + ?freeze: bool, + ?frozen_string_literal: bool, + ?line: Integer, + ?main_script: bool, + ?partial_script: bool, + ?scopes: Array[Array[Symbol]], + ?version: String + ) -> String + + def self.parse_comments: ( + String source, + ?command_line: String, + ?encoding: Encoding | false, + ?filepath: String, + ?freeze: bool, + ?frozen_string_literal: bool, + ?line: Integer, + ?main_script: bool, + ?partial_script: bool, + ?scopes: Array[Array[Symbol]], + ?version: String + ) -> Array[comment] + + def self.parse_success?: ( + String source, + ?command_line: String, + ?encoding: Encoding | false, + ?filepath: String, + ?freeze: bool, + ?frozen_string_literal: bool, + ?line: Integer, + ?main_script: bool, + ?partial_script: bool, + ?scopes: Array[Array[Symbol]], + ?version: String + ) -> bool + + def self.parse_failure?: ( + String source, + ?command_line: String, + ?encoding: Encoding | false, + ?filepath: String, + ?freeze: bool, + ?frozen_string_literal: bool, + ?line: Integer, + ?main_script: bool, + ?partial_script: bool, + ?scopes: Array[Array[Symbol]], + ?version: String + ) -> bool + + def self.load: ( + String source, + String serialized, + ?bool freeze + ) -> ParseResult + + # Methods taking a path to a Ruby file: + + def self.parse_file: ( + String filepath, + ?command_line: String, + ?encoding: Encoding | false, + ?freeze: bool, + ?frozen_string_literal: bool, + ?line: Integer, + ?main_script: bool, + ?partial_script: bool, + ?scopes: Array[Array[Symbol]], + ?version: String + ) -> ParseResult + + def self.profile_file: ( + String filepath, + ?command_line: String, + ?encoding: Encoding | false, + ?freeze: bool, + ?frozen_string_literal: bool, + ?line: Integer, + ?main_script: bool, + ?partial_script: bool, + ?scopes: Array[Array[Symbol]], + ?version: String + ) -> nil + + def self.lex_file: ( + String filepath, + ?command_line: String, + ?encoding: Encoding | false, + ?freeze: bool, + ?frozen_string_literal: bool, + ?line: Integer, + ?main_script: bool, + ?partial_script: bool, + ?scopes: Array[Array[Symbol]], + ?version: String + ) -> LexResult + + def self.parse_lex_file: ( + String filepath, + ?command_line: String, + ?encoding: Encoding | false, + ?freeze: bool, + ?frozen_string_literal: bool, + ?line: Integer, + ?main_script: bool, + ?partial_script: bool, + ?scopes: Array[Array[Symbol]], + ?version: String + ) -> ParseLexResult + + def self.dump_file: ( + String filepath, + ?command_line: String, + ?encoding: Encoding | false, + ?freeze: bool, + ?frozen_string_literal: bool, + ?line: Integer, + ?main_script: bool, + ?partial_script: bool, + ?scopes: Array[Array[Symbol]], + ?version: String + ) -> String + + def self.parse_file_comments: ( + String filepath, + ?command_line: String, + ?encoding: Encoding | false, + ?freeze: bool, + ?frozen_string_literal: bool, + ?line: Integer, + ?main_script: bool, + ?partial_script: bool, + ?scopes: Array[Array[Symbol]], + ?version: String + ) -> Array[comment] + + def self.parse_file_success?: ( + String filepath, + ?command_line: String, + ?encoding: Encoding | false, + ?freeze: bool, + ?frozen_string_literal: bool, + ?line: Integer, + ?main_script: bool, + ?partial_script: bool, + ?scopes: Array[Array[Symbol]], + ?version: String + ) -> bool + + def self.parse_file_failure?: ( + String filepath, + ?command_line: String, + ?encoding: Encoding | false, + ?freeze: bool, + ?frozen_string_literal: bool, + ?line: Integer, + ?main_script: bool, + ?partial_script: bool, + ?scopes: Array[Array[Symbol]], + ?version: String + ) -> bool + + interface _Stream + def gets: (?Integer integer) -> (String | nil) + end + + def self.parse_stream: ( + _Stream stream, + ?command_line: String, + ?encoding: Encoding | false, + ?filepath: String, + ?freeze: bool, + ?frozen_string_literal: bool, + ?line: Integer, + ?main_script: bool, + ?partial_script: bool, + ?scopes: Array[Array[Symbol]], + ?version: String + ) -> ParseResult + + def self.scope: (?locals: Array[Symbol], ?forwarding: Array[Symbol]) -> Scope +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/compiler.rbs b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/compiler.rbs new file mode 100644 index 0000000..1afa7db --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/compiler.rbs @@ -0,0 +1,9 @@ +module Prism + class Compiler + include _Visitor + + def visit: (Prism::node?) -> untyped + def visit_all: (Array[Prism::node?]) -> untyped + def visit_child_nodes: (Prism::node) -> void + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/dispatcher.rbs b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/dispatcher.rbs new file mode 100644 index 0000000..f58b866 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/dispatcher.rbs @@ -0,0 +1,19 @@ +module Prism + class Dispatcher < Visitor + attr_reader listeners: Hash[Symbol, Array[untyped]] + + def initialize: () -> void + def register: (untyped, *Symbol) -> void + def register_public_methods: (untyped) -> void + def dispatch: (Prism::node) -> void + def dispatch_once: (Prism::node) -> void + + private def register_events: (untyped, Array[Symbol]) -> void + + class DispatchOnce < Visitor + attr_reader listeners: Hash[Symbol, Array[untyped]] + + def initialize: (Hash[Symbol, Array[untyped]]) -> void + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/dot_visitor.rbs b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/dot_visitor.rbs new file mode 100644 index 0000000..46b3dbf --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/dot_visitor.rbs @@ -0,0 +1,6 @@ +module Prism + class DotVisitor < Visitor + def initialize: () -> void + def to_dot: () -> String + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/dsl.rbs b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/dsl.rbs new file mode 100644 index 0000000..3670dd6 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/dsl.rbs @@ -0,0 +1,351 @@ +# This file is generated by the templates/template.rb script and should not be +# modified manually. See templates/sig/prism/dsl.rbs.erb +# if you are looking to modify the template + +module Prism + module DSL + def source: (String string) -> Source + + def location: (?source: Source, ?start_offset: Integer, ?length: Integer) -> Location + + def alias_global_variable_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?new_name: GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode, ?old_name: GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode | SymbolNode | MissingNode, ?keyword_loc: Location) -> AliasGlobalVariableNode + + def alias_method_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?new_name: SymbolNode | InterpolatedSymbolNode, ?old_name: SymbolNode | InterpolatedSymbolNode | GlobalVariableReadNode | MissingNode, ?keyword_loc: Location) -> AliasMethodNode + + def alternation_pattern_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?left: Prism::node, ?right: Prism::node, ?operator_loc: Location) -> AlternationPatternNode + + def and_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?left: Prism::node, ?right: Prism::node, ?operator_loc: Location) -> AndNode + + def arguments_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?arguments: Array[Prism::node]) -> ArgumentsNode + + def array_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?elements: Array[Prism::node], ?opening_loc: Location?, ?closing_loc: Location?) -> ArrayNode + + def array_pattern_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?constant: ConstantPathNode | ConstantReadNode | nil, ?requireds: Array[Prism::node], ?rest: Prism::node?, ?posts: Array[Prism::node], ?opening_loc: Location?, ?closing_loc: Location?) -> ArrayPatternNode + + def assoc_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?key: Prism::node, ?value: Prism::node, ?operator_loc: Location?) -> AssocNode + + def assoc_splat_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?value: Prism::node?, ?operator_loc: Location) -> AssocSplatNode + + def back_reference_read_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> BackReferenceReadNode + + def begin_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?begin_keyword_loc: Location?, ?statements: StatementsNode?, ?rescue_clause: RescueNode?, ?else_clause: ElseNode?, ?ensure_clause: EnsureNode?, ?end_keyword_loc: Location?) -> BeginNode + + def block_argument_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?expression: Prism::node?, ?operator_loc: Location) -> BlockArgumentNode + + def block_local_variable_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> BlockLocalVariableNode + + def block_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?locals: Array[Symbol], ?parameters: BlockParametersNode | NumberedParametersNode | ItParametersNode | nil, ?body: StatementsNode | BeginNode | nil, ?opening_loc: Location, ?closing_loc: Location) -> BlockNode + + def block_parameter_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol?, ?name_loc: Location?, ?operator_loc: Location) -> BlockParameterNode + + def block_parameters_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?parameters: ParametersNode?, ?locals: Array[BlockLocalVariableNode], ?opening_loc: Location?, ?closing_loc: Location?) -> BlockParametersNode + + def break_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?arguments: ArgumentsNode?, ?keyword_loc: Location) -> BreakNode + + def call_and_write_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?receiver: Prism::node?, ?call_operator_loc: Location?, ?message_loc: Location?, ?read_name: Symbol, ?write_name: Symbol, ?operator_loc: Location, ?value: Prism::node) -> CallAndWriteNode + + def call_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?receiver: Prism::node?, ?call_operator_loc: Location?, ?name: Symbol, ?message_loc: Location?, ?opening_loc: Location?, ?arguments: ArgumentsNode?, ?closing_loc: Location?, ?equal_loc: Location?, ?block: BlockNode | BlockArgumentNode | nil) -> CallNode + + def call_operator_write_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?receiver: Prism::node?, ?call_operator_loc: Location?, ?message_loc: Location?, ?read_name: Symbol, ?write_name: Symbol, ?binary_operator: Symbol, ?binary_operator_loc: Location, ?value: Prism::node) -> CallOperatorWriteNode + + def call_or_write_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?receiver: Prism::node?, ?call_operator_loc: Location?, ?message_loc: Location?, ?read_name: Symbol, ?write_name: Symbol, ?operator_loc: Location, ?value: Prism::node) -> CallOrWriteNode + + def call_target_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?receiver: Prism::node, ?call_operator_loc: Location, ?name: Symbol, ?message_loc: Location) -> CallTargetNode + + def capture_pattern_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?value: Prism::node, ?target: LocalVariableTargetNode, ?operator_loc: Location) -> CapturePatternNode + + def case_match_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?predicate: Prism::node?, ?conditions: Array[InNode], ?else_clause: ElseNode?, ?case_keyword_loc: Location, ?end_keyword_loc: Location) -> CaseMatchNode + + def case_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?predicate: Prism::node?, ?conditions: Array[WhenNode], ?else_clause: ElseNode?, ?case_keyword_loc: Location, ?end_keyword_loc: Location) -> CaseNode + + def class_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?locals: Array[Symbol], ?class_keyword_loc: Location, ?constant_path: ConstantReadNode | ConstantPathNode | CallNode, ?inheritance_operator_loc: Location?, ?superclass: Prism::node?, ?body: StatementsNode | BeginNode | nil, ?end_keyword_loc: Location, ?name: Symbol) -> ClassNode + + def class_variable_and_write_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> ClassVariableAndWriteNode + + def class_variable_operator_write_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?binary_operator_loc: Location, ?value: Prism::node, ?binary_operator: Symbol) -> ClassVariableOperatorWriteNode + + def class_variable_or_write_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> ClassVariableOrWriteNode + + def class_variable_read_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> ClassVariableReadNode + + def class_variable_target_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> ClassVariableTargetNode + + def class_variable_write_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?value: Prism::node, ?operator_loc: Location) -> ClassVariableWriteNode + + def constant_and_write_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> ConstantAndWriteNode + + def constant_operator_write_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?binary_operator_loc: Location, ?value: Prism::node, ?binary_operator: Symbol) -> ConstantOperatorWriteNode + + def constant_or_write_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> ConstantOrWriteNode + + def constant_path_and_write_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?target: ConstantPathNode, ?operator_loc: Location, ?value: Prism::node) -> ConstantPathAndWriteNode + + def constant_path_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?parent: Prism::node?, ?name: Symbol?, ?delimiter_loc: Location, ?name_loc: Location) -> ConstantPathNode + + def constant_path_operator_write_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?target: ConstantPathNode, ?binary_operator_loc: Location, ?value: Prism::node, ?binary_operator: Symbol) -> ConstantPathOperatorWriteNode + + def constant_path_or_write_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?target: ConstantPathNode, ?operator_loc: Location, ?value: Prism::node) -> ConstantPathOrWriteNode + + def constant_path_target_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?parent: Prism::node?, ?name: Symbol?, ?delimiter_loc: Location, ?name_loc: Location) -> ConstantPathTargetNode + + def constant_path_write_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?target: ConstantPathNode, ?operator_loc: Location, ?value: Prism::node) -> ConstantPathWriteNode + + def constant_read_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> ConstantReadNode + + def constant_target_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> ConstantTargetNode + + def constant_write_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?value: Prism::node, ?operator_loc: Location) -> ConstantWriteNode + + def def_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?receiver: Prism::node?, ?parameters: ParametersNode?, ?body: StatementsNode | BeginNode | nil, ?locals: Array[Symbol], ?def_keyword_loc: Location, ?operator_loc: Location?, ?lparen_loc: Location?, ?rparen_loc: Location?, ?equal_loc: Location?, ?end_keyword_loc: Location?) -> DefNode + + def defined_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?lparen_loc: Location?, ?value: Prism::node, ?rparen_loc: Location?, ?keyword_loc: Location) -> DefinedNode + + def else_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?else_keyword_loc: Location, ?statements: StatementsNode?, ?end_keyword_loc: Location?) -> ElseNode + + def embedded_statements_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?statements: StatementsNode?, ?closing_loc: Location) -> EmbeddedStatementsNode + + def embedded_variable_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?operator_loc: Location, ?variable: InstanceVariableReadNode | ClassVariableReadNode | GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode) -> EmbeddedVariableNode + + def ensure_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?ensure_keyword_loc: Location, ?statements: StatementsNode?, ?end_keyword_loc: Location) -> EnsureNode + + def false_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer) -> FalseNode + + def find_pattern_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?constant: ConstantPathNode | ConstantReadNode | nil, ?left: SplatNode, ?requireds: Array[Prism::node], ?right: SplatNode | MissingNode, ?opening_loc: Location?, ?closing_loc: Location?) -> FindPatternNode + + def flip_flop_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?left: Prism::node?, ?right: Prism::node?, ?operator_loc: Location) -> FlipFlopNode + + def float_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?value: Float) -> FloatNode + + def for_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?index: LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | BackReferenceReadNode | NumberedReferenceReadNode | MissingNode, ?collection: Prism::node, ?statements: StatementsNode?, ?for_keyword_loc: Location, ?in_keyword_loc: Location, ?do_keyword_loc: Location?, ?end_keyword_loc: Location) -> ForNode + + def forwarding_arguments_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer) -> ForwardingArgumentsNode + + def forwarding_parameter_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer) -> ForwardingParameterNode + + def forwarding_super_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?block: BlockNode?) -> ForwardingSuperNode + + def global_variable_and_write_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> GlobalVariableAndWriteNode + + def global_variable_operator_write_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?binary_operator_loc: Location, ?value: Prism::node, ?binary_operator: Symbol) -> GlobalVariableOperatorWriteNode + + def global_variable_or_write_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> GlobalVariableOrWriteNode + + def global_variable_read_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> GlobalVariableReadNode + + def global_variable_target_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> GlobalVariableTargetNode + + def global_variable_write_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?value: Prism::node, ?operator_loc: Location) -> GlobalVariableWriteNode + + def hash_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?elements: Array[AssocNode | AssocSplatNode], ?closing_loc: Location) -> HashNode + + def hash_pattern_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?constant: ConstantPathNode | ConstantReadNode | nil, ?elements: Array[AssocNode], ?rest: AssocSplatNode | NoKeywordsParameterNode | nil, ?opening_loc: Location?, ?closing_loc: Location?) -> HashPatternNode + + def if_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?if_keyword_loc: Location?, ?predicate: Prism::node, ?then_keyword_loc: Location?, ?statements: StatementsNode?, ?subsequent: ElseNode | IfNode | nil, ?end_keyword_loc: Location?) -> IfNode + + def imaginary_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?numeric: FloatNode | IntegerNode | RationalNode) -> ImaginaryNode + + def implicit_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?value: LocalVariableReadNode | CallNode | ConstantReadNode | LocalVariableTargetNode) -> ImplicitNode + + def implicit_rest_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer) -> ImplicitRestNode + + def in_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?pattern: Prism::node, ?statements: StatementsNode?, ?in_loc: Location, ?then_loc: Location?) -> InNode + + def index_and_write_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?receiver: Prism::node?, ?call_operator_loc: Location?, ?opening_loc: Location, ?arguments: ArgumentsNode?, ?closing_loc: Location, ?block: BlockArgumentNode?, ?operator_loc: Location, ?value: Prism::node) -> IndexAndWriteNode + + def index_operator_write_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?receiver: Prism::node?, ?call_operator_loc: Location?, ?opening_loc: Location, ?arguments: ArgumentsNode?, ?closing_loc: Location, ?block: BlockArgumentNode?, ?binary_operator: Symbol, ?binary_operator_loc: Location, ?value: Prism::node) -> IndexOperatorWriteNode + + def index_or_write_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?receiver: Prism::node?, ?call_operator_loc: Location?, ?opening_loc: Location, ?arguments: ArgumentsNode?, ?closing_loc: Location, ?block: BlockArgumentNode?, ?operator_loc: Location, ?value: Prism::node) -> IndexOrWriteNode + + def index_target_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?receiver: Prism::node, ?opening_loc: Location, ?arguments: ArgumentsNode?, ?closing_loc: Location, ?block: BlockArgumentNode?) -> IndexTargetNode + + def instance_variable_and_write_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> InstanceVariableAndWriteNode + + def instance_variable_operator_write_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?binary_operator_loc: Location, ?value: Prism::node, ?binary_operator: Symbol) -> InstanceVariableOperatorWriteNode + + def instance_variable_or_write_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> InstanceVariableOrWriteNode + + def instance_variable_read_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> InstanceVariableReadNode + + def instance_variable_target_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> InstanceVariableTargetNode + + def instance_variable_write_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?value: Prism::node, ?operator_loc: Location) -> InstanceVariableWriteNode + + def integer_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?value: Integer) -> IntegerNode + + def interpolated_match_last_line_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode], ?closing_loc: Location) -> InterpolatedMatchLastLineNode + + def interpolated_regular_expression_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode], ?closing_loc: Location) -> InterpolatedRegularExpressionNode + + def interpolated_string_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location?, ?parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode | InterpolatedStringNode | XStringNode | InterpolatedXStringNode | SymbolNode | InterpolatedSymbolNode], ?closing_loc: Location?) -> InterpolatedStringNode + + def interpolated_symbol_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location?, ?parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode], ?closing_loc: Location?) -> InterpolatedSymbolNode + + def interpolated_x_string_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode], ?closing_loc: Location) -> InterpolatedXStringNode + + def it_local_variable_read_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer) -> ItLocalVariableReadNode + + def it_parameters_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer) -> ItParametersNode + + def keyword_hash_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?elements: Array[AssocNode | AssocSplatNode]) -> KeywordHashNode + + def keyword_rest_parameter_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol?, ?name_loc: Location?, ?operator_loc: Location) -> KeywordRestParameterNode + + def lambda_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?locals: Array[Symbol], ?operator_loc: Location, ?opening_loc: Location, ?closing_loc: Location, ?parameters: BlockParametersNode | NumberedParametersNode | ItParametersNode | nil, ?body: StatementsNode | BeginNode | nil) -> LambdaNode + + def local_variable_and_write_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node, ?name: Symbol, ?depth: Integer) -> LocalVariableAndWriteNode + + def local_variable_operator_write_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name_loc: Location, ?binary_operator_loc: Location, ?value: Prism::node, ?name: Symbol, ?binary_operator: Symbol, ?depth: Integer) -> LocalVariableOperatorWriteNode + + def local_variable_or_write_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node, ?name: Symbol, ?depth: Integer) -> LocalVariableOrWriteNode + + def local_variable_read_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?depth: Integer) -> LocalVariableReadNode + + def local_variable_target_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?depth: Integer) -> LocalVariableTargetNode + + def local_variable_write_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?depth: Integer, ?name_loc: Location, ?value: Prism::node, ?operator_loc: Location) -> LocalVariableWriteNode + + def match_last_line_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?content_loc: Location, ?closing_loc: Location, ?unescaped: String) -> MatchLastLineNode + + def match_predicate_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?value: Prism::node, ?pattern: Prism::node, ?operator_loc: Location) -> MatchPredicateNode + + def match_required_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?value: Prism::node, ?pattern: Prism::node, ?operator_loc: Location) -> MatchRequiredNode + + def match_write_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?call: CallNode, ?targets: Array[LocalVariableTargetNode]) -> MatchWriteNode + + def missing_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer) -> MissingNode + + def module_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?locals: Array[Symbol], ?module_keyword_loc: Location, ?constant_path: ConstantReadNode | ConstantPathNode | MissingNode, ?body: StatementsNode | BeginNode | nil, ?end_keyword_loc: Location, ?name: Symbol) -> ModuleNode + + def multi_target_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?lefts: Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | RequiredParameterNode | BackReferenceReadNode | NumberedReferenceReadNode], ?rest: ImplicitRestNode | SplatNode | nil, ?rights: Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | RequiredParameterNode | BackReferenceReadNode | NumberedReferenceReadNode], ?lparen_loc: Location?, ?rparen_loc: Location?) -> MultiTargetNode + + def multi_write_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?lefts: Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | BackReferenceReadNode | NumberedReferenceReadNode], ?rest: ImplicitRestNode | SplatNode | nil, ?rights: Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | BackReferenceReadNode | NumberedReferenceReadNode], ?lparen_loc: Location?, ?rparen_loc: Location?, ?operator_loc: Location, ?value: Prism::node) -> MultiWriteNode + + def next_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?arguments: ArgumentsNode?, ?keyword_loc: Location) -> NextNode + + def nil_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer) -> NilNode + + def no_keywords_parameter_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?operator_loc: Location, ?keyword_loc: Location) -> NoKeywordsParameterNode + + def numbered_parameters_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?maximum: Integer) -> NumberedParametersNode + + def numbered_reference_read_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?number: Integer) -> NumberedReferenceReadNode + + def optional_keyword_parameter_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?value: Prism::node) -> OptionalKeywordParameterNode + + def optional_parameter_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> OptionalParameterNode + + def or_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?left: Prism::node, ?right: Prism::node, ?operator_loc: Location) -> OrNode + + def parameters_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?requireds: Array[RequiredParameterNode | MultiTargetNode], ?optionals: Array[OptionalParameterNode], ?rest: RestParameterNode | ImplicitRestNode | nil, ?posts: Array[RequiredParameterNode | MultiTargetNode | KeywordRestParameterNode | NoKeywordsParameterNode | ForwardingParameterNode], ?keywords: Array[RequiredKeywordParameterNode | OptionalKeywordParameterNode], ?keyword_rest: KeywordRestParameterNode | ForwardingParameterNode | NoKeywordsParameterNode | nil, ?block: BlockParameterNode?) -> ParametersNode + + def parentheses_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?body: Prism::node?, ?opening_loc: Location, ?closing_loc: Location) -> ParenthesesNode + + def pinned_expression_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?expression: Prism::node, ?operator_loc: Location, ?lparen_loc: Location, ?rparen_loc: Location) -> PinnedExpressionNode + + def pinned_variable_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?variable: LocalVariableReadNode | InstanceVariableReadNode | ClassVariableReadNode | GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode | ItLocalVariableReadNode | MissingNode, ?operator_loc: Location) -> PinnedVariableNode + + def post_execution_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?statements: StatementsNode?, ?keyword_loc: Location, ?opening_loc: Location, ?closing_loc: Location) -> PostExecutionNode + + def pre_execution_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?statements: StatementsNode?, ?keyword_loc: Location, ?opening_loc: Location, ?closing_loc: Location) -> PreExecutionNode + + def program_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?locals: Array[Symbol], ?statements: StatementsNode) -> ProgramNode + + def range_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?left: Prism::node?, ?right: Prism::node?, ?operator_loc: Location) -> RangeNode + + def rational_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?numerator: Integer, ?denominator: Integer) -> RationalNode + + def redo_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer) -> RedoNode + + def regular_expression_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?content_loc: Location, ?closing_loc: Location, ?unescaped: String) -> RegularExpressionNode + + def required_keyword_parameter_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location) -> RequiredKeywordParameterNode + + def required_parameter_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> RequiredParameterNode + + def rescue_modifier_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?expression: Prism::node, ?keyword_loc: Location, ?rescue_expression: Prism::node) -> RescueModifierNode + + def rescue_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?keyword_loc: Location, ?exceptions: Array[Prism::node], ?operator_loc: Location?, ?reference: LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | BackReferenceReadNode | NumberedReferenceReadNode | MissingNode | nil, ?then_keyword_loc: Location?, ?statements: StatementsNode?, ?subsequent: RescueNode?) -> RescueNode + + def rest_parameter_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol?, ?name_loc: Location?, ?operator_loc: Location) -> RestParameterNode + + def retry_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer) -> RetryNode + + def return_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?keyword_loc: Location, ?arguments: ArgumentsNode?) -> ReturnNode + + def self_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer) -> SelfNode + + def shareable_constant_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?write: ConstantWriteNode | ConstantAndWriteNode | ConstantOrWriteNode | ConstantOperatorWriteNode | ConstantPathWriteNode | ConstantPathAndWriteNode | ConstantPathOrWriteNode | ConstantPathOperatorWriteNode) -> ShareableConstantNode + + def singleton_class_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?locals: Array[Symbol], ?class_keyword_loc: Location, ?operator_loc: Location, ?expression: Prism::node, ?body: StatementsNode | BeginNode | nil, ?end_keyword_loc: Location) -> SingletonClassNode + + def source_encoding_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer) -> SourceEncodingNode + + def source_file_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?filepath: String) -> SourceFileNode + + def source_line_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer) -> SourceLineNode + + def splat_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?operator_loc: Location, ?expression: Prism::node?) -> SplatNode + + def statements_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?body: Array[Prism::node]) -> StatementsNode + + def string_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location?, ?content_loc: Location, ?closing_loc: Location?, ?unescaped: String) -> StringNode + + def super_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?keyword_loc: Location, ?lparen_loc: Location?, ?arguments: ArgumentsNode?, ?rparen_loc: Location?, ?block: BlockNode | BlockArgumentNode | nil) -> SuperNode + + def symbol_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location?, ?value_loc: Location?, ?closing_loc: Location?, ?unescaped: String) -> SymbolNode + + def true_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer) -> TrueNode + + def undef_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?names: Array[SymbolNode | InterpolatedSymbolNode], ?keyword_loc: Location) -> UndefNode + + def unless_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?keyword_loc: Location, ?predicate: Prism::node, ?then_keyword_loc: Location?, ?statements: StatementsNode?, ?else_clause: ElseNode?, ?end_keyword_loc: Location?) -> UnlessNode + + def until_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?keyword_loc: Location, ?do_keyword_loc: Location?, ?closing_loc: Location?, ?predicate: Prism::node, ?statements: StatementsNode?) -> UntilNode + + def when_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?keyword_loc: Location, ?conditions: Array[Prism::node], ?then_keyword_loc: Location?, ?statements: StatementsNode?) -> WhenNode + + def while_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?keyword_loc: Location, ?do_keyword_loc: Location?, ?closing_loc: Location?, ?predicate: Prism::node, ?statements: StatementsNode?) -> WhileNode + + def x_string_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?content_loc: Location, ?closing_loc: Location, ?unescaped: String) -> XStringNode + + def yield_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?keyword_loc: Location, ?lparen_loc: Location?, ?arguments: ArgumentsNode?, ?rparen_loc: Location?) -> YieldNode + + def arguments_node_flag: (Symbol name) -> Integer + + def array_node_flag: (Symbol name) -> Integer + + def call_node_flag: (Symbol name) -> Integer + + def encoding_flag: (Symbol name) -> Integer + + def integer_base_flag: (Symbol name) -> Integer + + def interpolated_string_node_flag: (Symbol name) -> Integer + + def keyword_hash_node_flag: (Symbol name) -> Integer + + def loop_flag: (Symbol name) -> Integer + + def parameter_flag: (Symbol name) -> Integer + + def parentheses_node_flag: (Symbol name) -> Integer + + def range_flag: (Symbol name) -> Integer + + def regular_expression_flag: (Symbol name) -> Integer + + def shareable_constant_node_flag: (Symbol name) -> Integer + + def string_flag: (Symbol name) -> Integer + + def symbol_flag: (Symbol name) -> Integer + + private + + def default_source: () -> Source + + def default_location: () -> Location + + def default_node: (Source source, Location location) -> node + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/inspect_visitor.rbs b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/inspect_visitor.rbs new file mode 100644 index 0000000..70fa878 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/inspect_visitor.rbs @@ -0,0 +1,22 @@ +module Prism + class InspectVisitor < Visitor + class Replace + attr_reader value: String + + def initialize: (String value) -> void + end + + attr_reader indent: String + attr_reader commands: Array[[String | node | Replace, String]] + + def initialize: (?String indent) -> void + def compose: () -> String + + def self.compose: (node node) -> String + + private + + def inspect_node: (String name, node node) -> String + def inspect_location: (Location? location) -> String + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/lex_compat.rbs b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/lex_compat.rbs new file mode 100644 index 0000000..5dc1f87 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/lex_compat.rbs @@ -0,0 +1,10 @@ +module Prism + class LexCompat + class Result < Prism::Result + attr_reader value: Array[[[Integer, Integer], Symbol, String, untyped]] + + def initialize: (Array[[[Integer, Integer], Symbol, String, untyped]] value, Array[comment] comments, Array[MagicComment] magic_comments, Location? data_loc, Array[ParseError] errors, Array[ParseWarning] warnings, Source source) -> void + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/mutation_compiler.rbs b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/mutation_compiler.rbs new file mode 100644 index 0000000..1bb83c3 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/mutation_compiler.rbs @@ -0,0 +1,159 @@ +# This file is generated by the templates/template.rb script and should not be +# modified manually. See templates/sig/prism/mutation_compiler.rbs.erb +# if you are looking to modify the template + +module Prism + class MutationCompiler < Compiler + def visit_alias_global_variable_node: (AliasGlobalVariableNode) -> node? + def visit_alias_method_node: (AliasMethodNode) -> node? + def visit_alternation_pattern_node: (AlternationPatternNode) -> node? + def visit_and_node: (AndNode) -> node? + def visit_arguments_node: (ArgumentsNode) -> node? + def visit_array_node: (ArrayNode) -> node? + def visit_array_pattern_node: (ArrayPatternNode) -> node? + def visit_assoc_node: (AssocNode) -> node? + def visit_assoc_splat_node: (AssocSplatNode) -> node? + def visit_back_reference_read_node: (BackReferenceReadNode) -> node? + def visit_begin_node: (BeginNode) -> node? + def visit_block_argument_node: (BlockArgumentNode) -> node? + def visit_block_local_variable_node: (BlockLocalVariableNode) -> node? + def visit_block_node: (BlockNode) -> node? + def visit_block_parameter_node: (BlockParameterNode) -> node? + def visit_block_parameters_node: (BlockParametersNode) -> node? + def visit_break_node: (BreakNode) -> node? + def visit_call_and_write_node: (CallAndWriteNode) -> node? + def visit_call_node: (CallNode) -> node? + def visit_call_operator_write_node: (CallOperatorWriteNode) -> node? + def visit_call_or_write_node: (CallOrWriteNode) -> node? + def visit_call_target_node: (CallTargetNode) -> node? + def visit_capture_pattern_node: (CapturePatternNode) -> node? + def visit_case_match_node: (CaseMatchNode) -> node? + def visit_case_node: (CaseNode) -> node? + def visit_class_node: (ClassNode) -> node? + def visit_class_variable_and_write_node: (ClassVariableAndWriteNode) -> node? + def visit_class_variable_operator_write_node: (ClassVariableOperatorWriteNode) -> node? + def visit_class_variable_or_write_node: (ClassVariableOrWriteNode) -> node? + def visit_class_variable_read_node: (ClassVariableReadNode) -> node? + def visit_class_variable_target_node: (ClassVariableTargetNode) -> node? + def visit_class_variable_write_node: (ClassVariableWriteNode) -> node? + def visit_constant_and_write_node: (ConstantAndWriteNode) -> node? + def visit_constant_operator_write_node: (ConstantOperatorWriteNode) -> node? + def visit_constant_or_write_node: (ConstantOrWriteNode) -> node? + def visit_constant_path_and_write_node: (ConstantPathAndWriteNode) -> node? + def visit_constant_path_node: (ConstantPathNode) -> node? + def visit_constant_path_operator_write_node: (ConstantPathOperatorWriteNode) -> node? + def visit_constant_path_or_write_node: (ConstantPathOrWriteNode) -> node? + def visit_constant_path_target_node: (ConstantPathTargetNode) -> node? + def visit_constant_path_write_node: (ConstantPathWriteNode) -> node? + def visit_constant_read_node: (ConstantReadNode) -> node? + def visit_constant_target_node: (ConstantTargetNode) -> node? + def visit_constant_write_node: (ConstantWriteNode) -> node? + def visit_def_node: (DefNode) -> node? + def visit_defined_node: (DefinedNode) -> node? + def visit_else_node: (ElseNode) -> node? + def visit_embedded_statements_node: (EmbeddedStatementsNode) -> node? + def visit_embedded_variable_node: (EmbeddedVariableNode) -> node? + def visit_ensure_node: (EnsureNode) -> node? + def visit_false_node: (FalseNode) -> node? + def visit_find_pattern_node: (FindPatternNode) -> node? + def visit_flip_flop_node: (FlipFlopNode) -> node? + def visit_float_node: (FloatNode) -> node? + def visit_for_node: (ForNode) -> node? + def visit_forwarding_arguments_node: (ForwardingArgumentsNode) -> node? + def visit_forwarding_parameter_node: (ForwardingParameterNode) -> node? + def visit_forwarding_super_node: (ForwardingSuperNode) -> node? + def visit_global_variable_and_write_node: (GlobalVariableAndWriteNode) -> node? + def visit_global_variable_operator_write_node: (GlobalVariableOperatorWriteNode) -> node? + def visit_global_variable_or_write_node: (GlobalVariableOrWriteNode) -> node? + def visit_global_variable_read_node: (GlobalVariableReadNode) -> node? + def visit_global_variable_target_node: (GlobalVariableTargetNode) -> node? + def visit_global_variable_write_node: (GlobalVariableWriteNode) -> node? + def visit_hash_node: (HashNode) -> node? + def visit_hash_pattern_node: (HashPatternNode) -> node? + def visit_if_node: (IfNode) -> node? + def visit_imaginary_node: (ImaginaryNode) -> node? + def visit_implicit_node: (ImplicitNode) -> node? + def visit_implicit_rest_node: (ImplicitRestNode) -> node? + def visit_in_node: (InNode) -> node? + def visit_index_and_write_node: (IndexAndWriteNode) -> node? + def visit_index_operator_write_node: (IndexOperatorWriteNode) -> node? + def visit_index_or_write_node: (IndexOrWriteNode) -> node? + def visit_index_target_node: (IndexTargetNode) -> node? + def visit_instance_variable_and_write_node: (InstanceVariableAndWriteNode) -> node? + def visit_instance_variable_operator_write_node: (InstanceVariableOperatorWriteNode) -> node? + def visit_instance_variable_or_write_node: (InstanceVariableOrWriteNode) -> node? + def visit_instance_variable_read_node: (InstanceVariableReadNode) -> node? + def visit_instance_variable_target_node: (InstanceVariableTargetNode) -> node? + def visit_instance_variable_write_node: (InstanceVariableWriteNode) -> node? + def visit_integer_node: (IntegerNode) -> node? + def visit_interpolated_match_last_line_node: (InterpolatedMatchLastLineNode) -> node? + def visit_interpolated_regular_expression_node: (InterpolatedRegularExpressionNode) -> node? + def visit_interpolated_string_node: (InterpolatedStringNode) -> node? + def visit_interpolated_symbol_node: (InterpolatedSymbolNode) -> node? + def visit_interpolated_x_string_node: (InterpolatedXStringNode) -> node? + def visit_it_local_variable_read_node: (ItLocalVariableReadNode) -> node? + def visit_it_parameters_node: (ItParametersNode) -> node? + def visit_keyword_hash_node: (KeywordHashNode) -> node? + def visit_keyword_rest_parameter_node: (KeywordRestParameterNode) -> node? + def visit_lambda_node: (LambdaNode) -> node? + def visit_local_variable_and_write_node: (LocalVariableAndWriteNode) -> node? + def visit_local_variable_operator_write_node: (LocalVariableOperatorWriteNode) -> node? + def visit_local_variable_or_write_node: (LocalVariableOrWriteNode) -> node? + def visit_local_variable_read_node: (LocalVariableReadNode) -> node? + def visit_local_variable_target_node: (LocalVariableTargetNode) -> node? + def visit_local_variable_write_node: (LocalVariableWriteNode) -> node? + def visit_match_last_line_node: (MatchLastLineNode) -> node? + def visit_match_predicate_node: (MatchPredicateNode) -> node? + def visit_match_required_node: (MatchRequiredNode) -> node? + def visit_match_write_node: (MatchWriteNode) -> node? + def visit_missing_node: (MissingNode) -> node? + def visit_module_node: (ModuleNode) -> node? + def visit_multi_target_node: (MultiTargetNode) -> node? + def visit_multi_write_node: (MultiWriteNode) -> node? + def visit_next_node: (NextNode) -> node? + def visit_nil_node: (NilNode) -> node? + def visit_no_keywords_parameter_node: (NoKeywordsParameterNode) -> node? + def visit_numbered_parameters_node: (NumberedParametersNode) -> node? + def visit_numbered_reference_read_node: (NumberedReferenceReadNode) -> node? + def visit_optional_keyword_parameter_node: (OptionalKeywordParameterNode) -> node? + def visit_optional_parameter_node: (OptionalParameterNode) -> node? + def visit_or_node: (OrNode) -> node? + def visit_parameters_node: (ParametersNode) -> node? + def visit_parentheses_node: (ParenthesesNode) -> node? + def visit_pinned_expression_node: (PinnedExpressionNode) -> node? + def visit_pinned_variable_node: (PinnedVariableNode) -> node? + def visit_post_execution_node: (PostExecutionNode) -> node? + def visit_pre_execution_node: (PreExecutionNode) -> node? + def visit_program_node: (ProgramNode) -> node? + def visit_range_node: (RangeNode) -> node? + def visit_rational_node: (RationalNode) -> node? + def visit_redo_node: (RedoNode) -> node? + def visit_regular_expression_node: (RegularExpressionNode) -> node? + def visit_required_keyword_parameter_node: (RequiredKeywordParameterNode) -> node? + def visit_required_parameter_node: (RequiredParameterNode) -> node? + def visit_rescue_modifier_node: (RescueModifierNode) -> node? + def visit_rescue_node: (RescueNode) -> node? + def visit_rest_parameter_node: (RestParameterNode) -> node? + def visit_retry_node: (RetryNode) -> node? + def visit_return_node: (ReturnNode) -> node? + def visit_self_node: (SelfNode) -> node? + def visit_shareable_constant_node: (ShareableConstantNode) -> node? + def visit_singleton_class_node: (SingletonClassNode) -> node? + def visit_source_encoding_node: (SourceEncodingNode) -> node? + def visit_source_file_node: (SourceFileNode) -> node? + def visit_source_line_node: (SourceLineNode) -> node? + def visit_splat_node: (SplatNode) -> node? + def visit_statements_node: (StatementsNode) -> node? + def visit_string_node: (StringNode) -> node? + def visit_super_node: (SuperNode) -> node? + def visit_symbol_node: (SymbolNode) -> node? + def visit_true_node: (TrueNode) -> node? + def visit_undef_node: (UndefNode) -> node? + def visit_unless_node: (UnlessNode) -> node? + def visit_until_node: (UntilNode) -> node? + def visit_when_node: (WhenNode) -> node? + def visit_while_node: (WhileNode) -> node? + def visit_x_string_node: (XStringNode) -> node? + def visit_yield_node: (YieldNode) -> node? + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/node.rbs b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/node.rbs new file mode 100644 index 0000000..da12806 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/node.rbs @@ -0,0 +1,4042 @@ +# This file is generated by the templates/template.rb script and should not be +# modified manually. See templates/sig/prism/node.rbs.erb +# if you are looking to modify the template + +module Prism + class Node + attr_reader source: Source + attr_reader node_id: Integer + attr_reader location: Location + attr_reader flags: Integer + + def newline?: () -> bool + def static_literal?: () -> bool + + def accept: (_Visitor) -> void + def child_nodes: () -> Array[Prism::node?] + def comment_targets: () -> Array[Prism::node | Location] + def compact_child_nodes: () -> Array[Prism::node] + def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] + def self.fields: () -> Array[Prism::Reflection::Field] + def type: () -> Symbol + def self.type: () -> Symbol + + def source_lines: () -> Array[String] + alias script_lines source_lines + def slice: () -> String + def slice_lines: () -> String + def pretty_print: (untyped q) -> untyped + def to_dot: () -> String + def tunnel: (Integer line, Integer column) -> Array[Prism::node] + def breadth_first_search: () { (Prism::node) -> bool } -> Prism::node? + alias find breadth_first_search + def breadth_first_search_all: () { (Prism::node) -> bool } -> Array[Prism::node] + alias find_all breadth_first_search_all + def newline!: (Array[untyped]) -> void + + def save: (_Repository repository) -> void + def save_location: (_Repository repository) -> void + + def leading_comments: () -> Array[comment] + def trailing_comments: () -> Array[comment] + def comments: () -> Array[comment] + + def start_offset: () -> Integer + def start_character_offset: () -> Integer + def start_code_units_offset: (Encoding encoding) -> Integer + def cached_start_code_units_offset: (_CodeUnitsCache cache) -> Integer + + def end_offset: () -> Integer + def end_character_offset: () -> Integer + def end_code_units_offset: (Encoding encoding) -> Integer + def cached_end_code_units_offset: (_CodeUnitsCache cache) -> Integer + + def start_line: () -> Integer + def end_line: () -> Integer + + def start_column: () -> Integer + def start_character_column: () -> Integer + def start_code_units_column: (Encoding encoding) -> Integer + def cached_start_code_units_column: (_CodeUnitsCache cache) -> Integer + + def end_column: () -> Integer + def end_character_column: () -> Integer + def end_code_units_column: (Encoding encoding) -> Integer + def cached_end_code_units_column: (_CodeUnitsCache cache) -> Integer + end + + # Methods implemented by every subclass of Node + interface _Node + def deconstruct: () -> Array[Prism::node?] + def inspect: () -> String + end + + type node = Node & _Node + + interface _Repository + def enter: (Integer node_id, Symbol field_name) -> void + end + + + # Represents the use of the `alias` keyword to alias a global variable. + # + # alias $foo $bar + # ^^^^^^^^^^^^^^^ + class AliasGlobalVariableNode < Node + include _Node + + attr_reader new_name: GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode + attr_reader old_name: GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode | SymbolNode | MissingNode + attr_reader keyword_loc: Location + + def save_keyword_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode new_name, GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode | SymbolNode | MissingNode old_name, Location keyword_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?new_name: GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode, ?old_name: GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode | SymbolNode | MissingNode, ?keyword_loc: Location) -> AliasGlobalVariableNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, new_name: GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode, old_name: GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode | SymbolNode | MissingNode, keyword_loc: Location } + def keyword: () -> String + def type: () -> :alias_global_variable_node + | ... + def self.type: () -> :alias_global_variable_node + end + + # Represents the use of the `alias` keyword to alias a method. + # + # alias foo bar + # ^^^^^^^^^^^^^ + class AliasMethodNode < Node + include _Node + + attr_reader new_name: SymbolNode | InterpolatedSymbolNode + attr_reader old_name: SymbolNode | InterpolatedSymbolNode | GlobalVariableReadNode | MissingNode + attr_reader keyword_loc: Location + + def save_keyword_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, SymbolNode | InterpolatedSymbolNode new_name, SymbolNode | InterpolatedSymbolNode | GlobalVariableReadNode | MissingNode old_name, Location keyword_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?new_name: SymbolNode | InterpolatedSymbolNode, ?old_name: SymbolNode | InterpolatedSymbolNode | GlobalVariableReadNode | MissingNode, ?keyword_loc: Location) -> AliasMethodNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, new_name: SymbolNode | InterpolatedSymbolNode, old_name: SymbolNode | InterpolatedSymbolNode | GlobalVariableReadNode | MissingNode, keyword_loc: Location } + def keyword: () -> String + def type: () -> :alias_method_node + | ... + def self.type: () -> :alias_method_node + end + + # Represents an alternation pattern in pattern matching. + # + # foo => bar | baz + # ^^^^^^^^^ + class AlternationPatternNode < Node + include _Node + + attr_reader left: Prism::node + attr_reader right: Prism::node + attr_reader operator_loc: Location + + def save_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Prism::node left, Prism::node right, Location operator_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?left: Prism::node, ?right: Prism::node, ?operator_loc: Location) -> AlternationPatternNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, left: Prism::node, right: Prism::node, operator_loc: Location } + def operator: () -> String + def type: () -> :alternation_pattern_node + | ... + def self.type: () -> :alternation_pattern_node + end + + # Represents the use of the `&&` operator or the `and` keyword. + # + # left and right + # ^^^^^^^^^^^^^^ + class AndNode < Node + include _Node + + attr_reader left: Prism::node + attr_reader right: Prism::node + attr_reader operator_loc: Location + + def save_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Prism::node left, Prism::node right, Location operator_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?left: Prism::node, ?right: Prism::node, ?operator_loc: Location) -> AndNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, left: Prism::node, right: Prism::node, operator_loc: Location } + def operator: () -> String + def type: () -> :and_node + | ... + def self.type: () -> :and_node + end + + # Represents a set of arguments to a method or a keyword. + # + # return foo, bar, baz + # ^^^^^^^^^^^^^ + class ArgumentsNode < Node + include _Node + + def contains_forwarding?: () -> bool + def contains_keywords?: () -> bool + def contains_keyword_splat?: () -> bool + def contains_splat?: () -> bool + def contains_multiple_splats?: () -> bool + + attr_reader arguments: Array[Prism::node] + + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Array[Prism::node] arguments) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?arguments: Array[Prism::node]) -> ArgumentsNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, arguments: Array[Prism::node] } + def type: () -> :arguments_node + | ... + def self.type: () -> :arguments_node + end + + # Represents an array literal. This can be a regular array using brackets or a special array using % like %w or %i. + # + # [1, 2, 3] + # ^^^^^^^^^ + class ArrayNode < Node + include _Node + + def contains_splat?: () -> bool + + attr_reader elements: Array[Prism::node] + attr_reader opening_loc: Location? + attr_reader closing_loc: Location? + + def save_opening_loc: (_Repository repository) -> void + def save_closing_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Array[Prism::node] elements, Location? opening_loc, Location? closing_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?elements: Array[Prism::node], ?opening_loc: Location?, ?closing_loc: Location?) -> ArrayNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, elements: Array[Prism::node], opening_loc: Location?, closing_loc: Location? } + def opening: () -> String? + def closing: () -> String? + def type: () -> :array_node + | ... + def self.type: () -> :array_node + end + + # Represents an array pattern in pattern matching. + # + # foo in 1, 2 + # ^^^^^^^^^^^ + # + # foo in [1, 2] + # ^^^^^^^^^^^^^ + # + # foo in *bar + # ^^^^^^^^^^^ + # + # foo in Bar[] + # ^^^^^^^^^^^^ + # + # foo in Bar[1, 2, 3] + # ^^^^^^^^^^^^^^^^^^^ + class ArrayPatternNode < Node + include _Node + + attr_reader constant: ConstantPathNode | ConstantReadNode | nil + attr_reader requireds: Array[Prism::node] + attr_reader rest: Prism::node? + attr_reader posts: Array[Prism::node] + attr_reader opening_loc: Location? + attr_reader closing_loc: Location? + + def save_opening_loc: (_Repository repository) -> void + def save_closing_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, ConstantPathNode | ConstantReadNode | nil constant, Array[Prism::node] requireds, Prism::node? rest, Array[Prism::node] posts, Location? opening_loc, Location? closing_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?constant: ConstantPathNode | ConstantReadNode | nil, ?requireds: Array[Prism::node], ?rest: Prism::node?, ?posts: Array[Prism::node], ?opening_loc: Location?, ?closing_loc: Location?) -> ArrayPatternNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, constant: ConstantPathNode | ConstantReadNode | nil, requireds: Array[Prism::node], rest: Prism::node?, posts: Array[Prism::node], opening_loc: Location?, closing_loc: Location? } + def opening: () -> String? + def closing: () -> String? + def type: () -> :array_pattern_node + | ... + def self.type: () -> :array_pattern_node + end + + # Represents a hash key/value pair. + # + # { a => b } + # ^^^^^^ + class AssocNode < Node + include _Node + + attr_reader key: Prism::node + attr_reader value: Prism::node + attr_reader operator_loc: Location? + + def save_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Prism::node key, Prism::node value, Location? operator_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?key: Prism::node, ?value: Prism::node, ?operator_loc: Location?) -> AssocNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, key: Prism::node, value: Prism::node, operator_loc: Location? } + def operator: () -> String? + def type: () -> :assoc_node + | ... + def self.type: () -> :assoc_node + end + + # Represents a splat in a hash literal. + # + # { **foo } + # ^^^^^ + class AssocSplatNode < Node + include _Node + + attr_reader value: Prism::node? + attr_reader operator_loc: Location + + def save_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Prism::node? value, Location operator_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?value: Prism::node?, ?operator_loc: Location) -> AssocSplatNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, value: Prism::node?, operator_loc: Location } + def operator: () -> String + def type: () -> :assoc_splat_node + | ... + def self.type: () -> :assoc_splat_node + end + + # Represents reading a reference to a field in the previous match. + # + # $' + # ^^ + class BackReferenceReadNode < Node + include _Node + + attr_reader name: Symbol + + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> BackReferenceReadNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol } + def type: () -> :back_reference_read_node + | ... + def self.type: () -> :back_reference_read_node + end + + # Represents a begin statement. + # + # begin + # foo + # end + # ^^^^^ + class BeginNode < Node + include _Node + + attr_reader begin_keyword_loc: Location? + attr_reader statements: StatementsNode? + attr_reader rescue_clause: RescueNode? + attr_reader else_clause: ElseNode? + attr_reader ensure_clause: EnsureNode? + attr_reader end_keyword_loc: Location? + + def save_begin_keyword_loc: (_Repository repository) -> void + def save_end_keyword_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location? begin_keyword_loc, StatementsNode? statements, RescueNode? rescue_clause, ElseNode? else_clause, EnsureNode? ensure_clause, Location? end_keyword_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?begin_keyword_loc: Location?, ?statements: StatementsNode?, ?rescue_clause: RescueNode?, ?else_clause: ElseNode?, ?ensure_clause: EnsureNode?, ?end_keyword_loc: Location?) -> BeginNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, begin_keyword_loc: Location?, statements: StatementsNode?, rescue_clause: RescueNode?, else_clause: ElseNode?, ensure_clause: EnsureNode?, end_keyword_loc: Location? } + def begin_keyword: () -> String? + def end_keyword: () -> String? + def type: () -> :begin_node + | ... + def self.type: () -> :begin_node + end + + # Represents a block argument using `&`. + # + # bar(&args) + # ^^^^^^^^^^ + class BlockArgumentNode < Node + include _Node + + attr_reader expression: Prism::node? + attr_reader operator_loc: Location + + def save_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Prism::node? expression, Location operator_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?expression: Prism::node?, ?operator_loc: Location) -> BlockArgumentNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, expression: Prism::node?, operator_loc: Location } + def operator: () -> String + def type: () -> :block_argument_node + | ... + def self.type: () -> :block_argument_node + end + + # Represents a block local variable. + # + # a { |; b| } + # ^ + class BlockLocalVariableNode < Node + include _Node + + def repeated_parameter?: () -> bool + + attr_reader name: Symbol + + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> BlockLocalVariableNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol } + def type: () -> :block_local_variable_node + | ... + def self.type: () -> :block_local_variable_node + end + + # Represents a block of ruby code. + # + # [1, 2, 3].each { |i| puts x } + # ^^^^^^^^^^^^^^ + class BlockNode < Node + include _Node + + attr_reader locals: Array[Symbol] + attr_reader parameters: BlockParametersNode | NumberedParametersNode | ItParametersNode | nil + attr_reader body: StatementsNode | BeginNode | nil + attr_reader opening_loc: Location + attr_reader closing_loc: Location + + def save_opening_loc: (_Repository repository) -> void + def save_closing_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Array[Symbol] locals, BlockParametersNode | NumberedParametersNode | ItParametersNode | nil parameters, StatementsNode | BeginNode | nil body, Location opening_loc, Location closing_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?locals: Array[Symbol], ?parameters: BlockParametersNode | NumberedParametersNode | ItParametersNode | nil, ?body: StatementsNode | BeginNode | nil, ?opening_loc: Location, ?closing_loc: Location) -> BlockNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, locals: Array[Symbol], parameters: BlockParametersNode | NumberedParametersNode | ItParametersNode | nil, body: StatementsNode | BeginNode | nil, opening_loc: Location, closing_loc: Location } + def opening: () -> String + def closing: () -> String + def type: () -> :block_node + | ... + def self.type: () -> :block_node + end + + # Represents a block parameter of a method, block, or lambda definition. + # + # def a(&b) + # ^^ + # end + class BlockParameterNode < Node + include _Node + + def repeated_parameter?: () -> bool + + attr_reader name: Symbol? + attr_reader name_loc: Location? + attr_reader operator_loc: Location + + def save_name_loc: (_Repository repository) -> void + def save_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol? name, Location? name_loc, Location operator_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol?, ?name_loc: Location?, ?operator_loc: Location) -> BlockParameterNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol?, name_loc: Location?, operator_loc: Location } + def operator: () -> String + def type: () -> :block_parameter_node + | ... + def self.type: () -> :block_parameter_node + end + + # Represents a block's parameters declaration. + # + # -> (a, b = 1; local) { } + # ^^^^^^^^^^^^^^^^^ + # + # foo do |a, b = 1; local| + # ^^^^^^^^^^^^^^^^^ + # end + class BlockParametersNode < Node + include _Node + + attr_reader parameters: ParametersNode? + attr_reader locals: Array[BlockLocalVariableNode] + attr_reader opening_loc: Location? + attr_reader closing_loc: Location? + + def save_opening_loc: (_Repository repository) -> void + def save_closing_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, ParametersNode? parameters, Array[BlockLocalVariableNode] locals, Location? opening_loc, Location? closing_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?parameters: ParametersNode?, ?locals: Array[BlockLocalVariableNode], ?opening_loc: Location?, ?closing_loc: Location?) -> BlockParametersNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, parameters: ParametersNode?, locals: Array[BlockLocalVariableNode], opening_loc: Location?, closing_loc: Location? } + def opening: () -> String? + def closing: () -> String? + def type: () -> :block_parameters_node + | ... + def self.type: () -> :block_parameters_node + end + + # Represents the use of the `break` keyword. + # + # break foo + # ^^^^^^^^^ + class BreakNode < Node + include _Node + + attr_reader arguments: ArgumentsNode? + attr_reader keyword_loc: Location + + def save_keyword_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, ArgumentsNode? arguments, Location keyword_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?arguments: ArgumentsNode?, ?keyword_loc: Location) -> BreakNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, arguments: ArgumentsNode?, keyword_loc: Location } + def keyword: () -> String + def type: () -> :break_node + | ... + def self.type: () -> :break_node + end + + # Represents the use of the `&&=` operator on a call. + # + # foo.bar &&= value + # ^^^^^^^^^^^^^^^^^ + class CallAndWriteNode < Node + include _Node + + def safe_navigation?: () -> bool + def variable_call?: () -> bool + def attribute_write?: () -> bool + def ignore_visibility?: () -> bool + + attr_reader receiver: Prism::node? + attr_reader call_operator_loc: Location? + attr_reader message_loc: Location? + attr_reader read_name: Symbol + attr_reader write_name: Symbol + attr_reader operator_loc: Location + attr_reader value: Prism::node + + def save_call_operator_loc: (_Repository repository) -> void + def save_message_loc: (_Repository repository) -> void + def save_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Prism::node? receiver, Location? call_operator_loc, Location? message_loc, Symbol read_name, Symbol write_name, Location operator_loc, Prism::node value) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?receiver: Prism::node?, ?call_operator_loc: Location?, ?message_loc: Location?, ?read_name: Symbol, ?write_name: Symbol, ?operator_loc: Location, ?value: Prism::node) -> CallAndWriteNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, receiver: Prism::node?, call_operator_loc: Location?, message_loc: Location?, read_name: Symbol, write_name: Symbol, operator_loc: Location, value: Prism::node } + def call_operator: () -> String? + def message: () -> String? + def operator: () -> String + def type: () -> :call_and_write_node + | ... + def self.type: () -> :call_and_write_node + end + + # Represents a method call, in all of the various forms that can take. + # + # foo + # ^^^ + # + # foo() + # ^^^^^ + # + # +foo + # ^^^^ + # + # foo + bar + # ^^^^^^^^^ + # + # foo.bar + # ^^^^^^^ + # + # foo&.bar + # ^^^^^^^^ + class CallNode < Node + include _Node + + def safe_navigation?: () -> bool + def variable_call?: () -> bool + def attribute_write?: () -> bool + def ignore_visibility?: () -> bool + + attr_reader receiver: Prism::node? + attr_reader call_operator_loc: Location? + attr_reader name: Symbol + attr_reader message_loc: Location? + attr_reader opening_loc: Location? + attr_reader arguments: ArgumentsNode? + attr_reader closing_loc: Location? + attr_reader equal_loc: Location? + attr_reader block: BlockNode | BlockArgumentNode | nil + + def save_call_operator_loc: (_Repository repository) -> void + def save_message_loc: (_Repository repository) -> void + def save_opening_loc: (_Repository repository) -> void + def save_closing_loc: (_Repository repository) -> void + def save_equal_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Prism::node? receiver, Location? call_operator_loc, Symbol name, Location? message_loc, Location? opening_loc, ArgumentsNode? arguments, Location? closing_loc, Location? equal_loc, BlockNode | BlockArgumentNode | nil block) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?receiver: Prism::node?, ?call_operator_loc: Location?, ?name: Symbol, ?message_loc: Location?, ?opening_loc: Location?, ?arguments: ArgumentsNode?, ?closing_loc: Location?, ?equal_loc: Location?, ?block: BlockNode | BlockArgumentNode | nil) -> CallNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, receiver: Prism::node?, call_operator_loc: Location?, name: Symbol, message_loc: Location?, opening_loc: Location?, arguments: ArgumentsNode?, closing_loc: Location?, equal_loc: Location?, block: BlockNode | BlockArgumentNode | nil } + def call_operator: () -> String? + def message: () -> String? + def opening: () -> String? + def closing: () -> String? + def equal: () -> String? + def type: () -> :call_node + | ... + def self.type: () -> :call_node + end + + # Represents the use of an assignment operator on a call. + # + # foo.bar += baz + # ^^^^^^^^^^^^^^ + class CallOperatorWriteNode < Node + include _Node + + def safe_navigation?: () -> bool + def variable_call?: () -> bool + def attribute_write?: () -> bool + def ignore_visibility?: () -> bool + + attr_reader receiver: Prism::node? + attr_reader call_operator_loc: Location? + attr_reader message_loc: Location? + attr_reader read_name: Symbol + attr_reader write_name: Symbol + attr_reader binary_operator: Symbol + attr_reader binary_operator_loc: Location + attr_reader value: Prism::node + + def save_call_operator_loc: (_Repository repository) -> void + def save_message_loc: (_Repository repository) -> void + def save_binary_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Prism::node? receiver, Location? call_operator_loc, Location? message_loc, Symbol read_name, Symbol write_name, Symbol binary_operator, Location binary_operator_loc, Prism::node value) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?receiver: Prism::node?, ?call_operator_loc: Location?, ?message_loc: Location?, ?read_name: Symbol, ?write_name: Symbol, ?binary_operator: Symbol, ?binary_operator_loc: Location, ?value: Prism::node) -> CallOperatorWriteNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, receiver: Prism::node?, call_operator_loc: Location?, message_loc: Location?, read_name: Symbol, write_name: Symbol, binary_operator: Symbol, binary_operator_loc: Location, value: Prism::node } + def call_operator: () -> String? + def message: () -> String? + def type: () -> :call_operator_write_node + | ... + def self.type: () -> :call_operator_write_node + end + + # Represents the use of the `||=` operator on a call. + # + # foo.bar ||= value + # ^^^^^^^^^^^^^^^^^ + class CallOrWriteNode < Node + include _Node + + def safe_navigation?: () -> bool + def variable_call?: () -> bool + def attribute_write?: () -> bool + def ignore_visibility?: () -> bool + + attr_reader receiver: Prism::node? + attr_reader call_operator_loc: Location? + attr_reader message_loc: Location? + attr_reader read_name: Symbol + attr_reader write_name: Symbol + attr_reader operator_loc: Location + attr_reader value: Prism::node + + def save_call_operator_loc: (_Repository repository) -> void + def save_message_loc: (_Repository repository) -> void + def save_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Prism::node? receiver, Location? call_operator_loc, Location? message_loc, Symbol read_name, Symbol write_name, Location operator_loc, Prism::node value) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?receiver: Prism::node?, ?call_operator_loc: Location?, ?message_loc: Location?, ?read_name: Symbol, ?write_name: Symbol, ?operator_loc: Location, ?value: Prism::node) -> CallOrWriteNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, receiver: Prism::node?, call_operator_loc: Location?, message_loc: Location?, read_name: Symbol, write_name: Symbol, operator_loc: Location, value: Prism::node } + def call_operator: () -> String? + def message: () -> String? + def operator: () -> String + def type: () -> :call_or_write_node + | ... + def self.type: () -> :call_or_write_node + end + + # Represents assigning to a method call. + # + # foo.bar, = 1 + # ^^^^^^^ + # + # begin + # rescue => foo.bar + # ^^^^^^^ + # end + # + # for foo.bar in baz do end + # ^^^^^^^ + class CallTargetNode < Node + include _Node + + def safe_navigation?: () -> bool + def variable_call?: () -> bool + def attribute_write?: () -> bool + def ignore_visibility?: () -> bool + + attr_reader receiver: Prism::node + attr_reader call_operator_loc: Location + attr_reader name: Symbol + attr_reader message_loc: Location + + def save_call_operator_loc: (_Repository repository) -> void + def save_message_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Prism::node receiver, Location call_operator_loc, Symbol name, Location message_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?receiver: Prism::node, ?call_operator_loc: Location, ?name: Symbol, ?message_loc: Location) -> CallTargetNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, receiver: Prism::node, call_operator_loc: Location, name: Symbol, message_loc: Location } + def call_operator: () -> String + def message: () -> String + def type: () -> :call_target_node + | ... + def self.type: () -> :call_target_node + end + + # Represents assigning to a local variable in pattern matching. + # + # foo => [bar => baz] + # ^^^^^^^^^^^^ + class CapturePatternNode < Node + include _Node + + attr_reader value: Prism::node + attr_reader target: LocalVariableTargetNode + attr_reader operator_loc: Location + + def save_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Prism::node value, LocalVariableTargetNode target, Location operator_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?value: Prism::node, ?target: LocalVariableTargetNode, ?operator_loc: Location) -> CapturePatternNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, value: Prism::node, target: LocalVariableTargetNode, operator_loc: Location } + def operator: () -> String + def type: () -> :capture_pattern_node + | ... + def self.type: () -> :capture_pattern_node + end + + # Represents the use of a case statement for pattern matching. + # + # case true + # in false + # end + # ^^^^^^^^^ + class CaseMatchNode < Node + include _Node + + attr_reader predicate: Prism::node? + attr_reader conditions: Array[InNode] + attr_reader else_clause: ElseNode? + attr_reader case_keyword_loc: Location + attr_reader end_keyword_loc: Location + + def save_case_keyword_loc: (_Repository repository) -> void + def save_end_keyword_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Prism::node? predicate, Array[InNode] conditions, ElseNode? else_clause, Location case_keyword_loc, Location end_keyword_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?predicate: Prism::node?, ?conditions: Array[InNode], ?else_clause: ElseNode?, ?case_keyword_loc: Location, ?end_keyword_loc: Location) -> CaseMatchNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, predicate: Prism::node?, conditions: Array[InNode], else_clause: ElseNode?, case_keyword_loc: Location, end_keyword_loc: Location } + def case_keyword: () -> String + def end_keyword: () -> String + def type: () -> :case_match_node + | ... + def self.type: () -> :case_match_node + end + + # Represents the use of a case statement. + # + # case true + # when false + # end + # ^^^^^^^^^^ + class CaseNode < Node + include _Node + + attr_reader predicate: Prism::node? + attr_reader conditions: Array[WhenNode] + attr_reader else_clause: ElseNode? + attr_reader case_keyword_loc: Location + attr_reader end_keyword_loc: Location + + def save_case_keyword_loc: (_Repository repository) -> void + def save_end_keyword_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Prism::node? predicate, Array[WhenNode] conditions, ElseNode? else_clause, Location case_keyword_loc, Location end_keyword_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?predicate: Prism::node?, ?conditions: Array[WhenNode], ?else_clause: ElseNode?, ?case_keyword_loc: Location, ?end_keyword_loc: Location) -> CaseNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, predicate: Prism::node?, conditions: Array[WhenNode], else_clause: ElseNode?, case_keyword_loc: Location, end_keyword_loc: Location } + def case_keyword: () -> String + def end_keyword: () -> String + def type: () -> :case_node + | ... + def self.type: () -> :case_node + end + + # Represents a class declaration involving the `class` keyword. + # + # class Foo end + # ^^^^^^^^^^^^^ + class ClassNode < Node + include _Node + + attr_reader locals: Array[Symbol] + attr_reader class_keyword_loc: Location + attr_reader constant_path: ConstantReadNode | ConstantPathNode | CallNode + attr_reader inheritance_operator_loc: Location? + attr_reader superclass: Prism::node? + attr_reader body: StatementsNode | BeginNode | nil + attr_reader end_keyword_loc: Location + attr_reader name: Symbol + + def save_class_keyword_loc: (_Repository repository) -> void + def save_inheritance_operator_loc: (_Repository repository) -> void + def save_end_keyword_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Array[Symbol] locals, Location class_keyword_loc, ConstantReadNode | ConstantPathNode | CallNode constant_path, Location? inheritance_operator_loc, Prism::node? superclass, StatementsNode | BeginNode | nil body, Location end_keyword_loc, Symbol name) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?locals: Array[Symbol], ?class_keyword_loc: Location, ?constant_path: ConstantReadNode | ConstantPathNode | CallNode, ?inheritance_operator_loc: Location?, ?superclass: Prism::node?, ?body: StatementsNode | BeginNode | nil, ?end_keyword_loc: Location, ?name: Symbol) -> ClassNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, locals: Array[Symbol], class_keyword_loc: Location, constant_path: ConstantReadNode | ConstantPathNode | CallNode, inheritance_operator_loc: Location?, superclass: Prism::node?, body: StatementsNode | BeginNode | nil, end_keyword_loc: Location, name: Symbol } + def class_keyword: () -> String + def inheritance_operator: () -> String? + def end_keyword: () -> String + def type: () -> :class_node + | ... + def self.type: () -> :class_node + end + + # Represents the use of the `&&=` operator for assignment to a class variable. + # + # @@target &&= value + # ^^^^^^^^^^^^^^^^^^ + class ClassVariableAndWriteNode < Node + include _Node + + attr_reader name: Symbol + attr_reader name_loc: Location + attr_reader operator_loc: Location + attr_reader value: Prism::node + + def save_name_loc: (_Repository repository) -> void + def save_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Location operator_loc, Prism::node value) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> ClassVariableAndWriteNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, name_loc: Location, operator_loc: Location, value: Prism::node } + def operator: () -> String + def type: () -> :class_variable_and_write_node + | ... + def self.type: () -> :class_variable_and_write_node + end + + # Represents assigning to a class variable using an operator that isn't `=`. + # + # @@target += value + # ^^^^^^^^^^^^^^^^^ + class ClassVariableOperatorWriteNode < Node + include _Node + + attr_reader name: Symbol + attr_reader name_loc: Location + attr_reader binary_operator_loc: Location + attr_reader value: Prism::node + attr_reader binary_operator: Symbol + + def save_name_loc: (_Repository repository) -> void + def save_binary_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Location binary_operator_loc, Prism::node value, Symbol binary_operator) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?binary_operator_loc: Location, ?value: Prism::node, ?binary_operator: Symbol) -> ClassVariableOperatorWriteNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, name_loc: Location, binary_operator_loc: Location, value: Prism::node, binary_operator: Symbol } + def type: () -> :class_variable_operator_write_node + | ... + def self.type: () -> :class_variable_operator_write_node + end + + # Represents the use of the `||=` operator for assignment to a class variable. + # + # @@target ||= value + # ^^^^^^^^^^^^^^^^^^ + class ClassVariableOrWriteNode < Node + include _Node + + attr_reader name: Symbol + attr_reader name_loc: Location + attr_reader operator_loc: Location + attr_reader value: Prism::node + + def save_name_loc: (_Repository repository) -> void + def save_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Location operator_loc, Prism::node value) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> ClassVariableOrWriteNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, name_loc: Location, operator_loc: Location, value: Prism::node } + def operator: () -> String + def type: () -> :class_variable_or_write_node + | ... + def self.type: () -> :class_variable_or_write_node + end + + # Represents referencing a class variable. + # + # @@foo + # ^^^^^ + class ClassVariableReadNode < Node + include _Node + + attr_reader name: Symbol + + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> ClassVariableReadNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol } + def type: () -> :class_variable_read_node + | ... + def self.type: () -> :class_variable_read_node + end + + # Represents writing to a class variable in a context that doesn't have an explicit value. + # + # @@foo, @@bar = baz + # ^^^^^ ^^^^^ + class ClassVariableTargetNode < Node + include _Node + + attr_reader name: Symbol + + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> ClassVariableTargetNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol } + def type: () -> :class_variable_target_node + | ... + def self.type: () -> :class_variable_target_node + end + + # Represents writing to a class variable. + # + # @@foo = 1 + # ^^^^^^^^^ + class ClassVariableWriteNode < Node + include _Node + + attr_reader name: Symbol + attr_reader name_loc: Location + attr_reader value: Prism::node + attr_reader operator_loc: Location + + def save_name_loc: (_Repository repository) -> void + def save_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Prism::node value, Location operator_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?value: Prism::node, ?operator_loc: Location) -> ClassVariableWriteNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, name_loc: Location, value: Prism::node, operator_loc: Location } + def operator: () -> String + def type: () -> :class_variable_write_node + | ... + def self.type: () -> :class_variable_write_node + end + + # Represents the use of the `&&=` operator for assignment to a constant. + # + # Target &&= value + # ^^^^^^^^^^^^^^^^ + class ConstantAndWriteNode < Node + include _Node + + attr_reader name: Symbol + attr_reader name_loc: Location + attr_reader operator_loc: Location + attr_reader value: Prism::node + + def save_name_loc: (_Repository repository) -> void + def save_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Location operator_loc, Prism::node value) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> ConstantAndWriteNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, name_loc: Location, operator_loc: Location, value: Prism::node } + def operator: () -> String + def type: () -> :constant_and_write_node + | ... + def self.type: () -> :constant_and_write_node + end + + # Represents assigning to a constant using an operator that isn't `=`. + # + # Target += value + # ^^^^^^^^^^^^^^^ + class ConstantOperatorWriteNode < Node + include _Node + + attr_reader name: Symbol + attr_reader name_loc: Location + attr_reader binary_operator_loc: Location + attr_reader value: Prism::node + attr_reader binary_operator: Symbol + + def save_name_loc: (_Repository repository) -> void + def save_binary_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Location binary_operator_loc, Prism::node value, Symbol binary_operator) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?binary_operator_loc: Location, ?value: Prism::node, ?binary_operator: Symbol) -> ConstantOperatorWriteNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, name_loc: Location, binary_operator_loc: Location, value: Prism::node, binary_operator: Symbol } + def type: () -> :constant_operator_write_node + | ... + def self.type: () -> :constant_operator_write_node + end + + # Represents the use of the `||=` operator for assignment to a constant. + # + # Target ||= value + # ^^^^^^^^^^^^^^^^ + class ConstantOrWriteNode < Node + include _Node + + attr_reader name: Symbol + attr_reader name_loc: Location + attr_reader operator_loc: Location + attr_reader value: Prism::node + + def save_name_loc: (_Repository repository) -> void + def save_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Location operator_loc, Prism::node value) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> ConstantOrWriteNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, name_loc: Location, operator_loc: Location, value: Prism::node } + def operator: () -> String + def type: () -> :constant_or_write_node + | ... + def self.type: () -> :constant_or_write_node + end + + # Represents the use of the `&&=` operator for assignment to a constant path. + # + # Parent::Child &&= value + # ^^^^^^^^^^^^^^^^^^^^^^^ + class ConstantPathAndWriteNode < Node + include _Node + + attr_reader target: ConstantPathNode + attr_reader operator_loc: Location + attr_reader value: Prism::node + + def save_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, ConstantPathNode target, Location operator_loc, Prism::node value) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?target: ConstantPathNode, ?operator_loc: Location, ?value: Prism::node) -> ConstantPathAndWriteNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, target: ConstantPathNode, operator_loc: Location, value: Prism::node } + def operator: () -> String + def type: () -> :constant_path_and_write_node + | ... + def self.type: () -> :constant_path_and_write_node + end + + # Represents accessing a constant through a path of `::` operators. + # + # Foo::Bar + # ^^^^^^^^ + class ConstantPathNode < Node + include _Node + + attr_reader parent: Prism::node? + attr_reader name: Symbol? + attr_reader delimiter_loc: Location + attr_reader name_loc: Location + + def save_delimiter_loc: (_Repository repository) -> void + def save_name_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Prism::node? parent, Symbol? name, Location delimiter_loc, Location name_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?parent: Prism::node?, ?name: Symbol?, ?delimiter_loc: Location, ?name_loc: Location) -> ConstantPathNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, parent: Prism::node?, name: Symbol?, delimiter_loc: Location, name_loc: Location } + def delimiter: () -> String + def type: () -> :constant_path_node + | ... + def self.type: () -> :constant_path_node + end + + # Represents assigning to a constant path using an operator that isn't `=`. + # + # Parent::Child += value + # ^^^^^^^^^^^^^^^^^^^^^^ + class ConstantPathOperatorWriteNode < Node + include _Node + + attr_reader target: ConstantPathNode + attr_reader binary_operator_loc: Location + attr_reader value: Prism::node + attr_reader binary_operator: Symbol + + def save_binary_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, ConstantPathNode target, Location binary_operator_loc, Prism::node value, Symbol binary_operator) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?target: ConstantPathNode, ?binary_operator_loc: Location, ?value: Prism::node, ?binary_operator: Symbol) -> ConstantPathOperatorWriteNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, target: ConstantPathNode, binary_operator_loc: Location, value: Prism::node, binary_operator: Symbol } + def type: () -> :constant_path_operator_write_node + | ... + def self.type: () -> :constant_path_operator_write_node + end + + # Represents the use of the `||=` operator for assignment to a constant path. + # + # Parent::Child ||= value + # ^^^^^^^^^^^^^^^^^^^^^^^ + class ConstantPathOrWriteNode < Node + include _Node + + attr_reader target: ConstantPathNode + attr_reader operator_loc: Location + attr_reader value: Prism::node + + def save_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, ConstantPathNode target, Location operator_loc, Prism::node value) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?target: ConstantPathNode, ?operator_loc: Location, ?value: Prism::node) -> ConstantPathOrWriteNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, target: ConstantPathNode, operator_loc: Location, value: Prism::node } + def operator: () -> String + def type: () -> :constant_path_or_write_node + | ... + def self.type: () -> :constant_path_or_write_node + end + + # Represents writing to a constant path in a context that doesn't have an explicit value. + # + # Foo::Foo, Bar::Bar = baz + # ^^^^^^^^ ^^^^^^^^ + class ConstantPathTargetNode < Node + include _Node + + attr_reader parent: Prism::node? + attr_reader name: Symbol? + attr_reader delimiter_loc: Location + attr_reader name_loc: Location + + def save_delimiter_loc: (_Repository repository) -> void + def save_name_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Prism::node? parent, Symbol? name, Location delimiter_loc, Location name_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?parent: Prism::node?, ?name: Symbol?, ?delimiter_loc: Location, ?name_loc: Location) -> ConstantPathTargetNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, parent: Prism::node?, name: Symbol?, delimiter_loc: Location, name_loc: Location } + def delimiter: () -> String + def type: () -> :constant_path_target_node + | ... + def self.type: () -> :constant_path_target_node + end + + # Represents writing to a constant path. + # + # ::Foo = 1 + # ^^^^^^^^^ + # + # Foo::Bar = 1 + # ^^^^^^^^^^^^ + # + # ::Foo::Bar = 1 + # ^^^^^^^^^^^^^^ + class ConstantPathWriteNode < Node + include _Node + + attr_reader target: ConstantPathNode + attr_reader operator_loc: Location + attr_reader value: Prism::node + + def save_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, ConstantPathNode target, Location operator_loc, Prism::node value) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?target: ConstantPathNode, ?operator_loc: Location, ?value: Prism::node) -> ConstantPathWriteNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, target: ConstantPathNode, operator_loc: Location, value: Prism::node } + def operator: () -> String + def type: () -> :constant_path_write_node + | ... + def self.type: () -> :constant_path_write_node + end + + # Represents referencing a constant. + # + # Foo + # ^^^ + class ConstantReadNode < Node + include _Node + + attr_reader name: Symbol + + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> ConstantReadNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol } + def type: () -> :constant_read_node + | ... + def self.type: () -> :constant_read_node + end + + # Represents writing to a constant in a context that doesn't have an explicit value. + # + # Foo, Bar = baz + # ^^^ ^^^ + class ConstantTargetNode < Node + include _Node + + attr_reader name: Symbol + + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> ConstantTargetNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol } + def type: () -> :constant_target_node + | ... + def self.type: () -> :constant_target_node + end + + # Represents writing to a constant. + # + # Foo = 1 + # ^^^^^^^ + class ConstantWriteNode < Node + include _Node + + attr_reader name: Symbol + attr_reader name_loc: Location + attr_reader value: Prism::node + attr_reader operator_loc: Location + + def save_name_loc: (_Repository repository) -> void + def save_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Prism::node value, Location operator_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?value: Prism::node, ?operator_loc: Location) -> ConstantWriteNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, name_loc: Location, value: Prism::node, operator_loc: Location } + def operator: () -> String + def type: () -> :constant_write_node + | ... + def self.type: () -> :constant_write_node + end + + # Represents a method definition. + # + # def method + # end + # ^^^^^^^^^^ + class DefNode < Node + include _Node + + attr_reader name: Symbol + attr_reader name_loc: Location + attr_reader receiver: Prism::node? + attr_reader parameters: ParametersNode? + attr_reader body: StatementsNode | BeginNode | nil + attr_reader locals: Array[Symbol] + attr_reader def_keyword_loc: Location + attr_reader operator_loc: Location? + attr_reader lparen_loc: Location? + attr_reader rparen_loc: Location? + attr_reader equal_loc: Location? + attr_reader end_keyword_loc: Location? + + def save_name_loc: (_Repository repository) -> void + def save_def_keyword_loc: (_Repository repository) -> void + def save_operator_loc: (_Repository repository) -> void + def save_lparen_loc: (_Repository repository) -> void + def save_rparen_loc: (_Repository repository) -> void + def save_equal_loc: (_Repository repository) -> void + def save_end_keyword_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Prism::node? receiver, ParametersNode? parameters, StatementsNode | BeginNode | nil body, Array[Symbol] locals, Location def_keyword_loc, Location? operator_loc, Location? lparen_loc, Location? rparen_loc, Location? equal_loc, Location? end_keyword_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?receiver: Prism::node?, ?parameters: ParametersNode?, ?body: StatementsNode | BeginNode | nil, ?locals: Array[Symbol], ?def_keyword_loc: Location, ?operator_loc: Location?, ?lparen_loc: Location?, ?rparen_loc: Location?, ?equal_loc: Location?, ?end_keyword_loc: Location?) -> DefNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, name_loc: Location, receiver: Prism::node?, parameters: ParametersNode?, body: StatementsNode | BeginNode | nil, locals: Array[Symbol], def_keyword_loc: Location, operator_loc: Location?, lparen_loc: Location?, rparen_loc: Location?, equal_loc: Location?, end_keyword_loc: Location? } + def def_keyword: () -> String + def operator: () -> String? + def lparen: () -> String? + def rparen: () -> String? + def equal: () -> String? + def end_keyword: () -> String? + def type: () -> :def_node + | ... + def self.type: () -> :def_node + end + + # Represents the use of the `defined?` keyword. + # + # defined?(a) + # ^^^^^^^^^^^ + class DefinedNode < Node + include _Node + + attr_reader lparen_loc: Location? + attr_reader value: Prism::node + attr_reader rparen_loc: Location? + attr_reader keyword_loc: Location + + def save_lparen_loc: (_Repository repository) -> void + def save_rparen_loc: (_Repository repository) -> void + def save_keyword_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location? lparen_loc, Prism::node value, Location? rparen_loc, Location keyword_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?lparen_loc: Location?, ?value: Prism::node, ?rparen_loc: Location?, ?keyword_loc: Location) -> DefinedNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, lparen_loc: Location?, value: Prism::node, rparen_loc: Location?, keyword_loc: Location } + def lparen: () -> String? + def rparen: () -> String? + def keyword: () -> String + def type: () -> :defined_node + | ... + def self.type: () -> :defined_node + end + + # Represents an `else` clause in a `case`, `if`, or `unless` statement. + # + # if a then b else c end + # ^^^^^^^^^^ + class ElseNode < Node + include _Node + + attr_reader else_keyword_loc: Location + attr_reader statements: StatementsNode? + attr_reader end_keyword_loc: Location? + + def save_else_keyword_loc: (_Repository repository) -> void + def save_end_keyword_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location else_keyword_loc, StatementsNode? statements, Location? end_keyword_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?else_keyword_loc: Location, ?statements: StatementsNode?, ?end_keyword_loc: Location?) -> ElseNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, else_keyword_loc: Location, statements: StatementsNode?, end_keyword_loc: Location? } + def else_keyword: () -> String + def end_keyword: () -> String? + def type: () -> :else_node + | ... + def self.type: () -> :else_node + end + + # Represents an interpolated set of statements. + # + # "foo #{bar}" + # ^^^^^^ + class EmbeddedStatementsNode < Node + include _Node + + attr_reader opening_loc: Location + attr_reader statements: StatementsNode? + attr_reader closing_loc: Location + + def save_opening_loc: (_Repository repository) -> void + def save_closing_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location opening_loc, StatementsNode? statements, Location closing_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?statements: StatementsNode?, ?closing_loc: Location) -> EmbeddedStatementsNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, opening_loc: Location, statements: StatementsNode?, closing_loc: Location } + def opening: () -> String + def closing: () -> String + def type: () -> :embedded_statements_node + | ... + def self.type: () -> :embedded_statements_node + end + + # Represents an interpolated variable. + # + # "foo #@bar" + # ^^^^^ + class EmbeddedVariableNode < Node + include _Node + + attr_reader operator_loc: Location + attr_reader variable: InstanceVariableReadNode | ClassVariableReadNode | GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode + + def save_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location operator_loc, InstanceVariableReadNode | ClassVariableReadNode | GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode variable) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?operator_loc: Location, ?variable: InstanceVariableReadNode | ClassVariableReadNode | GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode) -> EmbeddedVariableNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, operator_loc: Location, variable: InstanceVariableReadNode | ClassVariableReadNode | GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode } + def operator: () -> String + def type: () -> :embedded_variable_node + | ... + def self.type: () -> :embedded_variable_node + end + + # Represents an `ensure` clause in a `begin` statement. + # + # begin + # foo + # ensure + # ^^^^^^ + # bar + # end + class EnsureNode < Node + include _Node + + attr_reader ensure_keyword_loc: Location + attr_reader statements: StatementsNode? + attr_reader end_keyword_loc: Location + + def save_ensure_keyword_loc: (_Repository repository) -> void + def save_end_keyword_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location ensure_keyword_loc, StatementsNode? statements, Location end_keyword_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?ensure_keyword_loc: Location, ?statements: StatementsNode?, ?end_keyword_loc: Location) -> EnsureNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, ensure_keyword_loc: Location, statements: StatementsNode?, end_keyword_loc: Location } + def ensure_keyword: () -> String + def end_keyword: () -> String + def type: () -> :ensure_node + | ... + def self.type: () -> :ensure_node + end + + # Represents the use of the literal `false` keyword. + # + # false + # ^^^^^ + class FalseNode < Node + include _Node + + + + def initialize: (Source source, Integer node_id, Location location, Integer flags) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer) -> FalseNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location } + def type: () -> :false_node + | ... + def self.type: () -> :false_node + end + + # Represents a find pattern in pattern matching. + # + # foo in *bar, baz, *qux + # ^^^^^^^^^^^^^^^ + # + # foo in [*bar, baz, *qux] + # ^^^^^^^^^^^^^^^^^ + # + # foo in Foo(*bar, baz, *qux) + # ^^^^^^^^^^^^^^^^^^^^ + # + # foo => *bar, baz, *qux + # ^^^^^^^^^^^^^^^ + class FindPatternNode < Node + include _Node + + attr_reader constant: ConstantPathNode | ConstantReadNode | nil + attr_reader left: SplatNode + attr_reader requireds: Array[Prism::node] + attr_reader right: SplatNode | MissingNode + attr_reader opening_loc: Location? + attr_reader closing_loc: Location? + + def save_opening_loc: (_Repository repository) -> void + def save_closing_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, ConstantPathNode | ConstantReadNode | nil constant, SplatNode left, Array[Prism::node] requireds, SplatNode | MissingNode right, Location? opening_loc, Location? closing_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?constant: ConstantPathNode | ConstantReadNode | nil, ?left: SplatNode, ?requireds: Array[Prism::node], ?right: SplatNode | MissingNode, ?opening_loc: Location?, ?closing_loc: Location?) -> FindPatternNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, constant: ConstantPathNode | ConstantReadNode | nil, left: SplatNode, requireds: Array[Prism::node], right: SplatNode | MissingNode, opening_loc: Location?, closing_loc: Location? } + def opening: () -> String? + def closing: () -> String? + def type: () -> :find_pattern_node + | ... + def self.type: () -> :find_pattern_node + end + + # Represents the use of the `..` or `...` operators to create flip flops. + # + # baz if foo .. bar + # ^^^^^^^^^^ + class FlipFlopNode < Node + include _Node + + def exclude_end?: () -> bool + + attr_reader left: Prism::node? + attr_reader right: Prism::node? + attr_reader operator_loc: Location + + def save_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Prism::node? left, Prism::node? right, Location operator_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?left: Prism::node?, ?right: Prism::node?, ?operator_loc: Location) -> FlipFlopNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, left: Prism::node?, right: Prism::node?, operator_loc: Location } + def operator: () -> String + def type: () -> :flip_flop_node + | ... + def self.type: () -> :flip_flop_node + end + + # Represents a floating point number literal. + # + # 1.0 + # ^^^ + class FloatNode < Node + include _Node + + attr_reader value: Float + + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Float value) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?value: Float) -> FloatNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, value: Float } + def type: () -> :float_node + | ... + def self.type: () -> :float_node + end + + # Represents the use of the `for` keyword. + # + # for i in a end + # ^^^^^^^^^^^^^^ + class ForNode < Node + include _Node + + attr_reader index: LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | BackReferenceReadNode | NumberedReferenceReadNode | MissingNode + attr_reader collection: Prism::node + attr_reader statements: StatementsNode? + attr_reader for_keyword_loc: Location + attr_reader in_keyword_loc: Location + attr_reader do_keyword_loc: Location? + attr_reader end_keyword_loc: Location + + def save_for_keyword_loc: (_Repository repository) -> void + def save_in_keyword_loc: (_Repository repository) -> void + def save_do_keyword_loc: (_Repository repository) -> void + def save_end_keyword_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | BackReferenceReadNode | NumberedReferenceReadNode | MissingNode index, Prism::node collection, StatementsNode? statements, Location for_keyword_loc, Location in_keyword_loc, Location? do_keyword_loc, Location end_keyword_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?index: LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | BackReferenceReadNode | NumberedReferenceReadNode | MissingNode, ?collection: Prism::node, ?statements: StatementsNode?, ?for_keyword_loc: Location, ?in_keyword_loc: Location, ?do_keyword_loc: Location?, ?end_keyword_loc: Location) -> ForNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, index: LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | BackReferenceReadNode | NumberedReferenceReadNode | MissingNode, collection: Prism::node, statements: StatementsNode?, for_keyword_loc: Location, in_keyword_loc: Location, do_keyword_loc: Location?, end_keyword_loc: Location } + def for_keyword: () -> String + def in_keyword: () -> String + def do_keyword: () -> String? + def end_keyword: () -> String + def type: () -> :for_node + | ... + def self.type: () -> :for_node + end + + # Represents forwarding all arguments to this method to another method. + # + # def foo(...) + # bar(...) + # ^^^ + # end + class ForwardingArgumentsNode < Node + include _Node + + + + def initialize: (Source source, Integer node_id, Location location, Integer flags) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer) -> ForwardingArgumentsNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location } + def type: () -> :forwarding_arguments_node + | ... + def self.type: () -> :forwarding_arguments_node + end + + # Represents the use of the forwarding parameter in a method, block, or lambda declaration. + # + # def foo(...) + # ^^^ + # end + class ForwardingParameterNode < Node + include _Node + + + + def initialize: (Source source, Integer node_id, Location location, Integer flags) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer) -> ForwardingParameterNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location } + def type: () -> :forwarding_parameter_node + | ... + def self.type: () -> :forwarding_parameter_node + end + + # Represents the use of the `super` keyword without parentheses or arguments, but which might have a block. + # + # super + # ^^^^^ + # + # super { 123 } + # ^^^^^^^^^^^^^ + # + # If it has any other arguments, it would be a `SuperNode` instead. + class ForwardingSuperNode < Node + include _Node + + attr_reader block: BlockNode? + + + def initialize: (Source source, Integer node_id, Location location, Integer flags, BlockNode? block) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?block: BlockNode?) -> ForwardingSuperNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, block: BlockNode? } + def type: () -> :forwarding_super_node + | ... + def self.type: () -> :forwarding_super_node + end + + # Represents the use of the `&&=` operator for assignment to a global variable. + # + # $target &&= value + # ^^^^^^^^^^^^^^^^^ + class GlobalVariableAndWriteNode < Node + include _Node + + attr_reader name: Symbol + attr_reader name_loc: Location + attr_reader operator_loc: Location + attr_reader value: Prism::node + + def save_name_loc: (_Repository repository) -> void + def save_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Location operator_loc, Prism::node value) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> GlobalVariableAndWriteNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, name_loc: Location, operator_loc: Location, value: Prism::node } + def operator: () -> String + def type: () -> :global_variable_and_write_node + | ... + def self.type: () -> :global_variable_and_write_node + end + + # Represents assigning to a global variable using an operator that isn't `=`. + # + # $target += value + # ^^^^^^^^^^^^^^^^ + class GlobalVariableOperatorWriteNode < Node + include _Node + + attr_reader name: Symbol + attr_reader name_loc: Location + attr_reader binary_operator_loc: Location + attr_reader value: Prism::node + attr_reader binary_operator: Symbol + + def save_name_loc: (_Repository repository) -> void + def save_binary_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Location binary_operator_loc, Prism::node value, Symbol binary_operator) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?binary_operator_loc: Location, ?value: Prism::node, ?binary_operator: Symbol) -> GlobalVariableOperatorWriteNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, name_loc: Location, binary_operator_loc: Location, value: Prism::node, binary_operator: Symbol } + def type: () -> :global_variable_operator_write_node + | ... + def self.type: () -> :global_variable_operator_write_node + end + + # Represents the use of the `||=` operator for assignment to a global variable. + # + # $target ||= value + # ^^^^^^^^^^^^^^^^^ + class GlobalVariableOrWriteNode < Node + include _Node + + attr_reader name: Symbol + attr_reader name_loc: Location + attr_reader operator_loc: Location + attr_reader value: Prism::node + + def save_name_loc: (_Repository repository) -> void + def save_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Location operator_loc, Prism::node value) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> GlobalVariableOrWriteNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, name_loc: Location, operator_loc: Location, value: Prism::node } + def operator: () -> String + def type: () -> :global_variable_or_write_node + | ... + def self.type: () -> :global_variable_or_write_node + end + + # Represents referencing a global variable. + # + # $foo + # ^^^^ + class GlobalVariableReadNode < Node + include _Node + + attr_reader name: Symbol + + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> GlobalVariableReadNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol } + def type: () -> :global_variable_read_node + | ... + def self.type: () -> :global_variable_read_node + end + + # Represents writing to a global variable in a context that doesn't have an explicit value. + # + # $foo, $bar = baz + # ^^^^ ^^^^ + class GlobalVariableTargetNode < Node + include _Node + + attr_reader name: Symbol + + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> GlobalVariableTargetNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol } + def type: () -> :global_variable_target_node + | ... + def self.type: () -> :global_variable_target_node + end + + # Represents writing to a global variable. + # + # $foo = 1 + # ^^^^^^^^ + class GlobalVariableWriteNode < Node + include _Node + + attr_reader name: Symbol + attr_reader name_loc: Location + attr_reader value: Prism::node + attr_reader operator_loc: Location + + def save_name_loc: (_Repository repository) -> void + def save_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Prism::node value, Location operator_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?value: Prism::node, ?operator_loc: Location) -> GlobalVariableWriteNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, name_loc: Location, value: Prism::node, operator_loc: Location } + def operator: () -> String + def type: () -> :global_variable_write_node + | ... + def self.type: () -> :global_variable_write_node + end + + # Represents a hash literal. + # + # { a => b } + # ^^^^^^^^^^ + class HashNode < Node + include _Node + + attr_reader opening_loc: Location + attr_reader elements: Array[AssocNode | AssocSplatNode] + attr_reader closing_loc: Location + + def save_opening_loc: (_Repository repository) -> void + def save_closing_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location opening_loc, Array[AssocNode | AssocSplatNode] elements, Location closing_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?elements: Array[AssocNode | AssocSplatNode], ?closing_loc: Location) -> HashNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, opening_loc: Location, elements: Array[AssocNode | AssocSplatNode], closing_loc: Location } + def opening: () -> String + def closing: () -> String + def type: () -> :hash_node + | ... + def self.type: () -> :hash_node + end + + # Represents a hash pattern in pattern matching. + # + # foo => { a: 1, b: 2 } + # ^^^^^^^^^^^^^^ + # + # foo => { a: 1, b: 2, **c } + # ^^^^^^^^^^^^^^^^^^^ + # + # foo => Bar[a: 1, b: 2] + # ^^^^^^^^^^^^^^^ + # + # foo in { a: 1, b: 2 } + # ^^^^^^^^^^^^^^ + class HashPatternNode < Node + include _Node + + attr_reader constant: ConstantPathNode | ConstantReadNode | nil + attr_reader elements: Array[AssocNode] + attr_reader rest: AssocSplatNode | NoKeywordsParameterNode | nil + attr_reader opening_loc: Location? + attr_reader closing_loc: Location? + + def save_opening_loc: (_Repository repository) -> void + def save_closing_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, ConstantPathNode | ConstantReadNode | nil constant, Array[AssocNode] elements, AssocSplatNode | NoKeywordsParameterNode | nil rest, Location? opening_loc, Location? closing_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?constant: ConstantPathNode | ConstantReadNode | nil, ?elements: Array[AssocNode], ?rest: AssocSplatNode | NoKeywordsParameterNode | nil, ?opening_loc: Location?, ?closing_loc: Location?) -> HashPatternNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, constant: ConstantPathNode | ConstantReadNode | nil, elements: Array[AssocNode], rest: AssocSplatNode | NoKeywordsParameterNode | nil, opening_loc: Location?, closing_loc: Location? } + def opening: () -> String? + def closing: () -> String? + def type: () -> :hash_pattern_node + | ... + def self.type: () -> :hash_pattern_node + end + + # 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 + # ^^^^^^^^^^^^^^^ + class IfNode < Node + include _Node + + attr_reader if_keyword_loc: Location? + attr_reader predicate: Prism::node + attr_reader then_keyword_loc: Location? + attr_reader statements: StatementsNode? + attr_reader subsequent: ElseNode | IfNode | nil + attr_reader end_keyword_loc: Location? + + def save_if_keyword_loc: (_Repository repository) -> void + def save_then_keyword_loc: (_Repository repository) -> void + def save_end_keyword_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location? if_keyword_loc, Prism::node predicate, Location? then_keyword_loc, StatementsNode? statements, ElseNode | IfNode | nil subsequent, Location? end_keyword_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?if_keyword_loc: Location?, ?predicate: Prism::node, ?then_keyword_loc: Location?, ?statements: StatementsNode?, ?subsequent: ElseNode | IfNode | nil, ?end_keyword_loc: Location?) -> IfNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, if_keyword_loc: Location?, predicate: Prism::node, then_keyword_loc: Location?, statements: StatementsNode?, subsequent: ElseNode | IfNode | nil, end_keyword_loc: Location? } + def if_keyword: () -> String? + def then_keyword: () -> String? + def end_keyword: () -> String? + def type: () -> :if_node + | ... + def self.type: () -> :if_node + end + + # Represents an imaginary number literal. + # + # 1.0i + # ^^^^ + class ImaginaryNode < Node + include _Node + + attr_reader numeric: FloatNode | IntegerNode | RationalNode + + + def initialize: (Source source, Integer node_id, Location location, Integer flags, FloatNode | IntegerNode | RationalNode numeric) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?numeric: FloatNode | IntegerNode | RationalNode) -> ImaginaryNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, numeric: FloatNode | IntegerNode | RationalNode } + def type: () -> :imaginary_node + | ... + def self.type: () -> :imaginary_node + end + + # 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: } + # ^^^^ + class ImplicitNode < Node + include _Node + + attr_reader value: LocalVariableReadNode | CallNode | ConstantReadNode | LocalVariableTargetNode + + + def initialize: (Source source, Integer node_id, Location location, Integer flags, LocalVariableReadNode | CallNode | ConstantReadNode | LocalVariableTargetNode value) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?value: LocalVariableReadNode | CallNode | ConstantReadNode | LocalVariableTargetNode) -> ImplicitNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, value: LocalVariableReadNode | CallNode | ConstantReadNode | LocalVariableTargetNode } + def type: () -> :implicit_node + | ... + def self.type: () -> :implicit_node + end + + # Represents using a trailing comma to indicate an implicit rest parameter. + # + # foo { |bar,| } + # ^ + # + # foo in [bar,] + # ^ + # + # for foo, in bar do end + # ^ + # + # foo, = bar + # ^ + class ImplicitRestNode < Node + include _Node + + + + def initialize: (Source source, Integer node_id, Location location, Integer flags) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer) -> ImplicitRestNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location } + def type: () -> :implicit_rest_node + | ... + def self.type: () -> :implicit_rest_node + end + + # Represents the use of the `in` keyword in a case statement. + # + # case a; in b then c end + # ^^^^^^^^^^^ + class InNode < Node + include _Node + + attr_reader pattern: Prism::node + attr_reader statements: StatementsNode? + attr_reader in_loc: Location + attr_reader then_loc: Location? + + def save_in_loc: (_Repository repository) -> void + def save_then_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Prism::node pattern, StatementsNode? statements, Location in_loc, Location? then_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?pattern: Prism::node, ?statements: StatementsNode?, ?in_loc: Location, ?then_loc: Location?) -> InNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, pattern: Prism::node, statements: StatementsNode?, in_loc: Location, then_loc: Location? } + def in: () -> String + def then: () -> String? + def type: () -> :in_node + | ... + def self.type: () -> :in_node + end + + # Represents the use of the `&&=` operator on a call to the `[]` method. + # + # foo.bar[baz] &&= value + # ^^^^^^^^^^^^^^^^^^^^^^ + class IndexAndWriteNode < Node + include _Node + + def safe_navigation?: () -> bool + def variable_call?: () -> bool + def attribute_write?: () -> bool + def ignore_visibility?: () -> bool + + attr_reader receiver: Prism::node? + attr_reader call_operator_loc: Location? + attr_reader opening_loc: Location + attr_reader arguments: ArgumentsNode? + attr_reader closing_loc: Location + attr_reader block: BlockArgumentNode? + attr_reader operator_loc: Location + attr_reader value: Prism::node + + def save_call_operator_loc: (_Repository repository) -> void + def save_opening_loc: (_Repository repository) -> void + def save_closing_loc: (_Repository repository) -> void + def save_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Prism::node? receiver, Location? call_operator_loc, Location opening_loc, ArgumentsNode? arguments, Location closing_loc, BlockArgumentNode? block, Location operator_loc, Prism::node value) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?receiver: Prism::node?, ?call_operator_loc: Location?, ?opening_loc: Location, ?arguments: ArgumentsNode?, ?closing_loc: Location, ?block: BlockArgumentNode?, ?operator_loc: Location, ?value: Prism::node) -> IndexAndWriteNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, receiver: Prism::node?, call_operator_loc: Location?, opening_loc: Location, arguments: ArgumentsNode?, closing_loc: Location, block: BlockArgumentNode?, operator_loc: Location, value: Prism::node } + def call_operator: () -> String? + def opening: () -> String + def closing: () -> String + def operator: () -> String + def type: () -> :index_and_write_node + | ... + def self.type: () -> :index_and_write_node + end + + # Represents the use of an assignment operator on a call to `[]`. + # + # foo.bar[baz] += value + # ^^^^^^^^^^^^^^^^^^^^^ + class IndexOperatorWriteNode < Node + include _Node + + def safe_navigation?: () -> bool + def variable_call?: () -> bool + def attribute_write?: () -> bool + def ignore_visibility?: () -> bool + + attr_reader receiver: Prism::node? + attr_reader call_operator_loc: Location? + attr_reader opening_loc: Location + attr_reader arguments: ArgumentsNode? + attr_reader closing_loc: Location + attr_reader block: BlockArgumentNode? + attr_reader binary_operator: Symbol + attr_reader binary_operator_loc: Location + attr_reader value: Prism::node + + def save_call_operator_loc: (_Repository repository) -> void + def save_opening_loc: (_Repository repository) -> void + def save_closing_loc: (_Repository repository) -> void + def save_binary_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Prism::node? receiver, Location? call_operator_loc, Location opening_loc, ArgumentsNode? arguments, Location closing_loc, BlockArgumentNode? block, Symbol binary_operator, Location binary_operator_loc, Prism::node value) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?receiver: Prism::node?, ?call_operator_loc: Location?, ?opening_loc: Location, ?arguments: ArgumentsNode?, ?closing_loc: Location, ?block: BlockArgumentNode?, ?binary_operator: Symbol, ?binary_operator_loc: Location, ?value: Prism::node) -> IndexOperatorWriteNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, receiver: Prism::node?, call_operator_loc: Location?, opening_loc: Location, arguments: ArgumentsNode?, closing_loc: Location, block: BlockArgumentNode?, binary_operator: Symbol, binary_operator_loc: Location, value: Prism::node } + def call_operator: () -> String? + def opening: () -> String + def closing: () -> String + def type: () -> :index_operator_write_node + | ... + def self.type: () -> :index_operator_write_node + end + + # Represents the use of the `||=` operator on a call to `[]`. + # + # foo.bar[baz] ||= value + # ^^^^^^^^^^^^^^^^^^^^^^ + class IndexOrWriteNode < Node + include _Node + + def safe_navigation?: () -> bool + def variable_call?: () -> bool + def attribute_write?: () -> bool + def ignore_visibility?: () -> bool + + attr_reader receiver: Prism::node? + attr_reader call_operator_loc: Location? + attr_reader opening_loc: Location + attr_reader arguments: ArgumentsNode? + attr_reader closing_loc: Location + attr_reader block: BlockArgumentNode? + attr_reader operator_loc: Location + attr_reader value: Prism::node + + def save_call_operator_loc: (_Repository repository) -> void + def save_opening_loc: (_Repository repository) -> void + def save_closing_loc: (_Repository repository) -> void + def save_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Prism::node? receiver, Location? call_operator_loc, Location opening_loc, ArgumentsNode? arguments, Location closing_loc, BlockArgumentNode? block, Location operator_loc, Prism::node value) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?receiver: Prism::node?, ?call_operator_loc: Location?, ?opening_loc: Location, ?arguments: ArgumentsNode?, ?closing_loc: Location, ?block: BlockArgumentNode?, ?operator_loc: Location, ?value: Prism::node) -> IndexOrWriteNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, receiver: Prism::node?, call_operator_loc: Location?, opening_loc: Location, arguments: ArgumentsNode?, closing_loc: Location, block: BlockArgumentNode?, operator_loc: Location, value: Prism::node } + def call_operator: () -> String? + def opening: () -> String + def closing: () -> String + def operator: () -> String + def type: () -> :index_or_write_node + | ... + def self.type: () -> :index_or_write_node + end + + # Represents assigning to an index. + # + # foo[bar], = 1 + # ^^^^^^^^ + # + # begin + # rescue => foo[bar] + # ^^^^^^^^ + # end + # + # for foo[bar] in baz do end + # ^^^^^^^^ + class IndexTargetNode < Node + include _Node + + def safe_navigation?: () -> bool + def variable_call?: () -> bool + def attribute_write?: () -> bool + def ignore_visibility?: () -> bool + + attr_reader receiver: Prism::node + attr_reader opening_loc: Location + attr_reader arguments: ArgumentsNode? + attr_reader closing_loc: Location + attr_reader block: BlockArgumentNode? + + def save_opening_loc: (_Repository repository) -> void + def save_closing_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Prism::node receiver, Location opening_loc, ArgumentsNode? arguments, Location closing_loc, BlockArgumentNode? block) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?receiver: Prism::node, ?opening_loc: Location, ?arguments: ArgumentsNode?, ?closing_loc: Location, ?block: BlockArgumentNode?) -> IndexTargetNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, receiver: Prism::node, opening_loc: Location, arguments: ArgumentsNode?, closing_loc: Location, block: BlockArgumentNode? } + def opening: () -> String + def closing: () -> String + def type: () -> :index_target_node + | ... + def self.type: () -> :index_target_node + end + + # Represents the use of the `&&=` operator for assignment to an instance variable. + # + # @target &&= value + # ^^^^^^^^^^^^^^^^^ + class InstanceVariableAndWriteNode < Node + include _Node + + attr_reader name: Symbol + attr_reader name_loc: Location + attr_reader operator_loc: Location + attr_reader value: Prism::node + + def save_name_loc: (_Repository repository) -> void + def save_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Location operator_loc, Prism::node value) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> InstanceVariableAndWriteNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, name_loc: Location, operator_loc: Location, value: Prism::node } + def operator: () -> String + def type: () -> :instance_variable_and_write_node + | ... + def self.type: () -> :instance_variable_and_write_node + end + + # Represents assigning to an instance variable using an operator that isn't `=`. + # + # @target += value + # ^^^^^^^^^^^^^^^^ + class InstanceVariableOperatorWriteNode < Node + include _Node + + attr_reader name: Symbol + attr_reader name_loc: Location + attr_reader binary_operator_loc: Location + attr_reader value: Prism::node + attr_reader binary_operator: Symbol + + def save_name_loc: (_Repository repository) -> void + def save_binary_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Location binary_operator_loc, Prism::node value, Symbol binary_operator) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?binary_operator_loc: Location, ?value: Prism::node, ?binary_operator: Symbol) -> InstanceVariableOperatorWriteNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, name_loc: Location, binary_operator_loc: Location, value: Prism::node, binary_operator: Symbol } + def type: () -> :instance_variable_operator_write_node + | ... + def self.type: () -> :instance_variable_operator_write_node + end + + # Represents the use of the `||=` operator for assignment to an instance variable. + # + # @target ||= value + # ^^^^^^^^^^^^^^^^^ + class InstanceVariableOrWriteNode < Node + include _Node + + attr_reader name: Symbol + attr_reader name_loc: Location + attr_reader operator_loc: Location + attr_reader value: Prism::node + + def save_name_loc: (_Repository repository) -> void + def save_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Location operator_loc, Prism::node value) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> InstanceVariableOrWriteNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, name_loc: Location, operator_loc: Location, value: Prism::node } + def operator: () -> String + def type: () -> :instance_variable_or_write_node + | ... + def self.type: () -> :instance_variable_or_write_node + end + + # Represents referencing an instance variable. + # + # @foo + # ^^^^ + class InstanceVariableReadNode < Node + include _Node + + attr_reader name: Symbol + + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> InstanceVariableReadNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol } + def type: () -> :instance_variable_read_node + | ... + def self.type: () -> :instance_variable_read_node + end + + # Represents writing to an instance variable in a context that doesn't have an explicit value. + # + # @foo, @bar = baz + # ^^^^ ^^^^ + class InstanceVariableTargetNode < Node + include _Node + + attr_reader name: Symbol + + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> InstanceVariableTargetNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol } + def type: () -> :instance_variable_target_node + | ... + def self.type: () -> :instance_variable_target_node + end + + # Represents writing to an instance variable. + # + # @foo = 1 + # ^^^^^^^^ + class InstanceVariableWriteNode < Node + include _Node + + attr_reader name: Symbol + attr_reader name_loc: Location + attr_reader value: Prism::node + attr_reader operator_loc: Location + + def save_name_loc: (_Repository repository) -> void + def save_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Prism::node value, Location operator_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?value: Prism::node, ?operator_loc: Location) -> InstanceVariableWriteNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, name_loc: Location, value: Prism::node, operator_loc: Location } + def operator: () -> String + def type: () -> :instance_variable_write_node + | ... + def self.type: () -> :instance_variable_write_node + end + + # Represents an integer number literal. + # + # 1 + # ^ + class IntegerNode < Node + include _Node + + def binary?: () -> bool + def decimal?: () -> bool + def octal?: () -> bool + def hexadecimal?: () -> bool + + attr_reader value: Integer + + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Integer value) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?value: Integer) -> IntegerNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, value: Integer } + def type: () -> :integer_node + | ... + def self.type: () -> :integer_node + end + + # 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 + # ^^^^^^^^^^^^^^^^ + class InterpolatedMatchLastLineNode < Node + include _Node + + def ignore_case?: () -> bool + def extended?: () -> bool + def multi_line?: () -> bool + def once?: () -> bool + def euc_jp?: () -> bool + def ascii_8bit?: () -> bool + def windows_31j?: () -> bool + def utf_8?: () -> bool + def forced_utf8_encoding?: () -> bool + def forced_binary_encoding?: () -> bool + def forced_us_ascii_encoding?: () -> bool + + attr_reader opening_loc: Location + attr_reader parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode] + attr_reader closing_loc: Location + + def save_opening_loc: (_Repository repository) -> void + def save_closing_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location opening_loc, Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode] parts, Location closing_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode], ?closing_loc: Location) -> InterpolatedMatchLastLineNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, opening_loc: Location, parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode], closing_loc: Location } + def opening: () -> String + def closing: () -> String + def type: () -> :interpolated_match_last_line_node + | ... + def self.type: () -> :interpolated_match_last_line_node + end + + # Represents a regular expression literal that contains interpolation. + # + # /foo #{bar} baz/ + # ^^^^^^^^^^^^^^^^ + class InterpolatedRegularExpressionNode < Node + include _Node + + def ignore_case?: () -> bool + def extended?: () -> bool + def multi_line?: () -> bool + def once?: () -> bool + def euc_jp?: () -> bool + def ascii_8bit?: () -> bool + def windows_31j?: () -> bool + def utf_8?: () -> bool + def forced_utf8_encoding?: () -> bool + def forced_binary_encoding?: () -> bool + def forced_us_ascii_encoding?: () -> bool + + attr_reader opening_loc: Location + attr_reader parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode] + attr_reader closing_loc: Location + + def save_opening_loc: (_Repository repository) -> void + def save_closing_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location opening_loc, Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode] parts, Location closing_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode], ?closing_loc: Location) -> InterpolatedRegularExpressionNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, opening_loc: Location, parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode], closing_loc: Location } + def opening: () -> String + def closing: () -> String + def type: () -> :interpolated_regular_expression_node + | ... + def self.type: () -> :interpolated_regular_expression_node + end + + # Represents a string literal that contains interpolation. + # + # "foo #{bar} baz" + # ^^^^^^^^^^^^^^^^ + class InterpolatedStringNode < Node + include _Node + + def frozen?: () -> bool + def mutable?: () -> bool + + attr_reader opening_loc: Location? + attr_reader parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode | InterpolatedStringNode | XStringNode | InterpolatedXStringNode | SymbolNode | InterpolatedSymbolNode] + attr_reader closing_loc: Location? + + def save_opening_loc: (_Repository repository) -> void + def save_closing_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location? opening_loc, Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode | InterpolatedStringNode | XStringNode | InterpolatedXStringNode | SymbolNode | InterpolatedSymbolNode] parts, Location? closing_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location?, ?parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode | InterpolatedStringNode | XStringNode | InterpolatedXStringNode | SymbolNode | InterpolatedSymbolNode], ?closing_loc: Location?) -> InterpolatedStringNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, opening_loc: Location?, parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode | InterpolatedStringNode | XStringNode | InterpolatedXStringNode | SymbolNode | InterpolatedSymbolNode], closing_loc: Location? } + def opening: () -> String? + def closing: () -> String? + def type: () -> :interpolated_string_node + | ... + def self.type: () -> :interpolated_string_node + end + + # Represents a symbol literal that contains interpolation. + # + # :"foo #{bar} baz" + # ^^^^^^^^^^^^^^^^^ + class InterpolatedSymbolNode < Node + include _Node + + attr_reader opening_loc: Location? + attr_reader parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode] + attr_reader closing_loc: Location? + + def save_opening_loc: (_Repository repository) -> void + def save_closing_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location? opening_loc, Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode] parts, Location? closing_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location?, ?parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode], ?closing_loc: Location?) -> InterpolatedSymbolNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, opening_loc: Location?, parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode], closing_loc: Location? } + def opening: () -> String? + def closing: () -> String? + def type: () -> :interpolated_symbol_node + | ... + def self.type: () -> :interpolated_symbol_node + end + + # Represents an xstring literal that contains interpolation. + # + # `foo #{bar} baz` + # ^^^^^^^^^^^^^^^^ + class InterpolatedXStringNode < Node + include _Node + + attr_reader opening_loc: Location + attr_reader parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode] + attr_reader closing_loc: Location + + def save_opening_loc: (_Repository repository) -> void + def save_closing_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location opening_loc, Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode] parts, Location closing_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode], ?closing_loc: Location) -> InterpolatedXStringNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, opening_loc: Location, parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode], closing_loc: Location } + def opening: () -> String + def closing: () -> String + def type: () -> :interpolated_x_string_node + | ... + def self.type: () -> :interpolated_x_string_node + end + + # Represents reading from the implicit `it` local variable. + # + # -> { it } + # ^^ + class ItLocalVariableReadNode < Node + include _Node + + + + def initialize: (Source source, Integer node_id, Location location, Integer flags) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer) -> ItLocalVariableReadNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location } + def type: () -> :it_local_variable_read_node + | ... + def self.type: () -> :it_local_variable_read_node + end + + # Represents an implicit set of parameters through the use of the `it` keyword within a block or lambda. + # + # -> { it + it } + # ^^^^^^^^^^^^^^ + class ItParametersNode < Node + include _Node + + + + def initialize: (Source source, Integer node_id, Location location, Integer flags) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer) -> ItParametersNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location } + def type: () -> :it_parameters_node + | ... + def self.type: () -> :it_parameters_node + end + + # Represents a hash literal without opening and closing braces. + # + # foo(a: b) + # ^^^^ + class KeywordHashNode < Node + include _Node + + def symbol_keys?: () -> bool + + attr_reader elements: Array[AssocNode | AssocSplatNode] + + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Array[AssocNode | AssocSplatNode] elements) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?elements: Array[AssocNode | AssocSplatNode]) -> KeywordHashNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, elements: Array[AssocNode | AssocSplatNode] } + def type: () -> :keyword_hash_node + | ... + def self.type: () -> :keyword_hash_node + end + + # Represents a keyword rest parameter to a method, block, or lambda definition. + # + # def a(**b) + # ^^^ + # end + class KeywordRestParameterNode < Node + include _Node + + def repeated_parameter?: () -> bool + + attr_reader name: Symbol? + attr_reader name_loc: Location? + attr_reader operator_loc: Location + + def save_name_loc: (_Repository repository) -> void + def save_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol? name, Location? name_loc, Location operator_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol?, ?name_loc: Location?, ?operator_loc: Location) -> KeywordRestParameterNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol?, name_loc: Location?, operator_loc: Location } + def operator: () -> String + def type: () -> :keyword_rest_parameter_node + | ... + def self.type: () -> :keyword_rest_parameter_node + end + + # Represents using a lambda literal (not the lambda method call). + # + # ->(value) { value * 2 } + # ^^^^^^^^^^^^^^^^^^^^^^^ + class LambdaNode < Node + include _Node + + attr_reader locals: Array[Symbol] + attr_reader operator_loc: Location + attr_reader opening_loc: Location + attr_reader closing_loc: Location + attr_reader parameters: BlockParametersNode | NumberedParametersNode | ItParametersNode | nil + attr_reader body: StatementsNode | BeginNode | nil + + def save_operator_loc: (_Repository repository) -> void + def save_opening_loc: (_Repository repository) -> void + def save_closing_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Array[Symbol] locals, Location operator_loc, Location opening_loc, Location closing_loc, BlockParametersNode | NumberedParametersNode | ItParametersNode | nil parameters, StatementsNode | BeginNode | nil body) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?locals: Array[Symbol], ?operator_loc: Location, ?opening_loc: Location, ?closing_loc: Location, ?parameters: BlockParametersNode | NumberedParametersNode | ItParametersNode | nil, ?body: StatementsNode | BeginNode | nil) -> LambdaNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, locals: Array[Symbol], operator_loc: Location, opening_loc: Location, closing_loc: Location, parameters: BlockParametersNode | NumberedParametersNode | ItParametersNode | nil, body: StatementsNode | BeginNode | nil } + def operator: () -> String + def opening: () -> String + def closing: () -> String + def type: () -> :lambda_node + | ... + def self.type: () -> :lambda_node + end + + # Represents the use of the `&&=` operator for assignment to a local variable. + # + # target &&= value + # ^^^^^^^^^^^^^^^^ + class LocalVariableAndWriteNode < Node + include _Node + + attr_reader name_loc: Location + attr_reader operator_loc: Location + attr_reader value: Prism::node + attr_reader name: Symbol + attr_reader depth: Integer + + def save_name_loc: (_Repository repository) -> void + def save_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location name_loc, Location operator_loc, Prism::node value, Symbol name, Integer depth) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node, ?name: Symbol, ?depth: Integer) -> LocalVariableAndWriteNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name_loc: Location, operator_loc: Location, value: Prism::node, name: Symbol, depth: Integer } + def operator: () -> String + def type: () -> :local_variable_and_write_node + | ... + def self.type: () -> :local_variable_and_write_node + end + + # Represents assigning to a local variable using an operator that isn't `=`. + # + # target += value + # ^^^^^^^^^^^^^^^ + class LocalVariableOperatorWriteNode < Node + include _Node + + attr_reader name_loc: Location + attr_reader binary_operator_loc: Location + attr_reader value: Prism::node + attr_reader name: Symbol + attr_reader binary_operator: Symbol + attr_reader depth: Integer + + def save_name_loc: (_Repository repository) -> void + def save_binary_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location name_loc, Location binary_operator_loc, Prism::node value, Symbol name, Symbol binary_operator, Integer depth) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name_loc: Location, ?binary_operator_loc: Location, ?value: Prism::node, ?name: Symbol, ?binary_operator: Symbol, ?depth: Integer) -> LocalVariableOperatorWriteNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name_loc: Location, binary_operator_loc: Location, value: Prism::node, name: Symbol, binary_operator: Symbol, depth: Integer } + def type: () -> :local_variable_operator_write_node + | ... + def self.type: () -> :local_variable_operator_write_node + end + + # Represents the use of the `||=` operator for assignment to a local variable. + # + # target ||= value + # ^^^^^^^^^^^^^^^^ + class LocalVariableOrWriteNode < Node + include _Node + + attr_reader name_loc: Location + attr_reader operator_loc: Location + attr_reader value: Prism::node + attr_reader name: Symbol + attr_reader depth: Integer + + def save_name_loc: (_Repository repository) -> void + def save_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location name_loc, Location operator_loc, Prism::node value, Symbol name, Integer depth) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node, ?name: Symbol, ?depth: Integer) -> LocalVariableOrWriteNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name_loc: Location, operator_loc: Location, value: Prism::node, name: Symbol, depth: Integer } + def operator: () -> String + def type: () -> :local_variable_or_write_node + | ... + def self.type: () -> :local_variable_or_write_node + end + + # 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 + # ^^^ + class LocalVariableReadNode < Node + include _Node + + attr_reader name: Symbol + attr_reader depth: Integer + + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name, Integer depth) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?depth: Integer) -> LocalVariableReadNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, depth: Integer } + def type: () -> :local_variable_read_node + | ... + def self.type: () -> :local_variable_read_node + end + + # Represents writing to a local variable in a context that doesn't have an explicit value. + # + # foo, bar = baz + # ^^^ ^^^ + # + # foo => baz + # ^^^ + class LocalVariableTargetNode < Node + include _Node + + attr_reader name: Symbol + attr_reader depth: Integer + + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name, Integer depth) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?depth: Integer) -> LocalVariableTargetNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, depth: Integer } + def type: () -> :local_variable_target_node + | ... + def self.type: () -> :local_variable_target_node + end + + # Represents writing to a local variable. + # + # foo = 1 + # ^^^^^^^ + class LocalVariableWriteNode < Node + include _Node + + attr_reader name: Symbol + attr_reader depth: Integer + attr_reader name_loc: Location + attr_reader value: Prism::node + attr_reader operator_loc: Location + + def save_name_loc: (_Repository repository) -> void + def save_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name, Integer depth, Location name_loc, Prism::node value, Location operator_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?depth: Integer, ?name_loc: Location, ?value: Prism::node, ?operator_loc: Location) -> LocalVariableWriteNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, depth: Integer, name_loc: Location, value: Prism::node, operator_loc: Location } + def operator: () -> String + def type: () -> :local_variable_write_node + | ... + def self.type: () -> :local_variable_write_node + end + + # 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 + # ^^^^^^ + class MatchLastLineNode < Node + include _Node + + def ignore_case?: () -> bool + def extended?: () -> bool + def multi_line?: () -> bool + def once?: () -> bool + def euc_jp?: () -> bool + def ascii_8bit?: () -> bool + def windows_31j?: () -> bool + def utf_8?: () -> bool + def forced_utf8_encoding?: () -> bool + def forced_binary_encoding?: () -> bool + def forced_us_ascii_encoding?: () -> bool + + attr_reader opening_loc: Location + attr_reader content_loc: Location + attr_reader closing_loc: Location + attr_reader unescaped: String + + def save_opening_loc: (_Repository repository) -> void + def save_content_loc: (_Repository repository) -> void + def save_closing_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location opening_loc, Location content_loc, Location closing_loc, String unescaped) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?content_loc: Location, ?closing_loc: Location, ?unescaped: String) -> MatchLastLineNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, opening_loc: Location, content_loc: Location, closing_loc: Location, unescaped: String } + def opening: () -> String + def content: () -> String + def closing: () -> String + def type: () -> :match_last_line_node + | ... + def self.type: () -> :match_last_line_node + end + + # Represents the use of the modifier `in` operator. + # + # foo in bar + # ^^^^^^^^^^ + class MatchPredicateNode < Node + include _Node + + attr_reader value: Prism::node + attr_reader pattern: Prism::node + attr_reader operator_loc: Location + + def save_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Prism::node value, Prism::node pattern, Location operator_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?value: Prism::node, ?pattern: Prism::node, ?operator_loc: Location) -> MatchPredicateNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, value: Prism::node, pattern: Prism::node, operator_loc: Location } + def operator: () -> String + def type: () -> :match_predicate_node + | ... + def self.type: () -> :match_predicate_node + end + + # Represents the use of the `=>` operator. + # + # foo => bar + # ^^^^^^^^^^ + class MatchRequiredNode < Node + include _Node + + attr_reader value: Prism::node + attr_reader pattern: Prism::node + attr_reader operator_loc: Location + + def save_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Prism::node value, Prism::node pattern, Location operator_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?value: Prism::node, ?pattern: Prism::node, ?operator_loc: Location) -> MatchRequiredNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, value: Prism::node, pattern: Prism::node, operator_loc: Location } + def operator: () -> String + def type: () -> :match_required_node + | ... + def self.type: () -> :match_required_node + end + + # Represents writing local variables using a regular expression match with named capture groups. + # + # /(?bar)/ =~ baz + # ^^^^^^^^^^^^^^^^^^^^ + class MatchWriteNode < Node + include _Node + + attr_reader call: CallNode + attr_reader targets: Array[LocalVariableTargetNode] + + + def initialize: (Source source, Integer node_id, Location location, Integer flags, CallNode call, Array[LocalVariableTargetNode] targets) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?call: CallNode, ?targets: Array[LocalVariableTargetNode]) -> MatchWriteNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, call: CallNode, targets: Array[LocalVariableTargetNode] } + def type: () -> :match_write_node + | ... + def self.type: () -> :match_write_node + end + + # Represents a node that is missing from the source and results in a syntax error. + class MissingNode < Node + include _Node + + + + def initialize: (Source source, Integer node_id, Location location, Integer flags) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer) -> MissingNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location } + def type: () -> :missing_node + | ... + def self.type: () -> :missing_node + end + + # Represents a module declaration involving the `module` keyword. + # + # module Foo end + # ^^^^^^^^^^^^^^ + class ModuleNode < Node + include _Node + + attr_reader locals: Array[Symbol] + attr_reader module_keyword_loc: Location + attr_reader constant_path: ConstantReadNode | ConstantPathNode | MissingNode + attr_reader body: StatementsNode | BeginNode | nil + attr_reader end_keyword_loc: Location + attr_reader name: Symbol + + def save_module_keyword_loc: (_Repository repository) -> void + def save_end_keyword_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Array[Symbol] locals, Location module_keyword_loc, ConstantReadNode | ConstantPathNode | MissingNode constant_path, StatementsNode | BeginNode | nil body, Location end_keyword_loc, Symbol name) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?locals: Array[Symbol], ?module_keyword_loc: Location, ?constant_path: ConstantReadNode | ConstantPathNode | MissingNode, ?body: StatementsNode | BeginNode | nil, ?end_keyword_loc: Location, ?name: Symbol) -> ModuleNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, locals: Array[Symbol], module_keyword_loc: Location, constant_path: ConstantReadNode | ConstantPathNode | MissingNode, body: StatementsNode | BeginNode | nil, end_keyword_loc: Location, name: Symbol } + def module_keyword: () -> String + def end_keyword: () -> String + def type: () -> :module_node + | ... + def self.type: () -> :module_node + end + + # Represents a multi-target expression. + # + # a, (b, c) = 1, 2, 3 + # ^^^^^^ + # + # This can be a part of `MultiWriteNode` as above, or the target of a `for` loop + # + # for a, b in [[1, 2], [3, 4]] + # ^^^^ + class MultiTargetNode < Node + include _Node + + attr_reader lefts: Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | RequiredParameterNode | BackReferenceReadNode | NumberedReferenceReadNode] + attr_reader rest: ImplicitRestNode | SplatNode | nil + attr_reader rights: Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | RequiredParameterNode | BackReferenceReadNode | NumberedReferenceReadNode] + attr_reader lparen_loc: Location? + attr_reader rparen_loc: Location? + + def save_lparen_loc: (_Repository repository) -> void + def save_rparen_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | RequiredParameterNode | BackReferenceReadNode | NumberedReferenceReadNode] lefts, ImplicitRestNode | SplatNode | nil rest, Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | RequiredParameterNode | BackReferenceReadNode | NumberedReferenceReadNode] rights, Location? lparen_loc, Location? rparen_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?lefts: Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | RequiredParameterNode | BackReferenceReadNode | NumberedReferenceReadNode], ?rest: ImplicitRestNode | SplatNode | nil, ?rights: Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | RequiredParameterNode | BackReferenceReadNode | NumberedReferenceReadNode], ?lparen_loc: Location?, ?rparen_loc: Location?) -> MultiTargetNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, lefts: Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | RequiredParameterNode | BackReferenceReadNode | NumberedReferenceReadNode], rest: ImplicitRestNode | SplatNode | nil, rights: Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | RequiredParameterNode | BackReferenceReadNode | NumberedReferenceReadNode], lparen_loc: Location?, rparen_loc: Location? } + def lparen: () -> String? + def rparen: () -> String? + def type: () -> :multi_target_node + | ... + def self.type: () -> :multi_target_node + end + + # Represents a write to a multi-target expression. + # + # a, b, c = 1, 2, 3 + # ^^^^^^^^^^^^^^^^^ + class MultiWriteNode < Node + include _Node + + attr_reader lefts: Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | BackReferenceReadNode | NumberedReferenceReadNode] + attr_reader rest: ImplicitRestNode | SplatNode | nil + attr_reader rights: Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | BackReferenceReadNode | NumberedReferenceReadNode] + attr_reader lparen_loc: Location? + attr_reader rparen_loc: Location? + attr_reader operator_loc: Location + attr_reader value: Prism::node + + def save_lparen_loc: (_Repository repository) -> void + def save_rparen_loc: (_Repository repository) -> void + def save_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | BackReferenceReadNode | NumberedReferenceReadNode] lefts, ImplicitRestNode | SplatNode | nil rest, Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | BackReferenceReadNode | NumberedReferenceReadNode] rights, Location? lparen_loc, Location? rparen_loc, Location operator_loc, Prism::node value) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?lefts: Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | BackReferenceReadNode | NumberedReferenceReadNode], ?rest: ImplicitRestNode | SplatNode | nil, ?rights: Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | BackReferenceReadNode | NumberedReferenceReadNode], ?lparen_loc: Location?, ?rparen_loc: Location?, ?operator_loc: Location, ?value: Prism::node) -> MultiWriteNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, lefts: Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | BackReferenceReadNode | NumberedReferenceReadNode], rest: ImplicitRestNode | SplatNode | nil, rights: Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | BackReferenceReadNode | NumberedReferenceReadNode], lparen_loc: Location?, rparen_loc: Location?, operator_loc: Location, value: Prism::node } + def lparen: () -> String? + def rparen: () -> String? + def operator: () -> String + def type: () -> :multi_write_node + | ... + def self.type: () -> :multi_write_node + end + + # Represents the use of the `next` keyword. + # + # next 1 + # ^^^^^^ + class NextNode < Node + include _Node + + attr_reader arguments: ArgumentsNode? + attr_reader keyword_loc: Location + + def save_keyword_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, ArgumentsNode? arguments, Location keyword_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?arguments: ArgumentsNode?, ?keyword_loc: Location) -> NextNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, arguments: ArgumentsNode?, keyword_loc: Location } + def keyword: () -> String + def type: () -> :next_node + | ... + def self.type: () -> :next_node + end + + # Represents the use of the `nil` keyword. + # + # nil + # ^^^ + class NilNode < Node + include _Node + + + + def initialize: (Source source, Integer node_id, Location location, Integer flags) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer) -> NilNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location } + def type: () -> :nil_node + | ... + def self.type: () -> :nil_node + end + + # Represents the use of `**nil` inside method arguments. + # + # def a(**nil) + # ^^^^^ + # end + class NoKeywordsParameterNode < Node + include _Node + + attr_reader operator_loc: Location + attr_reader keyword_loc: Location + + def save_operator_loc: (_Repository repository) -> void + def save_keyword_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location operator_loc, Location keyword_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?operator_loc: Location, ?keyword_loc: Location) -> NoKeywordsParameterNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, operator_loc: Location, keyword_loc: Location } + def operator: () -> String + def keyword: () -> String + def type: () -> :no_keywords_parameter_node + | ... + def self.type: () -> :no_keywords_parameter_node + end + + # Represents an implicit set of parameters through the use of numbered parameters within a block or lambda. + # + # -> { _1 + _2 } + # ^^^^^^^^^^^^^^ + class NumberedParametersNode < Node + include _Node + + attr_reader maximum: Integer + + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Integer maximum) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?maximum: Integer) -> NumberedParametersNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, maximum: Integer } + def type: () -> :numbered_parameters_node + | ... + def self.type: () -> :numbered_parameters_node + end + + # Represents reading a numbered reference to a capture in the previous match. + # + # $1 + # ^^ + class NumberedReferenceReadNode < Node + include _Node + + attr_reader number: Integer + + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Integer number) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?number: Integer) -> NumberedReferenceReadNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, number: Integer } + def type: () -> :numbered_reference_read_node + | ... + def self.type: () -> :numbered_reference_read_node + end + + # Represents an optional keyword parameter to a method, block, or lambda definition. + # + # def a(b: 1) + # ^^^^ + # end + class OptionalKeywordParameterNode < Node + include _Node + + def repeated_parameter?: () -> bool + + attr_reader name: Symbol + attr_reader name_loc: Location + attr_reader value: Prism::node + + def save_name_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Prism::node value) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?value: Prism::node) -> OptionalKeywordParameterNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, name_loc: Location, value: Prism::node } + def type: () -> :optional_keyword_parameter_node + | ... + def self.type: () -> :optional_keyword_parameter_node + end + + # Represents an optional parameter to a method, block, or lambda definition. + # + # def a(b = 1) + # ^^^^^ + # end + class OptionalParameterNode < Node + include _Node + + def repeated_parameter?: () -> bool + + attr_reader name: Symbol + attr_reader name_loc: Location + attr_reader operator_loc: Location + attr_reader value: Prism::node + + def save_name_loc: (_Repository repository) -> void + def save_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Location operator_loc, Prism::node value) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> OptionalParameterNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, name_loc: Location, operator_loc: Location, value: Prism::node } + def operator: () -> String + def type: () -> :optional_parameter_node + | ... + def self.type: () -> :optional_parameter_node + end + + # Represents the use of the `||` operator or the `or` keyword. + # + # left or right + # ^^^^^^^^^^^^^ + class OrNode < Node + include _Node + + attr_reader left: Prism::node + attr_reader right: Prism::node + attr_reader operator_loc: Location + + def save_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Prism::node left, Prism::node right, Location operator_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?left: Prism::node, ?right: Prism::node, ?operator_loc: Location) -> OrNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, left: Prism::node, right: Prism::node, operator_loc: Location } + def operator: () -> String + def type: () -> :or_node + | ... + def self.type: () -> :or_node + end + + # Represents the list of parameters on a method, block, or lambda definition. + # + # def a(b, c, d) + # ^^^^^^^ + # end + class ParametersNode < Node + include _Node + + attr_reader requireds: Array[RequiredParameterNode | MultiTargetNode] + attr_reader optionals: Array[OptionalParameterNode] + attr_reader rest: RestParameterNode | ImplicitRestNode | nil + attr_reader posts: Array[RequiredParameterNode | MultiTargetNode | KeywordRestParameterNode | NoKeywordsParameterNode | ForwardingParameterNode] + attr_reader keywords: Array[RequiredKeywordParameterNode | OptionalKeywordParameterNode] + attr_reader keyword_rest: KeywordRestParameterNode | ForwardingParameterNode | NoKeywordsParameterNode | nil + attr_reader block: BlockParameterNode? + + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Array[RequiredParameterNode | MultiTargetNode] requireds, Array[OptionalParameterNode] optionals, RestParameterNode | ImplicitRestNode | nil rest, Array[RequiredParameterNode | MultiTargetNode | KeywordRestParameterNode | NoKeywordsParameterNode | ForwardingParameterNode] posts, Array[RequiredKeywordParameterNode | OptionalKeywordParameterNode] keywords, KeywordRestParameterNode | ForwardingParameterNode | NoKeywordsParameterNode | nil keyword_rest, BlockParameterNode? block) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?requireds: Array[RequiredParameterNode | MultiTargetNode], ?optionals: Array[OptionalParameterNode], ?rest: RestParameterNode | ImplicitRestNode | nil, ?posts: Array[RequiredParameterNode | MultiTargetNode | KeywordRestParameterNode | NoKeywordsParameterNode | ForwardingParameterNode], ?keywords: Array[RequiredKeywordParameterNode | OptionalKeywordParameterNode], ?keyword_rest: KeywordRestParameterNode | ForwardingParameterNode | NoKeywordsParameterNode | nil, ?block: BlockParameterNode?) -> ParametersNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, requireds: Array[RequiredParameterNode | MultiTargetNode], optionals: Array[OptionalParameterNode], rest: RestParameterNode | ImplicitRestNode | nil, posts: Array[RequiredParameterNode | MultiTargetNode | KeywordRestParameterNode | NoKeywordsParameterNode | ForwardingParameterNode], keywords: Array[RequiredKeywordParameterNode | OptionalKeywordParameterNode], keyword_rest: KeywordRestParameterNode | ForwardingParameterNode | NoKeywordsParameterNode | nil, block: BlockParameterNode? } + def type: () -> :parameters_node + | ... + def self.type: () -> :parameters_node + end + + # Represents a parenthesized expression + # + # (10 + 34) + # ^^^^^^^^^ + class ParenthesesNode < Node + include _Node + + def multiple_statements?: () -> bool + + attr_reader body: Prism::node? + attr_reader opening_loc: Location + attr_reader closing_loc: Location + + def save_opening_loc: (_Repository repository) -> void + def save_closing_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Prism::node? body, Location opening_loc, Location closing_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?body: Prism::node?, ?opening_loc: Location, ?closing_loc: Location) -> ParenthesesNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, body: Prism::node?, opening_loc: Location, closing_loc: Location } + def opening: () -> String + def closing: () -> String + def type: () -> :parentheses_node + | ... + def self.type: () -> :parentheses_node + end + + # Represents the use of the `^` operator for pinning an expression in a pattern matching expression. + # + # foo in ^(bar) + # ^^^^^^ + class PinnedExpressionNode < Node + include _Node + + attr_reader expression: Prism::node + attr_reader operator_loc: Location + attr_reader lparen_loc: Location + attr_reader rparen_loc: Location + + def save_operator_loc: (_Repository repository) -> void + def save_lparen_loc: (_Repository repository) -> void + def save_rparen_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Prism::node expression, Location operator_loc, Location lparen_loc, Location rparen_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?expression: Prism::node, ?operator_loc: Location, ?lparen_loc: Location, ?rparen_loc: Location) -> PinnedExpressionNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, expression: Prism::node, operator_loc: Location, lparen_loc: Location, rparen_loc: Location } + def operator: () -> String + def lparen: () -> String + def rparen: () -> String + def type: () -> :pinned_expression_node + | ... + def self.type: () -> :pinned_expression_node + end + + # Represents the use of the `^` operator for pinning a variable in a pattern matching expression. + # + # foo in ^bar + # ^^^^ + class PinnedVariableNode < Node + include _Node + + attr_reader variable: LocalVariableReadNode | InstanceVariableReadNode | ClassVariableReadNode | GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode | ItLocalVariableReadNode | MissingNode + attr_reader operator_loc: Location + + def save_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, LocalVariableReadNode | InstanceVariableReadNode | ClassVariableReadNode | GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode | ItLocalVariableReadNode | MissingNode variable, Location operator_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?variable: LocalVariableReadNode | InstanceVariableReadNode | ClassVariableReadNode | GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode | ItLocalVariableReadNode | MissingNode, ?operator_loc: Location) -> PinnedVariableNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, variable: LocalVariableReadNode | InstanceVariableReadNode | ClassVariableReadNode | GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode | ItLocalVariableReadNode | MissingNode, operator_loc: Location } + def operator: () -> String + def type: () -> :pinned_variable_node + | ... + def self.type: () -> :pinned_variable_node + end + + # Represents the use of the `END` keyword. + # + # END { foo } + # ^^^^^^^^^^^ + class PostExecutionNode < Node + include _Node + + attr_reader statements: StatementsNode? + attr_reader keyword_loc: Location + attr_reader opening_loc: Location + attr_reader closing_loc: Location + + def save_keyword_loc: (_Repository repository) -> void + def save_opening_loc: (_Repository repository) -> void + def save_closing_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, StatementsNode? statements, Location keyword_loc, Location opening_loc, Location closing_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?statements: StatementsNode?, ?keyword_loc: Location, ?opening_loc: Location, ?closing_loc: Location) -> PostExecutionNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, statements: StatementsNode?, keyword_loc: Location, opening_loc: Location, closing_loc: Location } + def keyword: () -> String + def opening: () -> String + def closing: () -> String + def type: () -> :post_execution_node + | ... + def self.type: () -> :post_execution_node + end + + # Represents the use of the `BEGIN` keyword. + # + # BEGIN { foo } + # ^^^^^^^^^^^^^ + class PreExecutionNode < Node + include _Node + + attr_reader statements: StatementsNode? + attr_reader keyword_loc: Location + attr_reader opening_loc: Location + attr_reader closing_loc: Location + + def save_keyword_loc: (_Repository repository) -> void + def save_opening_loc: (_Repository repository) -> void + def save_closing_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, StatementsNode? statements, Location keyword_loc, Location opening_loc, Location closing_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?statements: StatementsNode?, ?keyword_loc: Location, ?opening_loc: Location, ?closing_loc: Location) -> PreExecutionNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, statements: StatementsNode?, keyword_loc: Location, opening_loc: Location, closing_loc: Location } + def keyword: () -> String + def opening: () -> String + def closing: () -> String + def type: () -> :pre_execution_node + | ... + def self.type: () -> :pre_execution_node + end + + # The top level node of any parse tree. + class ProgramNode < Node + include _Node + + attr_reader locals: Array[Symbol] + attr_reader statements: StatementsNode + + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Array[Symbol] locals, StatementsNode statements) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?locals: Array[Symbol], ?statements: StatementsNode) -> ProgramNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, locals: Array[Symbol], statements: StatementsNode } + def type: () -> :program_node + | ... + def self.type: () -> :program_node + end + + # Represents the use of the `..` or `...` operators. + # + # 1..2 + # ^^^^ + # + # c if a =~ /left/ ... b =~ /right/ + # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + class RangeNode < Node + include _Node + + def exclude_end?: () -> bool + + attr_reader left: Prism::node? + attr_reader right: Prism::node? + attr_reader operator_loc: Location + + def save_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Prism::node? left, Prism::node? right, Location operator_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?left: Prism::node?, ?right: Prism::node?, ?operator_loc: Location) -> RangeNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, left: Prism::node?, right: Prism::node?, operator_loc: Location } + def operator: () -> String + def type: () -> :range_node + | ... + def self.type: () -> :range_node + end + + # Represents a rational number literal. + # + # 1.0r + # ^^^^ + class RationalNode < Node + include _Node + + def binary?: () -> bool + def decimal?: () -> bool + def octal?: () -> bool + def hexadecimal?: () -> bool + + attr_reader numerator: Integer + attr_reader denominator: Integer + + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Integer numerator, Integer denominator) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?numerator: Integer, ?denominator: Integer) -> RationalNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, numerator: Integer, denominator: Integer } + def type: () -> :rational_node + | ... + def self.type: () -> :rational_node + end + + # Represents the use of the `redo` keyword. + # + # redo + # ^^^^ + class RedoNode < Node + include _Node + + + + def initialize: (Source source, Integer node_id, Location location, Integer flags) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer) -> RedoNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location } + def type: () -> :redo_node + | ... + def self.type: () -> :redo_node + end + + # Represents a regular expression literal with no interpolation. + # + # /foo/i + # ^^^^^^ + class RegularExpressionNode < Node + include _Node + + def ignore_case?: () -> bool + def extended?: () -> bool + def multi_line?: () -> bool + def once?: () -> bool + def euc_jp?: () -> bool + def ascii_8bit?: () -> bool + def windows_31j?: () -> bool + def utf_8?: () -> bool + def forced_utf8_encoding?: () -> bool + def forced_binary_encoding?: () -> bool + def forced_us_ascii_encoding?: () -> bool + + attr_reader opening_loc: Location + attr_reader content_loc: Location + attr_reader closing_loc: Location + attr_reader unescaped: String + + def save_opening_loc: (_Repository repository) -> void + def save_content_loc: (_Repository repository) -> void + def save_closing_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location opening_loc, Location content_loc, Location closing_loc, String unescaped) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?content_loc: Location, ?closing_loc: Location, ?unescaped: String) -> RegularExpressionNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, opening_loc: Location, content_loc: Location, closing_loc: Location, unescaped: String } + def opening: () -> String + def content: () -> String + def closing: () -> String + def type: () -> :regular_expression_node + | ... + def self.type: () -> :regular_expression_node + end + + # Represents a required keyword parameter to a method, block, or lambda definition. + # + # def a(b: ) + # ^^ + # end + class RequiredKeywordParameterNode < Node + include _Node + + def repeated_parameter?: () -> bool + + attr_reader name: Symbol + attr_reader name_loc: Location + + def save_name_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location) -> RequiredKeywordParameterNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol, name_loc: Location } + def type: () -> :required_keyword_parameter_node + | ... + def self.type: () -> :required_keyword_parameter_node + end + + # Represents a required parameter to a method, block, or lambda definition. + # + # def a(b) + # ^ + # end + class RequiredParameterNode < Node + include _Node + + def repeated_parameter?: () -> bool + + attr_reader name: Symbol + + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> RequiredParameterNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol } + def type: () -> :required_parameter_node + | ... + def self.type: () -> :required_parameter_node + end + + # Represents an expression modified with a rescue. + # + # foo rescue nil + # ^^^^^^^^^^^^^^ + class RescueModifierNode < Node + include _Node + + attr_reader expression: Prism::node + attr_reader keyword_loc: Location + attr_reader rescue_expression: Prism::node + + def save_keyword_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Prism::node expression, Location keyword_loc, Prism::node rescue_expression) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?expression: Prism::node, ?keyword_loc: Location, ?rescue_expression: Prism::node) -> RescueModifierNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, expression: Prism::node, keyword_loc: Location, rescue_expression: Prism::node } + def keyword: () -> String + def type: () -> :rescue_modifier_node + | ... + def self.type: () -> :rescue_modifier_node + end + + # Represents a rescue statement. + # + # begin + # rescue Foo, *splat, Bar => ex + # foo + # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + # end + # + # `Foo, *splat, Bar` are in the `exceptions` field. `ex` is in the `reference` field. + class RescueNode < Node + include _Node + + attr_reader keyword_loc: Location + attr_reader exceptions: Array[Prism::node] + attr_reader operator_loc: Location? + attr_reader reference: LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | BackReferenceReadNode | NumberedReferenceReadNode | MissingNode | nil + attr_reader then_keyword_loc: Location? + attr_reader statements: StatementsNode? + attr_reader subsequent: RescueNode? + + def save_keyword_loc: (_Repository repository) -> void + def save_operator_loc: (_Repository repository) -> void + def save_then_keyword_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location keyword_loc, Array[Prism::node] exceptions, Location? operator_loc, LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | BackReferenceReadNode | NumberedReferenceReadNode | MissingNode | nil reference, Location? then_keyword_loc, StatementsNode? statements, RescueNode? subsequent) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?keyword_loc: Location, ?exceptions: Array[Prism::node], ?operator_loc: Location?, ?reference: LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | BackReferenceReadNode | NumberedReferenceReadNode | MissingNode | nil, ?then_keyword_loc: Location?, ?statements: StatementsNode?, ?subsequent: RescueNode?) -> RescueNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, keyword_loc: Location, exceptions: Array[Prism::node], operator_loc: Location?, reference: LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | BackReferenceReadNode | NumberedReferenceReadNode | MissingNode | nil, then_keyword_loc: Location?, statements: StatementsNode?, subsequent: RescueNode? } + def keyword: () -> String + def operator: () -> String? + def then_keyword: () -> String? + def type: () -> :rescue_node + | ... + def self.type: () -> :rescue_node + end + + # Represents a rest parameter to a method, block, or lambda definition. + # + # def a(*b) + # ^^ + # end + class RestParameterNode < Node + include _Node + + def repeated_parameter?: () -> bool + + attr_reader name: Symbol? + attr_reader name_loc: Location? + attr_reader operator_loc: Location + + def save_name_loc: (_Repository repository) -> void + def save_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol? name, Location? name_loc, Location operator_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol?, ?name_loc: Location?, ?operator_loc: Location) -> RestParameterNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, name: Symbol?, name_loc: Location?, operator_loc: Location } + def operator: () -> String + def type: () -> :rest_parameter_node + | ... + def self.type: () -> :rest_parameter_node + end + + # Represents the use of the `retry` keyword. + # + # retry + # ^^^^^ + class RetryNode < Node + include _Node + + + + def initialize: (Source source, Integer node_id, Location location, Integer flags) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer) -> RetryNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location } + def type: () -> :retry_node + | ... + def self.type: () -> :retry_node + end + + # Represents the use of the `return` keyword. + # + # return 1 + # ^^^^^^^^ + class ReturnNode < Node + include _Node + + attr_reader keyword_loc: Location + attr_reader arguments: ArgumentsNode? + + def save_keyword_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location keyword_loc, ArgumentsNode? arguments) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?keyword_loc: Location, ?arguments: ArgumentsNode?) -> ReturnNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, keyword_loc: Location, arguments: ArgumentsNode? } + def keyword: () -> String + def type: () -> :return_node + | ... + def self.type: () -> :return_node + end + + # Represents the `self` keyword. + # + # self + # ^^^^ + class SelfNode < Node + include _Node + + + + def initialize: (Source source, Integer node_id, Location location, Integer flags) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer) -> SelfNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location } + def type: () -> :self_node + | ... + def self.type: () -> :self_node + end + + # 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 } + # ^^^^^^^^^^^^ + class ShareableConstantNode < Node + include _Node + + def literal?: () -> bool + def experimental_everything?: () -> bool + def experimental_copy?: () -> bool + + attr_reader write: ConstantWriteNode | ConstantAndWriteNode | ConstantOrWriteNode | ConstantOperatorWriteNode | ConstantPathWriteNode | ConstantPathAndWriteNode | ConstantPathOrWriteNode | ConstantPathOperatorWriteNode + + + def initialize: (Source source, Integer node_id, Location location, Integer flags, ConstantWriteNode | ConstantAndWriteNode | ConstantOrWriteNode | ConstantOperatorWriteNode | ConstantPathWriteNode | ConstantPathAndWriteNode | ConstantPathOrWriteNode | ConstantPathOperatorWriteNode write) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?write: ConstantWriteNode | ConstantAndWriteNode | ConstantOrWriteNode | ConstantOperatorWriteNode | ConstantPathWriteNode | ConstantPathAndWriteNode | ConstantPathOrWriteNode | ConstantPathOperatorWriteNode) -> ShareableConstantNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, write: ConstantWriteNode | ConstantAndWriteNode | ConstantOrWriteNode | ConstantOperatorWriteNode | ConstantPathWriteNode | ConstantPathAndWriteNode | ConstantPathOrWriteNode | ConstantPathOperatorWriteNode } + def type: () -> :shareable_constant_node + | ... + def self.type: () -> :shareable_constant_node + end + + # Represents a singleton class declaration involving the `class` keyword. + # + # class << self end + # ^^^^^^^^^^^^^^^^^ + class SingletonClassNode < Node + include _Node + + attr_reader locals: Array[Symbol] + attr_reader class_keyword_loc: Location + attr_reader operator_loc: Location + attr_reader expression: Prism::node + attr_reader body: StatementsNode | BeginNode | nil + attr_reader end_keyword_loc: Location + + def save_class_keyword_loc: (_Repository repository) -> void + def save_operator_loc: (_Repository repository) -> void + def save_end_keyword_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Array[Symbol] locals, Location class_keyword_loc, Location operator_loc, Prism::node expression, StatementsNode | BeginNode | nil body, Location end_keyword_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?locals: Array[Symbol], ?class_keyword_loc: Location, ?operator_loc: Location, ?expression: Prism::node, ?body: StatementsNode | BeginNode | nil, ?end_keyword_loc: Location) -> SingletonClassNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, locals: Array[Symbol], class_keyword_loc: Location, operator_loc: Location, expression: Prism::node, body: StatementsNode | BeginNode | nil, end_keyword_loc: Location } + def class_keyword: () -> String + def operator: () -> String + def end_keyword: () -> String + def type: () -> :singleton_class_node + | ... + def self.type: () -> :singleton_class_node + end + + # Represents the use of the `__ENCODING__` keyword. + # + # __ENCODING__ + # ^^^^^^^^^^^^ + class SourceEncodingNode < Node + include _Node + + + + def initialize: (Source source, Integer node_id, Location location, Integer flags) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer) -> SourceEncodingNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location } + def type: () -> :source_encoding_node + | ... + def self.type: () -> :source_encoding_node + end + + # Represents the use of the `__FILE__` keyword. + # + # __FILE__ + # ^^^^^^^^ + class SourceFileNode < Node + include _Node + + def forced_utf8_encoding?: () -> bool + def forced_binary_encoding?: () -> bool + def frozen?: () -> bool + def mutable?: () -> bool + + attr_reader filepath: String + + + def initialize: (Source source, Integer node_id, Location location, Integer flags, String filepath) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?filepath: String) -> SourceFileNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, filepath: String } + def type: () -> :source_file_node + | ... + def self.type: () -> :source_file_node + end + + # Represents the use of the `__LINE__` keyword. + # + # __LINE__ + # ^^^^^^^^ + class SourceLineNode < Node + include _Node + + + + def initialize: (Source source, Integer node_id, Location location, Integer flags) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer) -> SourceLineNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location } + def type: () -> :source_line_node + | ... + def self.type: () -> :source_line_node + end + + # Represents the use of the splat operator. + # + # [*a] + # ^^ + class SplatNode < Node + include _Node + + attr_reader operator_loc: Location + attr_reader expression: Prism::node? + + def save_operator_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location operator_loc, Prism::node? expression) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?operator_loc: Location, ?expression: Prism::node?) -> SplatNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, operator_loc: Location, expression: Prism::node? } + def operator: () -> String + def type: () -> :splat_node + | ... + def self.type: () -> :splat_node + end + + # Represents a set of statements contained within some scope. + # + # foo; bar; baz + # ^^^^^^^^^^^^^ + class StatementsNode < Node + include _Node + + attr_reader body: Array[Prism::node] + + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Array[Prism::node] body) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?body: Array[Prism::node]) -> StatementsNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, body: Array[Prism::node] } + def type: () -> :statements_node + | ... + def self.type: () -> :statements_node + end + + # 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" + # ^^^^ ^^^^ + class StringNode < Node + include _Node + + def forced_utf8_encoding?: () -> bool + def forced_binary_encoding?: () -> bool + def frozen?: () -> bool + def mutable?: () -> bool + + attr_reader opening_loc: Location? + attr_reader content_loc: Location + attr_reader closing_loc: Location? + attr_reader unescaped: String + + def save_opening_loc: (_Repository repository) -> void + def save_content_loc: (_Repository repository) -> void + def save_closing_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location? opening_loc, Location content_loc, Location? closing_loc, String unescaped) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location?, ?content_loc: Location, ?closing_loc: Location?, ?unescaped: String) -> StringNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, opening_loc: Location?, content_loc: Location, closing_loc: Location?, unescaped: String } + def opening: () -> String? + def content: () -> String + def closing: () -> String? + def type: () -> :string_node + | ... + def self.type: () -> :string_node + end + + # Represents the use of the `super` keyword with parentheses or arguments. + # + # super() + # ^^^^^^^ + # + # super foo, bar + # ^^^^^^^^^^^^^^ + # + # If no arguments are provided (except for a block), it would be a `ForwardingSuperNode` instead. + class SuperNode < Node + include _Node + + attr_reader keyword_loc: Location + attr_reader lparen_loc: Location? + attr_reader arguments: ArgumentsNode? + attr_reader rparen_loc: Location? + attr_reader block: BlockNode | BlockArgumentNode | nil + + def save_keyword_loc: (_Repository repository) -> void + def save_lparen_loc: (_Repository repository) -> void + def save_rparen_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location keyword_loc, Location? lparen_loc, ArgumentsNode? arguments, Location? rparen_loc, BlockNode | BlockArgumentNode | nil block) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?keyword_loc: Location, ?lparen_loc: Location?, ?arguments: ArgumentsNode?, ?rparen_loc: Location?, ?block: BlockNode | BlockArgumentNode | nil) -> SuperNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, keyword_loc: Location, lparen_loc: Location?, arguments: ArgumentsNode?, rparen_loc: Location?, block: BlockNode | BlockArgumentNode | nil } + def keyword: () -> String + def lparen: () -> String? + def rparen: () -> String? + def type: () -> :super_node + | ... + def self.type: () -> :super_node + end + + # Represents a symbol literal or a symbol contained within a `%i` list. + # + # :foo + # ^^^^ + # + # %i[foo] + # ^^^ + class SymbolNode < Node + include _Node + + def forced_utf8_encoding?: () -> bool + def forced_binary_encoding?: () -> bool + def forced_us_ascii_encoding?: () -> bool + + attr_reader opening_loc: Location? + attr_reader value_loc: Location? + attr_reader closing_loc: Location? + attr_reader unescaped: String + + def save_opening_loc: (_Repository repository) -> void + def save_value_loc: (_Repository repository) -> void + def save_closing_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location? opening_loc, Location? value_loc, Location? closing_loc, String unescaped) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location?, ?value_loc: Location?, ?closing_loc: Location?, ?unescaped: String) -> SymbolNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, opening_loc: Location?, value_loc: Location?, closing_loc: Location?, unescaped: String } + def opening: () -> String? + def value: () -> String? + def closing: () -> String? + def type: () -> :symbol_node + | ... + def self.type: () -> :symbol_node + end + + # Represents the use of the literal `true` keyword. + # + # true + # ^^^^ + class TrueNode < Node + include _Node + + + + def initialize: (Source source, Integer node_id, Location location, Integer flags) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer) -> TrueNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location } + def type: () -> :true_node + | ... + def self.type: () -> :true_node + end + + # Represents the use of the `undef` keyword. + # + # undef :foo, :bar, :baz + # ^^^^^^^^^^^^^^^^^^^^^^ + class UndefNode < Node + include _Node + + attr_reader names: Array[SymbolNode | InterpolatedSymbolNode] + attr_reader keyword_loc: Location + + def save_keyword_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Array[SymbolNode | InterpolatedSymbolNode] names, Location keyword_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?names: Array[SymbolNode | InterpolatedSymbolNode], ?keyword_loc: Location) -> UndefNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, names: Array[SymbolNode | InterpolatedSymbolNode], keyword_loc: Location } + def keyword: () -> String + def type: () -> :undef_node + | ... + def self.type: () -> :undef_node + end + + # Represents the use of the `unless` keyword, either in the block form or the modifier form. + # + # bar unless foo + # ^^^^^^^^^^^^^^ + # + # unless foo then bar end + # ^^^^^^^^^^^^^^^^^^^^^^^ + class UnlessNode < Node + include _Node + + attr_reader keyword_loc: Location + attr_reader predicate: Prism::node + attr_reader then_keyword_loc: Location? + attr_reader statements: StatementsNode? + attr_reader else_clause: ElseNode? + attr_reader end_keyword_loc: Location? + + def save_keyword_loc: (_Repository repository) -> void + def save_then_keyword_loc: (_Repository repository) -> void + def save_end_keyword_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location keyword_loc, Prism::node predicate, Location? then_keyword_loc, StatementsNode? statements, ElseNode? else_clause, Location? end_keyword_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?keyword_loc: Location, ?predicate: Prism::node, ?then_keyword_loc: Location?, ?statements: StatementsNode?, ?else_clause: ElseNode?, ?end_keyword_loc: Location?) -> UnlessNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, keyword_loc: Location, predicate: Prism::node, then_keyword_loc: Location?, statements: StatementsNode?, else_clause: ElseNode?, end_keyword_loc: Location? } + def keyword: () -> String + def then_keyword: () -> String? + def end_keyword: () -> String? + def type: () -> :unless_node + | ... + def self.type: () -> :unless_node + end + + # Represents the use of the `until` keyword, either in the block form or the modifier form. + # + # bar until foo + # ^^^^^^^^^^^^^ + # + # until foo do bar end + # ^^^^^^^^^^^^^^^^^^^^ + class UntilNode < Node + include _Node + + def begin_modifier?: () -> bool + + attr_reader keyword_loc: Location + attr_reader do_keyword_loc: Location? + attr_reader closing_loc: Location? + attr_reader predicate: Prism::node + attr_reader statements: StatementsNode? + + def save_keyword_loc: (_Repository repository) -> void + def save_do_keyword_loc: (_Repository repository) -> void + def save_closing_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location keyword_loc, Location? do_keyword_loc, Location? closing_loc, Prism::node predicate, StatementsNode? statements) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?keyword_loc: Location, ?do_keyword_loc: Location?, ?closing_loc: Location?, ?predicate: Prism::node, ?statements: StatementsNode?) -> UntilNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, keyword_loc: Location, do_keyword_loc: Location?, closing_loc: Location?, predicate: Prism::node, statements: StatementsNode? } + def keyword: () -> String + def do_keyword: () -> String? + def closing: () -> String? + def type: () -> :until_node + | ... + def self.type: () -> :until_node + end + + # Represents the use of the `when` keyword within a case statement. + # + # case true + # when true + # ^^^^^^^^^ + # end + class WhenNode < Node + include _Node + + attr_reader keyword_loc: Location + attr_reader conditions: Array[Prism::node] + attr_reader then_keyword_loc: Location? + attr_reader statements: StatementsNode? + + def save_keyword_loc: (_Repository repository) -> void + def save_then_keyword_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location keyword_loc, Array[Prism::node] conditions, Location? then_keyword_loc, StatementsNode? statements) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?keyword_loc: Location, ?conditions: Array[Prism::node], ?then_keyword_loc: Location?, ?statements: StatementsNode?) -> WhenNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, keyword_loc: Location, conditions: Array[Prism::node], then_keyword_loc: Location?, statements: StatementsNode? } + def keyword: () -> String + def then_keyword: () -> String? + def type: () -> :when_node + | ... + def self.type: () -> :when_node + end + + # Represents the use of the `while` keyword, either in the block form or the modifier form. + # + # bar while foo + # ^^^^^^^^^^^^^ + # + # while foo do bar end + # ^^^^^^^^^^^^^^^^^^^^ + class WhileNode < Node + include _Node + + def begin_modifier?: () -> bool + + attr_reader keyword_loc: Location + attr_reader do_keyword_loc: Location? + attr_reader closing_loc: Location? + attr_reader predicate: Prism::node + attr_reader statements: StatementsNode? + + def save_keyword_loc: (_Repository repository) -> void + def save_do_keyword_loc: (_Repository repository) -> void + def save_closing_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location keyword_loc, Location? do_keyword_loc, Location? closing_loc, Prism::node predicate, StatementsNode? statements) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?keyword_loc: Location, ?do_keyword_loc: Location?, ?closing_loc: Location?, ?predicate: Prism::node, ?statements: StatementsNode?) -> WhileNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, keyword_loc: Location, do_keyword_loc: Location?, closing_loc: Location?, predicate: Prism::node, statements: StatementsNode? } + def keyword: () -> String + def do_keyword: () -> String? + def closing: () -> String? + def type: () -> :while_node + | ... + def self.type: () -> :while_node + end + + # Represents an xstring literal with no interpolation. + # + # `foo` + # ^^^^^ + class XStringNode < Node + include _Node + + def forced_utf8_encoding?: () -> bool + def forced_binary_encoding?: () -> bool + + attr_reader opening_loc: Location + attr_reader content_loc: Location + attr_reader closing_loc: Location + attr_reader unescaped: String + + def save_opening_loc: (_Repository repository) -> void + def save_content_loc: (_Repository repository) -> void + def save_closing_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location opening_loc, Location content_loc, Location closing_loc, String unescaped) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?content_loc: Location, ?closing_loc: Location, ?unescaped: String) -> XStringNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, opening_loc: Location, content_loc: Location, closing_loc: Location, unescaped: String } + def opening: () -> String + def content: () -> String + def closing: () -> String + def type: () -> :x_string_node + | ... + def self.type: () -> :x_string_node + end + + # Represents the use of the `yield` keyword. + # + # yield 1 + # ^^^^^^^ + class YieldNode < Node + include _Node + + attr_reader keyword_loc: Location + attr_reader lparen_loc: Location? + attr_reader arguments: ArgumentsNode? + attr_reader rparen_loc: Location? + + def save_keyword_loc: (_Repository repository) -> void + def save_lparen_loc: (_Repository repository) -> void + def save_rparen_loc: (_Repository repository) -> void + + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location keyword_loc, Location? lparen_loc, ArgumentsNode? arguments, Location? rparen_loc) -> void + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?keyword_loc: Location, ?lparen_loc: Location?, ?arguments: ArgumentsNode?, ?rparen_loc: Location?) -> YieldNode + def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, keyword_loc: Location, lparen_loc: Location?, arguments: ArgumentsNode?, rparen_loc: Location? } + def keyword: () -> String + def lparen: () -> String? + def rparen: () -> String? + def type: () -> :yield_node + | ... + def self.type: () -> :yield_node + end + + # Flags for arguments nodes. + module ArgumentsNodeFlags + # if the arguments contain forwarding + CONTAINS_FORWARDING: Integer + # if the arguments contain keywords + CONTAINS_KEYWORDS: Integer + # if the arguments contain a keyword splat + CONTAINS_KEYWORD_SPLAT: Integer + # if the arguments contain a splat + CONTAINS_SPLAT: Integer + # if the arguments contain multiple splats + CONTAINS_MULTIPLE_SPLATS: Integer + end + + # Flags for array nodes. + module ArrayNodeFlags + # if array contains splat nodes + CONTAINS_SPLAT: Integer + end + + # Flags for call nodes. + module CallNodeFlags + # &. operator + SAFE_NAVIGATION: Integer + # a call that could have been a local variable + VARIABLE_CALL: Integer + # a call that is an attribute write, so the value being written should be returned + ATTRIBUTE_WRITE: Integer + # a call that ignores method visibility + IGNORE_VISIBILITY: Integer + end + + # Flags for nodes that have unescaped content. + module EncodingFlags + # internal bytes forced the encoding to UTF-8 + FORCED_UTF8_ENCODING: Integer + # internal bytes forced the encoding to binary + FORCED_BINARY_ENCODING: Integer + end + + # Flags for integer nodes that correspond to the base of the integer. + module IntegerBaseFlags + # 0b prefix + BINARY: Integer + # 0d or no prefix + DECIMAL: Integer + # 0o or 0 prefix + OCTAL: Integer + # 0x prefix + HEXADECIMAL: Integer + end + + # Flags for interpolated string nodes that indicated mutability if they are also marked as literals. + module InterpolatedStringNodeFlags + # frozen by virtue of a `frozen_string_literal: true` comment or `--enable-frozen-string-literal`; only for adjacent string literals like `'a' 'b'` + FROZEN: Integer + # mutable by virtue of a `frozen_string_literal: false` comment or `--disable-frozen-string-literal`; only for adjacent string literals like `'a' 'b'` + MUTABLE: Integer + end + + # Flags for keyword hash nodes. + module KeywordHashNodeFlags + # a keyword hash which only has `AssocNode` elements all with symbol keys, which means the elements can be treated as keyword arguments + SYMBOL_KEYS: Integer + end + + # Flags for while and until loop nodes. + module LoopFlags + # a loop after a begin statement, so the body is executed first before the condition + BEGIN_MODIFIER: Integer + end + + # Flags for parameter nodes. + module ParameterFlags + # a parameter name that has been repeated in the method signature + REPEATED_PARAMETER: Integer + end + + # Flags for parentheses nodes. + module ParenthesesNodeFlags + # parentheses that contain multiple potentially void statements + MULTIPLE_STATEMENTS: Integer + end + + # Flags for range and flip-flop nodes. + module RangeFlags + # ... operator + EXCLUDE_END: Integer + end + + # Flags for regular expression and match last line nodes. + module RegularExpressionFlags + # i - ignores the case of characters when matching + IGNORE_CASE: Integer + # x - ignores whitespace and allows comments in regular expressions + EXTENDED: Integer + # m - allows $ to match the end of lines within strings + MULTI_LINE: Integer + # o - only interpolates values into the regular expression once + ONCE: Integer + # e - forces the EUC-JP encoding + EUC_JP: Integer + # n - forces the ASCII-8BIT encoding + ASCII_8BIT: Integer + # s - forces the Windows-31J encoding + WINDOWS_31J: Integer + # u - forces the UTF-8 encoding + UTF_8: Integer + # internal bytes forced the encoding to UTF-8 + FORCED_UTF8_ENCODING: Integer + # internal bytes forced the encoding to binary + FORCED_BINARY_ENCODING: Integer + # internal bytes forced the encoding to US-ASCII + FORCED_US_ASCII_ENCODING: Integer + end + + # Flags for shareable constant nodes. + module ShareableConstantNodeFlags + # constant writes that should be modified with shareable constant value literal + LITERAL: Integer + # constant writes that should be modified with shareable constant value experimental everything + EXPERIMENTAL_EVERYTHING: Integer + # constant writes that should be modified with shareable constant value experimental copy + EXPERIMENTAL_COPY: Integer + end + + # Flags for string nodes. + module StringFlags + # internal bytes forced the encoding to UTF-8 + FORCED_UTF8_ENCODING: Integer + # internal bytes forced the encoding to binary + FORCED_BINARY_ENCODING: Integer + # frozen by virtue of a `frozen_string_literal: true` comment or `--enable-frozen-string-literal` + FROZEN: Integer + # mutable by virtue of a `frozen_string_literal: false` comment or `--disable-frozen-string-literal` + MUTABLE: Integer + end + + # Flags for symbol nodes. + module SymbolFlags + # internal bytes forced the encoding to UTF-8 + FORCED_UTF8_ENCODING: Integer + # internal bytes forced the encoding to binary + FORCED_BINARY_ENCODING: Integer + # internal bytes forced the encoding to US-ASCII + FORCED_US_ASCII_ENCODING: Integer + end + + # The flags that are common to all nodes. + module NodeFlags + NEWLINE: Integer + STATIC_LITERAL: Integer + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/node_ext.rbs b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/node_ext.rbs new file mode 100644 index 0000000..a187c1d --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/node_ext.rbs @@ -0,0 +1,149 @@ +module Prism + class Node + def deprecated: (*String replacements) -> void + end + + class InterpolatedMatchLastLineNode < Node + def options: () -> Integer + end + + class InterpolatedRegularExpressionNode < Node + def options: () -> Integer + end + + class MatchLastLineNode < Node + def options: () -> Integer + end + + class RegularExpressionNode < Node + def options: () -> Integer + end + + class InterpolatedStringNode < Node + def heredoc?: () -> bool + end + + class InterpolatedXStringNode < Node + def heredoc?: () -> bool + end + + class StringNode < Node + def heredoc?: () -> bool + def to_interpolated: () -> InterpolatedStringNode + end + + class XStringNode < Node + def heredoc?: () -> bool + def to_interpolated: () -> InterpolatedXStringNode + end + + class ImaginaryNode < Node + def value: () -> Complex + end + + class RationalNode < Node + def value: () -> Rational + def numeric: () -> (IntegerNode | FloatNode) + end + + class ConstantReadNode < Node + def full_name_parts: () -> Array[Symbol] + def full_name: () -> String + end + + class ConstantWriteNode < Node + def full_name_parts: () -> Array[Symbol] + def full_name: () -> String + end + + class ConstantPathNode < Node + class DynamicPartsInConstantPathError < StandardError + end + + class MissingNodesInConstantPathError < StandardError + end + + def full_name_parts: () -> Array[Symbol] + def full_name: () -> String + def child: () -> (ConstantReadNode | MissingNode) + end + + class ConstantPathTargetNode < Node + def full_name_parts: () -> Array[Symbol] + def full_name: () -> String + def child: () -> (ConstantReadNode | MissingNode) + end + + class ConstantTargetNode < Node + def full_name_parts: () -> Array[Symbol] + def full_name: () -> String + end + + class ParametersNode < Node + def signature: () -> Array[[Symbol, Symbol] | [Symbol]] + end + + class CallNode < Node + def full_message_loc: () -> Location? + end + + class CallOperatorWriteNode < Node + def operator: () -> Symbol + def operator_loc: () -> Location + end + + class ClassVariableOperatorWriteNode < Node + def operator: () -> Symbol + def operator_loc: () -> Location + end + + class ConstantOperatorWriteNode < Node + def operator: () -> Symbol + def operator_loc: () -> Location + end + + class ConstantPathOperatorWriteNode < Node + def operator: () -> Symbol + def operator_loc: () -> Location + end + + class GlobalVariableOperatorWriteNode < Node + def operator: () -> Symbol + def operator_loc: () -> Location + end + + class IndexOperatorWriteNode < Node + def operator: () -> Symbol + def operator_loc: () -> Location + end + + class InstanceVariableOperatorWriteNode < Node + def operator: () -> Symbol + def operator_loc: () -> Location + end + + class LocalVariableOperatorWriteNode < Node + def operator: () -> Symbol + def operator_loc: () -> Location + end + + class CaseMatchNode < Node + def consequent: () -> ElseNode? + end + + class CaseNode < Node + def consequent: () -> ElseNode? + end + + class IfNode < Node + def consequent: () -> (ElseNode | IfNode | nil) + end + + class RescueNode < Node + def consequent: () -> RescueNode? + end + + class UnlessNode < Node + def consequent: () -> ElseNode? + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/pack.rbs b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/pack.rbs new file mode 100644 index 0000000..acbc92b --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/pack.rbs @@ -0,0 +1,43 @@ +module Prism + module Pack + type variant = :pack | :unpack + + def self.parse: (Symbol version, variant variant, String source) -> Format + + class Directive + type directive_type = :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 + + type signness = :UNSIGNED | :SIGNED | :SIGNED_NA + + type endianness = :AGNOSTIC_ENDIAN | :LITTLE_ENDIAN | :BIG_ENDIAN | :NATIVE_ENDIAN | :ENDIAN_NA + + type size = :SIZE_SHORT | :SIZE_INT | :SIZE_LONG | :SIZE_LONG_LONG | :SIZE_8 | :SIZE_16 | :SIZE_32 | + :SIZE_64 | :SIZE_P | :SIZE_NA + + type length_type = :LENGTH_FIXED | :LENGTH_MAX | :LENGTH_RELATIVE | :LENGTH_NA + + + attr_reader version: Symbol + attr_reader variant: variant + attr_reader source: String + attr_reader type: directive_type + attr_reader signed: signness + attr_reader endian: endianness + attr_reader size: size + attr_reader length_type: length_type + attr_reader length: Integer + + def describe: () -> String + end + + class Format + attr_reader directives: Array[Directive] + attr_reader encoding: Encoding + + def describe: () -> String + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/parse_result.rbs b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/parse_result.rbs new file mode 100644 index 0000000..cbcf3fc --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/parse_result.rbs @@ -0,0 +1,197 @@ +module Prism + interface _CodeUnitsCache + def []: (Integer byte_offset) -> Integer + end + + class Source + attr_reader source: String + attr_reader start_line: Integer + attr_reader offsets: Array[Integer] + + def initialize: (String source, ?Integer start_line, ?Array[Integer] offsets) -> void + def replace_start_line: (Integer start_line) -> void + def replace_offsets: (Array[Integer] offsets) -> void + def encoding: () -> Encoding + def lines: () -> Array[String] + def slice: (Integer byte_offset, Integer length) -> String + def byte_offset: (Integer line, Integer column) -> Integer + def line: (Integer byte_offset) -> Integer + def line_start: (Integer byte_offset) -> Integer + def line_end: (Integer byte_offset) -> Integer + def line_offset: (Integer byte_offset) -> Integer + def column: (Integer byte_offset) -> Integer + def character_offset: (Integer byte_offset) -> Integer + def character_column: (Integer byte_offset) -> Integer + def code_units_offset: (Integer byte_offset, Encoding encoding) -> Integer + def code_units_cache: (Encoding encoding) -> _CodeUnitsCache + def code_units_column: (Integer byte_offset, Encoding encoding) -> Integer + def deep_freeze: () -> void + + def self.for: (String source) -> Source + end + + class CodeUnitsCache + def initialize: (String source, Encoding encoding) -> void + def []: (Integer byte_offset) -> Integer + end + + class ASCIISource < Source + def character_offset: (Integer byte_offset) -> Integer + def character_column: (Integer byte_offset) -> Integer + def code_units_offset: (Integer byte_offset, Encoding encoding) -> Integer + def code_units_cache: (Encoding encoding) -> _CodeUnitsCache + def code_units_column: (Integer byte_offset, Encoding encoding) -> Integer + end + + class Location + attr_reader source: Source + attr_reader start_offset: Integer + attr_reader length: Integer + + def initialize: (Source source, Integer start_offset, Integer length) -> void + def leading_comments: () -> Array[comment] + def leading_comment: (comment) -> void + def trailing_comments: () -> Array[comment] + def trailing_comment: (comment) -> void + def comments: () -> Array[comment] + def copy: (?source: Source, ?start_offset: Integer, ?length: Integer) -> Location + def chop: () -> Location + def source_lines: () -> Array[String] + def slice: () -> String + def slice_lines: () -> String + def start_character_offset: () -> Integer + def start_code_units_offset: (Encoding encoding) -> Integer + def cached_start_code_units_offset: (_CodeUnitsCache cache) -> Integer + def end_offset: () -> Integer + def end_character_offset: () -> Integer + def end_code_units_offset: (Encoding encoding) -> Integer + def cached_end_code_units_offset: (_CodeUnitsCache cache) -> Integer + def start_line: () -> Integer + def start_line_slice: () -> String + def end_line: () -> Integer + def start_column: () -> Integer + def start_character_column: () -> Integer + def start_code_units_column: (Encoding encoding) -> Integer + def cached_start_code_units_column: (_CodeUnitsCache cache) -> Integer + def end_column: () -> Integer + def end_character_column: () -> Integer + def end_code_units_column: (Encoding encoding) -> Integer + def cached_end_code_units_column: (_CodeUnitsCache cache) -> Integer + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def pretty_print: (untyped q) -> untyped + def join: (Location other) -> Location + def adjoin: (String string) -> Location + end + + class Comment + attr_reader location: Location + + def initialize: (Location location) -> void + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def slice: () -> String + end + + interface _Comment + def trailing?: () -> bool + end + + type comment = Comment & _Comment + + class InlineComment < Comment + include _Comment + end + + class EmbDocComment < Comment + include _Comment + end + + class MagicComment + attr_reader key_loc: Location + attr_reader value_loc: Location + + def initialize: (Location key_loc, Location value_loc) -> void + + def key: () -> String + def value: () -> String + + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + end + + class ParseError + attr_reader type: Symbol + attr_reader message: String + attr_reader location: Location + attr_reader level: Symbol + + def initialize: (Symbol type, String message, Location location, Symbol level) -> void + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + end + + class ParseWarning + attr_reader type: Symbol + attr_reader message: String + attr_reader location: Location + attr_reader level: Symbol + + def initialize: (Symbol type, String message, Location location, Symbol level) -> void + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + end + + class Result + attr_reader comments: Array[comment] + attr_reader magic_comments: Array[MagicComment] + attr_reader data_loc: Location? + attr_reader errors: Array[ParseError] + attr_reader warnings: Array[ParseWarning] + attr_reader source: Source + + def initialize: (Array[comment] comments, Array[MagicComment] magic_comments, Location? data_loc, Array[ParseError] errors, Array[ParseWarning] warnings, Source source) -> void + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def encoding: () -> Encoding + def success?: () -> bool + def failure?: () -> bool + def code_units_cache: (Encoding encoding) -> _CodeUnitsCache + end + + class ParseResult < Result + attr_reader value: ProgramNode + + def initialize: (ProgramNode value, Array[comment] comments, Array[MagicComment] magic_comments, Location? data_loc, Array[ParseError] errors, Array[ParseWarning] warnings, Source source) -> void + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def errors_format: () -> String + end + + class LexResult < Result + attr_reader value: Array[[Token, Integer]] + + def initialize: (Array[[Token, Integer]] value, Array[comment] comments, Array[MagicComment] magic_comments, Location? data_loc, Array[ParseError] errors, Array[ParseWarning] warnings, Source source) -> void + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + end + + class ParseLexResult < Result + attr_reader value: [ProgramNode, Array[[Token, Integer]]] + + def initialize: ([ProgramNode, Array[[Token, Integer]]] value, Array[comment] comments, Array[MagicComment] magic_comments, Location? data_loc, Array[ParseError] errors, Array[ParseWarning] warnings, Source source) -> void + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + end + + class Token + attr_reader source: Source + attr_reader type: Symbol + attr_reader value: String + attr_reader location: Location + + def initialize: (Source source, Symbol type, String value, Location location) -> void + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def pretty_print: (untyped q) -> untyped + def ==: (untyped other) -> bool + def deep_freeze: () -> void + end + + class Scope + attr_reader locals: Array[Symbol] + attr_reader forwarding: Array[Symbol] + + def initialize: (Array[Symbol] locals, Array[Symbol] forwarding) -> void + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/parse_result/comments.rbs b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/parse_result/comments.rbs new file mode 100644 index 0000000..5b9e315 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/parse_result/comments.rbs @@ -0,0 +1,38 @@ +module Prism + class ParseResult < Result + class Comments + interface _Target + def start_offset: () -> Integer + def end_offset: () -> Integer + def encloses?: (comment) -> bool + def leading_comment: (comment) -> void + def trailing_comment: (comment) -> void + end + + class NodeTarget + include _Target + + attr_reader node: node + + def initialize: (node) -> void + end + + class LocationTarget + include _Target + + attr_reader location: Location + + def initialize: (Location location) -> void + end + + attr_reader parse_result: ParseResult + + def initialize: (ParseResult parse_result) -> void + def attach!: () -> void + + private + + def nearest_targets: (node, comment) -> [_Target?, _Target, _Target?] + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/pattern.rbs b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/pattern.rbs new file mode 100644 index 0000000..a70293e --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/pattern.rbs @@ -0,0 +1,13 @@ +module Prism + class Pattern + class CompilationError < StandardError + end + + attr_reader query: String + + def initialize: (String query) -> void + def compile: () -> Proc + def scan: (Prism::node root) { (Prism::node) -> void } -> void + | (Prism::node root) -> ::Enumerator[Prism::node, void] + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/reflection.rbs b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/reflection.rbs new file mode 100644 index 0000000..047ea32 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/reflection.rbs @@ -0,0 +1,50 @@ +module Prism + module Reflection + class Field + attr_reader name: Symbol + + def initialize: (Symbol name) -> void + end + + class NodeField < Field + end + + class OptionalNodeField < Field + end + + class NodeListField < Field + end + + class ConstantField < Field + end + + class OptionalConstantField < Field + end + + class ConstantListField < Field + end + + class StringField < Field + end + + class LocationField < Field + end + + class OptionalLocationField < Field + end + + class IntegerField < Field + end + + class FloatField < Field + end + + class FlagsField < Field + attr_reader flags: Array[Symbol] + + def initialize: (Symbol name, Array[Symbol] flags) -> void + end + + def self.fields_for: (singleton(Node) node) -> Array[Field] + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/relocation.rbs b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/relocation.rbs new file mode 100644 index 0000000..7f5637d --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/relocation.rbs @@ -0,0 +1,185 @@ +module Prism + module Relocation + interface _Value + def start_line: () -> Integer + def end_line: () -> Integer + def start_offset: () -> Integer + def end_offset: () -> Integer + def start_character_offset: () -> Integer + def end_character_offset: () -> Integer + def cached_start_code_units_offset: (_CodeUnitsCache cache) -> Integer + def cached_end_code_units_offset: (_CodeUnitsCache cache) -> Integer + def start_column: () -> Integer + def end_column: () -> Integer + def start_character_column: () -> Integer + def end_character_column: () -> Integer + def cached_start_code_units_column: (_CodeUnitsCache cache) -> Integer + def cached_end_code_units_column: (_CodeUnitsCache cache) -> Integer + def leading_comments: () -> Array[Comment] + def trailing_comments: () -> Array[Comment] + end + + interface _Field + def fields: (_Value value) -> entry_values + end + + type entry_value = untyped + type entry_values = Hash[Symbol, entry_value] + + class Entry + class MissingValueError < StandardError + end + + def initialize: (Repository repository) -> void + + def filepath: () -> String + + def start_line: () -> Integer + def end_line: () -> Integer + + def start_offset: () -> Integer + def end_offset: () -> Integer + def start_character_offset: () -> Integer + def end_character_offset: () -> Integer + def start_code_units_offset: () -> Integer + def end_code_units_offset: () -> Integer + + def start_column: () -> Integer + def end_column: () -> Integer + def start_character_column: () -> Integer + def end_character_column: () -> Integer + def start_code_units_column: () -> Integer + def end_code_units_column: () -> Integer + + def leading_comments: () -> Array[CommentsField::Comment] + def trailing_comments: () -> Array[CommentsField::Comment] + def comments: () -> Array[CommentsField::Comment] + + private + + def fetch_value: (Symbol name) -> entry_value + def values: () -> entry_values + end + + class Source + attr_reader value: untyped + + def initialize: (untyped value) -> void + + def result: () -> ParseResult + def code_units_cache: (Encoding encoding) -> _CodeUnitsCache + end + + class SourceFilepath < Source + def result: () -> ParseResult + end + + class SourceString < Source + def result: () -> ParseResult + end + + class FilepathField + attr_reader value: String + + def initialize: (String value) -> void + + def fields: (_Value value) -> entry_values + end + + class LinesField + def fields: (_Value value) -> entry_values + end + + class OffsetsField + def fields: (_Value value) -> entry_values + end + + class CharacterOffsetsField + def fields: (_Value value) -> entry_values + end + + class CodeUnitOffsetsField + attr_reader repository: Repository + attr_reader encoding: Encoding + + def initialize: (Repository repository, Encoding encoding) -> void + def fields: (_Value value) -> entry_values + + private + + def cache: () -> _CodeUnitsCache + end + + class ColumnsField + def fields: (_Value value) -> entry_values + end + + class CharacterColumnsField + def fields: (_Value value) -> entry_values + end + + class CodeUnitColumnsField + attr_reader repository: Repository + attr_reader encoding: Encoding + + def initialize: (Repository repository, Encoding encoding) -> void + def fields: (_Value value) -> entry_values + + private + + def cache: () -> _CodeUnitsCache + end + + class CommentsField + class Comment + attr_reader slice: String + + def initialize: (String slice) -> void + end + + private + + def comments: (entry_value value) -> Array[Comment] + end + + class LeadingCommentsField < CommentsField + def fields: (_Value value) -> entry_values + end + + class TrailingCommentsField < CommentsField + def fields: (_Value value) -> entry_values + end + + class Repository + class ConfigurationError < StandardError + end + + attr_reader source: Source + attr_reader fields: Hash[Symbol, _Field] + attr_reader entries: Hash[Integer, Hash[Symbol, Entry]] + + def initialize: (Source source) -> void + + def code_units_cache: (Encoding encoding) -> _CodeUnitsCache + + def filepath: () -> self + def lines: () -> self + def offsets: () -> self + def character_offsets: () -> self + def code_unit_offsets: (Encoding encoding) -> self + def columns: () -> self + def character_columns: () -> self + def code_unit_columns: (Encoding encoding) -> self + def leading_comments: () -> self + def trailing_comments: () -> self + def comments: () -> self + + private + + def field: (Symbol name, _Field) -> self + end + + def self.filepath: (String value) -> Repository + def self.string: (String value) -> Repository + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/serialize.rbs b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/serialize.rbs new file mode 100644 index 0000000..71a7c5c --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/serialize.rbs @@ -0,0 +1,8 @@ +module Prism + module Serialize + def self.load_parse: (String, String, bool) -> ParseResult + def self.load_lex: (String, String, bool) -> LexResult + def self.load_parse_comments: (String, String, bool) -> Array[comment] + def self.load_parse_lex: (String, String, bool) -> ParseLexResult + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/string_query.rbs b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/string_query.rbs new file mode 100644 index 0000000..098746e --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/string_query.rbs @@ -0,0 +1,11 @@ +module Prism + class StringQuery + attr_reader string: String + + def initialize: (String string) -> void + + def local?: () -> bool + def constant?: () -> bool + def method_name?: () -> bool + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/visitor.rbs b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/visitor.rbs new file mode 100644 index 0000000..0aa12dc --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/sig/prism/visitor.rbs @@ -0,0 +1,169 @@ +# This file is generated by the templates/template.rb script and should not be +# modified manually. See templates/sig/prism/visitor.rbs.erb +# if you are looking to modify the template + +module Prism + class BasicVisitor + def visit: (Prism::node?) -> void + def visit_all: (Array[Prism::node?]) -> void + def visit_child_nodes: (Prism::node) -> void + end + + interface _Visitor + def visit_alias_global_variable_node: (AliasGlobalVariableNode) -> void + def visit_alias_method_node: (AliasMethodNode) -> void + def visit_alternation_pattern_node: (AlternationPatternNode) -> void + def visit_and_node: (AndNode) -> void + def visit_arguments_node: (ArgumentsNode) -> void + def visit_array_node: (ArrayNode) -> void + def visit_array_pattern_node: (ArrayPatternNode) -> void + def visit_assoc_node: (AssocNode) -> void + def visit_assoc_splat_node: (AssocSplatNode) -> void + def visit_back_reference_read_node: (BackReferenceReadNode) -> void + def visit_begin_node: (BeginNode) -> void + def visit_block_argument_node: (BlockArgumentNode) -> void + def visit_block_local_variable_node: (BlockLocalVariableNode) -> void + def visit_block_node: (BlockNode) -> void + def visit_block_parameter_node: (BlockParameterNode) -> void + def visit_block_parameters_node: (BlockParametersNode) -> void + def visit_break_node: (BreakNode) -> void + def visit_call_and_write_node: (CallAndWriteNode) -> void + def visit_call_node: (CallNode) -> void + def visit_call_operator_write_node: (CallOperatorWriteNode) -> void + def visit_call_or_write_node: (CallOrWriteNode) -> void + def visit_call_target_node: (CallTargetNode) -> void + def visit_capture_pattern_node: (CapturePatternNode) -> void + def visit_case_match_node: (CaseMatchNode) -> void + def visit_case_node: (CaseNode) -> void + def visit_class_node: (ClassNode) -> void + def visit_class_variable_and_write_node: (ClassVariableAndWriteNode) -> void + def visit_class_variable_operator_write_node: (ClassVariableOperatorWriteNode) -> void + def visit_class_variable_or_write_node: (ClassVariableOrWriteNode) -> void + def visit_class_variable_read_node: (ClassVariableReadNode) -> void + def visit_class_variable_target_node: (ClassVariableTargetNode) -> void + def visit_class_variable_write_node: (ClassVariableWriteNode) -> void + def visit_constant_and_write_node: (ConstantAndWriteNode) -> void + def visit_constant_operator_write_node: (ConstantOperatorWriteNode) -> void + def visit_constant_or_write_node: (ConstantOrWriteNode) -> void + def visit_constant_path_and_write_node: (ConstantPathAndWriteNode) -> void + def visit_constant_path_node: (ConstantPathNode) -> void + def visit_constant_path_operator_write_node: (ConstantPathOperatorWriteNode) -> void + def visit_constant_path_or_write_node: (ConstantPathOrWriteNode) -> void + def visit_constant_path_target_node: (ConstantPathTargetNode) -> void + def visit_constant_path_write_node: (ConstantPathWriteNode) -> void + def visit_constant_read_node: (ConstantReadNode) -> void + def visit_constant_target_node: (ConstantTargetNode) -> void + def visit_constant_write_node: (ConstantWriteNode) -> void + def visit_def_node: (DefNode) -> void + def visit_defined_node: (DefinedNode) -> void + def visit_else_node: (ElseNode) -> void + def visit_embedded_statements_node: (EmbeddedStatementsNode) -> void + def visit_embedded_variable_node: (EmbeddedVariableNode) -> void + def visit_ensure_node: (EnsureNode) -> void + def visit_false_node: (FalseNode) -> void + def visit_find_pattern_node: (FindPatternNode) -> void + def visit_flip_flop_node: (FlipFlopNode) -> void + def visit_float_node: (FloatNode) -> void + def visit_for_node: (ForNode) -> void + def visit_forwarding_arguments_node: (ForwardingArgumentsNode) -> void + def visit_forwarding_parameter_node: (ForwardingParameterNode) -> void + def visit_forwarding_super_node: (ForwardingSuperNode) -> void + def visit_global_variable_and_write_node: (GlobalVariableAndWriteNode) -> void + def visit_global_variable_operator_write_node: (GlobalVariableOperatorWriteNode) -> void + def visit_global_variable_or_write_node: (GlobalVariableOrWriteNode) -> void + def visit_global_variable_read_node: (GlobalVariableReadNode) -> void + def visit_global_variable_target_node: (GlobalVariableTargetNode) -> void + def visit_global_variable_write_node: (GlobalVariableWriteNode) -> void + def visit_hash_node: (HashNode) -> void + def visit_hash_pattern_node: (HashPatternNode) -> void + def visit_if_node: (IfNode) -> void + def visit_imaginary_node: (ImaginaryNode) -> void + def visit_implicit_node: (ImplicitNode) -> void + def visit_implicit_rest_node: (ImplicitRestNode) -> void + def visit_in_node: (InNode) -> void + def visit_index_and_write_node: (IndexAndWriteNode) -> void + def visit_index_operator_write_node: (IndexOperatorWriteNode) -> void + def visit_index_or_write_node: (IndexOrWriteNode) -> void + def visit_index_target_node: (IndexTargetNode) -> void + def visit_instance_variable_and_write_node: (InstanceVariableAndWriteNode) -> void + def visit_instance_variable_operator_write_node: (InstanceVariableOperatorWriteNode) -> void + def visit_instance_variable_or_write_node: (InstanceVariableOrWriteNode) -> void + def visit_instance_variable_read_node: (InstanceVariableReadNode) -> void + def visit_instance_variable_target_node: (InstanceVariableTargetNode) -> void + def visit_instance_variable_write_node: (InstanceVariableWriteNode) -> void + def visit_integer_node: (IntegerNode) -> void + def visit_interpolated_match_last_line_node: (InterpolatedMatchLastLineNode) -> void + def visit_interpolated_regular_expression_node: (InterpolatedRegularExpressionNode) -> void + def visit_interpolated_string_node: (InterpolatedStringNode) -> void + def visit_interpolated_symbol_node: (InterpolatedSymbolNode) -> void + def visit_interpolated_x_string_node: (InterpolatedXStringNode) -> void + def visit_it_local_variable_read_node: (ItLocalVariableReadNode) -> void + def visit_it_parameters_node: (ItParametersNode) -> void + def visit_keyword_hash_node: (KeywordHashNode) -> void + def visit_keyword_rest_parameter_node: (KeywordRestParameterNode) -> void + def visit_lambda_node: (LambdaNode) -> void + def visit_local_variable_and_write_node: (LocalVariableAndWriteNode) -> void + def visit_local_variable_operator_write_node: (LocalVariableOperatorWriteNode) -> void + def visit_local_variable_or_write_node: (LocalVariableOrWriteNode) -> void + def visit_local_variable_read_node: (LocalVariableReadNode) -> void + def visit_local_variable_target_node: (LocalVariableTargetNode) -> void + def visit_local_variable_write_node: (LocalVariableWriteNode) -> void + def visit_match_last_line_node: (MatchLastLineNode) -> void + def visit_match_predicate_node: (MatchPredicateNode) -> void + def visit_match_required_node: (MatchRequiredNode) -> void + def visit_match_write_node: (MatchWriteNode) -> void + def visit_missing_node: (MissingNode) -> void + def visit_module_node: (ModuleNode) -> void + def visit_multi_target_node: (MultiTargetNode) -> void + def visit_multi_write_node: (MultiWriteNode) -> void + def visit_next_node: (NextNode) -> void + def visit_nil_node: (NilNode) -> void + def visit_no_keywords_parameter_node: (NoKeywordsParameterNode) -> void + def visit_numbered_parameters_node: (NumberedParametersNode) -> void + def visit_numbered_reference_read_node: (NumberedReferenceReadNode) -> void + def visit_optional_keyword_parameter_node: (OptionalKeywordParameterNode) -> void + def visit_optional_parameter_node: (OptionalParameterNode) -> void + def visit_or_node: (OrNode) -> void + def visit_parameters_node: (ParametersNode) -> void + def visit_parentheses_node: (ParenthesesNode) -> void + def visit_pinned_expression_node: (PinnedExpressionNode) -> void + def visit_pinned_variable_node: (PinnedVariableNode) -> void + def visit_post_execution_node: (PostExecutionNode) -> void + def visit_pre_execution_node: (PreExecutionNode) -> void + def visit_program_node: (ProgramNode) -> void + def visit_range_node: (RangeNode) -> void + def visit_rational_node: (RationalNode) -> void + def visit_redo_node: (RedoNode) -> void + def visit_regular_expression_node: (RegularExpressionNode) -> void + def visit_required_keyword_parameter_node: (RequiredKeywordParameterNode) -> void + def visit_required_parameter_node: (RequiredParameterNode) -> void + def visit_rescue_modifier_node: (RescueModifierNode) -> void + def visit_rescue_node: (RescueNode) -> void + def visit_rest_parameter_node: (RestParameterNode) -> void + def visit_retry_node: (RetryNode) -> void + def visit_return_node: (ReturnNode) -> void + def visit_self_node: (SelfNode) -> void + def visit_shareable_constant_node: (ShareableConstantNode) -> void + def visit_singleton_class_node: (SingletonClassNode) -> void + def visit_source_encoding_node: (SourceEncodingNode) -> void + def visit_source_file_node: (SourceFileNode) -> void + def visit_source_line_node: (SourceLineNode) -> void + def visit_splat_node: (SplatNode) -> void + def visit_statements_node: (StatementsNode) -> void + def visit_string_node: (StringNode) -> void + def visit_super_node: (SuperNode) -> void + def visit_symbol_node: (SymbolNode) -> void + def visit_true_node: (TrueNode) -> void + def visit_undef_node: (UndefNode) -> void + def visit_unless_node: (UnlessNode) -> void + def visit_until_node: (UntilNode) -> void + def visit_when_node: (WhenNode) -> void + def visit_while_node: (WhileNode) -> void + def visit_x_string_node: (XStringNode) -> void + def visit_yield_node: (YieldNode) -> void + end + + class Visitor < BasicVisitor + include _Visitor + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/diagnostic.c b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/diagnostic.c new file mode 100644 index 0000000..df0d564 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/diagnostic.c @@ -0,0 +1,854 @@ +/* :markup: markdown */ + +/*----------------------------------------------------------------------------*/ +/* This file is generated by the templates/template.rb script and should not */ +/* be modified manually. See */ +/* templates/src/diagnostic.c.erb */ +/* if you are looking to modify the */ +/* template */ +/*----------------------------------------------------------------------------*/ + +#include "prism/diagnostic.h" + +#define PM_DIAGNOSTIC_ID_MAX 324 + +/** 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_MULTI] = { "both block arg and actual block given; only one block is allowed", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ARGUMENT_CONFLICT_AMPERSAND] = { "unexpected `&`; anonymous block parameter is also used within block", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ARGUMENT_CONFLICT_STAR] = { "unexpected `*`; anonymous rest parameter is also used within block", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ARGUMENT_CONFLICT_STAR_STAR] = { "unexpected `**`; anonymous keyword rest parameter is also used within block", 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_NO_FORWARDING_AMPERSAND] = { "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] = { "unexpected %s; expected a `,` separator for the array elements", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ARRAY_TERM] = { "unexpected %s; 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_PARAMETERS] = { "could not parse the endless method parameters", 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] = { "unexpected %s; 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 escape character syntax", 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 hex escape sequence", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ESCAPE_INVALID_META] = { "Invalid escape character syntax", 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_LIST] = { "invalid Unicode list: %.*s", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ESCAPE_INVALID_UNICODE_LITERAL] = { "invalid Unicode escape sequence; Multiple codepoints at single character literal are disallowed", 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_SHORT] = { "too short escape sequence: %.*s", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ESCAPE_INVALID_UNICODE_TERM] = { "unterminated Unicode escape", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPECT_ARGUMENT] = { "unexpected %s; 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] = { "unexpected %s; 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_FOR_DELIMITER] = { "unexpected %s; expected a 'do', newline, or ';' after the 'for' loop collection", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPECT_IDENT_REQ_PARAMETER] = { "expected an identifier for the required parameter", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPECT_IN_DELIMITER] = { "expected a delimiter after the patterns of an `in` clause", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPECT_LPAREN_AFTER_NOT_LPAREN] = { "expected a `(` immediately after `not`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPECT_LPAREN_AFTER_NOT_OTHER] = { "expected a `(` after `not`", 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_SINGLETON_CLASS_DELIMITER] = { "unexpected %s; expected a newline or a ';' after the singleton class", 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_NUMBERED] = { "Can't assign to numbered parameter %.2s", 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] = { "unexpected %s; expected a value in the hash literal", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_HEREDOC_IDENTIFIER] = { "unterminated here document identifier", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_HEREDOC_TERM] = { "unterminated heredoc; can't find string \"%.*s\" anywhere before EOF", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INCOMPLETE_QUESTION_MARK] = { "incomplete expression at `?`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INCOMPLETE_VARIABLE_CLASS_3_3] = { "`%.*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] = { "`%.*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_COMMA] = { "invalid comma", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_ESCAPE_CHARACTER] = { "Invalid escape character syntax", 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_FRACTION] = { "unexpected fraction part after numeric literal", 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_INNER] = { "invalid underscore placement in number", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_NUMBER_UNDERSCORE_TRAILING] = { "trailing '_' in number", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_CHARACTER] = { "Invalid char '\\x%02X' in expression", 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] = { "unknown type of %string", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_PERCENT_EOF] = { "unterminated quoted string meets end of file", PM_ERROR_LEVEL_SYNTAX }, + [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_SYMBOL] = { "invalid symbol", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_VARIABLE_GLOBAL_3_3] = { "`%.*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 a numbered parameter is already used", 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] = { "unterminated list; expected a closing delimiter for the `%i`", 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] = { "unterminated list; expected a closing delimiter for the `%I`", 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] = { "unterminated list; expected a closing delimiter for the `%w`", 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] = { "unterminated list; expected a closing delimiter for the `%W`", 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_NESTING_TOO_DEEP] = { "nesting too deep", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_NO_LOCAL_VARIABLE] = { "%.*s: no such local variable", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_NON_ASSOCIATIVE_OPERATOR] = { "unexpected %s; %s is a non-associative operator", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_NOT_EXPRESSION] = { "expected an expression after `not`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_NUMBER_LITERAL_UNDERSCORE] = { "number literal ending with a `_`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_NUMBERED_PARAMETER_INNER_BLOCK] = { "numbered parameter is already used in inner block", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_NUMBERED_PARAMETER_IT] = { "numbered parameters are not allowed when 'it' is already used", 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_BLOCK] = { "numbered parameter is already used in outer block", 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_FORWARDING_AFTER_REST] = { "... after rest argument", 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_PARAMETER_UNEXPECTED_NO_KW] = { "unexpected **nil; no keywords marker disallowed after keywords", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PATTERN_ARRAY_MULTIPLE_RESTS] = { "unexpected multiple '*' rest patterns in an array pattern", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PATTERN_CAPTURE_DUPLICATE] = { "duplicated variable name", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PATTERN_CAPTURE_IN_ALTERNATIVE] = { "variable capture in alternative pattern", 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_FIND_MISSING_INNER] = { "find patterns need at least one required inner pattern", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PATTERN_HASH_IMPLICIT] = { "unexpected implicit hash in pattern; use '{' to delineate", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PATTERN_HASH_KEY] = { "unexpected %s; 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_INTERPOLATED] = { "symbol literal with interpolation is not allowed", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PATTERN_HASH_KEY_LABEL] = { "expected a label as the key in the hash pattern", PM_ERROR_LEVEL_SYNTAX }, + [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_PARSE_ERROR] = { "%s", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_REGEXP_UNKNOWN_OPTIONS] = { "unknown regexp %s - %.*s", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_REGEXP_TERM] = { "unterminated regexp meets end of file; expected a closing delimiter", 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] = { "unterminated string; 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] = { "unterminated quoted string; expected a closing delimiter for the dynamic symbol", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_SYMBOL_TERM_INTERPOLATED] = { "unterminated symbol; 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_UNARY_RECEIVER] = { "unexpected %s, expected a receiver for unary `%c`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_UNARY_DISALLOWED] = { "unexpected %s; unary calls are not allowed in this context", 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_UNEXPECTED_BLOCK_ARGUMENT] = { "block argument should not be given", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_UNEXPECTED_INDEX_BLOCK] = { "unexpected block arg given in index assignment; blocks are not allowed in index assignment expressions", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_UNEXPECTED_INDEX_KEYWORDS] = { "unexpected keyword arg given in index assignment; keywords are not allowed in index assignment expressions", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_UNEXPECTED_LABEL] = { "unexpected label", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_UNEXPECTED_MULTI_WRITE] = { "unexpected multiple assignment; multiple assignment is not allowed in this context", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_UNEXPECTED_PARAMETER_DEFAULT_VALUE] = { "unexpected %s; expected a default value for a parameter", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_UNEXPECTED_RANGE_OPERATOR] = { "unexpected range operator; .. and ... are non-associative and cannot be chained", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_UNEXPECTED_SAFE_NAVIGATION] = { "&. inside multiple assignment destination", 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_BINARY_OPERATOR] = { "'%s' after local variable or literal is interpreted as binary operator even though it seems like %s", PM_WARNING_LEVEL_VERBOSE }, + [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] = { "'when' clause on line %" PRIi32 " duplicates 'when' clause on line %" PRIi32 " and is ignored", PM_WARNING_LEVEL_VERBOSE }, + [PM_WARN_EQUAL_IN_CONDITIONAL_3_3] = { "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_INDENTATION_MISMATCH] = { "mismatched indentations at '%.*s' with '%.*s' at %" PRIi32, 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_MAGIC_COMMENT_VALUE] = { "invalid value for %.*s: %.*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_SHAREABLE_CONSTANT_VALUE_LINE] = { "'shareable_constant_value' is ignored unless in comment-only line", 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) { + case PM_ERR_ALIAS_ARGUMENT: return "alias_argument"; + case PM_ERR_ALIAS_ARGUMENT_NUMBERED_REFERENCE: return "alias_argument_numbered_reference"; + case PM_ERR_AMPAMPEQ_MULTI_ASSIGN: return "ampampeq_multi_assign"; + case PM_ERR_ARGUMENT_AFTER_BLOCK: return "argument_after_block"; + case PM_ERR_ARGUMENT_AFTER_FORWARDING_ELLIPSES: return "argument_after_forwarding_ellipses"; + case PM_ERR_ARGUMENT_BARE_HASH: return "argument_bare_hash"; + case PM_ERR_ARGUMENT_BLOCK_FORWARDING: return "argument_block_forwarding"; + case PM_ERR_ARGUMENT_BLOCK_MULTI: return "argument_block_multi"; + case PM_ERR_ARGUMENT_CONFLICT_AMPERSAND: return "argument_conflict_ampersand"; + case PM_ERR_ARGUMENT_CONFLICT_STAR: return "argument_conflict_star"; + case PM_ERR_ARGUMENT_CONFLICT_STAR_STAR: return "argument_conflict_star_star"; + case PM_ERR_ARGUMENT_FORMAL_CLASS: return "argument_formal_class"; + case PM_ERR_ARGUMENT_FORMAL_CONSTANT: return "argument_formal_constant"; + case PM_ERR_ARGUMENT_FORMAL_GLOBAL: return "argument_formal_global"; + case PM_ERR_ARGUMENT_FORMAL_IVAR: return "argument_formal_ivar"; + case PM_ERR_ARGUMENT_FORWARDING_UNBOUND: return "argument_forwarding_unbound"; + case PM_ERR_ARGUMENT_NO_FORWARDING_AMPERSAND: return "argument_no_forwarding_ampersand"; + case PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES: return "argument_no_forwarding_ellipses"; + case PM_ERR_ARGUMENT_NO_FORWARDING_STAR: return "argument_no_forwarding_star"; + case PM_ERR_ARGUMENT_NO_FORWARDING_STAR_STAR: return "argument_no_forwarding_star_star"; + case PM_ERR_ARGUMENT_SPLAT_AFTER_ASSOC_SPLAT: return "argument_splat_after_assoc_splat"; + case PM_ERR_ARGUMENT_SPLAT_AFTER_SPLAT: return "argument_splat_after_splat"; + case PM_ERR_ARGUMENT_TERM_PAREN: return "argument_term_paren"; + case PM_ERR_ARGUMENT_UNEXPECTED_BLOCK: return "argument_unexpected_block"; + case PM_ERR_ARRAY_ELEMENT: return "array_element"; + case PM_ERR_ARRAY_EXPRESSION: return "array_expression"; + case PM_ERR_ARRAY_EXPRESSION_AFTER_STAR: return "array_expression_after_star"; + case PM_ERR_ARRAY_SEPARATOR: return "array_separator"; + case PM_ERR_ARRAY_TERM: return "array_term"; + case PM_ERR_BEGIN_LONELY_ELSE: return "begin_lonely_else"; + case PM_ERR_BEGIN_TERM: return "begin_term"; + case PM_ERR_BEGIN_UPCASE_BRACE: return "begin_upcase_brace"; + case PM_ERR_BEGIN_UPCASE_TERM: return "begin_upcase_term"; + case PM_ERR_BEGIN_UPCASE_TOPLEVEL: return "begin_upcase_toplevel"; + case PM_ERR_BLOCK_PARAM_LOCAL_VARIABLE: return "block_param_local_variable"; + case PM_ERR_BLOCK_PARAM_PIPE_TERM: return "block_param_pipe_term"; + case PM_ERR_BLOCK_TERM_BRACE: return "block_term_brace"; + case PM_ERR_BLOCK_TERM_END: return "block_term_end"; + case PM_ERR_CANNOT_PARSE_EXPRESSION: return "cannot_parse_expression"; + case PM_ERR_CANNOT_PARSE_STRING_PART: return "cannot_parse_string_part"; + case PM_ERR_CASE_EXPRESSION_AFTER_CASE: return "case_expression_after_case"; + case PM_ERR_CASE_EXPRESSION_AFTER_WHEN: return "case_expression_after_when"; + case PM_ERR_CASE_MATCH_MISSING_PREDICATE: return "case_match_missing_predicate"; + case PM_ERR_CASE_MISSING_CONDITIONS: return "case_missing_conditions"; + case PM_ERR_CASE_TERM: return "case_term"; + case PM_ERR_CLASS_IN_METHOD: return "class_in_method"; + case PM_ERR_CLASS_NAME: return "class_name"; + case PM_ERR_CLASS_SUPERCLASS: return "class_superclass"; + case PM_ERR_CLASS_TERM: return "class_term"; + case PM_ERR_CLASS_UNEXPECTED_END: return "class_unexpected_end"; + case PM_ERR_CLASS_VARIABLE_BARE: return "class_variable_bare"; + case PM_ERR_CONDITIONAL_ELSIF_PREDICATE: return "conditional_elsif_predicate"; + case PM_ERR_CONDITIONAL_IF_PREDICATE: return "conditional_if_predicate"; + case PM_ERR_CONDITIONAL_PREDICATE_TERM: return "conditional_predicate_term"; + case PM_ERR_CONDITIONAL_TERM: return "conditional_term"; + case PM_ERR_CONDITIONAL_TERM_ELSE: return "conditional_term_else"; + case PM_ERR_CONDITIONAL_UNLESS_PREDICATE: return "conditional_unless_predicate"; + case PM_ERR_CONDITIONAL_UNTIL_PREDICATE: return "conditional_until_predicate"; + case PM_ERR_CONDITIONAL_WHILE_PREDICATE: return "conditional_while_predicate"; + case PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT: return "constant_path_colon_colon_constant"; + case PM_ERR_DEF_ENDLESS: return "def_endless"; + case PM_ERR_DEF_ENDLESS_PARAMETERS: return "def_endless_parameters"; + case PM_ERR_DEF_ENDLESS_SETTER: return "def_endless_setter"; + case PM_ERR_DEF_NAME: return "def_name"; + case PM_ERR_DEF_PARAMS_TERM: return "def_params_term"; + case PM_ERR_DEF_PARAMS_TERM_PAREN: return "def_params_term_paren"; + case PM_ERR_DEF_RECEIVER: return "def_receiver"; + case PM_ERR_DEF_RECEIVER_TERM: return "def_receiver_term"; + case PM_ERR_DEF_TERM: return "def_term"; + case PM_ERR_DEFINED_EXPRESSION: return "defined_expression"; + case PM_ERR_EMBDOC_TERM: return "embdoc_term"; + case PM_ERR_EMBEXPR_END: return "embexpr_end"; + case PM_ERR_EMBVAR_INVALID: return "embvar_invalid"; + case PM_ERR_END_UPCASE_BRACE: return "end_upcase_brace"; + case PM_ERR_END_UPCASE_TERM: return "end_upcase_term"; + case PM_ERR_ESCAPE_INVALID_CONTROL: return "escape_invalid_control"; + case PM_ERR_ESCAPE_INVALID_CONTROL_REPEAT: return "escape_invalid_control_repeat"; + case PM_ERR_ESCAPE_INVALID_HEXADECIMAL: return "escape_invalid_hexadecimal"; + case PM_ERR_ESCAPE_INVALID_META: return "escape_invalid_meta"; + case PM_ERR_ESCAPE_INVALID_META_REPEAT: return "escape_invalid_meta_repeat"; + case PM_ERR_ESCAPE_INVALID_UNICODE: return "escape_invalid_unicode"; + case PM_ERR_ESCAPE_INVALID_UNICODE_CM_FLAGS: return "escape_invalid_unicode_cm_flags"; + case PM_ERR_ESCAPE_INVALID_UNICODE_LIST: return "escape_invalid_unicode_list"; + case PM_ERR_ESCAPE_INVALID_UNICODE_LITERAL: return "escape_invalid_unicode_literal"; + case PM_ERR_ESCAPE_INVALID_UNICODE_LONG: return "escape_invalid_unicode_long"; + case PM_ERR_ESCAPE_INVALID_UNICODE_SHORT: return "escape_invalid_unicode_short"; + case PM_ERR_ESCAPE_INVALID_UNICODE_TERM: return "escape_invalid_unicode_term"; + case PM_ERR_EXPECT_ARGUMENT: return "expect_argument"; + case PM_ERR_EXPECT_EOL_AFTER_STATEMENT: return "expect_eol_after_statement"; + case PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ: return "expect_expression_after_ampampeq"; + case PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA: return "expect_expression_after_comma"; + case PM_ERR_EXPECT_EXPRESSION_AFTER_EQUAL: return "expect_expression_after_equal"; + case PM_ERR_EXPECT_EXPRESSION_AFTER_LESS_LESS: return "expect_expression_after_less_less"; + case PM_ERR_EXPECT_EXPRESSION_AFTER_LPAREN: return "expect_expression_after_lparen"; + case PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR: return "expect_expression_after_operator"; + case PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ: return "expect_expression_after_pipepipeeq"; + case PM_ERR_EXPECT_EXPRESSION_AFTER_QUESTION: return "expect_expression_after_question"; + case PM_ERR_EXPECT_EXPRESSION_AFTER_SPLAT: return "expect_expression_after_splat"; + case PM_ERR_EXPECT_EXPRESSION_AFTER_SPLAT_HASH: return "expect_expression_after_splat_hash"; + case PM_ERR_EXPECT_EXPRESSION_AFTER_STAR: return "expect_expression_after_star"; + case PM_ERR_EXPECT_FOR_DELIMITER: return "expect_for_delimiter"; + case PM_ERR_EXPECT_IDENT_REQ_PARAMETER: return "expect_ident_req_parameter"; + case PM_ERR_EXPECT_IN_DELIMITER: return "expect_in_delimiter"; + case PM_ERR_EXPECT_LPAREN_AFTER_NOT_LPAREN: return "expect_lparen_after_not_lparen"; + case PM_ERR_EXPECT_LPAREN_AFTER_NOT_OTHER: return "expect_lparen_after_not_other"; + case PM_ERR_EXPECT_LPAREN_REQ_PARAMETER: return "expect_lparen_req_parameter"; + case PM_ERR_EXPECT_MESSAGE: return "expect_message"; + case PM_ERR_EXPECT_RBRACKET: return "expect_rbracket"; + case PM_ERR_EXPECT_RPAREN: return "expect_rparen"; + case PM_ERR_EXPECT_RPAREN_AFTER_MULTI: return "expect_rparen_after_multi"; + case PM_ERR_EXPECT_RPAREN_REQ_PARAMETER: return "expect_rparen_req_parameter"; + case PM_ERR_EXPECT_SINGLETON_CLASS_DELIMITER: return "expect_singleton_class_delimiter"; + case PM_ERR_EXPECT_STRING_CONTENT: return "expect_string_content"; + case PM_ERR_EXPECT_WHEN_DELIMITER: return "expect_when_delimiter"; + case PM_ERR_EXPRESSION_BARE_HASH: return "expression_bare_hash"; + case PM_ERR_EXPRESSION_NOT_WRITABLE: return "expression_not_writable"; + case PM_ERR_EXPRESSION_NOT_WRITABLE_ENCODING: return "expression_not_writable_encoding"; + case PM_ERR_EXPRESSION_NOT_WRITABLE_FALSE: return "expression_not_writable_false"; + case PM_ERR_EXPRESSION_NOT_WRITABLE_FILE: return "expression_not_writable_file"; + case PM_ERR_EXPRESSION_NOT_WRITABLE_LINE: return "expression_not_writable_line"; + case PM_ERR_EXPRESSION_NOT_WRITABLE_NIL: return "expression_not_writable_nil"; + case PM_ERR_EXPRESSION_NOT_WRITABLE_NUMBERED: return "expression_not_writable_numbered"; + case PM_ERR_EXPRESSION_NOT_WRITABLE_SELF: return "expression_not_writable_self"; + case PM_ERR_EXPRESSION_NOT_WRITABLE_TRUE: return "expression_not_writable_true"; + case PM_ERR_FLOAT_PARSE: return "float_parse"; + case PM_ERR_FOR_COLLECTION: return "for_collection"; + case PM_ERR_FOR_IN: return "for_in"; + case PM_ERR_FOR_INDEX: return "for_index"; + case PM_ERR_FOR_TERM: return "for_term"; + case PM_ERR_GLOBAL_VARIABLE_BARE: return "global_variable_bare"; + case PM_ERR_HASH_EXPRESSION_AFTER_LABEL: return "hash_expression_after_label"; + case PM_ERR_HASH_KEY: return "hash_key"; + case PM_ERR_HASH_ROCKET: return "hash_rocket"; + case PM_ERR_HASH_TERM: return "hash_term"; + case PM_ERR_HASH_VALUE: return "hash_value"; + case PM_ERR_HEREDOC_IDENTIFIER: return "heredoc_identifier"; + case PM_ERR_HEREDOC_TERM: return "heredoc_term"; + case PM_ERR_INCOMPLETE_QUESTION_MARK: return "incomplete_question_mark"; + case PM_ERR_INCOMPLETE_VARIABLE_CLASS: return "incomplete_variable_class"; + case PM_ERR_INCOMPLETE_VARIABLE_CLASS_3_3: return "incomplete_variable_class_3_3"; + case PM_ERR_INCOMPLETE_VARIABLE_INSTANCE: return "incomplete_variable_instance"; + case PM_ERR_INCOMPLETE_VARIABLE_INSTANCE_3_3: return "incomplete_variable_instance_3_3"; + case PM_ERR_INSTANCE_VARIABLE_BARE: return "instance_variable_bare"; + case PM_ERR_INVALID_BLOCK_EXIT: return "invalid_block_exit"; + case PM_ERR_INVALID_CHARACTER: return "invalid_character"; + case PM_ERR_INVALID_COMMA: return "invalid_comma"; + case PM_ERR_INVALID_ENCODING_MAGIC_COMMENT: return "invalid_encoding_magic_comment"; + case PM_ERR_INVALID_ESCAPE_CHARACTER: return "invalid_escape_character"; + case PM_ERR_INVALID_FLOAT_EXPONENT: return "invalid_float_exponent"; + case PM_ERR_INVALID_LOCAL_VARIABLE_READ: return "invalid_local_variable_read"; + case PM_ERR_INVALID_LOCAL_VARIABLE_WRITE: return "invalid_local_variable_write"; + case PM_ERR_INVALID_MULTIBYTE_CHAR: return "invalid_multibyte_char"; + case PM_ERR_INVALID_MULTIBYTE_CHARACTER: return "invalid_multibyte_character"; + case PM_ERR_INVALID_MULTIBYTE_ESCAPE: return "invalid_multibyte_escape"; + case PM_ERR_INVALID_NUMBER_BINARY: return "invalid_number_binary"; + case PM_ERR_INVALID_NUMBER_DECIMAL: return "invalid_number_decimal"; + case PM_ERR_INVALID_NUMBER_FRACTION: return "invalid_number_fraction"; + case PM_ERR_INVALID_NUMBER_HEXADECIMAL: return "invalid_number_hexadecimal"; + case PM_ERR_INVALID_NUMBER_OCTAL: return "invalid_number_octal"; + case PM_ERR_INVALID_NUMBER_UNDERSCORE_INNER: return "invalid_number_underscore_inner"; + case PM_ERR_INVALID_NUMBER_UNDERSCORE_TRAILING: return "invalid_number_underscore_trailing"; + case PM_ERR_INVALID_PERCENT: return "invalid_percent"; + case PM_ERR_INVALID_PERCENT_EOF: return "invalid_percent_eof"; + case PM_ERR_INVALID_PRINTABLE_CHARACTER: return "invalid_printable_character"; + case PM_ERR_INVALID_RETRY_AFTER_ELSE: return "invalid_retry_after_else"; + case PM_ERR_INVALID_RETRY_AFTER_ENSURE: return "invalid_retry_after_ensure"; + case PM_ERR_INVALID_RETRY_WITHOUT_RESCUE: return "invalid_retry_without_rescue"; + case PM_ERR_INVALID_SYMBOL: return "invalid_symbol"; + case PM_ERR_INVALID_VARIABLE_GLOBAL: return "invalid_variable_global"; + case PM_ERR_INVALID_VARIABLE_GLOBAL_3_3: return "invalid_variable_global_3_3"; + case PM_ERR_INVALID_YIELD: return "invalid_yield"; + case PM_ERR_IT_NOT_ALLOWED_NUMBERED: return "it_not_allowed_numbered"; + case PM_ERR_IT_NOT_ALLOWED_ORDINARY: return "it_not_allowed_ordinary"; + case PM_ERR_LAMBDA_OPEN: return "lambda_open"; + case PM_ERR_LAMBDA_TERM_BRACE: return "lambda_term_brace"; + case PM_ERR_LAMBDA_TERM_END: return "lambda_term_end"; + case PM_ERR_LIST_I_LOWER_ELEMENT: return "list_i_lower_element"; + case PM_ERR_LIST_I_LOWER_TERM: return "list_i_lower_term"; + case PM_ERR_LIST_I_UPPER_ELEMENT: return "list_i_upper_element"; + case PM_ERR_LIST_I_UPPER_TERM: return "list_i_upper_term"; + case PM_ERR_LIST_W_LOWER_ELEMENT: return "list_w_lower_element"; + case PM_ERR_LIST_W_LOWER_TERM: return "list_w_lower_term"; + case PM_ERR_LIST_W_UPPER_ELEMENT: return "list_w_upper_element"; + case PM_ERR_LIST_W_UPPER_TERM: return "list_w_upper_term"; + case PM_ERR_MALLOC_FAILED: return "malloc_failed"; + case PM_ERR_MIXED_ENCODING: return "mixed_encoding"; + case PM_ERR_MODULE_IN_METHOD: return "module_in_method"; + case PM_ERR_MODULE_NAME: return "module_name"; + case PM_ERR_MODULE_TERM: return "module_term"; + case PM_ERR_MULTI_ASSIGN_MULTI_SPLATS: return "multi_assign_multi_splats"; + case PM_ERR_MULTI_ASSIGN_UNEXPECTED_REST: return "multi_assign_unexpected_rest"; + case PM_ERR_NESTING_TOO_DEEP: return "nesting_too_deep"; + case PM_ERR_NO_LOCAL_VARIABLE: return "no_local_variable"; + case PM_ERR_NON_ASSOCIATIVE_OPERATOR: return "non_associative_operator"; + case PM_ERR_NOT_EXPRESSION: return "not_expression"; + case PM_ERR_NUMBER_LITERAL_UNDERSCORE: return "number_literal_underscore"; + case PM_ERR_NUMBERED_PARAMETER_INNER_BLOCK: return "numbered_parameter_inner_block"; + case PM_ERR_NUMBERED_PARAMETER_IT: return "numbered_parameter_it"; + case PM_ERR_NUMBERED_PARAMETER_ORDINARY: return "numbered_parameter_ordinary"; + case PM_ERR_NUMBERED_PARAMETER_OUTER_BLOCK: return "numbered_parameter_outer_block"; + case PM_ERR_OPERATOR_MULTI_ASSIGN: return "operator_multi_assign"; + case PM_ERR_OPERATOR_WRITE_ARGUMENTS: return "operator_write_arguments"; + case PM_ERR_OPERATOR_WRITE_BLOCK: return "operator_write_block"; + case PM_ERR_PARAMETER_ASSOC_SPLAT_MULTI: return "parameter_assoc_splat_multi"; + case PM_ERR_PARAMETER_BLOCK_MULTI: return "parameter_block_multi"; + case PM_ERR_PARAMETER_CIRCULAR: return "parameter_circular"; + case PM_ERR_PARAMETER_FORWARDING_AFTER_REST: return "parameter_forwarding_after_rest"; + case PM_ERR_PARAMETER_METHOD_NAME: return "parameter_method_name"; + case PM_ERR_PARAMETER_NAME_DUPLICATED: return "parameter_name_duplicated"; + case PM_ERR_PARAMETER_NO_DEFAULT: return "parameter_no_default"; + case PM_ERR_PARAMETER_NO_DEFAULT_KW: return "parameter_no_default_kw"; + case PM_ERR_PARAMETER_NUMBERED_RESERVED: return "parameter_numbered_reserved"; + case PM_ERR_PARAMETER_ORDER: return "parameter_order"; + case PM_ERR_PARAMETER_SPLAT_MULTI: return "parameter_splat_multi"; + case PM_ERR_PARAMETER_STAR: return "parameter_star"; + case PM_ERR_PARAMETER_UNEXPECTED_FWD: return "parameter_unexpected_fwd"; + case PM_ERR_PARAMETER_UNEXPECTED_NO_KW: return "parameter_unexpected_no_kw"; + case PM_ERR_PARAMETER_WILD_LOOSE_COMMA: return "parameter_wild_loose_comma"; + case PM_ERR_PATTERN_ARRAY_MULTIPLE_RESTS: return "pattern_array_multiple_rests"; + case PM_ERR_PATTERN_CAPTURE_DUPLICATE: return "pattern_capture_duplicate"; + case PM_ERR_PATTERN_CAPTURE_IN_ALTERNATIVE: return "pattern_capture_in_alternative"; + case PM_ERR_PATTERN_EXPRESSION_AFTER_BRACKET: return "pattern_expression_after_bracket"; + case PM_ERR_PATTERN_EXPRESSION_AFTER_COMMA: return "pattern_expression_after_comma"; + case PM_ERR_PATTERN_EXPRESSION_AFTER_HROCKET: return "pattern_expression_after_hrocket"; + case PM_ERR_PATTERN_EXPRESSION_AFTER_IN: return "pattern_expression_after_in"; + case PM_ERR_PATTERN_EXPRESSION_AFTER_KEY: return "pattern_expression_after_key"; + case PM_ERR_PATTERN_EXPRESSION_AFTER_PAREN: return "pattern_expression_after_paren"; + case PM_ERR_PATTERN_EXPRESSION_AFTER_PIN: return "pattern_expression_after_pin"; + case PM_ERR_PATTERN_EXPRESSION_AFTER_PIPE: return "pattern_expression_after_pipe"; + case PM_ERR_PATTERN_EXPRESSION_AFTER_RANGE: return "pattern_expression_after_range"; + case PM_ERR_PATTERN_EXPRESSION_AFTER_REST: return "pattern_expression_after_rest"; + case PM_ERR_PATTERN_FIND_MISSING_INNER: return "pattern_find_missing_inner"; + case PM_ERR_PATTERN_HASH_IMPLICIT: return "pattern_hash_implicit"; + case PM_ERR_PATTERN_HASH_KEY: return "pattern_hash_key"; + case PM_ERR_PATTERN_HASH_KEY_DUPLICATE: return "pattern_hash_key_duplicate"; + case PM_ERR_PATTERN_HASH_KEY_INTERPOLATED: return "pattern_hash_key_interpolated"; + case PM_ERR_PATTERN_HASH_KEY_LABEL: return "pattern_hash_key_label"; + case PM_ERR_PATTERN_HASH_KEY_LOCALS: return "pattern_hash_key_locals"; + case PM_ERR_PATTERN_IDENT_AFTER_HROCKET: return "pattern_ident_after_hrocket"; + case PM_ERR_PATTERN_LABEL_AFTER_COMMA: return "pattern_label_after_comma"; + case PM_ERR_PATTERN_REST: return "pattern_rest"; + case PM_ERR_PATTERN_TERM_BRACE: return "pattern_term_brace"; + case PM_ERR_PATTERN_TERM_BRACKET: return "pattern_term_bracket"; + case PM_ERR_PATTERN_TERM_PAREN: return "pattern_term_paren"; + case PM_ERR_PIPEPIPEEQ_MULTI_ASSIGN: return "pipepipeeq_multi_assign"; + case PM_ERR_REGEXP_ENCODING_OPTION_MISMATCH: return "regexp_encoding_option_mismatch"; + case PM_ERR_REGEXP_INCOMPAT_CHAR_ENCODING: return "regexp_incompat_char_encoding"; + case PM_ERR_REGEXP_INVALID_UNICODE_RANGE: return "regexp_invalid_unicode_range"; + case PM_ERR_REGEXP_NON_ESCAPED_MBC: return "regexp_non_escaped_mbc"; + case PM_ERR_REGEXP_PARSE_ERROR: return "regexp_parse_error"; + case PM_ERR_REGEXP_TERM: return "regexp_term"; + case PM_ERR_REGEXP_UNKNOWN_OPTIONS: return "regexp_unknown_options"; + case PM_ERR_REGEXP_UTF8_CHAR_NON_UTF8_REGEXP: return "regexp_utf8_char_non_utf8_regexp"; + case PM_ERR_RESCUE_EXPRESSION: return "rescue_expression"; + case PM_ERR_RESCUE_MODIFIER_VALUE: return "rescue_modifier_value"; + case PM_ERR_RESCUE_TERM: return "rescue_term"; + case PM_ERR_RESCUE_VARIABLE: return "rescue_variable"; + case PM_ERR_RETURN_INVALID: return "return_invalid"; + case PM_ERR_SCRIPT_NOT_FOUND: return "script_not_found"; + case PM_ERR_SINGLETON_FOR_LITERALS: return "singleton_for_literals"; + case PM_ERR_STATEMENT_ALIAS: return "statement_alias"; + case PM_ERR_STATEMENT_POSTEXE_END: return "statement_postexe_end"; + case PM_ERR_STATEMENT_PREEXE_BEGIN: return "statement_preexe_begin"; + case PM_ERR_STATEMENT_UNDEF: return "statement_undef"; + case PM_ERR_STRING_CONCATENATION: return "string_concatenation"; + case PM_ERR_STRING_INTERPOLATED_TERM: return "string_interpolated_term"; + case PM_ERR_STRING_LITERAL_EOF: return "string_literal_eof"; + case PM_ERR_STRING_LITERAL_TERM: return "string_literal_term"; + case PM_ERR_SYMBOL_INVALID: return "symbol_invalid"; + case PM_ERR_SYMBOL_TERM_DYNAMIC: return "symbol_term_dynamic"; + case PM_ERR_SYMBOL_TERM_INTERPOLATED: return "symbol_term_interpolated"; + case PM_ERR_TERNARY_COLON: return "ternary_colon"; + case PM_ERR_TERNARY_EXPRESSION_FALSE: return "ternary_expression_false"; + case PM_ERR_TERNARY_EXPRESSION_TRUE: return "ternary_expression_true"; + case PM_ERR_UNARY_DISALLOWED: return "unary_disallowed"; + case PM_ERR_UNARY_RECEIVER: return "unary_receiver"; + case PM_ERR_UNDEF_ARGUMENT: return "undef_argument"; + case PM_ERR_UNEXPECTED_BLOCK_ARGUMENT: return "unexpected_block_argument"; + case PM_ERR_UNEXPECTED_INDEX_BLOCK: return "unexpected_index_block"; + case PM_ERR_UNEXPECTED_INDEX_KEYWORDS: return "unexpected_index_keywords"; + case PM_ERR_UNEXPECTED_LABEL: return "unexpected_label"; + case PM_ERR_UNEXPECTED_MULTI_WRITE: return "unexpected_multi_write"; + case PM_ERR_UNEXPECTED_PARAMETER_DEFAULT_VALUE: return "unexpected_parameter_default_value"; + case PM_ERR_UNEXPECTED_RANGE_OPERATOR: return "unexpected_range_operator"; + case PM_ERR_UNEXPECTED_SAFE_NAVIGATION: return "unexpected_safe_navigation"; + case PM_ERR_UNEXPECTED_TOKEN_CLOSE_CONTEXT: return "unexpected_token_close_context"; + case PM_ERR_UNEXPECTED_TOKEN_IGNORE: return "unexpected_token_ignore"; + case PM_ERR_UNTIL_TERM: return "until_term"; + case PM_ERR_VOID_EXPRESSION: return "void_expression"; + case PM_ERR_WHILE_TERM: return "while_term"; + case PM_ERR_WRITE_TARGET_IN_METHOD: return "write_target_in_method"; + case PM_ERR_WRITE_TARGET_READONLY: return "write_target_readonly"; + case PM_ERR_WRITE_TARGET_UNEXPECTED: return "write_target_unexpected"; + case PM_ERR_XSTRING_TERM: return "xstring_term"; + case PM_WARN_AMBIGUOUS_BINARY_OPERATOR: return "ambiguous_binary_operator"; + case PM_WARN_AMBIGUOUS_FIRST_ARGUMENT_MINUS: return "ambiguous_first_argument_minus"; + case PM_WARN_AMBIGUOUS_FIRST_ARGUMENT_PLUS: return "ambiguous_first_argument_plus"; + case PM_WARN_AMBIGUOUS_PREFIX_AMPERSAND: return "ambiguous_prefix_ampersand"; + case PM_WARN_AMBIGUOUS_PREFIX_STAR: return "ambiguous_prefix_star"; + case PM_WARN_AMBIGUOUS_PREFIX_STAR_STAR: return "ambiguous_prefix_star_star"; + case PM_WARN_AMBIGUOUS_SLASH: return "ambiguous_slash"; + case PM_WARN_COMPARISON_AFTER_COMPARISON: return "comparison_after_comparison"; + case PM_WARN_DOT_DOT_DOT_EOL: return "dot_dot_dot_eol"; + case PM_WARN_EQUAL_IN_CONDITIONAL: return "equal_in_conditional"; + case PM_WARN_EQUAL_IN_CONDITIONAL_3_3: return "equal_in_conditional_3_3"; + case PM_WARN_END_IN_METHOD: return "end_in_method"; + case PM_WARN_DUPLICATED_HASH_KEY: return "duplicated_hash_key"; + case PM_WARN_DUPLICATED_WHEN_CLAUSE: return "duplicated_when_clause"; + case PM_WARN_FLOAT_OUT_OF_RANGE: return "float_out_of_range"; + case PM_WARN_IGNORED_FROZEN_STRING_LITERAL: return "ignored_frozen_string_literal"; + case PM_WARN_INDENTATION_MISMATCH: return "indentation_mismatch"; + case PM_WARN_INTEGER_IN_FLIP_FLOP: return "integer_in_flip_flop"; + case PM_WARN_INVALID_CHARACTER: return "invalid_character"; + case PM_WARN_INVALID_MAGIC_COMMENT_VALUE: return "invalid_magic_comment_value"; + case PM_WARN_INVALID_NUMBERED_REFERENCE: return "invalid_numbered_reference"; + case PM_WARN_KEYWORD_EOL: return "keyword_eol"; + case PM_WARN_LITERAL_IN_CONDITION_DEFAULT: return "literal_in_condition_default"; + case PM_WARN_LITERAL_IN_CONDITION_VERBOSE: return "literal_in_condition_verbose"; + case PM_WARN_SHAREABLE_CONSTANT_VALUE_LINE: return "shareable_constant_value_line"; + case PM_WARN_SHEBANG_CARRIAGE_RETURN: return "shebang_carriage_return"; + case PM_WARN_UNEXPECTED_CARRIAGE_RETURN: return "unexpected_carriage_return"; + case PM_WARN_UNREACHABLE_STATEMENT: return "unreachable_statement"; + case PM_WARN_UNUSED_LOCAL_VARIABLE: return "unused_local_variable"; + case PM_WARN_VOID_STATEMENT: return "void_statement"; + } + + 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/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/encoding.c b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/encoding.c new file mode 100644 index 0000000..d7e5616 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/encoding.c @@ -0,0 +1,5340 @@ +#include "prism/encoding.h" + +typedef uint32_t pm_unicode_codepoint_t; + +#define UNICODE_ALPHA_CODEPOINTS_LENGTH 1508 +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, + 0x363, 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, 0x88F, + 0x897, 0x897, + 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, + 0xC5C, 0xC5D, + 0xC60, 0xC63, + 0xC80, 0xC83, + 0xC85, 0xC8C, + 0xC8E, 0xC90, + 0xC92, 0xCA8, + 0xCAA, 0xCB3, + 0xCB5, 0xCB9, + 0xCBD, 0xCC4, + 0xCC6, 0xCC8, + 0xCCA, 0xCCC, + 0xCD5, 0xCD6, + 0xCDC, 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, 0x1C8A, + 0x1C90, 0x1CBA, + 0x1CBD, 0x1CBF, + 0x1CE9, 0x1CEC, + 0x1CEE, 0x1CF3, + 0x1CF5, 0x1CF6, + 0x1CFA, 0x1CFA, + 0x1D00, 0x1DBF, + 0x1DD3, 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, 0xA7DC, + 0xA7F1, 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, + 0x105C0, 0x105F3, + 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, + 0x10940, 0x10959, + 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, + 0x10D4A, 0x10D65, + 0x10D69, 0x10D69, + 0x10D6F, 0x10D85, + 0x10E80, 0x10EA9, + 0x10EAB, 0x10EAC, + 0x10EB0, 0x10EB1, + 0x10EC2, 0x10EC7, + 0x10EFA, 0x10EFC, + 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, + 0x11380, 0x11389, + 0x1138B, 0x1138B, + 0x1138E, 0x1138E, + 0x11390, 0x113B5, + 0x113B7, 0x113C0, + 0x113C2, 0x113C2, + 0x113C5, 0x113C5, + 0x113C7, 0x113CA, + 0x113CC, 0x113CD, + 0x113D1, 0x113D1, + 0x113D3, 0x113D3, + 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, + 0x11B60, 0x11B67, + 0x11BC0, 0x11BE0, + 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, + 0x11DB0, 0x11DDB, + 0x11EE0, 0x11EF6, + 0x11F00, 0x11F10, + 0x11F12, 0x11F3A, + 0x11F3E, 0x11F40, + 0x11FB0, 0x11FB0, + 0x12000, 0x12399, + 0x12400, 0x1246E, + 0x12480, 0x12543, + 0x12F90, 0x12FF0, + 0x13000, 0x1342F, + 0x13441, 0x13446, + 0x13460, 0x143FA, + 0x14400, 0x14646, + 0x16100, 0x1612E, + 0x16800, 0x16A38, + 0x16A40, 0x16A5E, + 0x16A70, 0x16ABE, + 0x16AD0, 0x16AED, + 0x16B00, 0x16B2F, + 0x16B40, 0x16B43, + 0x16B63, 0x16B77, + 0x16B7D, 0x16B8F, + 0x16D40, 0x16D6C, + 0x16E40, 0x16E7F, + 0x16EA0, 0x16EB8, + 0x16EBB, 0x16ED3, + 0x16F00, 0x16F4A, + 0x16F4F, 0x16F87, + 0x16F8F, 0x16F9F, + 0x16FE0, 0x16FE1, + 0x16FE3, 0x16FE3, + 0x16FF0, 0x16FF6, + 0x17000, 0x18CD5, + 0x18CFF, 0x18D1E, + 0x18D80, 0x18DF2, + 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, + 0x1E5D0, 0x1E5ED, + 0x1E5F0, 0x1E5F0, + 0x1E6C0, 0x1E6DE, + 0x1E6E0, 0x1E6F5, + 0x1E6FE, 0x1E6FF, + 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, 0x2B81D, + 0x2B820, 0x2CEAD, + 0x2CEB0, 0x2EBE0, + 0x2EBF0, 0x2EE5D, + 0x2F800, 0x2FA1D, + 0x30000, 0x3134A, + 0x31350, 0x33479, +}; + +#define UNICODE_ALNUM_CODEPOINTS_LENGTH 1598 +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, + 0x363, 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, 0x88F, + 0x897, 0x897, + 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, + 0xC5C, 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, + 0xCDC, 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, 0x1C8A, + 0x1C90, 0x1CBA, + 0x1CBD, 0x1CBF, + 0x1CE9, 0x1CEC, + 0x1CEE, 0x1CF3, + 0x1CF5, 0x1CF6, + 0x1CFA, 0x1CFA, + 0x1D00, 0x1DBF, + 0x1DD3, 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, 0xA7DC, + 0xA7F1, 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, + 0x105C0, 0x105F3, + 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, + 0x10940, 0x10959, + 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, + 0x10D40, 0x10D65, + 0x10D69, 0x10D69, + 0x10D6F, 0x10D85, + 0x10E80, 0x10EA9, + 0x10EAB, 0x10EAC, + 0x10EB0, 0x10EB1, + 0x10EC2, 0x10EC7, + 0x10EFA, 0x10EFC, + 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, + 0x11380, 0x11389, + 0x1138B, 0x1138B, + 0x1138E, 0x1138E, + 0x11390, 0x113B5, + 0x113B7, 0x113C0, + 0x113C2, 0x113C2, + 0x113C5, 0x113C5, + 0x113C7, 0x113CA, + 0x113CC, 0x113CD, + 0x113D1, 0x113D1, + 0x113D3, 0x113D3, + 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, + 0x116D0, 0x116E3, + 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, + 0x11B60, 0x11B67, + 0x11BC0, 0x11BE0, + 0x11BF0, 0x11BF9, + 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, + 0x11DB0, 0x11DDB, + 0x11DE0, 0x11DE9, + 0x11EE0, 0x11EF6, + 0x11F00, 0x11F10, + 0x11F12, 0x11F3A, + 0x11F3E, 0x11F40, + 0x11F50, 0x11F59, + 0x11FB0, 0x11FB0, + 0x12000, 0x12399, + 0x12400, 0x1246E, + 0x12480, 0x12543, + 0x12F90, 0x12FF0, + 0x13000, 0x1342F, + 0x13441, 0x13446, + 0x13460, 0x143FA, + 0x14400, 0x14646, + 0x16100, 0x1612E, + 0x16130, 0x16139, + 0x16800, 0x16A38, + 0x16A40, 0x16A5E, + 0x16A60, 0x16A69, + 0x16A70, 0x16ABE, + 0x16AC0, 0x16AC9, + 0x16AD0, 0x16AED, + 0x16B00, 0x16B2F, + 0x16B40, 0x16B43, + 0x16B50, 0x16B59, + 0x16B63, 0x16B77, + 0x16B7D, 0x16B8F, + 0x16D40, 0x16D6C, + 0x16D70, 0x16D79, + 0x16E40, 0x16E7F, + 0x16EA0, 0x16EB8, + 0x16EBB, 0x16ED3, + 0x16F00, 0x16F4A, + 0x16F4F, 0x16F87, + 0x16F8F, 0x16F9F, + 0x16FE0, 0x16FE1, + 0x16FE3, 0x16FE3, + 0x16FF0, 0x16FF6, + 0x17000, 0x18CD5, + 0x18CFF, 0x18D1E, + 0x18D80, 0x18DF2, + 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, + 0x1CCF0, 0x1CCF9, + 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, + 0x1E5D0, 0x1E5ED, + 0x1E5F0, 0x1E5FA, + 0x1E6C0, 0x1E6DE, + 0x1E6E0, 0x1E6F5, + 0x1E6FE, 0x1E6FF, + 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, 0x2B81D, + 0x2B820, 0x2CEAD, + 0x2CEB0, 0x2EBE0, + 0x2EBF0, 0x2EE5D, + 0x2F800, 0x2FA1D, + 0x30000, 0x3134A, + 0x31350, 0x33479, +}; + +#define UNICODE_ISUPPER_CODEPOINTS_LENGTH 1320 +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, + 0x1C89, 0x1C89, + 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, + 0xA7CB, 0xA7CC, + 0xA7CE, 0xA7CE, + 0xA7D0, 0xA7D0, + 0xA7D2, 0xA7D2, + 0xA7D4, 0xA7D4, + 0xA7D6, 0xA7D6, + 0xA7D8, 0xA7D8, + 0xA7DA, 0xA7DA, + 0xA7DC, 0xA7DC, + 0xA7F5, 0xA7F5, + 0xFF21, 0xFF3A, + 0x10400, 0x10427, + 0x104B0, 0x104D3, + 0x10570, 0x1057A, + 0x1057C, 0x1058A, + 0x1058C, 0x10592, + 0x10594, 0x10595, + 0x10C80, 0x10CB2, + 0x10D50, 0x10D65, + 0x118A0, 0x118BF, + 0x16E40, 0x16E5F, + 0x16EA0, 0x16EB8, + 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 + * + * 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 (n == 0) { + return 0; + } + + 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 (n == 0) { + return 0; + } + + 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 (n == 0) { + return 0; + } + + 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 ((n > 0) && (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) { + if (n == 0) { + return 0; + } + + 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 (n == 0) { + return 0; + } + + 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 (n == 0) { + return 0; + } + + 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 (n == 0) { + return 0; + } + + 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, ptrdiff_t n) { \ + return ((n > 0) && (pm_encoding_ ##name ## _table[*b] & PRISM_ENCODING_ALPHABETIC_BIT)); \ + } \ + static size_t pm_encoding_ ##name ## _alnum_char(const uint8_t *b, ptrdiff_t n) { \ + return ((n > 0) && (pm_encoding_ ##name ## _table[*b] & PRISM_ENCODING_ALPHANUMERIC_BIT)) ? 1 : 0; \ + } \ + static bool pm_encoding_ ##name ## _isupper_char(const uint8_t *b, ptrdiff_t n) { \ + return ((n > 0) && (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, ptrdiff_t n) { + return ((n > 0) && (*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, ptrdiff_t n) { + return (n > 0) ? (pm_encoding_ascii_table[*b] & PRISM_ENCODING_ALPHABETIC_BIT) : 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_alpha_char_7bit(const uint8_t *b, ptrdiff_t n) { + return ((n > 0) && (*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, ptrdiff_t n) { + return ((n > 0) && (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 ((n > 0) && (*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, ptrdiff_t n) { + return (n > 0) && (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 ((n > 0) && (*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) { + if (n == 0) { + return 0; + } + // 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 (n > 0) && (*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 ((n > 0) && (*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 ((n > 0) && (*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 ((n > 0) && (*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 ((n > 0) && (*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 ((n > 0) && (*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 ((n > 0) && (*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 ((n > 0) && (*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/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/node.c b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/node.c new file mode 100644 index 0000000..e60ea19 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/node.c @@ -0,0 +1,8685 @@ +/* :markup: markdown */ + +/*----------------------------------------------------------------------------*/ +/* This file is generated by the templates/template.rb script and should not */ +/* be modified manually. See */ +/* templates/src/node.c.erb */ +/* if you are looking to modify the */ +/* template */ +/*----------------------------------------------------------------------------*/ + +#line 2 "prism/templates/src/node.c.erb" +#include "prism/node.h" + +/** + * 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)) { +#line 110 "prism/templates/src/node.c.erb" + case PM_ALIAS_GLOBAL_VARIABLE_NODE: { + pm_alias_global_variable_node_t *cast = (pm_alias_global_variable_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->new_name); + pm_node_destroy(parser, (pm_node_t *)cast->old_name); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_ALIAS_METHOD_NODE: { + pm_alias_method_node_t *cast = (pm_alias_method_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->new_name); + pm_node_destroy(parser, (pm_node_t *)cast->old_name); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_ALTERNATION_PATTERN_NODE: { + pm_alternation_pattern_node_t *cast = (pm_alternation_pattern_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->left); + pm_node_destroy(parser, (pm_node_t *)cast->right); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_AND_NODE: { + pm_and_node_t *cast = (pm_and_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->left); + pm_node_destroy(parser, (pm_node_t *)cast->right); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_ARGUMENTS_NODE: { + pm_arguments_node_t *cast = (pm_arguments_node_t *) node; + pm_node_list_destroy(parser, &cast->arguments); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_ARRAY_NODE: { + pm_array_node_t *cast = (pm_array_node_t *) node; + pm_node_list_destroy(parser, &cast->elements); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_ARRAY_PATTERN_NODE: { + pm_array_pattern_node_t *cast = (pm_array_pattern_node_t *) node; + if (cast->constant != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->constant); + } + pm_node_list_destroy(parser, &cast->requireds); + if (cast->rest != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->rest); + } + pm_node_list_destroy(parser, &cast->posts); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_ASSOC_NODE: { + pm_assoc_node_t *cast = (pm_assoc_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->key); + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_ASSOC_SPLAT_NODE: { + pm_assoc_splat_node_t *cast = (pm_assoc_splat_node_t *) node; + if (cast->value != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->value); + } + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_BACK_REFERENCE_READ_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_BEGIN_NODE: { + pm_begin_node_t *cast = (pm_begin_node_t *) node; + if (cast->statements != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->statements); + } + if (cast->rescue_clause != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->rescue_clause); + } + if (cast->else_clause != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->else_clause); + } + if (cast->ensure_clause != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->ensure_clause); + } + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_BLOCK_ARGUMENT_NODE: { + pm_block_argument_node_t *cast = (pm_block_argument_node_t *) node; + if (cast->expression != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->expression); + } + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_BLOCK_LOCAL_VARIABLE_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_BLOCK_NODE: { + pm_block_node_t *cast = (pm_block_node_t *) node; + pm_constant_id_list_free(&cast->locals); + if (cast->parameters != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->parameters); + } + if (cast->body != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->body); + } + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_BLOCK_PARAMETER_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_BLOCK_PARAMETERS_NODE: { + pm_block_parameters_node_t *cast = (pm_block_parameters_node_t *) node; + if (cast->parameters != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->parameters); + } + pm_node_list_destroy(parser, &cast->locals); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_BREAK_NODE: { + pm_break_node_t *cast = (pm_break_node_t *) node; + if (cast->arguments != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->arguments); + } + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_CALL_AND_WRITE_NODE: { + pm_call_and_write_node_t *cast = (pm_call_and_write_node_t *) node; + if (cast->receiver != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->receiver); + } + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_CALL_NODE: { + pm_call_node_t *cast = (pm_call_node_t *) node; + if (cast->receiver != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->receiver); + } + if (cast->arguments != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->arguments); + } + if (cast->block != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->block); + } + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_CALL_OPERATOR_WRITE_NODE: { + pm_call_operator_write_node_t *cast = (pm_call_operator_write_node_t *) node; + if (cast->receiver != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->receiver); + } + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_CALL_OR_WRITE_NODE: { + pm_call_or_write_node_t *cast = (pm_call_or_write_node_t *) node; + if (cast->receiver != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->receiver); + } + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_CALL_TARGET_NODE: { + pm_call_target_node_t *cast = (pm_call_target_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->receiver); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_CAPTURE_PATTERN_NODE: { + pm_capture_pattern_node_t *cast = (pm_capture_pattern_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + pm_node_destroy(parser, (pm_node_t *)cast->target); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_CASE_MATCH_NODE: { + pm_case_match_node_t *cast = (pm_case_match_node_t *) node; + if (cast->predicate != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->predicate); + } + pm_node_list_destroy(parser, &cast->conditions); + if (cast->else_clause != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->else_clause); + } + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_CASE_NODE: { + pm_case_node_t *cast = (pm_case_node_t *) node; + if (cast->predicate != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->predicate); + } + pm_node_list_destroy(parser, &cast->conditions); + if (cast->else_clause != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->else_clause); + } + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_CLASS_NODE: { + pm_class_node_t *cast = (pm_class_node_t *) node; + pm_constant_id_list_free(&cast->locals); + pm_node_destroy(parser, (pm_node_t *)cast->constant_path); + if (cast->superclass != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->superclass); + } + if (cast->body != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->body); + } + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_CLASS_VARIABLE_AND_WRITE_NODE: { + pm_class_variable_and_write_node_t *cast = (pm_class_variable_and_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE: { + pm_class_variable_operator_write_node_t *cast = (pm_class_variable_operator_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_CLASS_VARIABLE_OR_WRITE_NODE: { + pm_class_variable_or_write_node_t *cast = (pm_class_variable_or_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_CLASS_VARIABLE_READ_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_CLASS_VARIABLE_TARGET_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_CLASS_VARIABLE_WRITE_NODE: { + pm_class_variable_write_node_t *cast = (pm_class_variable_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_CONSTANT_AND_WRITE_NODE: { + pm_constant_and_write_node_t *cast = (pm_constant_and_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_CONSTANT_OPERATOR_WRITE_NODE: { + pm_constant_operator_write_node_t *cast = (pm_constant_operator_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_CONSTANT_OR_WRITE_NODE: { + pm_constant_or_write_node_t *cast = (pm_constant_or_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_CONSTANT_PATH_AND_WRITE_NODE: { + pm_constant_path_and_write_node_t *cast = (pm_constant_path_and_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->target); + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_CONSTANT_PATH_NODE: { + pm_constant_path_node_t *cast = (pm_constant_path_node_t *) node; + if (cast->parent != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->parent); + } + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_CONSTANT_PATH_OPERATOR_WRITE_NODE: { + pm_constant_path_operator_write_node_t *cast = (pm_constant_path_operator_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->target); + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_CONSTANT_PATH_OR_WRITE_NODE: { + pm_constant_path_or_write_node_t *cast = (pm_constant_path_or_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->target); + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_CONSTANT_PATH_TARGET_NODE: { + pm_constant_path_target_node_t *cast = (pm_constant_path_target_node_t *) node; + if (cast->parent != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->parent); + } + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_CONSTANT_PATH_WRITE_NODE: { + pm_constant_path_write_node_t *cast = (pm_constant_path_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->target); + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_CONSTANT_READ_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_CONSTANT_TARGET_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_CONSTANT_WRITE_NODE: { + pm_constant_write_node_t *cast = (pm_constant_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_DEF_NODE: { + pm_def_node_t *cast = (pm_def_node_t *) node; + if (cast->receiver != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->receiver); + } + if (cast->parameters != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->parameters); + } + if (cast->body != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->body); + } + pm_constant_id_list_free(&cast->locals); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_DEFINED_NODE: { + pm_defined_node_t *cast = (pm_defined_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_ELSE_NODE: { + pm_else_node_t *cast = (pm_else_node_t *) node; + if (cast->statements != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->statements); + } + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_EMBEDDED_STATEMENTS_NODE: { + pm_embedded_statements_node_t *cast = (pm_embedded_statements_node_t *) node; + if (cast->statements != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->statements); + } + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_EMBEDDED_VARIABLE_NODE: { + pm_embedded_variable_node_t *cast = (pm_embedded_variable_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->variable); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_ENSURE_NODE: { + pm_ensure_node_t *cast = (pm_ensure_node_t *) node; + if (cast->statements != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->statements); + } + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_FALSE_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_FIND_PATTERN_NODE: { + pm_find_pattern_node_t *cast = (pm_find_pattern_node_t *) node; + if (cast->constant != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->constant); + } + pm_node_destroy(parser, (pm_node_t *)cast->left); + pm_node_list_destroy(parser, &cast->requireds); + pm_node_destroy(parser, (pm_node_t *)cast->right); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_FLIP_FLOP_NODE: { + pm_flip_flop_node_t *cast = (pm_flip_flop_node_t *) node; + if (cast->left != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->left); + } + if (cast->right != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->right); + } + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_FLOAT_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_FOR_NODE: { + pm_for_node_t *cast = (pm_for_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->index); + pm_node_destroy(parser, (pm_node_t *)cast->collection); + if (cast->statements != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->statements); + } + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_FORWARDING_ARGUMENTS_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_FORWARDING_PARAMETER_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_FORWARDING_SUPER_NODE: { + pm_forwarding_super_node_t *cast = (pm_forwarding_super_node_t *) node; + if (cast->block != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->block); + } + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_GLOBAL_VARIABLE_AND_WRITE_NODE: { + pm_global_variable_and_write_node_t *cast = (pm_global_variable_and_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE: { + pm_global_variable_operator_write_node_t *cast = (pm_global_variable_operator_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_GLOBAL_VARIABLE_OR_WRITE_NODE: { + pm_global_variable_or_write_node_t *cast = (pm_global_variable_or_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_GLOBAL_VARIABLE_READ_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_GLOBAL_VARIABLE_TARGET_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_GLOBAL_VARIABLE_WRITE_NODE: { + pm_global_variable_write_node_t *cast = (pm_global_variable_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_HASH_NODE: { + pm_hash_node_t *cast = (pm_hash_node_t *) node; + pm_node_list_destroy(parser, &cast->elements); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_HASH_PATTERN_NODE: { + pm_hash_pattern_node_t *cast = (pm_hash_pattern_node_t *) node; + if (cast->constant != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->constant); + } + pm_node_list_destroy(parser, &cast->elements); + if (cast->rest != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->rest); + } + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_IF_NODE: { + pm_if_node_t *cast = (pm_if_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->predicate); + if (cast->statements != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->statements); + } + if (cast->subsequent != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->subsequent); + } + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_IMAGINARY_NODE: { + pm_imaginary_node_t *cast = (pm_imaginary_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->numeric); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_IMPLICIT_NODE: { + pm_implicit_node_t *cast = (pm_implicit_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_IMPLICIT_REST_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_IN_NODE: { + pm_in_node_t *cast = (pm_in_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->pattern); + if (cast->statements != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->statements); + } + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_INDEX_AND_WRITE_NODE: { + pm_index_and_write_node_t *cast = (pm_index_and_write_node_t *) node; + if (cast->receiver != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->receiver); + } + if (cast->arguments != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->arguments); + } + if (cast->block != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->block); + } + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_INDEX_OPERATOR_WRITE_NODE: { + pm_index_operator_write_node_t *cast = (pm_index_operator_write_node_t *) node; + if (cast->receiver != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->receiver); + } + if (cast->arguments != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->arguments); + } + if (cast->block != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->block); + } + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_INDEX_OR_WRITE_NODE: { + pm_index_or_write_node_t *cast = (pm_index_or_write_node_t *) node; + if (cast->receiver != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->receiver); + } + if (cast->arguments != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->arguments); + } + if (cast->block != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->block); + } + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_INDEX_TARGET_NODE: { + pm_index_target_node_t *cast = (pm_index_target_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->receiver); + if (cast->arguments != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->arguments); + } + if (cast->block != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->block); + } + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_INSTANCE_VARIABLE_AND_WRITE_NODE: { + pm_instance_variable_and_write_node_t *cast = (pm_instance_variable_and_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE: { + pm_instance_variable_operator_write_node_t *cast = (pm_instance_variable_operator_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_INSTANCE_VARIABLE_OR_WRITE_NODE: { + pm_instance_variable_or_write_node_t *cast = (pm_instance_variable_or_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_INSTANCE_VARIABLE_READ_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_INSTANCE_VARIABLE_TARGET_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_INSTANCE_VARIABLE_WRITE_NODE: { + pm_instance_variable_write_node_t *cast = (pm_instance_variable_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_INTEGER_NODE: { + pm_integer_node_t *cast = (pm_integer_node_t *) node; + pm_integer_free(&cast->value); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_INTERPOLATED_MATCH_LAST_LINE_NODE: { + pm_interpolated_match_last_line_node_t *cast = (pm_interpolated_match_last_line_node_t *) node; + pm_node_list_destroy(parser, &cast->parts); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: { + pm_interpolated_regular_expression_node_t *cast = (pm_interpolated_regular_expression_node_t *) node; + pm_node_list_destroy(parser, &cast->parts); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_INTERPOLATED_STRING_NODE: { + pm_interpolated_string_node_t *cast = (pm_interpolated_string_node_t *) node; + pm_node_list_destroy(parser, &cast->parts); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_INTERPOLATED_SYMBOL_NODE: { + pm_interpolated_symbol_node_t *cast = (pm_interpolated_symbol_node_t *) node; + pm_node_list_destroy(parser, &cast->parts); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_INTERPOLATED_X_STRING_NODE: { + pm_interpolated_x_string_node_t *cast = (pm_interpolated_x_string_node_t *) node; + pm_node_list_destroy(parser, &cast->parts); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_IT_LOCAL_VARIABLE_READ_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_IT_PARAMETERS_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_KEYWORD_HASH_NODE: { + pm_keyword_hash_node_t *cast = (pm_keyword_hash_node_t *) node; + pm_node_list_destroy(parser, &cast->elements); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_KEYWORD_REST_PARAMETER_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_LAMBDA_NODE: { + pm_lambda_node_t *cast = (pm_lambda_node_t *) node; + pm_constant_id_list_free(&cast->locals); + if (cast->parameters != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->parameters); + } + if (cast->body != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->body); + } + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_LOCAL_VARIABLE_AND_WRITE_NODE: { + pm_local_variable_and_write_node_t *cast = (pm_local_variable_and_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE: { + pm_local_variable_operator_write_node_t *cast = (pm_local_variable_operator_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_LOCAL_VARIABLE_OR_WRITE_NODE: { + pm_local_variable_or_write_node_t *cast = (pm_local_variable_or_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_LOCAL_VARIABLE_READ_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_LOCAL_VARIABLE_TARGET_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_LOCAL_VARIABLE_WRITE_NODE: { + pm_local_variable_write_node_t *cast = (pm_local_variable_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_MATCH_LAST_LINE_NODE: { + pm_match_last_line_node_t *cast = (pm_match_last_line_node_t *) node; + pm_string_free(&cast->unescaped); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_MATCH_PREDICATE_NODE: { + pm_match_predicate_node_t *cast = (pm_match_predicate_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + pm_node_destroy(parser, (pm_node_t *)cast->pattern); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_MATCH_REQUIRED_NODE: { + pm_match_required_node_t *cast = (pm_match_required_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + pm_node_destroy(parser, (pm_node_t *)cast->pattern); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_MATCH_WRITE_NODE: { + pm_match_write_node_t *cast = (pm_match_write_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->call); + pm_node_list_destroy(parser, &cast->targets); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_MISSING_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_MODULE_NODE: { + pm_module_node_t *cast = (pm_module_node_t *) node; + pm_constant_id_list_free(&cast->locals); + pm_node_destroy(parser, (pm_node_t *)cast->constant_path); + if (cast->body != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->body); + } + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_MULTI_TARGET_NODE: { + pm_multi_target_node_t *cast = (pm_multi_target_node_t *) node; + pm_node_list_destroy(parser, &cast->lefts); + if (cast->rest != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->rest); + } + pm_node_list_destroy(parser, &cast->rights); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_MULTI_WRITE_NODE: { + pm_multi_write_node_t *cast = (pm_multi_write_node_t *) node; + pm_node_list_destroy(parser, &cast->lefts); + if (cast->rest != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->rest); + } + pm_node_list_destroy(parser, &cast->rights); + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_NEXT_NODE: { + pm_next_node_t *cast = (pm_next_node_t *) node; + if (cast->arguments != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->arguments); + } + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_NIL_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_NO_KEYWORDS_PARAMETER_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_NUMBERED_PARAMETERS_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_NUMBERED_REFERENCE_READ_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_OPTIONAL_KEYWORD_PARAMETER_NODE: { + pm_optional_keyword_parameter_node_t *cast = (pm_optional_keyword_parameter_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_OPTIONAL_PARAMETER_NODE: { + pm_optional_parameter_node_t *cast = (pm_optional_parameter_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->value); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_OR_NODE: { + pm_or_node_t *cast = (pm_or_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->left); + pm_node_destroy(parser, (pm_node_t *)cast->right); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_PARAMETERS_NODE: { + pm_parameters_node_t *cast = (pm_parameters_node_t *) node; + pm_node_list_destroy(parser, &cast->requireds); + pm_node_list_destroy(parser, &cast->optionals); + if (cast->rest != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->rest); + } + pm_node_list_destroy(parser, &cast->posts); + pm_node_list_destroy(parser, &cast->keywords); + if (cast->keyword_rest != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->keyword_rest); + } + if (cast->block != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->block); + } + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_PARENTHESES_NODE: { + pm_parentheses_node_t *cast = (pm_parentheses_node_t *) node; + if (cast->body != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->body); + } + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_PINNED_EXPRESSION_NODE: { + pm_pinned_expression_node_t *cast = (pm_pinned_expression_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->expression); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_PINNED_VARIABLE_NODE: { + pm_pinned_variable_node_t *cast = (pm_pinned_variable_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->variable); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_POST_EXECUTION_NODE: { + pm_post_execution_node_t *cast = (pm_post_execution_node_t *) node; + if (cast->statements != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->statements); + } + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_PRE_EXECUTION_NODE: { + pm_pre_execution_node_t *cast = (pm_pre_execution_node_t *) node; + if (cast->statements != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->statements); + } + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_PROGRAM_NODE: { + pm_program_node_t *cast = (pm_program_node_t *) node; + pm_constant_id_list_free(&cast->locals); + pm_node_destroy(parser, (pm_node_t *)cast->statements); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_RANGE_NODE: { + pm_range_node_t *cast = (pm_range_node_t *) node; + if (cast->left != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->left); + } + if (cast->right != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->right); + } + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_RATIONAL_NODE: { + pm_rational_node_t *cast = (pm_rational_node_t *) node; + pm_integer_free(&cast->numerator); + pm_integer_free(&cast->denominator); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_REDO_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_REGULAR_EXPRESSION_NODE: { + pm_regular_expression_node_t *cast = (pm_regular_expression_node_t *) node; + pm_string_free(&cast->unescaped); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_REQUIRED_KEYWORD_PARAMETER_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_REQUIRED_PARAMETER_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_RESCUE_MODIFIER_NODE: { + pm_rescue_modifier_node_t *cast = (pm_rescue_modifier_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->expression); + pm_node_destroy(parser, (pm_node_t *)cast->rescue_expression); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_RESCUE_NODE: { + pm_rescue_node_t *cast = (pm_rescue_node_t *) node; + pm_node_list_destroy(parser, &cast->exceptions); + if (cast->reference != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->reference); + } + if (cast->statements != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->statements); + } + if (cast->subsequent != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->subsequent); + } + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_REST_PARAMETER_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_RETRY_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_RETURN_NODE: { + pm_return_node_t *cast = (pm_return_node_t *) node; + if (cast->arguments != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->arguments); + } + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_SELF_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_SHAREABLE_CONSTANT_NODE: { + pm_shareable_constant_node_t *cast = (pm_shareable_constant_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->write); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_SINGLETON_CLASS_NODE: { + pm_singleton_class_node_t *cast = (pm_singleton_class_node_t *) node; + pm_constant_id_list_free(&cast->locals); + pm_node_destroy(parser, (pm_node_t *)cast->expression); + if (cast->body != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->body); + } + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_SOURCE_ENCODING_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_SOURCE_FILE_NODE: { + pm_source_file_node_t *cast = (pm_source_file_node_t *) node; + pm_string_free(&cast->filepath); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_SOURCE_LINE_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_SPLAT_NODE: { + pm_splat_node_t *cast = (pm_splat_node_t *) node; + if (cast->expression != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->expression); + } + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_STATEMENTS_NODE: { + pm_statements_node_t *cast = (pm_statements_node_t *) node; + pm_node_list_destroy(parser, &cast->body); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_STRING_NODE: { + pm_string_node_t *cast = (pm_string_node_t *) node; + pm_string_free(&cast->unescaped); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_SUPER_NODE: { + pm_super_node_t *cast = (pm_super_node_t *) node; + if (cast->arguments != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->arguments); + } + if (cast->block != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->block); + } + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_SYMBOL_NODE: { + pm_symbol_node_t *cast = (pm_symbol_node_t *) node; + pm_string_free(&cast->unescaped); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_TRUE_NODE: { + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_UNDEF_NODE: { + pm_undef_node_t *cast = (pm_undef_node_t *) node; + pm_node_list_destroy(parser, &cast->names); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_UNLESS_NODE: { + pm_unless_node_t *cast = (pm_unless_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->predicate); + if (cast->statements != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->statements); + } + if (cast->else_clause != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->else_clause); + } + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_UNTIL_NODE: { + pm_until_node_t *cast = (pm_until_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->predicate); + if (cast->statements != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->statements); + } + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_WHEN_NODE: { + pm_when_node_t *cast = (pm_when_node_t *) node; + pm_node_list_destroy(parser, &cast->conditions); + if (cast->statements != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->statements); + } + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_WHILE_NODE: { + pm_while_node_t *cast = (pm_while_node_t *) node; + pm_node_destroy(parser, (pm_node_t *)cast->predicate); + if (cast->statements != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->statements); + } + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_X_STRING_NODE: { + pm_x_string_node_t *cast = (pm_x_string_node_t *) node; + pm_string_free(&cast->unescaped); + break; + } +#line 110 "prism/templates/src/node.c.erb" + case PM_YIELD_NODE: { + pm_yield_node_t *cast = (pm_yield_node_t *) node; + if (cast->arguments != NULL) { + pm_node_destroy(parser, (pm_node_t *)cast->arguments); + } + break; + } +#line 139 "prism/templates/src/node.c.erb" + default: + assert(false && "unreachable"); + break; + } + xfree(node); +} + +/** + * 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) { + case PM_ALIAS_GLOBAL_VARIABLE_NODE: + return "PM_ALIAS_GLOBAL_VARIABLE_NODE"; + case PM_ALIAS_METHOD_NODE: + return "PM_ALIAS_METHOD_NODE"; + case PM_ALTERNATION_PATTERN_NODE: + return "PM_ALTERNATION_PATTERN_NODE"; + case PM_AND_NODE: + return "PM_AND_NODE"; + case PM_ARGUMENTS_NODE: + return "PM_ARGUMENTS_NODE"; + case PM_ARRAY_NODE: + return "PM_ARRAY_NODE"; + case PM_ARRAY_PATTERN_NODE: + return "PM_ARRAY_PATTERN_NODE"; + case PM_ASSOC_NODE: + return "PM_ASSOC_NODE"; + case PM_ASSOC_SPLAT_NODE: + return "PM_ASSOC_SPLAT_NODE"; + case PM_BACK_REFERENCE_READ_NODE: + return "PM_BACK_REFERENCE_READ_NODE"; + case PM_BEGIN_NODE: + return "PM_BEGIN_NODE"; + case PM_BLOCK_ARGUMENT_NODE: + return "PM_BLOCK_ARGUMENT_NODE"; + case PM_BLOCK_LOCAL_VARIABLE_NODE: + return "PM_BLOCK_LOCAL_VARIABLE_NODE"; + case PM_BLOCK_NODE: + return "PM_BLOCK_NODE"; + case PM_BLOCK_PARAMETER_NODE: + return "PM_BLOCK_PARAMETER_NODE"; + case PM_BLOCK_PARAMETERS_NODE: + return "PM_BLOCK_PARAMETERS_NODE"; + case PM_BREAK_NODE: + return "PM_BREAK_NODE"; + case PM_CALL_AND_WRITE_NODE: + return "PM_CALL_AND_WRITE_NODE"; + case PM_CALL_NODE: + return "PM_CALL_NODE"; + case PM_CALL_OPERATOR_WRITE_NODE: + return "PM_CALL_OPERATOR_WRITE_NODE"; + case PM_CALL_OR_WRITE_NODE: + return "PM_CALL_OR_WRITE_NODE"; + case PM_CALL_TARGET_NODE: + return "PM_CALL_TARGET_NODE"; + case PM_CAPTURE_PATTERN_NODE: + return "PM_CAPTURE_PATTERN_NODE"; + case PM_CASE_MATCH_NODE: + return "PM_CASE_MATCH_NODE"; + case PM_CASE_NODE: + return "PM_CASE_NODE"; + case PM_CLASS_NODE: + return "PM_CLASS_NODE"; + case PM_CLASS_VARIABLE_AND_WRITE_NODE: + return "PM_CLASS_VARIABLE_AND_WRITE_NODE"; + case PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE: + return "PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE"; + case PM_CLASS_VARIABLE_OR_WRITE_NODE: + return "PM_CLASS_VARIABLE_OR_WRITE_NODE"; + case PM_CLASS_VARIABLE_READ_NODE: + return "PM_CLASS_VARIABLE_READ_NODE"; + case PM_CLASS_VARIABLE_TARGET_NODE: + return "PM_CLASS_VARIABLE_TARGET_NODE"; + case PM_CLASS_VARIABLE_WRITE_NODE: + return "PM_CLASS_VARIABLE_WRITE_NODE"; + case PM_CONSTANT_AND_WRITE_NODE: + return "PM_CONSTANT_AND_WRITE_NODE"; + case PM_CONSTANT_OPERATOR_WRITE_NODE: + return "PM_CONSTANT_OPERATOR_WRITE_NODE"; + case PM_CONSTANT_OR_WRITE_NODE: + return "PM_CONSTANT_OR_WRITE_NODE"; + case PM_CONSTANT_PATH_AND_WRITE_NODE: + return "PM_CONSTANT_PATH_AND_WRITE_NODE"; + case PM_CONSTANT_PATH_NODE: + return "PM_CONSTANT_PATH_NODE"; + case PM_CONSTANT_PATH_OPERATOR_WRITE_NODE: + return "PM_CONSTANT_PATH_OPERATOR_WRITE_NODE"; + case PM_CONSTANT_PATH_OR_WRITE_NODE: + return "PM_CONSTANT_PATH_OR_WRITE_NODE"; + case PM_CONSTANT_PATH_TARGET_NODE: + return "PM_CONSTANT_PATH_TARGET_NODE"; + case PM_CONSTANT_PATH_WRITE_NODE: + return "PM_CONSTANT_PATH_WRITE_NODE"; + case PM_CONSTANT_READ_NODE: + return "PM_CONSTANT_READ_NODE"; + case PM_CONSTANT_TARGET_NODE: + return "PM_CONSTANT_TARGET_NODE"; + case PM_CONSTANT_WRITE_NODE: + return "PM_CONSTANT_WRITE_NODE"; + case PM_DEF_NODE: + return "PM_DEF_NODE"; + case PM_DEFINED_NODE: + return "PM_DEFINED_NODE"; + case PM_ELSE_NODE: + return "PM_ELSE_NODE"; + case PM_EMBEDDED_STATEMENTS_NODE: + return "PM_EMBEDDED_STATEMENTS_NODE"; + case PM_EMBEDDED_VARIABLE_NODE: + return "PM_EMBEDDED_VARIABLE_NODE"; + case PM_ENSURE_NODE: + return "PM_ENSURE_NODE"; + case PM_FALSE_NODE: + return "PM_FALSE_NODE"; + case PM_FIND_PATTERN_NODE: + return "PM_FIND_PATTERN_NODE"; + case PM_FLIP_FLOP_NODE: + return "PM_FLIP_FLOP_NODE"; + case PM_FLOAT_NODE: + return "PM_FLOAT_NODE"; + case PM_FOR_NODE: + return "PM_FOR_NODE"; + case PM_FORWARDING_ARGUMENTS_NODE: + return "PM_FORWARDING_ARGUMENTS_NODE"; + case PM_FORWARDING_PARAMETER_NODE: + return "PM_FORWARDING_PARAMETER_NODE"; + case PM_FORWARDING_SUPER_NODE: + return "PM_FORWARDING_SUPER_NODE"; + case PM_GLOBAL_VARIABLE_AND_WRITE_NODE: + return "PM_GLOBAL_VARIABLE_AND_WRITE_NODE"; + case PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE: + return "PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE"; + case PM_GLOBAL_VARIABLE_OR_WRITE_NODE: + return "PM_GLOBAL_VARIABLE_OR_WRITE_NODE"; + case PM_GLOBAL_VARIABLE_READ_NODE: + return "PM_GLOBAL_VARIABLE_READ_NODE"; + case PM_GLOBAL_VARIABLE_TARGET_NODE: + return "PM_GLOBAL_VARIABLE_TARGET_NODE"; + case PM_GLOBAL_VARIABLE_WRITE_NODE: + return "PM_GLOBAL_VARIABLE_WRITE_NODE"; + case PM_HASH_NODE: + return "PM_HASH_NODE"; + case PM_HASH_PATTERN_NODE: + return "PM_HASH_PATTERN_NODE"; + case PM_IF_NODE: + return "PM_IF_NODE"; + case PM_IMAGINARY_NODE: + return "PM_IMAGINARY_NODE"; + case PM_IMPLICIT_NODE: + return "PM_IMPLICIT_NODE"; + case PM_IMPLICIT_REST_NODE: + return "PM_IMPLICIT_REST_NODE"; + case PM_IN_NODE: + return "PM_IN_NODE"; + case PM_INDEX_AND_WRITE_NODE: + return "PM_INDEX_AND_WRITE_NODE"; + case PM_INDEX_OPERATOR_WRITE_NODE: + return "PM_INDEX_OPERATOR_WRITE_NODE"; + case PM_INDEX_OR_WRITE_NODE: + return "PM_INDEX_OR_WRITE_NODE"; + case PM_INDEX_TARGET_NODE: + return "PM_INDEX_TARGET_NODE"; + case PM_INSTANCE_VARIABLE_AND_WRITE_NODE: + return "PM_INSTANCE_VARIABLE_AND_WRITE_NODE"; + case PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE: + return "PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE"; + case PM_INSTANCE_VARIABLE_OR_WRITE_NODE: + return "PM_INSTANCE_VARIABLE_OR_WRITE_NODE"; + case PM_INSTANCE_VARIABLE_READ_NODE: + return "PM_INSTANCE_VARIABLE_READ_NODE"; + case PM_INSTANCE_VARIABLE_TARGET_NODE: + return "PM_INSTANCE_VARIABLE_TARGET_NODE"; + case PM_INSTANCE_VARIABLE_WRITE_NODE: + return "PM_INSTANCE_VARIABLE_WRITE_NODE"; + case PM_INTEGER_NODE: + return "PM_INTEGER_NODE"; + case PM_INTERPOLATED_MATCH_LAST_LINE_NODE: + return "PM_INTERPOLATED_MATCH_LAST_LINE_NODE"; + case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: + return "PM_INTERPOLATED_REGULAR_EXPRESSION_NODE"; + case PM_INTERPOLATED_STRING_NODE: + return "PM_INTERPOLATED_STRING_NODE"; + case PM_INTERPOLATED_SYMBOL_NODE: + return "PM_INTERPOLATED_SYMBOL_NODE"; + case PM_INTERPOLATED_X_STRING_NODE: + return "PM_INTERPOLATED_X_STRING_NODE"; + case PM_IT_LOCAL_VARIABLE_READ_NODE: + return "PM_IT_LOCAL_VARIABLE_READ_NODE"; + case PM_IT_PARAMETERS_NODE: + return "PM_IT_PARAMETERS_NODE"; + case PM_KEYWORD_HASH_NODE: + return "PM_KEYWORD_HASH_NODE"; + case PM_KEYWORD_REST_PARAMETER_NODE: + return "PM_KEYWORD_REST_PARAMETER_NODE"; + case PM_LAMBDA_NODE: + return "PM_LAMBDA_NODE"; + case PM_LOCAL_VARIABLE_AND_WRITE_NODE: + return "PM_LOCAL_VARIABLE_AND_WRITE_NODE"; + case PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE: + return "PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE"; + case PM_LOCAL_VARIABLE_OR_WRITE_NODE: + return "PM_LOCAL_VARIABLE_OR_WRITE_NODE"; + case PM_LOCAL_VARIABLE_READ_NODE: + return "PM_LOCAL_VARIABLE_READ_NODE"; + case PM_LOCAL_VARIABLE_TARGET_NODE: + return "PM_LOCAL_VARIABLE_TARGET_NODE"; + case PM_LOCAL_VARIABLE_WRITE_NODE: + return "PM_LOCAL_VARIABLE_WRITE_NODE"; + case PM_MATCH_LAST_LINE_NODE: + return "PM_MATCH_LAST_LINE_NODE"; + case PM_MATCH_PREDICATE_NODE: + return "PM_MATCH_PREDICATE_NODE"; + case PM_MATCH_REQUIRED_NODE: + return "PM_MATCH_REQUIRED_NODE"; + case PM_MATCH_WRITE_NODE: + return "PM_MATCH_WRITE_NODE"; + case PM_MISSING_NODE: + return "PM_MISSING_NODE"; + case PM_MODULE_NODE: + return "PM_MODULE_NODE"; + case PM_MULTI_TARGET_NODE: + return "PM_MULTI_TARGET_NODE"; + case PM_MULTI_WRITE_NODE: + return "PM_MULTI_WRITE_NODE"; + case PM_NEXT_NODE: + return "PM_NEXT_NODE"; + case PM_NIL_NODE: + return "PM_NIL_NODE"; + case PM_NO_KEYWORDS_PARAMETER_NODE: + return "PM_NO_KEYWORDS_PARAMETER_NODE"; + case PM_NUMBERED_PARAMETERS_NODE: + return "PM_NUMBERED_PARAMETERS_NODE"; + case PM_NUMBERED_REFERENCE_READ_NODE: + return "PM_NUMBERED_REFERENCE_READ_NODE"; + case PM_OPTIONAL_KEYWORD_PARAMETER_NODE: + return "PM_OPTIONAL_KEYWORD_PARAMETER_NODE"; + case PM_OPTIONAL_PARAMETER_NODE: + return "PM_OPTIONAL_PARAMETER_NODE"; + case PM_OR_NODE: + return "PM_OR_NODE"; + case PM_PARAMETERS_NODE: + return "PM_PARAMETERS_NODE"; + case PM_PARENTHESES_NODE: + return "PM_PARENTHESES_NODE"; + case PM_PINNED_EXPRESSION_NODE: + return "PM_PINNED_EXPRESSION_NODE"; + case PM_PINNED_VARIABLE_NODE: + return "PM_PINNED_VARIABLE_NODE"; + case PM_POST_EXECUTION_NODE: + return "PM_POST_EXECUTION_NODE"; + case PM_PRE_EXECUTION_NODE: + return "PM_PRE_EXECUTION_NODE"; + case PM_PROGRAM_NODE: + return "PM_PROGRAM_NODE"; + case PM_RANGE_NODE: + return "PM_RANGE_NODE"; + case PM_RATIONAL_NODE: + return "PM_RATIONAL_NODE"; + case PM_REDO_NODE: + return "PM_REDO_NODE"; + case PM_REGULAR_EXPRESSION_NODE: + return "PM_REGULAR_EXPRESSION_NODE"; + case PM_REQUIRED_KEYWORD_PARAMETER_NODE: + return "PM_REQUIRED_KEYWORD_PARAMETER_NODE"; + case PM_REQUIRED_PARAMETER_NODE: + return "PM_REQUIRED_PARAMETER_NODE"; + case PM_RESCUE_MODIFIER_NODE: + return "PM_RESCUE_MODIFIER_NODE"; + case PM_RESCUE_NODE: + return "PM_RESCUE_NODE"; + case PM_REST_PARAMETER_NODE: + return "PM_REST_PARAMETER_NODE"; + case PM_RETRY_NODE: + return "PM_RETRY_NODE"; + case PM_RETURN_NODE: + return "PM_RETURN_NODE"; + case PM_SELF_NODE: + return "PM_SELF_NODE"; + case PM_SHAREABLE_CONSTANT_NODE: + return "PM_SHAREABLE_CONSTANT_NODE"; + case PM_SINGLETON_CLASS_NODE: + return "PM_SINGLETON_CLASS_NODE"; + case PM_SOURCE_ENCODING_NODE: + return "PM_SOURCE_ENCODING_NODE"; + case PM_SOURCE_FILE_NODE: + return "PM_SOURCE_FILE_NODE"; + case PM_SOURCE_LINE_NODE: + return "PM_SOURCE_LINE_NODE"; + case PM_SPLAT_NODE: + return "PM_SPLAT_NODE"; + case PM_STATEMENTS_NODE: + return "PM_STATEMENTS_NODE"; + case PM_STRING_NODE: + return "PM_STRING_NODE"; + case PM_SUPER_NODE: + return "PM_SUPER_NODE"; + case PM_SYMBOL_NODE: + return "PM_SYMBOL_NODE"; + case PM_TRUE_NODE: + return "PM_TRUE_NODE"; + case PM_UNDEF_NODE: + return "PM_UNDEF_NODE"; + case PM_UNLESS_NODE: + return "PM_UNLESS_NODE"; + case PM_UNTIL_NODE: + return "PM_UNTIL_NODE"; + case PM_WHEN_NODE: + return "PM_WHEN_NODE"; + case PM_WHILE_NODE: + return "PM_WHILE_NODE"; + case PM_X_STRING_NODE: + return "PM_X_STRING_NODE"; + case PM_YIELD_NODE: + return "PM_YIELD_NODE"; + } + 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)) { + case PM_ALIAS_GLOBAL_VARIABLE_NODE: { + const pm_alias_global_variable_node_t *cast = (const pm_alias_global_variable_node_t *) node; + + // Visit the new_name field + pm_visit_node((const pm_node_t *) cast->new_name, visitor, data); + + // Visit the old_name field + pm_visit_node((const pm_node_t *) cast->old_name, visitor, data); + + break; + } + case PM_ALIAS_METHOD_NODE: { + const pm_alias_method_node_t *cast = (const pm_alias_method_node_t *) node; + + // Visit the new_name field + pm_visit_node((const pm_node_t *) cast->new_name, visitor, data); + + // Visit the old_name field + pm_visit_node((const pm_node_t *) cast->old_name, visitor, data); + + break; + } + case PM_ALTERNATION_PATTERN_NODE: { + const pm_alternation_pattern_node_t *cast = (const pm_alternation_pattern_node_t *) node; + + // Visit the left field + pm_visit_node((const pm_node_t *) cast->left, visitor, data); + + // Visit the right field + pm_visit_node((const pm_node_t *) cast->right, visitor, data); + + break; + } + case PM_AND_NODE: { + const pm_and_node_t *cast = (const pm_and_node_t *) node; + + // Visit the left field + pm_visit_node((const pm_node_t *) cast->left, visitor, data); + + // Visit the right field + pm_visit_node((const pm_node_t *) cast->right, visitor, data); + + break; + } + case PM_ARGUMENTS_NODE: { + const pm_arguments_node_t *cast = (const pm_arguments_node_t *) node; + + // Visit the arguments field + const pm_node_list_t *arguments = &cast->arguments; + for (size_t index = 0; index < arguments->size; index++) { + pm_visit_node(arguments->nodes[index], visitor, data); + } + + break; + } + case PM_ARRAY_NODE: { + const pm_array_node_t *cast = (const pm_array_node_t *) node; + + // Visit the elements field + const pm_node_list_t *elements = &cast->elements; + for (size_t index = 0; index < elements->size; index++) { + pm_visit_node(elements->nodes[index], visitor, data); + } + + break; + } + case PM_ARRAY_PATTERN_NODE: { + const pm_array_pattern_node_t *cast = (const pm_array_pattern_node_t *) node; + + // Visit the constant field + if (cast->constant != NULL) { + pm_visit_node((const pm_node_t *) cast->constant, visitor, data); + } + + // Visit the requireds field + const pm_node_list_t *requireds = &cast->requireds; + for (size_t index = 0; index < requireds->size; index++) { + pm_visit_node(requireds->nodes[index], visitor, data); + } + + // Visit the rest field + if (cast->rest != NULL) { + pm_visit_node((const pm_node_t *) cast->rest, visitor, data); + } + + // Visit the posts field + const pm_node_list_t *posts = &cast->posts; + for (size_t index = 0; index < posts->size; index++) { + pm_visit_node(posts->nodes[index], visitor, data); + } + + break; + } + case PM_ASSOC_NODE: { + const pm_assoc_node_t *cast = (const pm_assoc_node_t *) node; + + // Visit the key field + pm_visit_node((const pm_node_t *) cast->key, visitor, data); + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_ASSOC_SPLAT_NODE: { + const pm_assoc_splat_node_t *cast = (const pm_assoc_splat_node_t *) node; + + // Visit the value field + if (cast->value != NULL) { + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + } + + break; + } + case PM_BACK_REFERENCE_READ_NODE: + break; + case PM_BEGIN_NODE: { + const pm_begin_node_t *cast = (const pm_begin_node_t *) node; + + // Visit the statements field + if (cast->statements != NULL) { + pm_visit_node((const pm_node_t *) cast->statements, visitor, data); + } + + // Visit the rescue_clause field + if (cast->rescue_clause != NULL) { + pm_visit_node((const pm_node_t *) cast->rescue_clause, visitor, data); + } + + // Visit the else_clause field + if (cast->else_clause != NULL) { + pm_visit_node((const pm_node_t *) cast->else_clause, visitor, data); + } + + // Visit the ensure_clause field + if (cast->ensure_clause != NULL) { + pm_visit_node((const pm_node_t *) cast->ensure_clause, visitor, data); + } + + break; + } + case PM_BLOCK_ARGUMENT_NODE: { + const pm_block_argument_node_t *cast = (const pm_block_argument_node_t *) node; + + // Visit the expression field + if (cast->expression != NULL) { + pm_visit_node((const pm_node_t *) cast->expression, visitor, data); + } + + break; + } + case PM_BLOCK_LOCAL_VARIABLE_NODE: + break; + case PM_BLOCK_NODE: { + const pm_block_node_t *cast = (const pm_block_node_t *) node; + + // Visit the parameters field + if (cast->parameters != NULL) { + pm_visit_node((const pm_node_t *) cast->parameters, visitor, data); + } + + // Visit the body field + if (cast->body != NULL) { + pm_visit_node((const pm_node_t *) cast->body, visitor, data); + } + + break; + } + case PM_BLOCK_PARAMETER_NODE: + break; + case PM_BLOCK_PARAMETERS_NODE: { + const pm_block_parameters_node_t *cast = (const pm_block_parameters_node_t *) node; + + // Visit the parameters field + if (cast->parameters != NULL) { + pm_visit_node((const pm_node_t *) cast->parameters, visitor, data); + } + + // Visit the locals field + const pm_node_list_t *locals = &cast->locals; + for (size_t index = 0; index < locals->size; index++) { + pm_visit_node(locals->nodes[index], visitor, data); + } + + break; + } + case PM_BREAK_NODE: { + const pm_break_node_t *cast = (const pm_break_node_t *) node; + + // Visit the arguments field + if (cast->arguments != NULL) { + pm_visit_node((const pm_node_t *) cast->arguments, visitor, data); + } + + break; + } + case PM_CALL_AND_WRITE_NODE: { + const pm_call_and_write_node_t *cast = (const pm_call_and_write_node_t *) node; + + // Visit the receiver field + if (cast->receiver != NULL) { + pm_visit_node((const pm_node_t *) cast->receiver, visitor, data); + } + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_CALL_NODE: { + const pm_call_node_t *cast = (const pm_call_node_t *) node; + + // Visit the receiver field + if (cast->receiver != NULL) { + pm_visit_node((const pm_node_t *) cast->receiver, visitor, data); + } + + // Visit the arguments field + if (cast->arguments != NULL) { + pm_visit_node((const pm_node_t *) cast->arguments, visitor, data); + } + + // Visit the block field + if (cast->block != NULL) { + pm_visit_node((const pm_node_t *) cast->block, visitor, data); + } + + break; + } + case PM_CALL_OPERATOR_WRITE_NODE: { + const pm_call_operator_write_node_t *cast = (const pm_call_operator_write_node_t *) node; + + // Visit the receiver field + if (cast->receiver != NULL) { + pm_visit_node((const pm_node_t *) cast->receiver, visitor, data); + } + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_CALL_OR_WRITE_NODE: { + const pm_call_or_write_node_t *cast = (const pm_call_or_write_node_t *) node; + + // Visit the receiver field + if (cast->receiver != NULL) { + pm_visit_node((const pm_node_t *) cast->receiver, visitor, data); + } + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_CALL_TARGET_NODE: { + const pm_call_target_node_t *cast = (const pm_call_target_node_t *) node; + + // Visit the receiver field + pm_visit_node((const pm_node_t *) cast->receiver, visitor, data); + + break; + } + case PM_CAPTURE_PATTERN_NODE: { + const pm_capture_pattern_node_t *cast = (const pm_capture_pattern_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + // Visit the target field + pm_visit_node((const pm_node_t *) cast->target, visitor, data); + + break; + } + case PM_CASE_MATCH_NODE: { + const pm_case_match_node_t *cast = (const pm_case_match_node_t *) node; + + // Visit the predicate field + if (cast->predicate != NULL) { + pm_visit_node((const pm_node_t *) cast->predicate, visitor, data); + } + + // Visit the conditions field + const pm_node_list_t *conditions = &cast->conditions; + for (size_t index = 0; index < conditions->size; index++) { + pm_visit_node(conditions->nodes[index], visitor, data); + } + + // Visit the else_clause field + if (cast->else_clause != NULL) { + pm_visit_node((const pm_node_t *) cast->else_clause, visitor, data); + } + + break; + } + case PM_CASE_NODE: { + const pm_case_node_t *cast = (const pm_case_node_t *) node; + + // Visit the predicate field + if (cast->predicate != NULL) { + pm_visit_node((const pm_node_t *) cast->predicate, visitor, data); + } + + // Visit the conditions field + const pm_node_list_t *conditions = &cast->conditions; + for (size_t index = 0; index < conditions->size; index++) { + pm_visit_node(conditions->nodes[index], visitor, data); + } + + // Visit the else_clause field + if (cast->else_clause != NULL) { + pm_visit_node((const pm_node_t *) cast->else_clause, visitor, data); + } + + break; + } + case PM_CLASS_NODE: { + const pm_class_node_t *cast = (const pm_class_node_t *) node; + + // Visit the constant_path field + pm_visit_node((const pm_node_t *) cast->constant_path, visitor, data); + + // Visit the superclass field + if (cast->superclass != NULL) { + pm_visit_node((const pm_node_t *) cast->superclass, visitor, data); + } + + // Visit the body field + if (cast->body != NULL) { + pm_visit_node((const pm_node_t *) cast->body, visitor, data); + } + + break; + } + case PM_CLASS_VARIABLE_AND_WRITE_NODE: { + const pm_class_variable_and_write_node_t *cast = (const pm_class_variable_and_write_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE: { + const pm_class_variable_operator_write_node_t *cast = (const pm_class_variable_operator_write_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_CLASS_VARIABLE_OR_WRITE_NODE: { + const pm_class_variable_or_write_node_t *cast = (const pm_class_variable_or_write_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_CLASS_VARIABLE_READ_NODE: + break; + case PM_CLASS_VARIABLE_TARGET_NODE: + break; + case PM_CLASS_VARIABLE_WRITE_NODE: { + const pm_class_variable_write_node_t *cast = (const pm_class_variable_write_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_CONSTANT_AND_WRITE_NODE: { + const pm_constant_and_write_node_t *cast = (const pm_constant_and_write_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_CONSTANT_OPERATOR_WRITE_NODE: { + const pm_constant_operator_write_node_t *cast = (const pm_constant_operator_write_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_CONSTANT_OR_WRITE_NODE: { + const pm_constant_or_write_node_t *cast = (const pm_constant_or_write_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_CONSTANT_PATH_AND_WRITE_NODE: { + const pm_constant_path_and_write_node_t *cast = (const pm_constant_path_and_write_node_t *) node; + + // Visit the target field + pm_visit_node((const pm_node_t *) cast->target, visitor, data); + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_CONSTANT_PATH_NODE: { + const pm_constant_path_node_t *cast = (const pm_constant_path_node_t *) node; + + // Visit the parent field + if (cast->parent != NULL) { + pm_visit_node((const pm_node_t *) cast->parent, visitor, data); + } + + break; + } + case PM_CONSTANT_PATH_OPERATOR_WRITE_NODE: { + const pm_constant_path_operator_write_node_t *cast = (const pm_constant_path_operator_write_node_t *) node; + + // Visit the target field + pm_visit_node((const pm_node_t *) cast->target, visitor, data); + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_CONSTANT_PATH_OR_WRITE_NODE: { + const pm_constant_path_or_write_node_t *cast = (const pm_constant_path_or_write_node_t *) node; + + // Visit the target field + pm_visit_node((const pm_node_t *) cast->target, visitor, data); + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_CONSTANT_PATH_TARGET_NODE: { + const pm_constant_path_target_node_t *cast = (const pm_constant_path_target_node_t *) node; + + // Visit the parent field + if (cast->parent != NULL) { + pm_visit_node((const pm_node_t *) cast->parent, visitor, data); + } + + break; + } + case PM_CONSTANT_PATH_WRITE_NODE: { + const pm_constant_path_write_node_t *cast = (const pm_constant_path_write_node_t *) node; + + // Visit the target field + pm_visit_node((const pm_node_t *) cast->target, visitor, data); + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_CONSTANT_READ_NODE: + break; + case PM_CONSTANT_TARGET_NODE: + break; + case PM_CONSTANT_WRITE_NODE: { + const pm_constant_write_node_t *cast = (const pm_constant_write_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_DEF_NODE: { + const pm_def_node_t *cast = (const pm_def_node_t *) node; + + // Visit the receiver field + if (cast->receiver != NULL) { + pm_visit_node((const pm_node_t *) cast->receiver, visitor, data); + } + + // Visit the parameters field + if (cast->parameters != NULL) { + pm_visit_node((const pm_node_t *) cast->parameters, visitor, data); + } + + // Visit the body field + if (cast->body != NULL) { + pm_visit_node((const pm_node_t *) cast->body, visitor, data); + } + + break; + } + case PM_DEFINED_NODE: { + const pm_defined_node_t *cast = (const pm_defined_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_ELSE_NODE: { + const pm_else_node_t *cast = (const pm_else_node_t *) node; + + // Visit the statements field + if (cast->statements != NULL) { + pm_visit_node((const pm_node_t *) cast->statements, visitor, data); + } + + break; + } + case PM_EMBEDDED_STATEMENTS_NODE: { + const pm_embedded_statements_node_t *cast = (const pm_embedded_statements_node_t *) node; + + // Visit the statements field + if (cast->statements != NULL) { + pm_visit_node((const pm_node_t *) cast->statements, visitor, data); + } + + break; + } + case PM_EMBEDDED_VARIABLE_NODE: { + const pm_embedded_variable_node_t *cast = (const pm_embedded_variable_node_t *) node; + + // Visit the variable field + pm_visit_node((const pm_node_t *) cast->variable, visitor, data); + + break; + } + case PM_ENSURE_NODE: { + const pm_ensure_node_t *cast = (const pm_ensure_node_t *) node; + + // Visit the statements field + if (cast->statements != NULL) { + pm_visit_node((const pm_node_t *) cast->statements, visitor, data); + } + + break; + } + case PM_FALSE_NODE: + break; + case PM_FIND_PATTERN_NODE: { + const pm_find_pattern_node_t *cast = (const pm_find_pattern_node_t *) node; + + // Visit the constant field + if (cast->constant != NULL) { + pm_visit_node((const pm_node_t *) cast->constant, visitor, data); + } + + // Visit the left field + pm_visit_node((const pm_node_t *) cast->left, visitor, data); + + // Visit the requireds field + const pm_node_list_t *requireds = &cast->requireds; + for (size_t index = 0; index < requireds->size; index++) { + pm_visit_node(requireds->nodes[index], visitor, data); + } + + // Visit the right field + pm_visit_node((const pm_node_t *) cast->right, visitor, data); + + break; + } + case PM_FLIP_FLOP_NODE: { + const pm_flip_flop_node_t *cast = (const pm_flip_flop_node_t *) node; + + // Visit the left field + if (cast->left != NULL) { + pm_visit_node((const pm_node_t *) cast->left, visitor, data); + } + + // Visit the right field + if (cast->right != NULL) { + pm_visit_node((const pm_node_t *) cast->right, visitor, data); + } + + break; + } + case PM_FLOAT_NODE: + break; + case PM_FOR_NODE: { + const pm_for_node_t *cast = (const pm_for_node_t *) node; + + // Visit the index field + pm_visit_node((const pm_node_t *) cast->index, visitor, data); + + // Visit the collection field + pm_visit_node((const pm_node_t *) cast->collection, visitor, data); + + // Visit the statements field + if (cast->statements != NULL) { + pm_visit_node((const pm_node_t *) cast->statements, visitor, data); + } + + break; + } + case PM_FORWARDING_ARGUMENTS_NODE: + break; + case PM_FORWARDING_PARAMETER_NODE: + break; + case PM_FORWARDING_SUPER_NODE: { + const pm_forwarding_super_node_t *cast = (const pm_forwarding_super_node_t *) node; + + // Visit the block field + if (cast->block != NULL) { + pm_visit_node((const pm_node_t *) cast->block, visitor, data); + } + + break; + } + case PM_GLOBAL_VARIABLE_AND_WRITE_NODE: { + const pm_global_variable_and_write_node_t *cast = (const pm_global_variable_and_write_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE: { + const pm_global_variable_operator_write_node_t *cast = (const pm_global_variable_operator_write_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_GLOBAL_VARIABLE_OR_WRITE_NODE: { + const pm_global_variable_or_write_node_t *cast = (const pm_global_variable_or_write_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_GLOBAL_VARIABLE_READ_NODE: + break; + case PM_GLOBAL_VARIABLE_TARGET_NODE: + break; + case PM_GLOBAL_VARIABLE_WRITE_NODE: { + const pm_global_variable_write_node_t *cast = (const pm_global_variable_write_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_HASH_NODE: { + const pm_hash_node_t *cast = (const pm_hash_node_t *) node; + + // Visit the elements field + const pm_node_list_t *elements = &cast->elements; + for (size_t index = 0; index < elements->size; index++) { + pm_visit_node(elements->nodes[index], visitor, data); + } + + break; + } + case PM_HASH_PATTERN_NODE: { + const pm_hash_pattern_node_t *cast = (const pm_hash_pattern_node_t *) node; + + // Visit the constant field + if (cast->constant != NULL) { + pm_visit_node((const pm_node_t *) cast->constant, visitor, data); + } + + // Visit the elements field + const pm_node_list_t *elements = &cast->elements; + for (size_t index = 0; index < elements->size; index++) { + pm_visit_node(elements->nodes[index], visitor, data); + } + + // Visit the rest field + if (cast->rest != NULL) { + pm_visit_node((const pm_node_t *) cast->rest, visitor, data); + } + + break; + } + case PM_IF_NODE: { + const pm_if_node_t *cast = (const pm_if_node_t *) node; + + // Visit the predicate field + pm_visit_node((const pm_node_t *) cast->predicate, visitor, data); + + // Visit the statements field + if (cast->statements != NULL) { + pm_visit_node((const pm_node_t *) cast->statements, visitor, data); + } + + // Visit the subsequent field + if (cast->subsequent != NULL) { + pm_visit_node((const pm_node_t *) cast->subsequent, visitor, data); + } + + break; + } + case PM_IMAGINARY_NODE: { + const pm_imaginary_node_t *cast = (const pm_imaginary_node_t *) node; + + // Visit the numeric field + pm_visit_node((const pm_node_t *) cast->numeric, visitor, data); + + break; + } + case PM_IMPLICIT_NODE: { + const pm_implicit_node_t *cast = (const pm_implicit_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_IMPLICIT_REST_NODE: + break; + case PM_IN_NODE: { + const pm_in_node_t *cast = (const pm_in_node_t *) node; + + // Visit the pattern field + pm_visit_node((const pm_node_t *) cast->pattern, visitor, data); + + // Visit the statements field + if (cast->statements != NULL) { + pm_visit_node((const pm_node_t *) cast->statements, visitor, data); + } + + break; + } + case PM_INDEX_AND_WRITE_NODE: { + const pm_index_and_write_node_t *cast = (const pm_index_and_write_node_t *) node; + + // Visit the receiver field + if (cast->receiver != NULL) { + pm_visit_node((const pm_node_t *) cast->receiver, visitor, data); + } + + // Visit the arguments field + if (cast->arguments != NULL) { + pm_visit_node((const pm_node_t *) cast->arguments, visitor, data); + } + + // Visit the block field + if (cast->block != NULL) { + pm_visit_node((const pm_node_t *) cast->block, visitor, data); + } + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_INDEX_OPERATOR_WRITE_NODE: { + const pm_index_operator_write_node_t *cast = (const pm_index_operator_write_node_t *) node; + + // Visit the receiver field + if (cast->receiver != NULL) { + pm_visit_node((const pm_node_t *) cast->receiver, visitor, data); + } + + // Visit the arguments field + if (cast->arguments != NULL) { + pm_visit_node((const pm_node_t *) cast->arguments, visitor, data); + } + + // Visit the block field + if (cast->block != NULL) { + pm_visit_node((const pm_node_t *) cast->block, visitor, data); + } + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_INDEX_OR_WRITE_NODE: { + const pm_index_or_write_node_t *cast = (const pm_index_or_write_node_t *) node; + + // Visit the receiver field + if (cast->receiver != NULL) { + pm_visit_node((const pm_node_t *) cast->receiver, visitor, data); + } + + // Visit the arguments field + if (cast->arguments != NULL) { + pm_visit_node((const pm_node_t *) cast->arguments, visitor, data); + } + + // Visit the block field + if (cast->block != NULL) { + pm_visit_node((const pm_node_t *) cast->block, visitor, data); + } + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_INDEX_TARGET_NODE: { + const pm_index_target_node_t *cast = (const pm_index_target_node_t *) node; + + // Visit the receiver field + pm_visit_node((const pm_node_t *) cast->receiver, visitor, data); + + // Visit the arguments field + if (cast->arguments != NULL) { + pm_visit_node((const pm_node_t *) cast->arguments, visitor, data); + } + + // Visit the block field + if (cast->block != NULL) { + pm_visit_node((const pm_node_t *) cast->block, visitor, data); + } + + break; + } + case PM_INSTANCE_VARIABLE_AND_WRITE_NODE: { + const pm_instance_variable_and_write_node_t *cast = (const pm_instance_variable_and_write_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE: { + const pm_instance_variable_operator_write_node_t *cast = (const pm_instance_variable_operator_write_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_INSTANCE_VARIABLE_OR_WRITE_NODE: { + const pm_instance_variable_or_write_node_t *cast = (const pm_instance_variable_or_write_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_INSTANCE_VARIABLE_READ_NODE: + break; + case PM_INSTANCE_VARIABLE_TARGET_NODE: + break; + case PM_INSTANCE_VARIABLE_WRITE_NODE: { + const pm_instance_variable_write_node_t *cast = (const pm_instance_variable_write_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_INTEGER_NODE: + break; + 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; + + // Visit the parts field + const pm_node_list_t *parts = &cast->parts; + for (size_t index = 0; index < parts->size; index++) { + pm_visit_node(parts->nodes[index], visitor, data); + } + + break; + } + case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: { + const pm_interpolated_regular_expression_node_t *cast = (const pm_interpolated_regular_expression_node_t *) node; + + // Visit the parts field + const pm_node_list_t *parts = &cast->parts; + for (size_t index = 0; index < parts->size; index++) { + pm_visit_node(parts->nodes[index], visitor, data); + } + + break; + } + case PM_INTERPOLATED_STRING_NODE: { + const pm_interpolated_string_node_t *cast = (const pm_interpolated_string_node_t *) node; + + // Visit the parts field + const pm_node_list_t *parts = &cast->parts; + for (size_t index = 0; index < parts->size; index++) { + pm_visit_node(parts->nodes[index], visitor, data); + } + + break; + } + case PM_INTERPOLATED_SYMBOL_NODE: { + const pm_interpolated_symbol_node_t *cast = (const pm_interpolated_symbol_node_t *) node; + + // Visit the parts field + const pm_node_list_t *parts = &cast->parts; + for (size_t index = 0; index < parts->size; index++) { + pm_visit_node(parts->nodes[index], visitor, data); + } + + break; + } + case PM_INTERPOLATED_X_STRING_NODE: { + const pm_interpolated_x_string_node_t *cast = (const pm_interpolated_x_string_node_t *) node; + + // Visit the parts field + const pm_node_list_t *parts = &cast->parts; + for (size_t index = 0; index < parts->size; index++) { + pm_visit_node(parts->nodes[index], visitor, data); + } + + break; + } + case PM_IT_LOCAL_VARIABLE_READ_NODE: + break; + case PM_IT_PARAMETERS_NODE: + break; + case PM_KEYWORD_HASH_NODE: { + const pm_keyword_hash_node_t *cast = (const pm_keyword_hash_node_t *) node; + + // Visit the elements field + const pm_node_list_t *elements = &cast->elements; + for (size_t index = 0; index < elements->size; index++) { + pm_visit_node(elements->nodes[index], visitor, data); + } + + break; + } + case PM_KEYWORD_REST_PARAMETER_NODE: + break; + case PM_LAMBDA_NODE: { + const pm_lambda_node_t *cast = (const pm_lambda_node_t *) node; + + // Visit the parameters field + if (cast->parameters != NULL) { + pm_visit_node((const pm_node_t *) cast->parameters, visitor, data); + } + + // Visit the body field + if (cast->body != NULL) { + pm_visit_node((const pm_node_t *) cast->body, visitor, data); + } + + break; + } + case PM_LOCAL_VARIABLE_AND_WRITE_NODE: { + const pm_local_variable_and_write_node_t *cast = (const pm_local_variable_and_write_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE: { + const pm_local_variable_operator_write_node_t *cast = (const pm_local_variable_operator_write_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_LOCAL_VARIABLE_OR_WRITE_NODE: { + const pm_local_variable_or_write_node_t *cast = (const pm_local_variable_or_write_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_LOCAL_VARIABLE_READ_NODE: + break; + case PM_LOCAL_VARIABLE_TARGET_NODE: + break; + case PM_LOCAL_VARIABLE_WRITE_NODE: { + const pm_local_variable_write_node_t *cast = (const pm_local_variable_write_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_MATCH_LAST_LINE_NODE: + break; + case PM_MATCH_PREDICATE_NODE: { + const pm_match_predicate_node_t *cast = (const pm_match_predicate_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + // Visit the pattern field + pm_visit_node((const pm_node_t *) cast->pattern, visitor, data); + + break; + } + case PM_MATCH_REQUIRED_NODE: { + const pm_match_required_node_t *cast = (const pm_match_required_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + // Visit the pattern field + pm_visit_node((const pm_node_t *) cast->pattern, visitor, data); + + break; + } + case PM_MATCH_WRITE_NODE: { + const pm_match_write_node_t *cast = (const pm_match_write_node_t *) node; + + // Visit the call field + pm_visit_node((const pm_node_t *) cast->call, visitor, data); + + // Visit the targets field + const pm_node_list_t *targets = &cast->targets; + for (size_t index = 0; index < targets->size; index++) { + pm_visit_node(targets->nodes[index], visitor, data); + } + + break; + } + case PM_MISSING_NODE: + break; + case PM_MODULE_NODE: { + const pm_module_node_t *cast = (const pm_module_node_t *) node; + + // Visit the constant_path field + pm_visit_node((const pm_node_t *) cast->constant_path, visitor, data); + + // Visit the body field + if (cast->body != NULL) { + pm_visit_node((const pm_node_t *) cast->body, visitor, data); + } + + break; + } + case PM_MULTI_TARGET_NODE: { + const pm_multi_target_node_t *cast = (const pm_multi_target_node_t *) node; + + // Visit the lefts field + const pm_node_list_t *lefts = &cast->lefts; + for (size_t index = 0; index < lefts->size; index++) { + pm_visit_node(lefts->nodes[index], visitor, data); + } + + // Visit the rest field + if (cast->rest != NULL) { + pm_visit_node((const pm_node_t *) cast->rest, visitor, data); + } + + // Visit the rights field + const pm_node_list_t *rights = &cast->rights; + for (size_t index = 0; index < rights->size; index++) { + pm_visit_node(rights->nodes[index], visitor, data); + } + + break; + } + case PM_MULTI_WRITE_NODE: { + const pm_multi_write_node_t *cast = (const pm_multi_write_node_t *) node; + + // Visit the lefts field + const pm_node_list_t *lefts = &cast->lefts; + for (size_t index = 0; index < lefts->size; index++) { + pm_visit_node(lefts->nodes[index], visitor, data); + } + + // Visit the rest field + if (cast->rest != NULL) { + pm_visit_node((const pm_node_t *) cast->rest, visitor, data); + } + + // Visit the rights field + const pm_node_list_t *rights = &cast->rights; + for (size_t index = 0; index < rights->size; index++) { + pm_visit_node(rights->nodes[index], visitor, data); + } + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_NEXT_NODE: { + const pm_next_node_t *cast = (const pm_next_node_t *) node; + + // Visit the arguments field + if (cast->arguments != NULL) { + pm_visit_node((const pm_node_t *) cast->arguments, visitor, data); + } + + break; + } + case PM_NIL_NODE: + break; + case PM_NO_KEYWORDS_PARAMETER_NODE: + break; + case PM_NUMBERED_PARAMETERS_NODE: + break; + case PM_NUMBERED_REFERENCE_READ_NODE: + break; + case PM_OPTIONAL_KEYWORD_PARAMETER_NODE: { + const pm_optional_keyword_parameter_node_t *cast = (const pm_optional_keyword_parameter_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_OPTIONAL_PARAMETER_NODE: { + const pm_optional_parameter_node_t *cast = (const pm_optional_parameter_node_t *) node; + + // Visit the value field + pm_visit_node((const pm_node_t *) cast->value, visitor, data); + + break; + } + case PM_OR_NODE: { + const pm_or_node_t *cast = (const pm_or_node_t *) node; + + // Visit the left field + pm_visit_node((const pm_node_t *) cast->left, visitor, data); + + // Visit the right field + pm_visit_node((const pm_node_t *) cast->right, visitor, data); + + break; + } + case PM_PARAMETERS_NODE: { + const pm_parameters_node_t *cast = (const pm_parameters_node_t *) node; + + // Visit the requireds field + const pm_node_list_t *requireds = &cast->requireds; + for (size_t index = 0; index < requireds->size; index++) { + pm_visit_node(requireds->nodes[index], visitor, data); + } + + // Visit the optionals field + const pm_node_list_t *optionals = &cast->optionals; + for (size_t index = 0; index < optionals->size; index++) { + pm_visit_node(optionals->nodes[index], visitor, data); + } + + // Visit the rest field + if (cast->rest != NULL) { + pm_visit_node((const pm_node_t *) cast->rest, visitor, data); + } + + // Visit the posts field + const pm_node_list_t *posts = &cast->posts; + for (size_t index = 0; index < posts->size; index++) { + pm_visit_node(posts->nodes[index], visitor, data); + } + + // Visit the keywords field + const pm_node_list_t *keywords = &cast->keywords; + for (size_t index = 0; index < keywords->size; index++) { + pm_visit_node(keywords->nodes[index], visitor, data); + } + + // Visit the keyword_rest field + if (cast->keyword_rest != NULL) { + pm_visit_node((const pm_node_t *) cast->keyword_rest, visitor, data); + } + + // Visit the block field + if (cast->block != NULL) { + pm_visit_node((const pm_node_t *) cast->block, visitor, data); + } + + break; + } + case PM_PARENTHESES_NODE: { + const pm_parentheses_node_t *cast = (const pm_parentheses_node_t *) node; + + // Visit the body field + if (cast->body != NULL) { + pm_visit_node((const pm_node_t *) cast->body, visitor, data); + } + + break; + } + case PM_PINNED_EXPRESSION_NODE: { + const pm_pinned_expression_node_t *cast = (const pm_pinned_expression_node_t *) node; + + // Visit the expression field + pm_visit_node((const pm_node_t *) cast->expression, visitor, data); + + break; + } + case PM_PINNED_VARIABLE_NODE: { + const pm_pinned_variable_node_t *cast = (const pm_pinned_variable_node_t *) node; + + // Visit the variable field + pm_visit_node((const pm_node_t *) cast->variable, visitor, data); + + break; + } + case PM_POST_EXECUTION_NODE: { + const pm_post_execution_node_t *cast = (const pm_post_execution_node_t *) node; + + // Visit the statements field + if (cast->statements != NULL) { + pm_visit_node((const pm_node_t *) cast->statements, visitor, data); + } + + break; + } + case PM_PRE_EXECUTION_NODE: { + const pm_pre_execution_node_t *cast = (const pm_pre_execution_node_t *) node; + + // Visit the statements field + if (cast->statements != NULL) { + pm_visit_node((const pm_node_t *) cast->statements, visitor, data); + } + + break; + } + case PM_PROGRAM_NODE: { + const pm_program_node_t *cast = (const pm_program_node_t *) node; + + // Visit the statements field + pm_visit_node((const pm_node_t *) cast->statements, visitor, data); + + break; + } + case PM_RANGE_NODE: { + const pm_range_node_t *cast = (const pm_range_node_t *) node; + + // Visit the left field + if (cast->left != NULL) { + pm_visit_node((const pm_node_t *) cast->left, visitor, data); + } + + // Visit the right field + if (cast->right != NULL) { + pm_visit_node((const pm_node_t *) cast->right, visitor, data); + } + + break; + } + case PM_RATIONAL_NODE: + break; + case PM_REDO_NODE: + break; + case PM_REGULAR_EXPRESSION_NODE: + break; + case PM_REQUIRED_KEYWORD_PARAMETER_NODE: + break; + case PM_REQUIRED_PARAMETER_NODE: + break; + case PM_RESCUE_MODIFIER_NODE: { + const pm_rescue_modifier_node_t *cast = (const pm_rescue_modifier_node_t *) node; + + // Visit the expression field + pm_visit_node((const pm_node_t *) cast->expression, visitor, data); + + // Visit the rescue_expression field + pm_visit_node((const pm_node_t *) cast->rescue_expression, visitor, data); + + break; + } + case PM_RESCUE_NODE: { + const pm_rescue_node_t *cast = (const pm_rescue_node_t *) node; + + // Visit the exceptions field + const pm_node_list_t *exceptions = &cast->exceptions; + for (size_t index = 0; index < exceptions->size; index++) { + pm_visit_node(exceptions->nodes[index], visitor, data); + } + + // Visit the reference field + if (cast->reference != NULL) { + pm_visit_node((const pm_node_t *) cast->reference, visitor, data); + } + + // Visit the statements field + if (cast->statements != NULL) { + pm_visit_node((const pm_node_t *) cast->statements, visitor, data); + } + + // Visit the subsequent field + if (cast->subsequent != NULL) { + pm_visit_node((const pm_node_t *) cast->subsequent, visitor, data); + } + + break; + } + case PM_REST_PARAMETER_NODE: + break; + case PM_RETRY_NODE: + break; + case PM_RETURN_NODE: { + const pm_return_node_t *cast = (const pm_return_node_t *) node; + + // Visit the arguments field + if (cast->arguments != NULL) { + pm_visit_node((const pm_node_t *) cast->arguments, visitor, data); + } + + break; + } + case PM_SELF_NODE: + break; + case PM_SHAREABLE_CONSTANT_NODE: { + const pm_shareable_constant_node_t *cast = (const pm_shareable_constant_node_t *) node; + + // Visit the write field + pm_visit_node((const pm_node_t *) cast->write, visitor, data); + + break; + } + case PM_SINGLETON_CLASS_NODE: { + const pm_singleton_class_node_t *cast = (const pm_singleton_class_node_t *) node; + + // Visit the expression field + pm_visit_node((const pm_node_t *) cast->expression, visitor, data); + + // Visit the body field + if (cast->body != NULL) { + pm_visit_node((const pm_node_t *) cast->body, visitor, data); + } + + break; + } + case PM_SOURCE_ENCODING_NODE: + break; + case PM_SOURCE_FILE_NODE: + break; + case PM_SOURCE_LINE_NODE: + break; + case PM_SPLAT_NODE: { + const pm_splat_node_t *cast = (const pm_splat_node_t *) node; + + // Visit the expression field + if (cast->expression != NULL) { + pm_visit_node((const pm_node_t *) cast->expression, visitor, data); + } + + break; + } + case PM_STATEMENTS_NODE: { + const pm_statements_node_t *cast = (const pm_statements_node_t *) node; + + // Visit the body field + const pm_node_list_t *body = &cast->body; + for (size_t index = 0; index < body->size; index++) { + pm_visit_node(body->nodes[index], visitor, data); + } + + break; + } + case PM_STRING_NODE: + break; + case PM_SUPER_NODE: { + const pm_super_node_t *cast = (const pm_super_node_t *) node; + + // Visit the arguments field + if (cast->arguments != NULL) { + pm_visit_node((const pm_node_t *) cast->arguments, visitor, data); + } + + // Visit the block field + if (cast->block != NULL) { + pm_visit_node((const pm_node_t *) cast->block, visitor, data); + } + + break; + } + case PM_SYMBOL_NODE: + break; + case PM_TRUE_NODE: + break; + case PM_UNDEF_NODE: { + const pm_undef_node_t *cast = (const pm_undef_node_t *) node; + + // Visit the names field + const pm_node_list_t *names = &cast->names; + for (size_t index = 0; index < names->size; index++) { + pm_visit_node(names->nodes[index], visitor, data); + } + + break; + } + case PM_UNLESS_NODE: { + const pm_unless_node_t *cast = (const pm_unless_node_t *) node; + + // Visit the predicate field + pm_visit_node((const pm_node_t *) cast->predicate, visitor, data); + + // Visit the statements field + if (cast->statements != NULL) { + pm_visit_node((const pm_node_t *) cast->statements, visitor, data); + } + + // Visit the else_clause field + if (cast->else_clause != NULL) { + pm_visit_node((const pm_node_t *) cast->else_clause, visitor, data); + } + + break; + } + case PM_UNTIL_NODE: { + const pm_until_node_t *cast = (const pm_until_node_t *) node; + + // Visit the predicate field + pm_visit_node((const pm_node_t *) cast->predicate, visitor, data); + + // Visit the statements field + if (cast->statements != NULL) { + pm_visit_node((const pm_node_t *) cast->statements, visitor, data); + } + + break; + } + case PM_WHEN_NODE: { + const pm_when_node_t *cast = (const pm_when_node_t *) node; + + // Visit the conditions field + const pm_node_list_t *conditions = &cast->conditions; + for (size_t index = 0; index < conditions->size; index++) { + pm_visit_node(conditions->nodes[index], visitor, data); + } + + // Visit the statements field + if (cast->statements != NULL) { + pm_visit_node((const pm_node_t *) cast->statements, visitor, data); + } + + break; + } + case PM_WHILE_NODE: { + const pm_while_node_t *cast = (const pm_while_node_t *) node; + + // Visit the predicate field + pm_visit_node((const pm_node_t *) cast->predicate, visitor, data); + + // Visit the statements field + if (cast->statements != NULL) { + pm_visit_node((const pm_node_t *) cast->statements, visitor, data); + } + + break; + } + case PM_X_STRING_NODE: + break; + case PM_YIELD_NODE: { + const pm_yield_node_t *cast = (const pm_yield_node_t *) node; + + // Visit the arguments field + if (cast->arguments != NULL) { + pm_visit_node((const pm_node_t *) cast->arguments, visitor, data); + } + + break; + } + 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)) { + case PM_ALIAS_GLOBAL_VARIABLE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"AliasGlobalVariableNode\",\"location\":", 45); + + const pm_alias_global_variable_node_t *cast = (const pm_alias_global_variable_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the new_name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"new_name\":", 11); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->new_name); + + // Dump the old_name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"old_name\":", 11); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->old_name); + + // Dump the keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"keyword_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->keyword_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_ALIAS_METHOD_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"AliasMethodNode\",\"location\":", 37); + + const pm_alias_method_node_t *cast = (const pm_alias_method_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the new_name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"new_name\":", 11); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->new_name); + + // Dump the old_name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"old_name\":", 11); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->old_name); + + // Dump the keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"keyword_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->keyword_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_ALTERNATION_PATTERN_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"AlternationPatternNode\",\"location\":", 44); + + const pm_alternation_pattern_node_t *cast = (const pm_alternation_pattern_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the left field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"left\":", 7); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->left); + + // Dump the right field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"right\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->right); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_AND_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"AndNode\",\"location\":", 29); + + const pm_and_node_t *cast = (const pm_and_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the left field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"left\":", 7); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->left); + + // Dump the right field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"right\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->right); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_ARGUMENTS_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ArgumentsNode\",\"location\":", 35); + + const pm_arguments_node_t *cast = (const pm_arguments_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the ArgumentsNodeFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ArgumentsNodeFlags\":", 21); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_FORWARDING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"CONTAINS_FORWARDING\"", 21); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"CONTAINS_KEYWORDS\"", 19); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"CONTAINS_KEYWORD_SPLAT\"", 24); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_SPLAT)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"CONTAINS_SPLAT\"", 16); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_MULTIPLE_SPLATS)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"CONTAINS_MULTIPLE_SPLATS\"", 26); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the arguments field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"arguments\":", 12); + const pm_node_list_t *arguments = &cast->arguments; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < arguments->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, arguments->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_ARRAY_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ArrayNode\",\"location\":", 31); + + const pm_array_node_t *cast = (const pm_array_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the ArrayNodeFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ArrayNodeFlags\":", 17); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_ARRAY_NODE_FLAGS_CONTAINS_SPLAT)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"CONTAINS_SPLAT\"", 16); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the elements field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"elements\":", 11); + const pm_node_list_t *elements = &cast->elements; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < elements->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, elements->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + if (cast->opening_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->opening_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + if (cast->closing_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->closing_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_ARRAY_PATTERN_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ArrayPatternNode\",\"location\":", 38); + + const pm_array_pattern_node_t *cast = (const pm_array_pattern_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the constant field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"constant\":", 11); + if (cast->constant != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->constant); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the requireds field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"requireds\":", 12); + const pm_node_list_t *requireds = &cast->requireds; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < requireds->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, requireds->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the rest field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"rest\":", 7); + if (cast->rest != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->rest); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the posts field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"posts\":", 8); + const pm_node_list_t *posts = &cast->posts; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < posts->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, posts->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + if (cast->opening_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->opening_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + if (cast->closing_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->closing_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_ASSOC_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"AssocNode\",\"location\":", 31); + + const pm_assoc_node_t *cast = (const pm_assoc_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the key field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"key\":", 6); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->key); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + if (cast->operator_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->operator_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_ASSOC_SPLAT_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"AssocSplatNode\",\"location\":", 36); + + const pm_assoc_splat_node_t *cast = (const pm_assoc_splat_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + if (cast->value != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_BACK_REFERENCE_READ_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"BackReferenceReadNode\",\"location\":", 43); + + const pm_back_reference_read_node_t *cast = (const pm_back_reference_read_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_BEGIN_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"BeginNode\",\"location\":", 31); + + const pm_begin_node_t *cast = (const pm_begin_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the begin_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"begin_keyword_loc\":", 20); + if (cast->begin_keyword_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->begin_keyword_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the statements field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"statements\":", 13); + if (cast->statements != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->statements); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the rescue_clause field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"rescue_clause\":", 16); + if (cast->rescue_clause != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->rescue_clause); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the else_clause field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"else_clause\":", 14); + if (cast->else_clause != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->else_clause); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the ensure_clause field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ensure_clause\":", 16); + if (cast->ensure_clause != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->ensure_clause); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the end_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"end_keyword_loc\":", 18); + if (cast->end_keyword_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->end_keyword_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_BLOCK_ARGUMENT_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"BlockArgumentNode\",\"location\":", 39); + + const pm_block_argument_node_t *cast = (const pm_block_argument_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the expression field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"expression\":", 13); + if (cast->expression != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->expression); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_BLOCK_LOCAL_VARIABLE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"BlockLocalVariableNode\",\"location\":", 44); + + const pm_block_local_variable_node_t *cast = (const pm_block_local_variable_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the ParameterFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ParameterFlags\":", 17); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"REPEATED_PARAMETER\"", 20); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_BLOCK_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"BlockNode\",\"location\":", 31); + + const pm_block_node_t *cast = (const pm_block_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the locals field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"locals\":", 9); + const pm_constant_id_list_t *locals = &cast->locals; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < locals->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json_constant(buffer, parser, locals->ids[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the parameters field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"parameters\":", 13); + if (cast->parameters != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->parameters); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the body field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"body\":", 7); + if (cast->body != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->body); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->opening_loc); + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->closing_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_BLOCK_PARAMETER_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"BlockParameterNode\",\"location\":", 40); + + const pm_block_parameter_node_t *cast = (const pm_block_parameter_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the ParameterFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ParameterFlags\":", 17); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"REPEATED_PARAMETER\"", 20); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + if (cast->name != PM_CONSTANT_ID_UNSET) { + pm_dump_json_constant(buffer, parser, cast->name); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + if (cast->name_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->name_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_BLOCK_PARAMETERS_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"BlockParametersNode\",\"location\":", 41); + + const pm_block_parameters_node_t *cast = (const pm_block_parameters_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the parameters field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"parameters\":", 13); + if (cast->parameters != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->parameters); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the locals field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"locals\":", 9); + const pm_node_list_t *locals = &cast->locals; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < locals->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, locals->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + if (cast->opening_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->opening_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + if (cast->closing_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->closing_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_BREAK_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"BreakNode\",\"location\":", 31); + + const pm_break_node_t *cast = (const pm_break_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the arguments field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"arguments\":", 12); + if (cast->arguments != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->arguments); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"keyword_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->keyword_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CALL_AND_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"CallAndWriteNode\",\"location\":", 38); + + const pm_call_and_write_node_t *cast = (const pm_call_and_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the CallNodeFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"CallNodeFlags\":", 16); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"SAFE_NAVIGATION\"", 17); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"VARIABLE_CALL\"", 15); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ATTRIBUTE_WRITE\"", 17); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"IGNORE_VISIBILITY\"", 19); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the receiver field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"receiver\":", 11); + if (cast->receiver != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->receiver); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the call_operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"call_operator_loc\":", 20); + if (cast->call_operator_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->call_operator_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the message_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"message_loc\":", 14); + if (cast->message_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->message_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the read_name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"read_name\":", 12); + pm_dump_json_constant(buffer, parser, cast->read_name); + + // Dump the write_name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"write_name\":", 13); + pm_dump_json_constant(buffer, parser, cast->write_name); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CALL_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"CallNode\",\"location\":", 30); + + const pm_call_node_t *cast = (const pm_call_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the CallNodeFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"CallNodeFlags\":", 16); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"SAFE_NAVIGATION\"", 17); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"VARIABLE_CALL\"", 15); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ATTRIBUTE_WRITE\"", 17); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"IGNORE_VISIBILITY\"", 19); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the receiver field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"receiver\":", 11); + if (cast->receiver != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->receiver); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the call_operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"call_operator_loc\":", 20); + if (cast->call_operator_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->call_operator_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the message_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"message_loc\":", 14); + if (cast->message_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->message_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + if (cast->opening_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->opening_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the arguments field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"arguments\":", 12); + if (cast->arguments != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->arguments); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + if (cast->closing_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->closing_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the equal_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"equal_loc\":", 12); + if (cast->equal_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->equal_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the block field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"block\":", 8); + if (cast->block != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->block); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CALL_OPERATOR_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"CallOperatorWriteNode\",\"location\":", 43); + + const pm_call_operator_write_node_t *cast = (const pm_call_operator_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the CallNodeFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"CallNodeFlags\":", 16); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"SAFE_NAVIGATION\"", 17); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"VARIABLE_CALL\"", 15); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ATTRIBUTE_WRITE\"", 17); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"IGNORE_VISIBILITY\"", 19); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the receiver field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"receiver\":", 11); + if (cast->receiver != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->receiver); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the call_operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"call_operator_loc\":", 20); + if (cast->call_operator_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->call_operator_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the message_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"message_loc\":", 14); + if (cast->message_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->message_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the read_name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"read_name\":", 12); + pm_dump_json_constant(buffer, parser, cast->read_name); + + // Dump the write_name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"write_name\":", 13); + pm_dump_json_constant(buffer, parser, cast->write_name); + + // Dump the binary_operator field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"binary_operator\":", 18); + pm_dump_json_constant(buffer, parser, cast->binary_operator); + + // Dump the binary_operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"binary_operator_loc\":", 22); + pm_dump_json_location(buffer, parser, &cast->binary_operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CALL_OR_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"CallOrWriteNode\",\"location\":", 37); + + const pm_call_or_write_node_t *cast = (const pm_call_or_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the CallNodeFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"CallNodeFlags\":", 16); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"SAFE_NAVIGATION\"", 17); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"VARIABLE_CALL\"", 15); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ATTRIBUTE_WRITE\"", 17); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"IGNORE_VISIBILITY\"", 19); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the receiver field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"receiver\":", 11); + if (cast->receiver != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->receiver); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the call_operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"call_operator_loc\":", 20); + if (cast->call_operator_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->call_operator_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the message_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"message_loc\":", 14); + if (cast->message_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->message_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the read_name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"read_name\":", 12); + pm_dump_json_constant(buffer, parser, cast->read_name); + + // Dump the write_name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"write_name\":", 13); + pm_dump_json_constant(buffer, parser, cast->write_name); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CALL_TARGET_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"CallTargetNode\",\"location\":", 36); + + const pm_call_target_node_t *cast = (const pm_call_target_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the CallNodeFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"CallNodeFlags\":", 16); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"SAFE_NAVIGATION\"", 17); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"VARIABLE_CALL\"", 15); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ATTRIBUTE_WRITE\"", 17); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"IGNORE_VISIBILITY\"", 19); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the receiver field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"receiver\":", 11); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->receiver); + + // Dump the call_operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"call_operator_loc\":", 20); + pm_dump_json_location(buffer, parser, &cast->call_operator_loc); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the message_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"message_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->message_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CAPTURE_PATTERN_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"CapturePatternNode\",\"location\":", 40); + + const pm_capture_pattern_node_t *cast = (const pm_capture_pattern_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + // Dump the target field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"target\":", 9); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->target); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CASE_MATCH_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"CaseMatchNode\",\"location\":", 35); + + const pm_case_match_node_t *cast = (const pm_case_match_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the predicate field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"predicate\":", 12); + if (cast->predicate != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->predicate); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the conditions field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"conditions\":", 13); + const pm_node_list_t *conditions = &cast->conditions; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < conditions->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, conditions->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the else_clause field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"else_clause\":", 14); + if (cast->else_clause != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->else_clause); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the case_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"case_keyword_loc\":", 19); + pm_dump_json_location(buffer, parser, &cast->case_keyword_loc); + + // Dump the end_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"end_keyword_loc\":", 18); + pm_dump_json_location(buffer, parser, &cast->end_keyword_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CASE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"CaseNode\",\"location\":", 30); + + const pm_case_node_t *cast = (const pm_case_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the predicate field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"predicate\":", 12); + if (cast->predicate != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->predicate); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the conditions field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"conditions\":", 13); + const pm_node_list_t *conditions = &cast->conditions; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < conditions->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, conditions->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the else_clause field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"else_clause\":", 14); + if (cast->else_clause != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->else_clause); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the case_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"case_keyword_loc\":", 19); + pm_dump_json_location(buffer, parser, &cast->case_keyword_loc); + + // Dump the end_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"end_keyword_loc\":", 18); + pm_dump_json_location(buffer, parser, &cast->end_keyword_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CLASS_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ClassNode\",\"location\":", 31); + + const pm_class_node_t *cast = (const pm_class_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the locals field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"locals\":", 9); + const pm_constant_id_list_t *locals = &cast->locals; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < locals->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json_constant(buffer, parser, locals->ids[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the class_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"class_keyword_loc\":", 20); + pm_dump_json_location(buffer, parser, &cast->class_keyword_loc); + + // Dump the constant_path field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"constant_path\":", 16); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->constant_path); + + // Dump the inheritance_operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"inheritance_operator_loc\":", 27); + if (cast->inheritance_operator_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->inheritance_operator_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the superclass field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"superclass\":", 13); + if (cast->superclass != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->superclass); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the body field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"body\":", 7); + if (cast->body != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->body); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the end_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"end_keyword_loc\":", 18); + pm_dump_json_location(buffer, parser, &cast->end_keyword_loc); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CLASS_VARIABLE_AND_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ClassVariableAndWriteNode\",\"location\":", 47); + + const pm_class_variable_and_write_node_t *cast = (const pm_class_variable_and_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ClassVariableOperatorWriteNode\",\"location\":", 52); + + const pm_class_variable_operator_write_node_t *cast = (const pm_class_variable_operator_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the binary_operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"binary_operator_loc\":", 22); + pm_dump_json_location(buffer, parser, &cast->binary_operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + // Dump the binary_operator field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"binary_operator\":", 18); + pm_dump_json_constant(buffer, parser, cast->binary_operator); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CLASS_VARIABLE_OR_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ClassVariableOrWriteNode\",\"location\":", 46); + + const pm_class_variable_or_write_node_t *cast = (const pm_class_variable_or_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CLASS_VARIABLE_READ_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ClassVariableReadNode\",\"location\":", 43); + + const pm_class_variable_read_node_t *cast = (const pm_class_variable_read_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CLASS_VARIABLE_TARGET_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ClassVariableTargetNode\",\"location\":", 45); + + const pm_class_variable_target_node_t *cast = (const pm_class_variable_target_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CLASS_VARIABLE_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ClassVariableWriteNode\",\"location\":", 44); + + const pm_class_variable_write_node_t *cast = (const pm_class_variable_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CONSTANT_AND_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ConstantAndWriteNode\",\"location\":", 42); + + const pm_constant_and_write_node_t *cast = (const pm_constant_and_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CONSTANT_OPERATOR_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ConstantOperatorWriteNode\",\"location\":", 47); + + const pm_constant_operator_write_node_t *cast = (const pm_constant_operator_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the binary_operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"binary_operator_loc\":", 22); + pm_dump_json_location(buffer, parser, &cast->binary_operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + // Dump the binary_operator field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"binary_operator\":", 18); + pm_dump_json_constant(buffer, parser, cast->binary_operator); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CONSTANT_OR_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ConstantOrWriteNode\",\"location\":", 41); + + const pm_constant_or_write_node_t *cast = (const pm_constant_or_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CONSTANT_PATH_AND_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ConstantPathAndWriteNode\",\"location\":", 46); + + const pm_constant_path_and_write_node_t *cast = (const pm_constant_path_and_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the target field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"target\":", 9); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->target); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CONSTANT_PATH_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ConstantPathNode\",\"location\":", 38); + + const pm_constant_path_node_t *cast = (const pm_constant_path_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the parent field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"parent\":", 9); + if (cast->parent != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->parent); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + if (cast->name != PM_CONSTANT_ID_UNSET) { + pm_dump_json_constant(buffer, parser, cast->name); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the delimiter_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"delimiter_loc\":", 16); + pm_dump_json_location(buffer, parser, &cast->delimiter_loc); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CONSTANT_PATH_OPERATOR_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ConstantPathOperatorWriteNode\",\"location\":", 51); + + const pm_constant_path_operator_write_node_t *cast = (const pm_constant_path_operator_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the target field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"target\":", 9); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->target); + + // Dump the binary_operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"binary_operator_loc\":", 22); + pm_dump_json_location(buffer, parser, &cast->binary_operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + // Dump the binary_operator field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"binary_operator\":", 18); + pm_dump_json_constant(buffer, parser, cast->binary_operator); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CONSTANT_PATH_OR_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ConstantPathOrWriteNode\",\"location\":", 45); + + const pm_constant_path_or_write_node_t *cast = (const pm_constant_path_or_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the target field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"target\":", 9); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->target); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CONSTANT_PATH_TARGET_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ConstantPathTargetNode\",\"location\":", 44); + + const pm_constant_path_target_node_t *cast = (const pm_constant_path_target_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the parent field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"parent\":", 9); + if (cast->parent != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->parent); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + if (cast->name != PM_CONSTANT_ID_UNSET) { + pm_dump_json_constant(buffer, parser, cast->name); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the delimiter_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"delimiter_loc\":", 16); + pm_dump_json_location(buffer, parser, &cast->delimiter_loc); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CONSTANT_PATH_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ConstantPathWriteNode\",\"location\":", 43); + + const pm_constant_path_write_node_t *cast = (const pm_constant_path_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the target field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"target\":", 9); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->target); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CONSTANT_READ_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ConstantReadNode\",\"location\":", 38); + + const pm_constant_read_node_t *cast = (const pm_constant_read_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CONSTANT_TARGET_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ConstantTargetNode\",\"location\":", 40); + + const pm_constant_target_node_t *cast = (const pm_constant_target_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_CONSTANT_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ConstantWriteNode\",\"location\":", 39); + + const pm_constant_write_node_t *cast = (const pm_constant_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_DEF_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"DefNode\",\"location\":", 29); + + const pm_def_node_t *cast = (const pm_def_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the receiver field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"receiver\":", 11); + if (cast->receiver != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->receiver); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the parameters field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"parameters\":", 13); + if (cast->parameters != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->parameters); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the body field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"body\":", 7); + if (cast->body != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->body); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the locals field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"locals\":", 9); + const pm_constant_id_list_t *locals = &cast->locals; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < locals->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json_constant(buffer, parser, locals->ids[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the def_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"def_keyword_loc\":", 18); + pm_dump_json_location(buffer, parser, &cast->def_keyword_loc); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + if (cast->operator_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->operator_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the lparen_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"lparen_loc\":", 13); + if (cast->lparen_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->lparen_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the rparen_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"rparen_loc\":", 13); + if (cast->rparen_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->rparen_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the equal_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"equal_loc\":", 12); + if (cast->equal_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->equal_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the end_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"end_keyword_loc\":", 18); + if (cast->end_keyword_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->end_keyword_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_DEFINED_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"DefinedNode\",\"location\":", 33); + + const pm_defined_node_t *cast = (const pm_defined_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the lparen_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"lparen_loc\":", 13); + if (cast->lparen_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->lparen_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + // Dump the rparen_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"rparen_loc\":", 13); + if (cast->rparen_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->rparen_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"keyword_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->keyword_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_ELSE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ElseNode\",\"location\":", 30); + + const pm_else_node_t *cast = (const pm_else_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the else_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"else_keyword_loc\":", 19); + pm_dump_json_location(buffer, parser, &cast->else_keyword_loc); + + // Dump the statements field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"statements\":", 13); + if (cast->statements != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->statements); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the end_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"end_keyword_loc\":", 18); + if (cast->end_keyword_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->end_keyword_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_EMBEDDED_STATEMENTS_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"EmbeddedStatementsNode\",\"location\":", 44); + + const pm_embedded_statements_node_t *cast = (const pm_embedded_statements_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->opening_loc); + + // Dump the statements field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"statements\":", 13); + if (cast->statements != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->statements); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->closing_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_EMBEDDED_VARIABLE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"EmbeddedVariableNode\",\"location\":", 42); + + const pm_embedded_variable_node_t *cast = (const pm_embedded_variable_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the variable field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"variable\":", 11); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->variable); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_ENSURE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"EnsureNode\",\"location\":", 32); + + const pm_ensure_node_t *cast = (const pm_ensure_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the ensure_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ensure_keyword_loc\":", 21); + pm_dump_json_location(buffer, parser, &cast->ensure_keyword_loc); + + // Dump the statements field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"statements\":", 13); + if (cast->statements != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->statements); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the end_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"end_keyword_loc\":", 18); + pm_dump_json_location(buffer, parser, &cast->end_keyword_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_FALSE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"FalseNode\",\"location\":", 31); + + const pm_false_node_t *cast = (const pm_false_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_FIND_PATTERN_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"FindPatternNode\",\"location\":", 37); + + const pm_find_pattern_node_t *cast = (const pm_find_pattern_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the constant field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"constant\":", 11); + if (cast->constant != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->constant); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the left field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"left\":", 7); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->left); + + // Dump the requireds field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"requireds\":", 12); + const pm_node_list_t *requireds = &cast->requireds; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < requireds->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, requireds->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the right field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"right\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->right); + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + if (cast->opening_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->opening_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + if (cast->closing_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->closing_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_FLIP_FLOP_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"FlipFlopNode\",\"location\":", 34); + + const pm_flip_flop_node_t *cast = (const pm_flip_flop_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the RangeFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"RangeFlags\":", 13); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_RANGE_FLAGS_EXCLUDE_END)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"EXCLUDE_END\"", 13); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the left field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"left\":", 7); + if (cast->left != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->left); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the right field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"right\":", 8); + if (cast->right != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->right); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_FLOAT_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"FloatNode\",\"location\":", 31); + + const pm_float_node_t *cast = (const pm_float_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_buffer_append_format(buffer, "%f", cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_FOR_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ForNode\",\"location\":", 29); + + const pm_for_node_t *cast = (const pm_for_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the index field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"index\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->index); + + // Dump the collection field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"collection\":", 13); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->collection); + + // Dump the statements field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"statements\":", 13); + if (cast->statements != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->statements); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the for_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"for_keyword_loc\":", 18); + pm_dump_json_location(buffer, parser, &cast->for_keyword_loc); + + // Dump the in_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"in_keyword_loc\":", 17); + pm_dump_json_location(buffer, parser, &cast->in_keyword_loc); + + // Dump the do_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"do_keyword_loc\":", 17); + if (cast->do_keyword_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->do_keyword_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the end_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"end_keyword_loc\":", 18); + pm_dump_json_location(buffer, parser, &cast->end_keyword_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_FORWARDING_ARGUMENTS_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ForwardingArgumentsNode\",\"location\":", 45); + + const pm_forwarding_arguments_node_t *cast = (const pm_forwarding_arguments_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_FORWARDING_PARAMETER_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ForwardingParameterNode\",\"location\":", 45); + + const pm_forwarding_parameter_node_t *cast = (const pm_forwarding_parameter_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_FORWARDING_SUPER_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ForwardingSuperNode\",\"location\":", 41); + + const pm_forwarding_super_node_t *cast = (const pm_forwarding_super_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the block field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"block\":", 8); + if (cast->block != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->block); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_GLOBAL_VARIABLE_AND_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"GlobalVariableAndWriteNode\",\"location\":", 48); + + const pm_global_variable_and_write_node_t *cast = (const pm_global_variable_and_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"GlobalVariableOperatorWriteNode\",\"location\":", 53); + + const pm_global_variable_operator_write_node_t *cast = (const pm_global_variable_operator_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the binary_operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"binary_operator_loc\":", 22); + pm_dump_json_location(buffer, parser, &cast->binary_operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + // Dump the binary_operator field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"binary_operator\":", 18); + pm_dump_json_constant(buffer, parser, cast->binary_operator); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_GLOBAL_VARIABLE_OR_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"GlobalVariableOrWriteNode\",\"location\":", 47); + + const pm_global_variable_or_write_node_t *cast = (const pm_global_variable_or_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_GLOBAL_VARIABLE_READ_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"GlobalVariableReadNode\",\"location\":", 44); + + const pm_global_variable_read_node_t *cast = (const pm_global_variable_read_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_GLOBAL_VARIABLE_TARGET_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"GlobalVariableTargetNode\",\"location\":", 46); + + const pm_global_variable_target_node_t *cast = (const pm_global_variable_target_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_GLOBAL_VARIABLE_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"GlobalVariableWriteNode\",\"location\":", 45); + + const pm_global_variable_write_node_t *cast = (const pm_global_variable_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_HASH_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"HashNode\",\"location\":", 30); + + const pm_hash_node_t *cast = (const pm_hash_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->opening_loc); + + // Dump the elements field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"elements\":", 11); + const pm_node_list_t *elements = &cast->elements; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < elements->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, elements->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->closing_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_HASH_PATTERN_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"HashPatternNode\",\"location\":", 37); + + const pm_hash_pattern_node_t *cast = (const pm_hash_pattern_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the constant field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"constant\":", 11); + if (cast->constant != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->constant); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the elements field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"elements\":", 11); + const pm_node_list_t *elements = &cast->elements; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < elements->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, elements->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the rest field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"rest\":", 7); + if (cast->rest != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->rest); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + if (cast->opening_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->opening_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + if (cast->closing_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->closing_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_IF_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"IfNode\",\"location\":", 28); + + const pm_if_node_t *cast = (const pm_if_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the if_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"if_keyword_loc\":", 17); + if (cast->if_keyword_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->if_keyword_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the predicate field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"predicate\":", 12); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->predicate); + + // Dump the then_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"then_keyword_loc\":", 19); + if (cast->then_keyword_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->then_keyword_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the statements field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"statements\":", 13); + if (cast->statements != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->statements); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the subsequent field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"subsequent\":", 13); + if (cast->subsequent != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->subsequent); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the end_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"end_keyword_loc\":", 18); + if (cast->end_keyword_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->end_keyword_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_IMAGINARY_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ImaginaryNode\",\"location\":", 35); + + const pm_imaginary_node_t *cast = (const pm_imaginary_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the numeric field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"numeric\":", 10); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->numeric); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_IMPLICIT_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ImplicitNode\",\"location\":", 34); + + const pm_implicit_node_t *cast = (const pm_implicit_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_IMPLICIT_REST_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ImplicitRestNode\",\"location\":", 38); + + const pm_implicit_rest_node_t *cast = (const pm_implicit_rest_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_IN_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"InNode\",\"location\":", 28); + + const pm_in_node_t *cast = (const pm_in_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the pattern field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"pattern\":", 10); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->pattern); + + // Dump the statements field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"statements\":", 13); + if (cast->statements != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->statements); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the in_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"in_loc\":", 9); + pm_dump_json_location(buffer, parser, &cast->in_loc); + + // Dump the then_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"then_loc\":", 11); + if (cast->then_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->then_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_INDEX_AND_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"IndexAndWriteNode\",\"location\":", 39); + + const pm_index_and_write_node_t *cast = (const pm_index_and_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the CallNodeFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"CallNodeFlags\":", 16); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"SAFE_NAVIGATION\"", 17); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"VARIABLE_CALL\"", 15); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ATTRIBUTE_WRITE\"", 17); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"IGNORE_VISIBILITY\"", 19); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the receiver field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"receiver\":", 11); + if (cast->receiver != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->receiver); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the call_operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"call_operator_loc\":", 20); + if (cast->call_operator_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->call_operator_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->opening_loc); + + // Dump the arguments field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"arguments\":", 12); + if (cast->arguments != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->arguments); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->closing_loc); + + // Dump the block field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"block\":", 8); + if (cast->block != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->block); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_INDEX_OPERATOR_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"IndexOperatorWriteNode\",\"location\":", 44); + + const pm_index_operator_write_node_t *cast = (const pm_index_operator_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the CallNodeFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"CallNodeFlags\":", 16); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"SAFE_NAVIGATION\"", 17); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"VARIABLE_CALL\"", 15); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ATTRIBUTE_WRITE\"", 17); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"IGNORE_VISIBILITY\"", 19); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the receiver field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"receiver\":", 11); + if (cast->receiver != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->receiver); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the call_operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"call_operator_loc\":", 20); + if (cast->call_operator_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->call_operator_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->opening_loc); + + // Dump the arguments field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"arguments\":", 12); + if (cast->arguments != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->arguments); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->closing_loc); + + // Dump the block field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"block\":", 8); + if (cast->block != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->block); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the binary_operator field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"binary_operator\":", 18); + pm_dump_json_constant(buffer, parser, cast->binary_operator); + + // Dump the binary_operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"binary_operator_loc\":", 22); + pm_dump_json_location(buffer, parser, &cast->binary_operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_INDEX_OR_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"IndexOrWriteNode\",\"location\":", 38); + + const pm_index_or_write_node_t *cast = (const pm_index_or_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the CallNodeFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"CallNodeFlags\":", 16); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"SAFE_NAVIGATION\"", 17); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"VARIABLE_CALL\"", 15); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ATTRIBUTE_WRITE\"", 17); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"IGNORE_VISIBILITY\"", 19); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the receiver field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"receiver\":", 11); + if (cast->receiver != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->receiver); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the call_operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"call_operator_loc\":", 20); + if (cast->call_operator_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->call_operator_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->opening_loc); + + // Dump the arguments field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"arguments\":", 12); + if (cast->arguments != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->arguments); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->closing_loc); + + // Dump the block field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"block\":", 8); + if (cast->block != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->block); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_INDEX_TARGET_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"IndexTargetNode\",\"location\":", 37); + + const pm_index_target_node_t *cast = (const pm_index_target_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the CallNodeFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"CallNodeFlags\":", 16); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"SAFE_NAVIGATION\"", 17); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"VARIABLE_CALL\"", 15); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ATTRIBUTE_WRITE\"", 17); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"IGNORE_VISIBILITY\"", 19); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the receiver field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"receiver\":", 11); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->receiver); + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->opening_loc); + + // Dump the arguments field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"arguments\":", 12); + if (cast->arguments != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->arguments); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->closing_loc); + + // Dump the block field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"block\":", 8); + if (cast->block != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->block); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_INSTANCE_VARIABLE_AND_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"InstanceVariableAndWriteNode\",\"location\":", 50); + + const pm_instance_variable_and_write_node_t *cast = (const pm_instance_variable_and_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"InstanceVariableOperatorWriteNode\",\"location\":", 55); + + const pm_instance_variable_operator_write_node_t *cast = (const pm_instance_variable_operator_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the binary_operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"binary_operator_loc\":", 22); + pm_dump_json_location(buffer, parser, &cast->binary_operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + // Dump the binary_operator field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"binary_operator\":", 18); + pm_dump_json_constant(buffer, parser, cast->binary_operator); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_INSTANCE_VARIABLE_OR_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"InstanceVariableOrWriteNode\",\"location\":", 49); + + const pm_instance_variable_or_write_node_t *cast = (const pm_instance_variable_or_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_INSTANCE_VARIABLE_READ_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"InstanceVariableReadNode\",\"location\":", 46); + + const pm_instance_variable_read_node_t *cast = (const pm_instance_variable_read_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_INSTANCE_VARIABLE_TARGET_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"InstanceVariableTargetNode\",\"location\":", 48); + + const pm_instance_variable_target_node_t *cast = (const pm_instance_variable_target_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_INSTANCE_VARIABLE_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"InstanceVariableWriteNode\",\"location\":", 47); + + const pm_instance_variable_write_node_t *cast = (const pm_instance_variable_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_INTEGER_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"IntegerNode\",\"location\":", 33); + + const pm_integer_node_t *cast = (const pm_integer_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the IntegerBaseFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"IntegerBaseFlags\":", 19); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_INTEGER_BASE_FLAGS_BINARY)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"BINARY\"", 8); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_INTEGER_BASE_FLAGS_DECIMAL)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"DECIMAL\"", 9); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_INTEGER_BASE_FLAGS_OCTAL)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"OCTAL\"", 7); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_INTEGER_BASE_FLAGS_HEXADECIMAL)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"HEXADECIMAL\"", 13); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_integer_string(buffer, &cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_INTERPOLATED_MATCH_LAST_LINE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"InterpolatedMatchLastLineNode\",\"location\":", 51); + + const pm_interpolated_match_last_line_node_t *cast = (const pm_interpolated_match_last_line_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the RegularExpressionFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"RegularExpressionFlags\":", 25); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"IGNORE_CASE\"", 13); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_EXTENDED)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"EXTENDED\"", 10); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"MULTI_LINE\"", 12); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_ONCE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ONCE\"", 6); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_EUC_JP)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"EUC_JP\"", 8); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ASCII_8BIT\"", 12); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"WINDOWS_31J\"", 13); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_UTF_8)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"UTF_8\"", 7); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_FORCED_UTF8_ENCODING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FORCED_UTF8_ENCODING\"", 22); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_FORCED_BINARY_ENCODING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FORCED_BINARY_ENCODING\"", 24); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_FORCED_US_ASCII_ENCODING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FORCED_US_ASCII_ENCODING\"", 26); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->opening_loc); + + // Dump the parts field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"parts\":", 8); + const pm_node_list_t *parts = &cast->parts; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < parts->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, parts->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->closing_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"InterpolatedRegularExpressionNode\",\"location\":", 55); + + const pm_interpolated_regular_expression_node_t *cast = (const pm_interpolated_regular_expression_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the RegularExpressionFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"RegularExpressionFlags\":", 25); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"IGNORE_CASE\"", 13); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_EXTENDED)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"EXTENDED\"", 10); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"MULTI_LINE\"", 12); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_ONCE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ONCE\"", 6); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_EUC_JP)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"EUC_JP\"", 8); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ASCII_8BIT\"", 12); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"WINDOWS_31J\"", 13); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_UTF_8)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"UTF_8\"", 7); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_FORCED_UTF8_ENCODING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FORCED_UTF8_ENCODING\"", 22); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_FORCED_BINARY_ENCODING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FORCED_BINARY_ENCODING\"", 24); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_FORCED_US_ASCII_ENCODING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FORCED_US_ASCII_ENCODING\"", 26); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->opening_loc); + + // Dump the parts field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"parts\":", 8); + const pm_node_list_t *parts = &cast->parts; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < parts->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, parts->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->closing_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_INTERPOLATED_STRING_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"InterpolatedStringNode\",\"location\":", 44); + + const pm_interpolated_string_node_t *cast = (const pm_interpolated_string_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the InterpolatedStringNodeFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"InterpolatedStringNodeFlags\":", 30); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FROZEN\"", 8); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"MUTABLE\"", 9); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + if (cast->opening_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->opening_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the parts field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"parts\":", 8); + const pm_node_list_t *parts = &cast->parts; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < parts->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, parts->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + if (cast->closing_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->closing_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_INTERPOLATED_SYMBOL_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"InterpolatedSymbolNode\",\"location\":", 44); + + const pm_interpolated_symbol_node_t *cast = (const pm_interpolated_symbol_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + if (cast->opening_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->opening_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the parts field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"parts\":", 8); + const pm_node_list_t *parts = &cast->parts; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < parts->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, parts->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + if (cast->closing_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->closing_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_INTERPOLATED_X_STRING_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"InterpolatedXStringNode\",\"location\":", 45); + + const pm_interpolated_x_string_node_t *cast = (const pm_interpolated_x_string_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->opening_loc); + + // Dump the parts field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"parts\":", 8); + const pm_node_list_t *parts = &cast->parts; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < parts->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, parts->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->closing_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_IT_LOCAL_VARIABLE_READ_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ItLocalVariableReadNode\",\"location\":", 45); + + const pm_it_local_variable_read_node_t *cast = (const pm_it_local_variable_read_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_IT_PARAMETERS_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ItParametersNode\",\"location\":", 38); + + const pm_it_parameters_node_t *cast = (const pm_it_parameters_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_KEYWORD_HASH_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"KeywordHashNode\",\"location\":", 37); + + const pm_keyword_hash_node_t *cast = (const pm_keyword_hash_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the KeywordHashNodeFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"KeywordHashNodeFlags\":", 23); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"SYMBOL_KEYS\"", 13); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the elements field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"elements\":", 11); + const pm_node_list_t *elements = &cast->elements; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < elements->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, elements->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_KEYWORD_REST_PARAMETER_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"KeywordRestParameterNode\",\"location\":", 46); + + const pm_keyword_rest_parameter_node_t *cast = (const pm_keyword_rest_parameter_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the ParameterFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ParameterFlags\":", 17); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"REPEATED_PARAMETER\"", 20); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + if (cast->name != PM_CONSTANT_ID_UNSET) { + pm_dump_json_constant(buffer, parser, cast->name); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + if (cast->name_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->name_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_LAMBDA_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"LambdaNode\",\"location\":", 32); + + const pm_lambda_node_t *cast = (const pm_lambda_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the locals field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"locals\":", 9); + const pm_constant_id_list_t *locals = &cast->locals; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < locals->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json_constant(buffer, parser, locals->ids[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->opening_loc); + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->closing_loc); + + // Dump the parameters field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"parameters\":", 13); + if (cast->parameters != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->parameters); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the body field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"body\":", 7); + if (cast->body != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->body); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_LOCAL_VARIABLE_AND_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"LocalVariableAndWriteNode\",\"location\":", 47); + + const pm_local_variable_and_write_node_t *cast = (const pm_local_variable_and_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the depth field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"depth\":", 8); + pm_buffer_append_format(buffer, "%" PRIu32, cast->depth); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"LocalVariableOperatorWriteNode\",\"location\":", 52); + + const pm_local_variable_operator_write_node_t *cast = (const pm_local_variable_operator_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the binary_operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"binary_operator_loc\":", 22); + pm_dump_json_location(buffer, parser, &cast->binary_operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the binary_operator field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"binary_operator\":", 18); + pm_dump_json_constant(buffer, parser, cast->binary_operator); + + // Dump the depth field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"depth\":", 8); + pm_buffer_append_format(buffer, "%" PRIu32, cast->depth); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_LOCAL_VARIABLE_OR_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"LocalVariableOrWriteNode\",\"location\":", 46); + + const pm_local_variable_or_write_node_t *cast = (const pm_local_variable_or_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the depth field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"depth\":", 8); + pm_buffer_append_format(buffer, "%" PRIu32, cast->depth); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_LOCAL_VARIABLE_READ_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"LocalVariableReadNode\",\"location\":", 43); + + const pm_local_variable_read_node_t *cast = (const pm_local_variable_read_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the depth field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"depth\":", 8); + pm_buffer_append_format(buffer, "%" PRIu32, cast->depth); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_LOCAL_VARIABLE_TARGET_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"LocalVariableTargetNode\",\"location\":", 45); + + const pm_local_variable_target_node_t *cast = (const pm_local_variable_target_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the depth field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"depth\":", 8); + pm_buffer_append_format(buffer, "%" PRIu32, cast->depth); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_LOCAL_VARIABLE_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"LocalVariableWriteNode\",\"location\":", 44); + + const pm_local_variable_write_node_t *cast = (const pm_local_variable_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the depth field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"depth\":", 8); + pm_buffer_append_format(buffer, "%" PRIu32, cast->depth); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_MATCH_LAST_LINE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"MatchLastLineNode\",\"location\":", 39); + + const pm_match_last_line_node_t *cast = (const pm_match_last_line_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the RegularExpressionFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"RegularExpressionFlags\":", 25); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"IGNORE_CASE\"", 13); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_EXTENDED)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"EXTENDED\"", 10); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"MULTI_LINE\"", 12); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_ONCE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ONCE\"", 6); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_EUC_JP)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"EUC_JP\"", 8); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ASCII_8BIT\"", 12); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"WINDOWS_31J\"", 13); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_UTF_8)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"UTF_8\"", 7); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_FORCED_UTF8_ENCODING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FORCED_UTF8_ENCODING\"", 22); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_FORCED_BINARY_ENCODING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FORCED_BINARY_ENCODING\"", 24); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_FORCED_US_ASCII_ENCODING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FORCED_US_ASCII_ENCODING\"", 26); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->opening_loc); + + // Dump the content_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"content_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->content_loc); + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->closing_loc); + + // Dump the unescaped field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"unescaped\":", 12); + const pm_string_t *unescaped = &cast->unescaped; + pm_buffer_append_byte(buffer, '"'); + pm_buffer_append_source(buffer, pm_string_source(unescaped), pm_string_length(unescaped), PM_BUFFER_ESCAPING_JSON); + pm_buffer_append_byte(buffer, '"'); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_MATCH_PREDICATE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"MatchPredicateNode\",\"location\":", 40); + + const pm_match_predicate_node_t *cast = (const pm_match_predicate_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + // Dump the pattern field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"pattern\":", 10); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->pattern); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_MATCH_REQUIRED_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"MatchRequiredNode\",\"location\":", 39); + + const pm_match_required_node_t *cast = (const pm_match_required_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + // Dump the pattern field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"pattern\":", 10); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->pattern); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_MATCH_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"MatchWriteNode\",\"location\":", 36); + + const pm_match_write_node_t *cast = (const pm_match_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the call field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"call\":", 7); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->call); + + // Dump the targets field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"targets\":", 10); + const pm_node_list_t *targets = &cast->targets; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < targets->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, targets->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_MISSING_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"MissingNode\",\"location\":", 33); + + const pm_missing_node_t *cast = (const pm_missing_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_MODULE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ModuleNode\",\"location\":", 32); + + const pm_module_node_t *cast = (const pm_module_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the locals field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"locals\":", 9); + const pm_constant_id_list_t *locals = &cast->locals; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < locals->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json_constant(buffer, parser, locals->ids[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the module_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"module_keyword_loc\":", 21); + pm_dump_json_location(buffer, parser, &cast->module_keyword_loc); + + // Dump the constant_path field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"constant_path\":", 16); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->constant_path); + + // Dump the body field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"body\":", 7); + if (cast->body != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->body); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the end_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"end_keyword_loc\":", 18); + pm_dump_json_location(buffer, parser, &cast->end_keyword_loc); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_MULTI_TARGET_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"MultiTargetNode\",\"location\":", 37); + + const pm_multi_target_node_t *cast = (const pm_multi_target_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the lefts field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"lefts\":", 8); + const pm_node_list_t *lefts = &cast->lefts; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < lefts->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, lefts->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the rest field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"rest\":", 7); + if (cast->rest != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->rest); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the rights field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"rights\":", 9); + const pm_node_list_t *rights = &cast->rights; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < rights->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, rights->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the lparen_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"lparen_loc\":", 13); + if (cast->lparen_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->lparen_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the rparen_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"rparen_loc\":", 13); + if (cast->rparen_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->rparen_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_MULTI_WRITE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"MultiWriteNode\",\"location\":", 36); + + const pm_multi_write_node_t *cast = (const pm_multi_write_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the lefts field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"lefts\":", 8); + const pm_node_list_t *lefts = &cast->lefts; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < lefts->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, lefts->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the rest field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"rest\":", 7); + if (cast->rest != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->rest); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the rights field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"rights\":", 9); + const pm_node_list_t *rights = &cast->rights; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < rights->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, rights->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the lparen_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"lparen_loc\":", 13); + if (cast->lparen_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->lparen_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the rparen_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"rparen_loc\":", 13); + if (cast->rparen_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->rparen_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_NEXT_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"NextNode\",\"location\":", 30); + + const pm_next_node_t *cast = (const pm_next_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the arguments field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"arguments\":", 12); + if (cast->arguments != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->arguments); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"keyword_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->keyword_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_NIL_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"NilNode\",\"location\":", 29); + + const pm_nil_node_t *cast = (const pm_nil_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_NO_KEYWORDS_PARAMETER_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"NoKeywordsParameterNode\",\"location\":", 45); + + const pm_no_keywords_parameter_node_t *cast = (const pm_no_keywords_parameter_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"keyword_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->keyword_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_NUMBERED_PARAMETERS_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"NumberedParametersNode\",\"location\":", 44); + + const pm_numbered_parameters_node_t *cast = (const pm_numbered_parameters_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the maximum field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"maximum\":", 10); + pm_buffer_append_format(buffer, "%" PRIu8, cast->maximum); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_NUMBERED_REFERENCE_READ_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"NumberedReferenceReadNode\",\"location\":", 47); + + const pm_numbered_reference_read_node_t *cast = (const pm_numbered_reference_read_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the number field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"number\":", 9); + pm_buffer_append_format(buffer, "%" PRIu32, cast->number); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_OPTIONAL_KEYWORD_PARAMETER_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"OptionalKeywordParameterNode\",\"location\":", 50); + + const pm_optional_keyword_parameter_node_t *cast = (const pm_optional_keyword_parameter_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the ParameterFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ParameterFlags\":", 17); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"REPEATED_PARAMETER\"", 20); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_OPTIONAL_PARAMETER_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"OptionalParameterNode\",\"location\":", 43); + + const pm_optional_parameter_node_t *cast = (const pm_optional_parameter_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the ParameterFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ParameterFlags\":", 17); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"REPEATED_PARAMETER\"", 20); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the value field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->value); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_OR_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"OrNode\",\"location\":", 28); + + const pm_or_node_t *cast = (const pm_or_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the left field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"left\":", 7); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->left); + + // Dump the right field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"right\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->right); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_PARAMETERS_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ParametersNode\",\"location\":", 36); + + const pm_parameters_node_t *cast = (const pm_parameters_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the requireds field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"requireds\":", 12); + const pm_node_list_t *requireds = &cast->requireds; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < requireds->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, requireds->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the optionals field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"optionals\":", 12); + const pm_node_list_t *optionals = &cast->optionals; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < optionals->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, optionals->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the rest field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"rest\":", 7); + if (cast->rest != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->rest); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the posts field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"posts\":", 8); + const pm_node_list_t *posts = &cast->posts; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < posts->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, posts->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the keywords field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"keywords\":", 11); + const pm_node_list_t *keywords = &cast->keywords; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < keywords->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, keywords->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the keyword_rest field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"keyword_rest\":", 15); + if (cast->keyword_rest != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->keyword_rest); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the block field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"block\":", 8); + if (cast->block != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->block); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_PARENTHESES_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ParenthesesNode\",\"location\":", 37); + + const pm_parentheses_node_t *cast = (const pm_parentheses_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the ParenthesesNodeFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ParenthesesNodeFlags\":", 23); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"MULTIPLE_STATEMENTS\"", 21); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the body field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"body\":", 7); + if (cast->body != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->body); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->opening_loc); + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->closing_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_PINNED_EXPRESSION_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"PinnedExpressionNode\",\"location\":", 42); + + const pm_pinned_expression_node_t *cast = (const pm_pinned_expression_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the expression field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"expression\":", 13); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->expression); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the lparen_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"lparen_loc\":", 13); + pm_dump_json_location(buffer, parser, &cast->lparen_loc); + + // Dump the rparen_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"rparen_loc\":", 13); + pm_dump_json_location(buffer, parser, &cast->rparen_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_PINNED_VARIABLE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"PinnedVariableNode\",\"location\":", 40); + + const pm_pinned_variable_node_t *cast = (const pm_pinned_variable_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the variable field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"variable\":", 11); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->variable); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_POST_EXECUTION_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"PostExecutionNode\",\"location\":", 39); + + const pm_post_execution_node_t *cast = (const pm_post_execution_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the statements field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"statements\":", 13); + if (cast->statements != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->statements); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"keyword_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->keyword_loc); + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->opening_loc); + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->closing_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_PRE_EXECUTION_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"PreExecutionNode\",\"location\":", 38); + + const pm_pre_execution_node_t *cast = (const pm_pre_execution_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the statements field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"statements\":", 13); + if (cast->statements != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->statements); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"keyword_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->keyword_loc); + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->opening_loc); + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->closing_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_PROGRAM_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ProgramNode\",\"location\":", 33); + + const pm_program_node_t *cast = (const pm_program_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the locals field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"locals\":", 9); + const pm_constant_id_list_t *locals = &cast->locals; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < locals->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json_constant(buffer, parser, locals->ids[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the statements field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"statements\":", 13); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->statements); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_RANGE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"RangeNode\",\"location\":", 31); + + const pm_range_node_t *cast = (const pm_range_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the RangeFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"RangeFlags\":", 13); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_RANGE_FLAGS_EXCLUDE_END)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"EXCLUDE_END\"", 13); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the left field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"left\":", 7); + if (cast->left != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->left); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the right field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"right\":", 8); + if (cast->right != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->right); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_RATIONAL_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"RationalNode\",\"location\":", 34); + + const pm_rational_node_t *cast = (const pm_rational_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the IntegerBaseFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"IntegerBaseFlags\":", 19); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_INTEGER_BASE_FLAGS_BINARY)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"BINARY\"", 8); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_INTEGER_BASE_FLAGS_DECIMAL)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"DECIMAL\"", 9); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_INTEGER_BASE_FLAGS_OCTAL)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"OCTAL\"", 7); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_INTEGER_BASE_FLAGS_HEXADECIMAL)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"HEXADECIMAL\"", 13); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the numerator field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"numerator\":", 12); + pm_integer_string(buffer, &cast->numerator); + + // Dump the denominator field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"denominator\":", 14); + pm_integer_string(buffer, &cast->denominator); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_REDO_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"RedoNode\",\"location\":", 30); + + const pm_redo_node_t *cast = (const pm_redo_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_REGULAR_EXPRESSION_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"RegularExpressionNode\",\"location\":", 43); + + const pm_regular_expression_node_t *cast = (const pm_regular_expression_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the RegularExpressionFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"RegularExpressionFlags\":", 25); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"IGNORE_CASE\"", 13); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_EXTENDED)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"EXTENDED\"", 10); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"MULTI_LINE\"", 12); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_ONCE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ONCE\"", 6); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_EUC_JP)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"EUC_JP\"", 8); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ASCII_8BIT\"", 12); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"WINDOWS_31J\"", 13); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_UTF_8)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"UTF_8\"", 7); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_FORCED_UTF8_ENCODING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FORCED_UTF8_ENCODING\"", 22); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_FORCED_BINARY_ENCODING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FORCED_BINARY_ENCODING\"", 24); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_REGULAR_EXPRESSION_FLAGS_FORCED_US_ASCII_ENCODING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FORCED_US_ASCII_ENCODING\"", 26); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->opening_loc); + + // Dump the content_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"content_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->content_loc); + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->closing_loc); + + // Dump the unescaped field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"unescaped\":", 12); + const pm_string_t *unescaped = &cast->unescaped; + pm_buffer_append_byte(buffer, '"'); + pm_buffer_append_source(buffer, pm_string_source(unescaped), pm_string_length(unescaped), PM_BUFFER_ESCAPING_JSON); + pm_buffer_append_byte(buffer, '"'); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_REQUIRED_KEYWORD_PARAMETER_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"RequiredKeywordParameterNode\",\"location\":", 50); + + const pm_required_keyword_parameter_node_t *cast = (const pm_required_keyword_parameter_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the ParameterFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ParameterFlags\":", 17); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"REPEATED_PARAMETER\"", 20); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + pm_dump_json_location(buffer, parser, &cast->name_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_REQUIRED_PARAMETER_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"RequiredParameterNode\",\"location\":", 43); + + const pm_required_parameter_node_t *cast = (const pm_required_parameter_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the ParameterFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ParameterFlags\":", 17); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"REPEATED_PARAMETER\"", 20); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + pm_dump_json_constant(buffer, parser, cast->name); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_RESCUE_MODIFIER_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"RescueModifierNode\",\"location\":", 40); + + const pm_rescue_modifier_node_t *cast = (const pm_rescue_modifier_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the expression field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"expression\":", 13); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->expression); + + // Dump the keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"keyword_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->keyword_loc); + + // Dump the rescue_expression field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"rescue_expression\":", 20); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->rescue_expression); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_RESCUE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"RescueNode\",\"location\":", 32); + + const pm_rescue_node_t *cast = (const pm_rescue_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"keyword_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->keyword_loc); + + // Dump the exceptions field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"exceptions\":", 13); + const pm_node_list_t *exceptions = &cast->exceptions; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < exceptions->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, exceptions->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + if (cast->operator_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->operator_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the reference field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"reference\":", 12); + if (cast->reference != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->reference); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the then_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"then_keyword_loc\":", 19); + if (cast->then_keyword_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->then_keyword_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the statements field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"statements\":", 13); + if (cast->statements != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->statements); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the subsequent field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"subsequent\":", 13); + if (cast->subsequent != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->subsequent); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_REST_PARAMETER_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"RestParameterNode\",\"location\":", 39); + + const pm_rest_parameter_node_t *cast = (const pm_rest_parameter_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the ParameterFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ParameterFlags\":", 17); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"REPEATED_PARAMETER\"", 20); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the name field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name\":", 7); + if (cast->name != PM_CONSTANT_ID_UNSET) { + pm_dump_json_constant(buffer, parser, cast->name); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the name_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"name_loc\":", 11); + if (cast->name_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->name_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_RETRY_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"RetryNode\",\"location\":", 31); + + const pm_retry_node_t *cast = (const pm_retry_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_RETURN_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ReturnNode\",\"location\":", 32); + + const pm_return_node_t *cast = (const pm_return_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"keyword_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->keyword_loc); + + // Dump the arguments field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"arguments\":", 12); + if (cast->arguments != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->arguments); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_SELF_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"SelfNode\",\"location\":", 30); + + const pm_self_node_t *cast = (const pm_self_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_SHAREABLE_CONSTANT_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"ShareableConstantNode\",\"location\":", 43); + + const pm_shareable_constant_node_t *cast = (const pm_shareable_constant_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the ShareableConstantNodeFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"ShareableConstantNodeFlags\":", 29); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_SHAREABLE_CONSTANT_NODE_FLAGS_LITERAL)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"LITERAL\"", 9); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_EVERYTHING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"EXPERIMENTAL_EVERYTHING\"", 25); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_COPY)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"EXPERIMENTAL_COPY\"", 19); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the write field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"write\":", 8); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->write); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_SINGLETON_CLASS_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"SingletonClassNode\",\"location\":", 40); + + const pm_singleton_class_node_t *cast = (const pm_singleton_class_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the locals field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"locals\":", 9); + const pm_constant_id_list_t *locals = &cast->locals; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < locals->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json_constant(buffer, parser, locals->ids[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the class_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"class_keyword_loc\":", 20); + pm_dump_json_location(buffer, parser, &cast->class_keyword_loc); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the expression field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"expression\":", 13); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->expression); + + // Dump the body field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"body\":", 7); + if (cast->body != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->body); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the end_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"end_keyword_loc\":", 18); + pm_dump_json_location(buffer, parser, &cast->end_keyword_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_SOURCE_ENCODING_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"SourceEncodingNode\",\"location\":", 40); + + const pm_source_encoding_node_t *cast = (const pm_source_encoding_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_SOURCE_FILE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"SourceFileNode\",\"location\":", 36); + + const pm_source_file_node_t *cast = (const pm_source_file_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the StringFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"StringFlags\":", 14); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_STRING_FLAGS_FORCED_UTF8_ENCODING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FORCED_UTF8_ENCODING\"", 22); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_STRING_FLAGS_FORCED_BINARY_ENCODING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FORCED_BINARY_ENCODING\"", 24); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_STRING_FLAGS_FROZEN)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FROZEN\"", 8); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_STRING_FLAGS_MUTABLE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"MUTABLE\"", 9); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the filepath field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"filepath\":", 11); + const pm_string_t *filepath = &cast->filepath; + pm_buffer_append_byte(buffer, '"'); + pm_buffer_append_source(buffer, pm_string_source(filepath), pm_string_length(filepath), PM_BUFFER_ESCAPING_JSON); + pm_buffer_append_byte(buffer, '"'); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_SOURCE_LINE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"SourceLineNode\",\"location\":", 36); + + const pm_source_line_node_t *cast = (const pm_source_line_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_SPLAT_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"SplatNode\",\"location\":", 31); + + const pm_splat_node_t *cast = (const pm_splat_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the operator_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"operator_loc\":", 15); + pm_dump_json_location(buffer, parser, &cast->operator_loc); + + // Dump the expression field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"expression\":", 13); + if (cast->expression != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->expression); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_STATEMENTS_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"StatementsNode\",\"location\":", 36); + + const pm_statements_node_t *cast = (const pm_statements_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the body field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"body\":", 7); + const pm_node_list_t *body = &cast->body; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < body->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, body->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_STRING_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"StringNode\",\"location\":", 32); + + const pm_string_node_t *cast = (const pm_string_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the StringFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"StringFlags\":", 14); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_STRING_FLAGS_FORCED_UTF8_ENCODING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FORCED_UTF8_ENCODING\"", 22); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_STRING_FLAGS_FORCED_BINARY_ENCODING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FORCED_BINARY_ENCODING\"", 24); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_STRING_FLAGS_FROZEN)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FROZEN\"", 8); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_STRING_FLAGS_MUTABLE)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"MUTABLE\"", 9); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + if (cast->opening_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->opening_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the content_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"content_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->content_loc); + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + if (cast->closing_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->closing_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the unescaped field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"unescaped\":", 12); + const pm_string_t *unescaped = &cast->unescaped; + pm_buffer_append_byte(buffer, '"'); + pm_buffer_append_source(buffer, pm_string_source(unescaped), pm_string_length(unescaped), PM_BUFFER_ESCAPING_JSON); + pm_buffer_append_byte(buffer, '"'); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_SUPER_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"SuperNode\",\"location\":", 31); + + const pm_super_node_t *cast = (const pm_super_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"keyword_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->keyword_loc); + + // Dump the lparen_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"lparen_loc\":", 13); + if (cast->lparen_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->lparen_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the arguments field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"arguments\":", 12); + if (cast->arguments != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->arguments); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the rparen_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"rparen_loc\":", 13); + if (cast->rparen_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->rparen_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the block field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"block\":", 8); + if (cast->block != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->block); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_SYMBOL_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"SymbolNode\",\"location\":", 32); + + const pm_symbol_node_t *cast = (const pm_symbol_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the SymbolFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"SymbolFlags\":", 14); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_SYMBOL_FLAGS_FORCED_UTF8_ENCODING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FORCED_UTF8_ENCODING\"", 22); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_SYMBOL_FLAGS_FORCED_BINARY_ENCODING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FORCED_BINARY_ENCODING\"", 24); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FORCED_US_ASCII_ENCODING\"", 26); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + if (cast->opening_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->opening_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the value_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"value_loc\":", 12); + if (cast->value_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->value_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + if (cast->closing_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->closing_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the unescaped field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"unescaped\":", 12); + const pm_string_t *unescaped = &cast->unescaped; + pm_buffer_append_byte(buffer, '"'); + pm_buffer_append_source(buffer, pm_string_source(unescaped), pm_string_length(unescaped), PM_BUFFER_ESCAPING_JSON); + pm_buffer_append_byte(buffer, '"'); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_TRUE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"TrueNode\",\"location\":", 30); + + const pm_true_node_t *cast = (const pm_true_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_UNDEF_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"UndefNode\",\"location\":", 31); + + const pm_undef_node_t *cast = (const pm_undef_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the names field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"names\":", 8); + const pm_node_list_t *names = &cast->names; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < names->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, names->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"keyword_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->keyword_loc); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_UNLESS_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"UnlessNode\",\"location\":", 32); + + const pm_unless_node_t *cast = (const pm_unless_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"keyword_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->keyword_loc); + + // Dump the predicate field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"predicate\":", 12); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->predicate); + + // Dump the then_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"then_keyword_loc\":", 19); + if (cast->then_keyword_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->then_keyword_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the statements field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"statements\":", 13); + if (cast->statements != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->statements); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the else_clause field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"else_clause\":", 14); + if (cast->else_clause != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->else_clause); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the end_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"end_keyword_loc\":", 18); + if (cast->end_keyword_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->end_keyword_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_UNTIL_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"UntilNode\",\"location\":", 31); + + const pm_until_node_t *cast = (const pm_until_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the LoopFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"LoopFlags\":", 12); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_LOOP_FLAGS_BEGIN_MODIFIER)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"BEGIN_MODIFIER\"", 16); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"keyword_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->keyword_loc); + + // Dump the do_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"do_keyword_loc\":", 17); + if (cast->do_keyword_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->do_keyword_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + if (cast->closing_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->closing_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the predicate field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"predicate\":", 12); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->predicate); + + // Dump the statements field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"statements\":", 13); + if (cast->statements != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->statements); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_WHEN_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"WhenNode\",\"location\":", 30); + + const pm_when_node_t *cast = (const pm_when_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"keyword_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->keyword_loc); + + // Dump the conditions field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"conditions\":", 13); + const pm_node_list_t *conditions = &cast->conditions; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < conditions->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, conditions->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the then_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"then_keyword_loc\":", 19); + if (cast->then_keyword_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->then_keyword_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the statements field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"statements\":", 13); + if (cast->statements != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->statements); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_WHILE_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"WhileNode\",\"location\":", 31); + + const pm_while_node_t *cast = (const pm_while_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the LoopFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"LoopFlags\":", 12); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_LOOP_FLAGS_BEGIN_MODIFIER)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"BEGIN_MODIFIER\"", 16); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"keyword_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->keyword_loc); + + // Dump the do_keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"do_keyword_loc\":", 17); + if (cast->do_keyword_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->do_keyword_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + if (cast->closing_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->closing_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the predicate field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"predicate\":", 12); + pm_dump_json(buffer, parser, (const pm_node_t *) cast->predicate); + + // Dump the statements field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"statements\":", 13); + if (cast->statements != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->statements); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_X_STRING_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"XStringNode\",\"location\":", 33); + + const pm_x_string_node_t *cast = (const pm_x_string_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the EncodingFlags field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"EncodingFlags\":", 16); + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + if (PM_NODE_FLAG_P(cast, PM_ENCODING_FLAGS_FORCED_UTF8_ENCODING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FORCED_UTF8_ENCODING\"", 22); + flags++; + } + if (PM_NODE_FLAG_P(cast, PM_ENCODING_FLAGS_FORCED_BINARY_ENCODING)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"FORCED_BINARY_ENCODING\"", 24); + flags++; + } + pm_buffer_append_byte(buffer, ']'); + + // Dump the opening_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"opening_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->opening_loc); + + // Dump the content_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"content_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->content_loc); + + // Dump the closing_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"closing_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->closing_loc); + + // Dump the unescaped field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"unescaped\":", 12); + const pm_string_t *unescaped = &cast->unescaped; + pm_buffer_append_byte(buffer, '"'); + pm_buffer_append_source(buffer, pm_string_source(unescaped), pm_string_length(unescaped), PM_BUFFER_ESCAPING_JSON); + pm_buffer_append_byte(buffer, '"'); + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_YIELD_NODE: { + pm_buffer_append_string(buffer, "{\"type\":\"YieldNode\",\"location\":", 31); + + const pm_yield_node_t *cast = (const pm_yield_node_t *) node; + pm_dump_json_location(buffer, parser, &cast->base.location); + + // Dump the keyword_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"keyword_loc\":", 14); + pm_dump_json_location(buffer, parser, &cast->keyword_loc); + + // Dump the lparen_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"lparen_loc\":", 13); + if (cast->lparen_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->lparen_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the arguments field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"arguments\":", 12); + if (cast->arguments != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast->arguments); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + // Dump the rparen_loc field + pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"rparen_loc\":", 13); + if (cast->rparen_loc.start != NULL) { + pm_dump_json_location(buffer, parser, &cast->rparen_loc); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + + pm_buffer_append_byte(buffer, '}'); + break; + } + case PM_SCOPE_NODE: + break; + } +} + +#endif diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/options.c b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/options.c new file mode 100644 index 0000000..09d2a65 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/options.c @@ -0,0 +1,338 @@ +#include "prism/options.h" + +/** + * Set the shebang callback option on the given options struct. + */ +PRISM_EXPORTED_FUNCTION void +pm_options_shebang_callback_set(pm_options_t *options, pm_options_shebang_callback_t shebang_callback, void *shebang_callback_data) { + options->shebang_callback = shebang_callback; + options->shebang_callback_data = shebang_callback_data; +} + +/** + * 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 encoding_locked option on the given options struct. + */ +PRISM_EXPORTED_FUNCTION void +pm_options_encoding_locked_set(pm_options_t *options, bool encoding_locked) { + options->encoding_locked = encoding_locked; +} + +/** + * 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; +} + +/** + * Checks if the given slice represents a number. + */ +static inline bool +is_number(const char *string, size_t length) { + return pm_strspn_decimal_digit((const uint8_t *) string, (ptrdiff_t) length) == length; +} + +/** + * 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) { + if (version == NULL) { + options->version = PM_OPTIONS_VERSION_LATEST; + return true; + } + + if (length == 3) { + if (strncmp(version, "3.3", 3) == 0) { + options->version = PM_OPTIONS_VERSION_CRUBY_3_3; + return true; + } + + if (strncmp(version, "3.4", 3) == 0) { + options->version = PM_OPTIONS_VERSION_CRUBY_3_4; + return true; + } + + if (strncmp(version, "3.5", 3) == 0 || strncmp(version, "4.0", 3) == 0) { + options->version = PM_OPTIONS_VERSION_CRUBY_4_0; + return true; + } + + if (strncmp(version, "4.1", 3) == 0) { + options->version = PM_OPTIONS_VERSION_CRUBY_4_1; + return true; + } + + return false; + } + + if (length >= 4 && is_number(version + 4, length - 4)) { + if (strncmp(version, "3.3.", 4) == 0) { + options->version = PM_OPTIONS_VERSION_CRUBY_3_3; + return true; + } + + if (strncmp(version, "3.4.", 4) == 0) { + options->version = PM_OPTIONS_VERSION_CRUBY_3_4; + return true; + } + + if (strncmp(version, "3.5.", 4) == 0 || strncmp(version, "4.0.", 4) == 0) { + options->version = PM_OPTIONS_VERSION_CRUBY_4_0; + return true; + } + + if (strncmp(version, "4.1.", 4) == 0) { + options->version = PM_OPTIONS_VERSION_CRUBY_4_1; + return true; + } + } + + if (length >= 6) { + if (strncmp(version, "latest", 7) == 0) { // 7 to compare the \0 as well + options->version = PM_OPTIONS_VERSION_LATEST; + return true; + } + } + + return false; +} + +/** + * Set the main script option on the given options struct. + */ +PRISM_EXPORTED_FUNCTION void +pm_options_main_script_set(pm_options_t *options, bool main_script) { + options->main_script = main_script; +} + +/** + * Set the partial script option on the given options struct. + */ +PRISM_EXPORTED_FUNCTION void +pm_options_partial_script_set(pm_options_t *options, bool partial_script) { + options->partial_script = partial_script; +} + +/** + * Set the freeze option on the given options struct. + */ +PRISM_EXPORTED_FUNCTION void +pm_options_freeze_set(pm_options_t *options, bool freeze) { + options->freeze = freeze; +} + +// 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)); + scope->forwarding = PM_OPTIONS_SCOPE_FORWARDING_NONE; + 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]; +} + +/** + * Set the forwarding option on the given scope struct. + */ +PRISM_EXPORTED_FUNCTION void +pm_options_scope_forwarding_set(pm_options_scope_t *scope, uint8_t forwarding) { + scope->forwarding = forwarding; +} + +/** + * 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++; + options->encoding_locked = ((uint8_t) *data++) > 0; + options->main_script = ((uint8_t) *data++) > 0; + options->partial_script = ((uint8_t) *data++) > 0; + options->freeze = ((uint8_t) *data++) > 0; + + 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; + } + + uint8_t forwarding = (uint8_t) *data++; + pm_options_scope_forwarding_set(&options->scopes[scope_index], forwarding); + + 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/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/pack.c b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/pack.c new file mode 100644 index 0000000..1388ca8 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/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 +#include + +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/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/prettyprint.c b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/prettyprint.c new file mode 100644 index 0000000..ad25c0a --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/prettyprint.c @@ -0,0 +1,8957 @@ +/* :markup: markdown */ + +/*----------------------------------------------------------------------------*/ +/* This file is generated by the templates/template.rb script and should not */ +/* be modified manually. See */ +/* templates/src/prettyprint.c.erb */ +/* if you are looking to modify the */ +/* template */ +/*----------------------------------------------------------------------------*/ + +#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; + case PM_ALIAS_GLOBAL_VARIABLE_NODE: { + pm_alias_global_variable_node_t *cast = (pm_alias_global_variable_node_t *) node; + pm_buffer_append_string(output_buffer, "@ AliasGlobalVariableNode (location: ", 37); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // new_name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- new_name:", 13); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->new_name, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // old_name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- old_name:", 13); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->old_name, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- keyword_loc:", 16); + pm_location_t *location = &cast->keyword_loc; + 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); + } + + break; + } + case PM_ALIAS_METHOD_NODE: { + pm_alias_method_node_t *cast = (pm_alias_method_node_t *) node; + pm_buffer_append_string(output_buffer, "@ AliasMethodNode (location: ", 29); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // new_name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- new_name:", 13); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->new_name, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // old_name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- old_name:", 13); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->old_name, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- keyword_loc:", 16); + pm_location_t *location = &cast->keyword_loc; + 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); + } + + break; + } + case PM_ALTERNATION_PATTERN_NODE: { + pm_alternation_pattern_node_t *cast = (pm_alternation_pattern_node_t *) node; + pm_buffer_append_string(output_buffer, "@ AlternationPatternNode (location: ", 36); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // left + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- left:", 9); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->left, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // right + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- right:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->right, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + + break; + } + case PM_AND_NODE: { + pm_and_node_t *cast = (pm_and_node_t *) node; + pm_buffer_append_string(output_buffer, "@ AndNode (location: ", 21); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // left + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- left:", 9); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->left, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // right + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- right:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->right, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + + break; + } + case PM_ARGUMENTS_NODE: { + pm_arguments_node_t *cast = (pm_arguments_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ArgumentsNode (location: ", 27); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // ArgumentsNodeFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ArgumentsNodeFlags:", 23); + bool found = false; + if (cast->base.flags & PM_ARGUMENTS_NODE_FLAGS_CONTAINS_FORWARDING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " contains_forwarding", 20); + found = true; + } + if (cast->base.flags & PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " contains_keywords", 18); + found = true; + } + if (cast->base.flags & PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " contains_keyword_splat", 23); + found = true; + } + if (cast->base.flags & PM_ARGUMENTS_NODE_FLAGS_CONTAINS_SPLAT) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " contains_splat", 15); + found = true; + } + if (cast->base.flags & PM_ARGUMENTS_NODE_FLAGS_CONTAINS_MULTIPLE_SPLATS) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " contains_multiple_splats", 25); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // arguments + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- arguments:", 14); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->arguments.size)); + + size_t last_index = cast->arguments.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 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->arguments.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + break; + } + case PM_ARRAY_NODE: { + pm_array_node_t *cast = (pm_array_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ArrayNode (location: ", 23); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // ArrayNodeFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ArrayNodeFlags:", 19); + bool found = false; + if (cast->base.flags & PM_ARRAY_NODE_FLAGS_CONTAINS_SPLAT) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " contains_splat", 15); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // elements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- elements:", 13); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->elements.size)); + + size_t last_index = cast->elements.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 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->elements.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + pm_location_t *location = &cast->opening_loc; + 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); + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + pm_location_t *location = &cast->closing_loc; + 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); + } + } + + break; + } + case PM_ARRAY_PATTERN_NODE: { + pm_array_pattern_node_t *cast = (pm_array_pattern_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ArrayPatternNode (location: ", 30); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // constant + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- constant:", 13); + if (cast->constant == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->constant, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // requireds + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- requireds:", 14); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->requireds.size)); + + size_t last_index = cast->requireds.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 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->requireds.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // rest + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- rest:", 9); + if (cast->rest == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->rest, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // posts + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- posts:", 10); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->posts.size)); + + size_t last_index = cast->posts.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 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->posts.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + pm_location_t *location = &cast->opening_loc; + 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); + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + pm_location_t *location = &cast->closing_loc; + 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); + } + } + + break; + } + case PM_ASSOC_NODE: { + pm_assoc_node_t *cast = (pm_assoc_node_t *) node; + pm_buffer_append_string(output_buffer, "@ AssocNode (location: ", 23); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // key + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- key:", 8); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->key, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + } + + break; + } + case PM_ASSOC_SPLAT_NODE: { + pm_assoc_splat_node_t *cast = (pm_assoc_splat_node_t *) node; + pm_buffer_append_string(output_buffer, "@ AssocSplatNode (location: ", 28); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + if (cast->value == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + + break; + } + case PM_BACK_REFERENCE_READ_NODE: { + pm_back_reference_read_node_t *cast = (pm_back_reference_read_node_t *) node; + pm_buffer_append_string(output_buffer, "@ BackReferenceReadNode (location: ", 35); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_BEGIN_NODE: { + pm_begin_node_t *cast = (pm_begin_node_t *) node; + pm_buffer_append_string(output_buffer, "@ BeginNode (location: ", 23); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // begin_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- begin_keyword_loc:", 22); + pm_location_t *location = &cast->begin_keyword_loc; + 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); + } + } + + // statements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- statements:", 15); + if (cast->statements == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->statements, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // rescue_clause + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- rescue_clause:", 18); + if (cast->rescue_clause == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->rescue_clause, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // else_clause + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- else_clause:", 16); + if (cast->else_clause == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->else_clause, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // ensure_clause + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ensure_clause:", 18); + if (cast->ensure_clause == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->ensure_clause, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // end_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- end_keyword_loc:", 20); + pm_location_t *location = &cast->end_keyword_loc; + 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); + } + } + + break; + } + case PM_BLOCK_ARGUMENT_NODE: { + pm_block_argument_node_t *cast = (pm_block_argument_node_t *) node; + pm_buffer_append_string(output_buffer, "@ BlockArgumentNode (location: ", 31); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // expression + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- expression:", 15); + if (cast->expression == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->expression, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + + break; + } + case PM_BLOCK_LOCAL_VARIABLE_NODE: { + pm_block_local_variable_node_t *cast = (pm_block_local_variable_node_t *) node; + pm_buffer_append_string(output_buffer, "@ BlockLocalVariableNode (location: ", 36); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // ParameterFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ParameterFlags:", 19); + bool found = false; + if (cast->base.flags & PM_PARAMETER_FLAGS_REPEATED_PARAMETER) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " repeated_parameter", 19); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_BLOCK_NODE: { + pm_block_node_t *cast = (pm_block_node_t *) node; + pm_buffer_append_string(output_buffer, "@ BlockNode (location: ", 23); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // locals + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- locals:", 11); + pm_buffer_append_string(output_buffer, " [", 2); + for (uint32_t index = 0; index < cast->locals.size; index++) { + if (index != 0) pm_buffer_append_string(output_buffer, ", ", 2); + prettyprint_constant(output_buffer, parser, cast->locals.ids[index]); + } + pm_buffer_append_string(output_buffer, "]\n", 2); + } + + // parameters + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- parameters:", 15); + if (cast->parameters == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->parameters, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // body + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- body:", 9); + if (cast->body == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->body, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + pm_location_t *location = &cast->opening_loc; + 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); + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + pm_location_t *location = &cast->closing_loc; + 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); + } + + break; + } + case PM_BLOCK_PARAMETER_NODE: { + pm_block_parameter_node_t *cast = (pm_block_parameter_node_t *) node; + pm_buffer_append_string(output_buffer, "@ BlockParameterNode (location: ", 32); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // ParameterFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ParameterFlags:", 19); + bool found = false; + if (cast->base.flags & PM_PARAMETER_FLAGS_REPEATED_PARAMETER) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " repeated_parameter", 19); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + if (cast->name == 0) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name_loc:", 13); + pm_location_t *location = &cast->name_loc; + 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); + } + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + + break; + } + case PM_BLOCK_PARAMETERS_NODE: { + pm_block_parameters_node_t *cast = (pm_block_parameters_node_t *) node; + pm_buffer_append_string(output_buffer, "@ BlockParametersNode (location: ", 33); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // parameters + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- parameters:", 15); + if (cast->parameters == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->parameters, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // locals + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- locals:", 11); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->locals.size)); + + size_t last_index = cast->locals.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 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->locals.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + pm_location_t *location = &cast->opening_loc; + 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); + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + pm_location_t *location = &cast->closing_loc; + 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); + } + } + + break; + } + case PM_BREAK_NODE: { + pm_break_node_t *cast = (pm_break_node_t *) node; + pm_buffer_append_string(output_buffer, "@ BreakNode (location: ", 23); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // arguments + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- arguments:", 14); + if (cast->arguments == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->arguments, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- keyword_loc:", 16); + pm_location_t *location = &cast->keyword_loc; + 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); + } + + break; + } + case PM_CALL_AND_WRITE_NODE: { + pm_call_and_write_node_t *cast = (pm_call_and_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ CallAndWriteNode (location: ", 30); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // CallNodeFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- CallNodeFlags:", 18); + bool found = false; + if (cast->base.flags & PM_CALL_NODE_FLAGS_SAFE_NAVIGATION) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " safe_navigation", 16); + found = true; + } + if (cast->base.flags & PM_CALL_NODE_FLAGS_VARIABLE_CALL) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " variable_call", 14); + found = true; + } + if (cast->base.flags & PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " attribute_write", 16); + found = true; + } + if (cast->base.flags & PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " ignore_visibility", 18); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // receiver + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- receiver:", 13); + if (cast->receiver == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->receiver, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // call_operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- call_operator_loc:", 22); + pm_location_t *location = &cast->call_operator_loc; + 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); + } + } + + // message_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- message_loc:", 16); + pm_location_t *location = &cast->message_loc; + 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); + } + } + + // read_name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- read_name:", 14); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->read_name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // write_name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- write_name:", 15); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->write_name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_CALL_NODE: { + pm_call_node_t *cast = (pm_call_node_t *) node; + pm_buffer_append_string(output_buffer, "@ CallNode (location: ", 22); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // CallNodeFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- CallNodeFlags:", 18); + bool found = false; + if (cast->base.flags & PM_CALL_NODE_FLAGS_SAFE_NAVIGATION) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " safe_navigation", 16); + found = true; + } + if (cast->base.flags & PM_CALL_NODE_FLAGS_VARIABLE_CALL) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " variable_call", 14); + found = true; + } + if (cast->base.flags & PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " attribute_write", 16); + found = true; + } + if (cast->base.flags & PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " ignore_visibility", 18); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // receiver + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- receiver:", 13); + if (cast->receiver == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->receiver, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // call_operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- call_operator_loc:", 22); + pm_location_t *location = &cast->call_operator_loc; + 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); + } + } + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // message_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- message_loc:", 16); + pm_location_t *location = &cast->message_loc; + 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); + } + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + pm_location_t *location = &cast->opening_loc; + 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); + } + } + + // arguments + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- arguments:", 14); + if (cast->arguments == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->arguments, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + pm_location_t *location = &cast->closing_loc; + 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); + } + } + + // equal_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- equal_loc:", 14); + pm_location_t *location = &cast->equal_loc; + 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); + } + } + + // block + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- block:", 10); + if (cast->block == 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, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->block, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + break; + } + case PM_CALL_OPERATOR_WRITE_NODE: { + pm_call_operator_write_node_t *cast = (pm_call_operator_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ CallOperatorWriteNode (location: ", 35); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // CallNodeFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- CallNodeFlags:", 18); + bool found = false; + if (cast->base.flags & PM_CALL_NODE_FLAGS_SAFE_NAVIGATION) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " safe_navigation", 16); + found = true; + } + if (cast->base.flags & PM_CALL_NODE_FLAGS_VARIABLE_CALL) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " variable_call", 14); + found = true; + } + if (cast->base.flags & PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " attribute_write", 16); + found = true; + } + if (cast->base.flags & PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " ignore_visibility", 18); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // receiver + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- receiver:", 13); + if (cast->receiver == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->receiver, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // call_operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- call_operator_loc:", 22); + pm_location_t *location = &cast->call_operator_loc; + 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); + } + } + + // message_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- message_loc:", 16); + pm_location_t *location = &cast->message_loc; + 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); + } + } + + // read_name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- read_name:", 14); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->read_name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // write_name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- write_name:", 15); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->write_name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // binary_operator + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- binary_operator:", 20); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->binary_operator); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // binary_operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- binary_operator_loc:", 24); + pm_location_t *location = &cast->binary_operator_loc; + 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); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_CALL_OR_WRITE_NODE: { + pm_call_or_write_node_t *cast = (pm_call_or_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ CallOrWriteNode (location: ", 29); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // CallNodeFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- CallNodeFlags:", 18); + bool found = false; + if (cast->base.flags & PM_CALL_NODE_FLAGS_SAFE_NAVIGATION) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " safe_navigation", 16); + found = true; + } + if (cast->base.flags & PM_CALL_NODE_FLAGS_VARIABLE_CALL) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " variable_call", 14); + found = true; + } + if (cast->base.flags & PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " attribute_write", 16); + found = true; + } + if (cast->base.flags & PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " ignore_visibility", 18); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // receiver + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- receiver:", 13); + if (cast->receiver == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->receiver, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // call_operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- call_operator_loc:", 22); + pm_location_t *location = &cast->call_operator_loc; + 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); + } + } + + // message_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- message_loc:", 16); + pm_location_t *location = &cast->message_loc; + 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); + } + } + + // read_name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- read_name:", 14); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->read_name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // write_name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- write_name:", 15); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->write_name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_CALL_TARGET_NODE: { + pm_call_target_node_t *cast = (pm_call_target_node_t *) node; + pm_buffer_append_string(output_buffer, "@ CallTargetNode (location: ", 28); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // CallNodeFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- CallNodeFlags:", 18); + bool found = false; + if (cast->base.flags & PM_CALL_NODE_FLAGS_SAFE_NAVIGATION) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " safe_navigation", 16); + found = true; + } + if (cast->base.flags & PM_CALL_NODE_FLAGS_VARIABLE_CALL) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " variable_call", 14); + found = true; + } + if (cast->base.flags & PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " attribute_write", 16); + found = true; + } + if (cast->base.flags & PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " ignore_visibility", 18); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // receiver + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- receiver:", 13); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->receiver, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // call_operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- call_operator_loc:", 22); + pm_location_t *location = &cast->call_operator_loc; + 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); + } + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // message_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- message_loc:", 16); + pm_location_t *location = &cast->message_loc; + 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); + } + + break; + } + case PM_CAPTURE_PATTERN_NODE: { + pm_capture_pattern_node_t *cast = (pm_capture_pattern_node_t *) node; + pm_buffer_append_string(output_buffer, "@ CapturePatternNode (location: ", 32); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // target + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- target:", 11); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->target, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + + break; + } + case PM_CASE_MATCH_NODE: { + pm_case_match_node_t *cast = (pm_case_match_node_t *) node; + pm_buffer_append_string(output_buffer, "@ CaseMatchNode (location: ", 27); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // predicate + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- predicate:", 14); + if (cast->predicate == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->predicate, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // conditions + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- conditions:", 15); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->conditions.size)); + + size_t last_index = cast->conditions.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 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->conditions.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // else_clause + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- else_clause:", 16); + if (cast->else_clause == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->else_clause, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // case_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- case_keyword_loc:", 21); + pm_location_t *location = &cast->case_keyword_loc; + 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); + } + + // end_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- end_keyword_loc:", 20); + pm_location_t *location = &cast->end_keyword_loc; + 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); + } + + break; + } + case PM_CASE_NODE: { + pm_case_node_t *cast = (pm_case_node_t *) node; + pm_buffer_append_string(output_buffer, "@ CaseNode (location: ", 22); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // predicate + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- predicate:", 14); + if (cast->predicate == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->predicate, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // conditions + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- conditions:", 15); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->conditions.size)); + + size_t last_index = cast->conditions.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 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->conditions.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // else_clause + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- else_clause:", 16); + if (cast->else_clause == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->else_clause, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // case_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- case_keyword_loc:", 21); + pm_location_t *location = &cast->case_keyword_loc; + 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); + } + + // end_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- end_keyword_loc:", 20); + pm_location_t *location = &cast->end_keyword_loc; + 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); + } + + break; + } + case PM_CLASS_NODE: { + pm_class_node_t *cast = (pm_class_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ClassNode (location: ", 23); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // locals + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- locals:", 11); + pm_buffer_append_string(output_buffer, " [", 2); + for (uint32_t index = 0; index < cast->locals.size; index++) { + if (index != 0) pm_buffer_append_string(output_buffer, ", ", 2); + prettyprint_constant(output_buffer, parser, cast->locals.ids[index]); + } + pm_buffer_append_string(output_buffer, "]\n", 2); + } + + // class_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- class_keyword_loc:", 22); + pm_location_t *location = &cast->class_keyword_loc; + 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); + } + + // constant_path + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- constant_path:", 18); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->constant_path, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // inheritance_operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- inheritance_operator_loc:", 29); + pm_location_t *location = &cast->inheritance_operator_loc; + 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); + } + } + + // superclass + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- superclass:", 15); + if (cast->superclass == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->superclass, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // body + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- body:", 9); + if (cast->body == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->body, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // end_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- end_keyword_loc:", 20); + pm_location_t *location = &cast->end_keyword_loc; + 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); + } + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_CLASS_VARIABLE_AND_WRITE_NODE: { + pm_class_variable_and_write_node_t *cast = (pm_class_variable_and_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ClassVariableAndWriteNode (location: ", 39); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name_loc:", 13); + pm_location_t *location = &cast->name_loc; + 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); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE: { + pm_class_variable_operator_write_node_t *cast = (pm_class_variable_operator_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ClassVariableOperatorWriteNode (location: ", 44); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name_loc:", 13); + pm_location_t *location = &cast->name_loc; + 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); + } + + // binary_operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- binary_operator_loc:", 24); + pm_location_t *location = &cast->binary_operator_loc; + 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); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // binary_operator + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- binary_operator:", 20); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->binary_operator); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_CLASS_VARIABLE_OR_WRITE_NODE: { + pm_class_variable_or_write_node_t *cast = (pm_class_variable_or_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ClassVariableOrWriteNode (location: ", 38); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name_loc:", 13); + pm_location_t *location = &cast->name_loc; + 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); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_CLASS_VARIABLE_READ_NODE: { + pm_class_variable_read_node_t *cast = (pm_class_variable_read_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ClassVariableReadNode (location: ", 35); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_CLASS_VARIABLE_TARGET_NODE: { + pm_class_variable_target_node_t *cast = (pm_class_variable_target_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ClassVariableTargetNode (location: ", 37); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_CLASS_VARIABLE_WRITE_NODE: { + pm_class_variable_write_node_t *cast = (pm_class_variable_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ClassVariableWriteNode (location: ", 36); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name_loc:", 13); + pm_location_t *location = &cast->name_loc; + 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); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + + break; + } + case PM_CONSTANT_AND_WRITE_NODE: { + pm_constant_and_write_node_t *cast = (pm_constant_and_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ConstantAndWriteNode (location: ", 34); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name_loc:", 13); + pm_location_t *location = &cast->name_loc; + 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); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_CONSTANT_OPERATOR_WRITE_NODE: { + pm_constant_operator_write_node_t *cast = (pm_constant_operator_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ConstantOperatorWriteNode (location: ", 39); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name_loc:", 13); + pm_location_t *location = &cast->name_loc; + 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); + } + + // binary_operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- binary_operator_loc:", 24); + pm_location_t *location = &cast->binary_operator_loc; + 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); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // binary_operator + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- binary_operator:", 20); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->binary_operator); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_CONSTANT_OR_WRITE_NODE: { + pm_constant_or_write_node_t *cast = (pm_constant_or_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ConstantOrWriteNode (location: ", 33); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name_loc:", 13); + pm_location_t *location = &cast->name_loc; + 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); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_CONSTANT_PATH_AND_WRITE_NODE: { + pm_constant_path_and_write_node_t *cast = (pm_constant_path_and_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ConstantPathAndWriteNode (location: ", 38); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // target + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- target:", 11); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->target, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_CONSTANT_PATH_NODE: { + pm_constant_path_node_t *cast = (pm_constant_path_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ConstantPathNode (location: ", 30); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // parent + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- parent:", 11); + if (cast->parent == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->parent, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + if (cast->name == 0) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + } + + // delimiter_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- delimiter_loc:", 18); + pm_location_t *location = &cast->delimiter_loc; + 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); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name_loc:", 13); + pm_location_t *location = &cast->name_loc; + 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); + } + + break; + } + case PM_CONSTANT_PATH_OPERATOR_WRITE_NODE: { + pm_constant_path_operator_write_node_t *cast = (pm_constant_path_operator_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ConstantPathOperatorWriteNode (location: ", 43); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // target + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- target:", 11); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->target, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // binary_operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- binary_operator_loc:", 24); + pm_location_t *location = &cast->binary_operator_loc; + 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); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // binary_operator + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- binary_operator:", 20); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->binary_operator); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_CONSTANT_PATH_OR_WRITE_NODE: { + pm_constant_path_or_write_node_t *cast = (pm_constant_path_or_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ConstantPathOrWriteNode (location: ", 37); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // target + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- target:", 11); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->target, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_CONSTANT_PATH_TARGET_NODE: { + pm_constant_path_target_node_t *cast = (pm_constant_path_target_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ConstantPathTargetNode (location: ", 36); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // parent + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- parent:", 11); + if (cast->parent == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->parent, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + if (cast->name == 0) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + } + + // delimiter_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- delimiter_loc:", 18); + pm_location_t *location = &cast->delimiter_loc; + 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); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name_loc:", 13); + pm_location_t *location = &cast->name_loc; + 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); + } + + break; + } + case PM_CONSTANT_PATH_WRITE_NODE: { + pm_constant_path_write_node_t *cast = (pm_constant_path_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ConstantPathWriteNode (location: ", 35); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // target + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- target:", 11); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->target, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_CONSTANT_READ_NODE: { + pm_constant_read_node_t *cast = (pm_constant_read_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ConstantReadNode (location: ", 30); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_CONSTANT_TARGET_NODE: { + pm_constant_target_node_t *cast = (pm_constant_target_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ConstantTargetNode (location: ", 32); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_CONSTANT_WRITE_NODE: { + pm_constant_write_node_t *cast = (pm_constant_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ConstantWriteNode (location: ", 31); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name_loc:", 13); + pm_location_t *location = &cast->name_loc; + 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); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + + break; + } + case PM_DEF_NODE: { + pm_def_node_t *cast = (pm_def_node_t *) node; + pm_buffer_append_string(output_buffer, "@ DefNode (location: ", 21); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name_loc:", 13); + pm_location_t *location = &cast->name_loc; + 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); + } + + // receiver + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- receiver:", 13); + if (cast->receiver == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->receiver, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // parameters + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- parameters:", 15); + if (cast->parameters == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->parameters, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // body + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- body:", 9); + if (cast->body == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->body, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // locals + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- locals:", 11); + pm_buffer_append_string(output_buffer, " [", 2); + for (uint32_t index = 0; index < cast->locals.size; index++) { + if (index != 0) pm_buffer_append_string(output_buffer, ", ", 2); + prettyprint_constant(output_buffer, parser, cast->locals.ids[index]); + } + pm_buffer_append_string(output_buffer, "]\n", 2); + } + + // def_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- def_keyword_loc:", 20); + pm_location_t *location = &cast->def_keyword_loc; + 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); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + } + + // lparen_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- lparen_loc:", 15); + pm_location_t *location = &cast->lparen_loc; + 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); + } + } + + // rparen_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- rparen_loc:", 15); + pm_location_t *location = &cast->rparen_loc; + 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); + } + } + + // equal_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- equal_loc:", 14); + pm_location_t *location = &cast->equal_loc; + 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); + } + } + + // end_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- end_keyword_loc:", 20); + pm_location_t *location = &cast->end_keyword_loc; + 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); + } + } + + break; + } + case PM_DEFINED_NODE: { + pm_defined_node_t *cast = (pm_defined_node_t *) node; + pm_buffer_append_string(output_buffer, "@ DefinedNode (location: ", 25); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // lparen_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- lparen_loc:", 15); + pm_location_t *location = &cast->lparen_loc; + 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); + } + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // rparen_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- rparen_loc:", 15); + pm_location_t *location = &cast->rparen_loc; + 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); + } + } + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- keyword_loc:", 16); + pm_location_t *location = &cast->keyword_loc; + 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); + } + + break; + } + case PM_ELSE_NODE: { + pm_else_node_t *cast = (pm_else_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ElseNode (location: ", 22); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // else_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- else_keyword_loc:", 21); + pm_location_t *location = &cast->else_keyword_loc; + 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); + } + + // statements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- statements:", 15); + if (cast->statements == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->statements, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // end_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- end_keyword_loc:", 20); + pm_location_t *location = &cast->end_keyword_loc; + 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); + } + } + + break; + } + case PM_EMBEDDED_STATEMENTS_NODE: { + pm_embedded_statements_node_t *cast = (pm_embedded_statements_node_t *) node; + pm_buffer_append_string(output_buffer, "@ EmbeddedStatementsNode (location: ", 36); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + pm_location_t *location = &cast->opening_loc; + 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); + } + + // statements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- statements:", 15); + if (cast->statements == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->statements, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + pm_location_t *location = &cast->closing_loc; + 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); + } + + break; + } + case PM_EMBEDDED_VARIABLE_NODE: { + pm_embedded_variable_node_t *cast = (pm_embedded_variable_node_t *) node; + pm_buffer_append_string(output_buffer, "@ EmbeddedVariableNode (location: ", 34); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + + // variable + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- variable:", 13); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->variable, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_ENSURE_NODE: { + pm_ensure_node_t *cast = (pm_ensure_node_t *) node; + pm_buffer_append_string(output_buffer, "@ EnsureNode (location: ", 24); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // ensure_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ensure_keyword_loc:", 23); + pm_location_t *location = &cast->ensure_keyword_loc; + 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); + } + + // statements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- statements:", 15); + if (cast->statements == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->statements, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // end_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- end_keyword_loc:", 20); + pm_location_t *location = &cast->end_keyword_loc; + 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); + } + + break; + } + case PM_FALSE_NODE: { + pm_buffer_append_string(output_buffer, "@ FalseNode (location: ", 23); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + break; + } + case PM_FIND_PATTERN_NODE: { + pm_find_pattern_node_t *cast = (pm_find_pattern_node_t *) node; + pm_buffer_append_string(output_buffer, "@ FindPatternNode (location: ", 29); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // constant + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- constant:", 13); + if (cast->constant == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->constant, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // left + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- left:", 9); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->left, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // requireds + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- requireds:", 14); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->requireds.size)); + + size_t last_index = cast->requireds.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 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->requireds.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // right + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- right:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->right, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + pm_location_t *location = &cast->opening_loc; + 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); + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + pm_location_t *location = &cast->closing_loc; + 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); + } + } + + break; + } + case PM_FLIP_FLOP_NODE: { + pm_flip_flop_node_t *cast = (pm_flip_flop_node_t *) node; + pm_buffer_append_string(output_buffer, "@ FlipFlopNode (location: ", 26); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // RangeFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- RangeFlags:", 15); + bool found = false; + if (cast->base.flags & PM_RANGE_FLAGS_EXCLUDE_END) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " exclude_end", 12); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // left + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- left:", 9); + if (cast->left == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->left, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // right + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- right:", 10); + if (cast->right == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->right, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + + break; + } + case PM_FLOAT_NODE: { + pm_float_node_t *cast = (pm_float_node_t *) node; + pm_buffer_append_string(output_buffer, "@ FloatNode (location: ", 23); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + pm_buffer_append_format(output_buffer, " %f\n", cast->value); + } + + break; + } + case PM_FOR_NODE: { + pm_for_node_t *cast = (pm_for_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ForNode (location: ", 21); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // index + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- index:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->index, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // collection + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- collection:", 15); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->collection, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // statements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- statements:", 15); + if (cast->statements == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->statements, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // for_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- for_keyword_loc:", 20); + pm_location_t *location = &cast->for_keyword_loc; + 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); + } + + // in_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- in_keyword_loc:", 19); + pm_location_t *location = &cast->in_keyword_loc; + 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); + } + + // do_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- do_keyword_loc:", 19); + pm_location_t *location = &cast->do_keyword_loc; + 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); + } + } + + // end_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- end_keyword_loc:", 20); + pm_location_t *location = &cast->end_keyword_loc; + 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); + } + + break; + } + case PM_FORWARDING_ARGUMENTS_NODE: { + pm_buffer_append_string(output_buffer, "@ ForwardingArgumentsNode (location: ", 37); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + break; + } + case PM_FORWARDING_PARAMETER_NODE: { + pm_buffer_append_string(output_buffer, "@ ForwardingParameterNode (location: ", 37); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + break; + } + case PM_FORWARDING_SUPER_NODE: { + pm_forwarding_super_node_t *cast = (pm_forwarding_super_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ForwardingSuperNode (location: ", 33); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // block + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- block:", 10); + if (cast->block == 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, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->block, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + break; + } + case PM_GLOBAL_VARIABLE_AND_WRITE_NODE: { + pm_global_variable_and_write_node_t *cast = (pm_global_variable_and_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ GlobalVariableAndWriteNode (location: ", 40); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name_loc:", 13); + pm_location_t *location = &cast->name_loc; + 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); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE: { + pm_global_variable_operator_write_node_t *cast = (pm_global_variable_operator_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ GlobalVariableOperatorWriteNode (location: ", 45); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name_loc:", 13); + pm_location_t *location = &cast->name_loc; + 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); + } + + // binary_operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- binary_operator_loc:", 24); + pm_location_t *location = &cast->binary_operator_loc; + 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); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // binary_operator + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- binary_operator:", 20); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->binary_operator); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_GLOBAL_VARIABLE_OR_WRITE_NODE: { + pm_global_variable_or_write_node_t *cast = (pm_global_variable_or_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ GlobalVariableOrWriteNode (location: ", 39); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name_loc:", 13); + pm_location_t *location = &cast->name_loc; + 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); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_GLOBAL_VARIABLE_READ_NODE: { + pm_global_variable_read_node_t *cast = (pm_global_variable_read_node_t *) node; + pm_buffer_append_string(output_buffer, "@ GlobalVariableReadNode (location: ", 36); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_GLOBAL_VARIABLE_TARGET_NODE: { + pm_global_variable_target_node_t *cast = (pm_global_variable_target_node_t *) node; + pm_buffer_append_string(output_buffer, "@ GlobalVariableTargetNode (location: ", 38); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_GLOBAL_VARIABLE_WRITE_NODE: { + pm_global_variable_write_node_t *cast = (pm_global_variable_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ GlobalVariableWriteNode (location: ", 37); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name_loc:", 13); + pm_location_t *location = &cast->name_loc; + 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); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + + break; + } + case PM_HASH_NODE: { + pm_hash_node_t *cast = (pm_hash_node_t *) node; + pm_buffer_append_string(output_buffer, "@ HashNode (location: ", 22); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + pm_location_t *location = &cast->opening_loc; + 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); + } + + // elements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- elements:", 13); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->elements.size)); + + size_t last_index = cast->elements.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 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->elements.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + pm_location_t *location = &cast->closing_loc; + 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); + } + + break; + } + case PM_HASH_PATTERN_NODE: { + pm_hash_pattern_node_t *cast = (pm_hash_pattern_node_t *) node; + pm_buffer_append_string(output_buffer, "@ HashPatternNode (location: ", 29); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // constant + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- constant:", 13); + if (cast->constant == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->constant, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // elements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- elements:", 13); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->elements.size)); + + size_t last_index = cast->elements.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 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->elements.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // rest + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- rest:", 9); + if (cast->rest == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->rest, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + pm_location_t *location = &cast->opening_loc; + 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); + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + pm_location_t *location = &cast->closing_loc; + 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); + } + } + + break; + } + case PM_IF_NODE: { + pm_if_node_t *cast = (pm_if_node_t *) node; + pm_buffer_append_string(output_buffer, "@ IfNode (location: ", 20); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // if_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- if_keyword_loc:", 19); + pm_location_t *location = &cast->if_keyword_loc; + 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); + } + } + + // predicate + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- predicate:", 14); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->predicate, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // then_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- then_keyword_loc:", 21); + pm_location_t *location = &cast->then_keyword_loc; + 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); + } + } + + // statements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- statements:", 15); + if (cast->statements == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->statements, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // subsequent + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- subsequent:", 15); + if (cast->subsequent == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->subsequent, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // end_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- end_keyword_loc:", 20); + pm_location_t *location = &cast->end_keyword_loc; + 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); + } + } + + break; + } + case PM_IMAGINARY_NODE: { + pm_imaginary_node_t *cast = (pm_imaginary_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ImaginaryNode (location: ", 27); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // numeric + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- numeric:", 12); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->numeric, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_IMPLICIT_NODE: { + pm_implicit_node_t *cast = (pm_implicit_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ImplicitNode (location: ", 26); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_IMPLICIT_REST_NODE: { + pm_buffer_append_string(output_buffer, "@ ImplicitRestNode (location: ", 30); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + break; + } + case PM_IN_NODE: { + pm_in_node_t *cast = (pm_in_node_t *) node; + pm_buffer_append_string(output_buffer, "@ InNode (location: ", 20); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // pattern + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- pattern:", 12); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->pattern, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // statements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- statements:", 15); + if (cast->statements == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->statements, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // in_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- in_loc:", 11); + pm_location_t *location = &cast->in_loc; + 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); + } + + // then_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- then_loc:", 13); + pm_location_t *location = &cast->then_loc; + 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); + } + } + + break; + } + case PM_INDEX_AND_WRITE_NODE: { + pm_index_and_write_node_t *cast = (pm_index_and_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ IndexAndWriteNode (location: ", 31); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // CallNodeFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- CallNodeFlags:", 18); + bool found = false; + if (cast->base.flags & PM_CALL_NODE_FLAGS_SAFE_NAVIGATION) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " safe_navigation", 16); + found = true; + } + if (cast->base.flags & PM_CALL_NODE_FLAGS_VARIABLE_CALL) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " variable_call", 14); + found = true; + } + if (cast->base.flags & PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " attribute_write", 16); + found = true; + } + if (cast->base.flags & PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " ignore_visibility", 18); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // receiver + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- receiver:", 13); + if (cast->receiver == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->receiver, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // call_operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- call_operator_loc:", 22); + pm_location_t *location = &cast->call_operator_loc; + 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); + } + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + pm_location_t *location = &cast->opening_loc; + 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); + } + + // arguments + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- arguments:", 14); + if (cast->arguments == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->arguments, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + pm_location_t *location = &cast->closing_loc; + 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); + } + + // block + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- block:", 10); + if (cast->block == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->block, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_INDEX_OPERATOR_WRITE_NODE: { + pm_index_operator_write_node_t *cast = (pm_index_operator_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ IndexOperatorWriteNode (location: ", 36); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // CallNodeFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- CallNodeFlags:", 18); + bool found = false; + if (cast->base.flags & PM_CALL_NODE_FLAGS_SAFE_NAVIGATION) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " safe_navigation", 16); + found = true; + } + if (cast->base.flags & PM_CALL_NODE_FLAGS_VARIABLE_CALL) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " variable_call", 14); + found = true; + } + if (cast->base.flags & PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " attribute_write", 16); + found = true; + } + if (cast->base.flags & PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " ignore_visibility", 18); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // receiver + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- receiver:", 13); + if (cast->receiver == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->receiver, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // call_operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- call_operator_loc:", 22); + pm_location_t *location = &cast->call_operator_loc; + 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); + } + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + pm_location_t *location = &cast->opening_loc; + 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); + } + + // arguments + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- arguments:", 14); + if (cast->arguments == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->arguments, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + pm_location_t *location = &cast->closing_loc; + 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); + } + + // block + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- block:", 10); + if (cast->block == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->block, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // binary_operator + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- binary_operator:", 20); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->binary_operator); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // binary_operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- binary_operator_loc:", 24); + pm_location_t *location = &cast->binary_operator_loc; + 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); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_INDEX_OR_WRITE_NODE: { + pm_index_or_write_node_t *cast = (pm_index_or_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ IndexOrWriteNode (location: ", 30); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // CallNodeFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- CallNodeFlags:", 18); + bool found = false; + if (cast->base.flags & PM_CALL_NODE_FLAGS_SAFE_NAVIGATION) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " safe_navigation", 16); + found = true; + } + if (cast->base.flags & PM_CALL_NODE_FLAGS_VARIABLE_CALL) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " variable_call", 14); + found = true; + } + if (cast->base.flags & PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " attribute_write", 16); + found = true; + } + if (cast->base.flags & PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " ignore_visibility", 18); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // receiver + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- receiver:", 13); + if (cast->receiver == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->receiver, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // call_operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- call_operator_loc:", 22); + pm_location_t *location = &cast->call_operator_loc; + 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); + } + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + pm_location_t *location = &cast->opening_loc; + 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); + } + + // arguments + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- arguments:", 14); + if (cast->arguments == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->arguments, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + pm_location_t *location = &cast->closing_loc; + 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); + } + + // block + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- block:", 10); + if (cast->block == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->block, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_INDEX_TARGET_NODE: { + pm_index_target_node_t *cast = (pm_index_target_node_t *) node; + pm_buffer_append_string(output_buffer, "@ IndexTargetNode (location: ", 29); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // CallNodeFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- CallNodeFlags:", 18); + bool found = false; + if (cast->base.flags & PM_CALL_NODE_FLAGS_SAFE_NAVIGATION) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " safe_navigation", 16); + found = true; + } + if (cast->base.flags & PM_CALL_NODE_FLAGS_VARIABLE_CALL) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " variable_call", 14); + found = true; + } + if (cast->base.flags & PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " attribute_write", 16); + found = true; + } + if (cast->base.flags & PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " ignore_visibility", 18); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // receiver + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- receiver:", 13); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->receiver, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + pm_location_t *location = &cast->opening_loc; + 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); + } + + // arguments + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- arguments:", 14); + if (cast->arguments == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->arguments, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + pm_location_t *location = &cast->closing_loc; + 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); + } + + // block + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- block:", 10); + if (cast->block == 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, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->block, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + break; + } + case PM_INSTANCE_VARIABLE_AND_WRITE_NODE: { + pm_instance_variable_and_write_node_t *cast = (pm_instance_variable_and_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ InstanceVariableAndWriteNode (location: ", 42); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name_loc:", 13); + pm_location_t *location = &cast->name_loc; + 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); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE: { + pm_instance_variable_operator_write_node_t *cast = (pm_instance_variable_operator_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ InstanceVariableOperatorWriteNode (location: ", 47); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name_loc:", 13); + pm_location_t *location = &cast->name_loc; + 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); + } + + // binary_operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- binary_operator_loc:", 24); + pm_location_t *location = &cast->binary_operator_loc; + 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); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // binary_operator + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- binary_operator:", 20); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->binary_operator); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_INSTANCE_VARIABLE_OR_WRITE_NODE: { + pm_instance_variable_or_write_node_t *cast = (pm_instance_variable_or_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ InstanceVariableOrWriteNode (location: ", 41); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name_loc:", 13); + pm_location_t *location = &cast->name_loc; + 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); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_INSTANCE_VARIABLE_READ_NODE: { + pm_instance_variable_read_node_t *cast = (pm_instance_variable_read_node_t *) node; + pm_buffer_append_string(output_buffer, "@ InstanceVariableReadNode (location: ", 38); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_INSTANCE_VARIABLE_TARGET_NODE: { + pm_instance_variable_target_node_t *cast = (pm_instance_variable_target_node_t *) node; + pm_buffer_append_string(output_buffer, "@ InstanceVariableTargetNode (location: ", 40); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_INSTANCE_VARIABLE_WRITE_NODE: { + pm_instance_variable_write_node_t *cast = (pm_instance_variable_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ InstanceVariableWriteNode (location: ", 39); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name_loc:", 13); + pm_location_t *location = &cast->name_loc; + 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); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + + break; + } + case PM_INTEGER_NODE: { + pm_integer_node_t *cast = (pm_integer_node_t *) node; + pm_buffer_append_string(output_buffer, "@ IntegerNode (location: ", 25); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // IntegerBaseFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- IntegerBaseFlags:", 21); + bool found = false; + if (cast->base.flags & PM_INTEGER_BASE_FLAGS_BINARY) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " binary", 7); + found = true; + } + if (cast->base.flags & PM_INTEGER_BASE_FLAGS_DECIMAL) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " decimal", 8); + found = true; + } + if (cast->base.flags & PM_INTEGER_BASE_FLAGS_OCTAL) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " octal", 6); + found = true; + } + if (cast->base.flags & PM_INTEGER_BASE_FLAGS_HEXADECIMAL) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " hexadecimal", 12); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + const pm_integer_t *integer = &cast->value; + pm_buffer_append_byte(output_buffer, ' '); + pm_integer_string(output_buffer, integer); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_INTERPOLATED_MATCH_LAST_LINE_NODE: { + pm_interpolated_match_last_line_node_t *cast = (pm_interpolated_match_last_line_node_t *) node; + pm_buffer_append_string(output_buffer, "@ InterpolatedMatchLastLineNode (location: ", 43); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // RegularExpressionFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- RegularExpressionFlags:", 27); + bool found = false; + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " ignore_case", 12); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_EXTENDED) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " extended", 9); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " multi_line", 11); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_ONCE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " once", 5); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_EUC_JP) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " euc_jp", 7); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " ascii_8bit", 11); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " windows_31j", 12); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_UTF_8) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " utf_8", 6); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_FORCED_UTF8_ENCODING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " forced_utf8_encoding", 21); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_FORCED_BINARY_ENCODING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " forced_binary_encoding", 23); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_FORCED_US_ASCII_ENCODING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " forced_us_ascii_encoding", 25); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + pm_location_t *location = &cast->opening_loc; + 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); + } + + // parts + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- parts:", 10); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->parts.size)); + + size_t last_index = cast->parts.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 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->parts.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + pm_location_t *location = &cast->closing_loc; + 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); + } + + break; + } + case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: { + pm_interpolated_regular_expression_node_t *cast = (pm_interpolated_regular_expression_node_t *) node; + pm_buffer_append_string(output_buffer, "@ InterpolatedRegularExpressionNode (location: ", 47); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // RegularExpressionFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- RegularExpressionFlags:", 27); + bool found = false; + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " ignore_case", 12); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_EXTENDED) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " extended", 9); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " multi_line", 11); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_ONCE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " once", 5); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_EUC_JP) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " euc_jp", 7); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " ascii_8bit", 11); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " windows_31j", 12); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_UTF_8) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " utf_8", 6); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_FORCED_UTF8_ENCODING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " forced_utf8_encoding", 21); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_FORCED_BINARY_ENCODING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " forced_binary_encoding", 23); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_FORCED_US_ASCII_ENCODING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " forced_us_ascii_encoding", 25); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + pm_location_t *location = &cast->opening_loc; + 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); + } + + // parts + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- parts:", 10); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->parts.size)); + + size_t last_index = cast->parts.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 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->parts.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + pm_location_t *location = &cast->closing_loc; + 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); + } + + break; + } + case PM_INTERPOLATED_STRING_NODE: { + pm_interpolated_string_node_t *cast = (pm_interpolated_string_node_t *) node; + pm_buffer_append_string(output_buffer, "@ InterpolatedStringNode (location: ", 36); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // InterpolatedStringNodeFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- InterpolatedStringNodeFlags:", 32); + bool found = false; + if (cast->base.flags & PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " frozen", 7); + found = true; + } + if (cast->base.flags & PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " mutable", 8); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + pm_location_t *location = &cast->opening_loc; + 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); + } + } + + // parts + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- parts:", 10); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->parts.size)); + + size_t last_index = cast->parts.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 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->parts.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + pm_location_t *location = &cast->closing_loc; + 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); + } + } + + break; + } + case PM_INTERPOLATED_SYMBOL_NODE: { + pm_interpolated_symbol_node_t *cast = (pm_interpolated_symbol_node_t *) node; + pm_buffer_append_string(output_buffer, "@ InterpolatedSymbolNode (location: ", 36); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + pm_location_t *location = &cast->opening_loc; + 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); + } + } + + // parts + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- parts:", 10); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->parts.size)); + + size_t last_index = cast->parts.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 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->parts.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + pm_location_t *location = &cast->closing_loc; + 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); + } + } + + break; + } + case PM_INTERPOLATED_X_STRING_NODE: { + pm_interpolated_x_string_node_t *cast = (pm_interpolated_x_string_node_t *) node; + pm_buffer_append_string(output_buffer, "@ InterpolatedXStringNode (location: ", 37); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + pm_location_t *location = &cast->opening_loc; + 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); + } + + // parts + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- parts:", 10); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->parts.size)); + + size_t last_index = cast->parts.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 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->parts.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + pm_location_t *location = &cast->closing_loc; + 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); + } + + break; + } + case PM_IT_LOCAL_VARIABLE_READ_NODE: { + pm_buffer_append_string(output_buffer, "@ ItLocalVariableReadNode (location: ", 37); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + break; + } + case PM_IT_PARAMETERS_NODE: { + pm_buffer_append_string(output_buffer, "@ ItParametersNode (location: ", 30); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + break; + } + case PM_KEYWORD_HASH_NODE: { + pm_keyword_hash_node_t *cast = (pm_keyword_hash_node_t *) node; + pm_buffer_append_string(output_buffer, "@ KeywordHashNode (location: ", 29); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // KeywordHashNodeFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- KeywordHashNodeFlags:", 25); + bool found = false; + if (cast->base.flags & PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " symbol_keys", 12); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // elements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- elements:", 13); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->elements.size)); + + size_t last_index = cast->elements.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 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->elements.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + break; + } + case PM_KEYWORD_REST_PARAMETER_NODE: { + pm_keyword_rest_parameter_node_t *cast = (pm_keyword_rest_parameter_node_t *) node; + pm_buffer_append_string(output_buffer, "@ KeywordRestParameterNode (location: ", 38); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // ParameterFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ParameterFlags:", 19); + bool found = false; + if (cast->base.flags & PM_PARAMETER_FLAGS_REPEATED_PARAMETER) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " repeated_parameter", 19); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + if (cast->name == 0) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name_loc:", 13); + pm_location_t *location = &cast->name_loc; + 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); + } + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + + break; + } + case PM_LAMBDA_NODE: { + pm_lambda_node_t *cast = (pm_lambda_node_t *) node; + pm_buffer_append_string(output_buffer, "@ LambdaNode (location: ", 24); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // locals + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- locals:", 11); + pm_buffer_append_string(output_buffer, " [", 2); + for (uint32_t index = 0; index < cast->locals.size; index++) { + if (index != 0) pm_buffer_append_string(output_buffer, ", ", 2); + prettyprint_constant(output_buffer, parser, cast->locals.ids[index]); + } + pm_buffer_append_string(output_buffer, "]\n", 2); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + pm_location_t *location = &cast->opening_loc; + 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); + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + pm_location_t *location = &cast->closing_loc; + 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); + } + + // parameters + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- parameters:", 15); + if (cast->parameters == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->parameters, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // body + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- body:", 9); + if (cast->body == 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, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->body, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + break; + } + case PM_LOCAL_VARIABLE_AND_WRITE_NODE: { + pm_local_variable_and_write_node_t *cast = (pm_local_variable_and_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ LocalVariableAndWriteNode (location: ", 39); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name_loc:", 13); + pm_location_t *location = &cast->name_loc; + 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); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // depth + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- depth:", 10); + pm_buffer_append_format(output_buffer, " %" PRIu32 "\n", cast->depth); + } + + break; + } + case PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE: { + pm_local_variable_operator_write_node_t *cast = (pm_local_variable_operator_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ LocalVariableOperatorWriteNode (location: ", 44); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name_loc:", 13); + pm_location_t *location = &cast->name_loc; + 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); + } + + // binary_operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- binary_operator_loc:", 24); + pm_location_t *location = &cast->binary_operator_loc; + 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); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // binary_operator + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- binary_operator:", 20); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->binary_operator); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // depth + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- depth:", 10); + pm_buffer_append_format(output_buffer, " %" PRIu32 "\n", cast->depth); + } + + break; + } + case PM_LOCAL_VARIABLE_OR_WRITE_NODE: { + pm_local_variable_or_write_node_t *cast = (pm_local_variable_or_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ LocalVariableOrWriteNode (location: ", 38); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name_loc:", 13); + pm_location_t *location = &cast->name_loc; + 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); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // depth + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- depth:", 10); + pm_buffer_append_format(output_buffer, " %" PRIu32 "\n", cast->depth); + } + + break; + } + case PM_LOCAL_VARIABLE_READ_NODE: { + pm_local_variable_read_node_t *cast = (pm_local_variable_read_node_t *) node; + pm_buffer_append_string(output_buffer, "@ LocalVariableReadNode (location: ", 35); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // depth + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- depth:", 10); + pm_buffer_append_format(output_buffer, " %" PRIu32 "\n", cast->depth); + } + + break; + } + case PM_LOCAL_VARIABLE_TARGET_NODE: { + pm_local_variable_target_node_t *cast = (pm_local_variable_target_node_t *) node; + pm_buffer_append_string(output_buffer, "@ LocalVariableTargetNode (location: ", 37); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // depth + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- depth:", 10); + pm_buffer_append_format(output_buffer, " %" PRIu32 "\n", cast->depth); + } + + break; + } + case PM_LOCAL_VARIABLE_WRITE_NODE: { + pm_local_variable_write_node_t *cast = (pm_local_variable_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ LocalVariableWriteNode (location: ", 36); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // depth + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- depth:", 10); + pm_buffer_append_format(output_buffer, " %" PRIu32 "\n", cast->depth); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name_loc:", 13); + pm_location_t *location = &cast->name_loc; + 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); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + + break; + } + case PM_MATCH_LAST_LINE_NODE: { + pm_match_last_line_node_t *cast = (pm_match_last_line_node_t *) node; + pm_buffer_append_string(output_buffer, "@ MatchLastLineNode (location: ", 31); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // RegularExpressionFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- RegularExpressionFlags:", 27); + bool found = false; + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " ignore_case", 12); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_EXTENDED) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " extended", 9); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " multi_line", 11); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_ONCE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " once", 5); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_EUC_JP) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " euc_jp", 7); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " ascii_8bit", 11); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " windows_31j", 12); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_UTF_8) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " utf_8", 6); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_FORCED_UTF8_ENCODING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " forced_utf8_encoding", 21); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_FORCED_BINARY_ENCODING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " forced_binary_encoding", 23); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_FORCED_US_ASCII_ENCODING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " forced_us_ascii_encoding", 25); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + pm_location_t *location = &cast->opening_loc; + 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); + } + + // content_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- content_loc:", 16); + pm_location_t *location = &cast->content_loc; + 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); + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + pm_location_t *location = &cast->closing_loc; + 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); + } + + // unescaped + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- unescaped:", 14); + pm_buffer_append_string(output_buffer, " \"", 2); + pm_buffer_append_source(output_buffer, pm_string_source(&cast->unescaped), pm_string_length(&cast->unescaped), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_MATCH_PREDICATE_NODE: { + pm_match_predicate_node_t *cast = (pm_match_predicate_node_t *) node; + pm_buffer_append_string(output_buffer, "@ MatchPredicateNode (location: ", 32); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // pattern + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- pattern:", 12); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->pattern, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + + break; + } + case PM_MATCH_REQUIRED_NODE: { + pm_match_required_node_t *cast = (pm_match_required_node_t *) node; + pm_buffer_append_string(output_buffer, "@ MatchRequiredNode (location: ", 31); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // pattern + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- pattern:", 12); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->pattern, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + + break; + } + case PM_MATCH_WRITE_NODE: { + pm_match_write_node_t *cast = (pm_match_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ MatchWriteNode (location: ", 28); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // call + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- call:", 9); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->call, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // targets + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- targets:", 12); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->targets.size)); + + size_t last_index = cast->targets.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 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->targets.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + break; + } + case PM_MISSING_NODE: { + pm_buffer_append_string(output_buffer, "@ MissingNode (location: ", 25); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + break; + } + case PM_MODULE_NODE: { + pm_module_node_t *cast = (pm_module_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ModuleNode (location: ", 24); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // locals + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- locals:", 11); + pm_buffer_append_string(output_buffer, " [", 2); + for (uint32_t index = 0; index < cast->locals.size; index++) { + if (index != 0) pm_buffer_append_string(output_buffer, ", ", 2); + prettyprint_constant(output_buffer, parser, cast->locals.ids[index]); + } + pm_buffer_append_string(output_buffer, "]\n", 2); + } + + // module_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- module_keyword_loc:", 23); + pm_location_t *location = &cast->module_keyword_loc; + 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); + } + + // constant_path + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- constant_path:", 18); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->constant_path, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // body + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- body:", 9); + if (cast->body == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->body, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // end_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- end_keyword_loc:", 20); + pm_location_t *location = &cast->end_keyword_loc; + 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); + } + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_MULTI_TARGET_NODE: { + pm_multi_target_node_t *cast = (pm_multi_target_node_t *) node; + pm_buffer_append_string(output_buffer, "@ MultiTargetNode (location: ", 29); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // lefts + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- lefts:", 10); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->lefts.size)); + + size_t last_index = cast->lefts.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 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->lefts.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // rest + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- rest:", 9); + if (cast->rest == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->rest, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // rights + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- rights:", 11); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->rights.size)); + + size_t last_index = cast->rights.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 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->rights.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // lparen_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- lparen_loc:", 15); + pm_location_t *location = &cast->lparen_loc; + 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); + } + } + + // rparen_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- rparen_loc:", 15); + pm_location_t *location = &cast->rparen_loc; + 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); + } + } + + break; + } + case PM_MULTI_WRITE_NODE: { + pm_multi_write_node_t *cast = (pm_multi_write_node_t *) node; + pm_buffer_append_string(output_buffer, "@ MultiWriteNode (location: ", 28); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // lefts + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- lefts:", 10); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->lefts.size)); + + size_t last_index = cast->lefts.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 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->lefts.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // rest + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- rest:", 9); + if (cast->rest == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->rest, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // rights + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- rights:", 11); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->rights.size)); + + size_t last_index = cast->rights.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 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->rights.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // lparen_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- lparen_loc:", 15); + pm_location_t *location = &cast->lparen_loc; + 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); + } + } + + // rparen_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- rparen_loc:", 15); + pm_location_t *location = &cast->rparen_loc; + 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); + } + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_NEXT_NODE: { + pm_next_node_t *cast = (pm_next_node_t *) node; + pm_buffer_append_string(output_buffer, "@ NextNode (location: ", 22); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // arguments + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- arguments:", 14); + if (cast->arguments == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->arguments, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- keyword_loc:", 16); + pm_location_t *location = &cast->keyword_loc; + 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); + } + + break; + } + case PM_NIL_NODE: { + pm_buffer_append_string(output_buffer, "@ NilNode (location: ", 21); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + break; + } + case PM_NO_KEYWORDS_PARAMETER_NODE: { + pm_no_keywords_parameter_node_t *cast = (pm_no_keywords_parameter_node_t *) node; + pm_buffer_append_string(output_buffer, "@ NoKeywordsParameterNode (location: ", 37); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- keyword_loc:", 16); + pm_location_t *location = &cast->keyword_loc; + 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); + } + + break; + } + case PM_NUMBERED_PARAMETERS_NODE: { + pm_numbered_parameters_node_t *cast = (pm_numbered_parameters_node_t *) node; + pm_buffer_append_string(output_buffer, "@ NumberedParametersNode (location: ", 36); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // maximum + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- maximum:", 12); + pm_buffer_append_format(output_buffer, " %" PRIu8 "\n", cast->maximum); + } + + break; + } + case PM_NUMBERED_REFERENCE_READ_NODE: { + pm_numbered_reference_read_node_t *cast = (pm_numbered_reference_read_node_t *) node; + pm_buffer_append_string(output_buffer, "@ NumberedReferenceReadNode (location: ", 39); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // number + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- number:", 11); + pm_buffer_append_format(output_buffer, " %" PRIu32 "\n", cast->number); + } + + break; + } + case PM_OPTIONAL_KEYWORD_PARAMETER_NODE: { + pm_optional_keyword_parameter_node_t *cast = (pm_optional_keyword_parameter_node_t *) node; + pm_buffer_append_string(output_buffer, "@ OptionalKeywordParameterNode (location: ", 42); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // ParameterFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ParameterFlags:", 19); + bool found = false; + if (cast->base.flags & PM_PARAMETER_FLAGS_REPEATED_PARAMETER) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " repeated_parameter", 19); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name_loc:", 13); + pm_location_t *location = &cast->name_loc; + 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); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_OPTIONAL_PARAMETER_NODE: { + pm_optional_parameter_node_t *cast = (pm_optional_parameter_node_t *) node; + pm_buffer_append_string(output_buffer, "@ OptionalParameterNode (location: ", 35); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // ParameterFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ParameterFlags:", 19); + bool found = false; + if (cast->base.flags & PM_PARAMETER_FLAGS_REPEATED_PARAMETER) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " repeated_parameter", 19); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name_loc:", 13); + pm_location_t *location = &cast->name_loc; + 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); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + + // value + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->value, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_OR_NODE: { + pm_or_node_t *cast = (pm_or_node_t *) node; + pm_buffer_append_string(output_buffer, "@ OrNode (location: ", 20); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // left + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- left:", 9); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->left, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // right + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- right:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->right, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + + break; + } + case PM_PARAMETERS_NODE: { + pm_parameters_node_t *cast = (pm_parameters_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ParametersNode (location: ", 28); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // requireds + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- requireds:", 14); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->requireds.size)); + + size_t last_index = cast->requireds.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 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->requireds.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // optionals + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- optionals:", 14); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->optionals.size)); + + size_t last_index = cast->optionals.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 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->optionals.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // rest + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- rest:", 9); + if (cast->rest == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->rest, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // posts + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- posts:", 10); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->posts.size)); + + size_t last_index = cast->posts.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 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->posts.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // keywords + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- keywords:", 13); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->keywords.size)); + + size_t last_index = cast->keywords.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 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->keywords.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // keyword_rest + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- keyword_rest:", 17); + if (cast->keyword_rest == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->keyword_rest, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // block + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- block:", 10); + if (cast->block == 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, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->block, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + break; + } + case PM_PARENTHESES_NODE: { + pm_parentheses_node_t *cast = (pm_parentheses_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ParenthesesNode (location: ", 29); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // ParenthesesNodeFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ParenthesesNodeFlags:", 25); + bool found = false; + if (cast->base.flags & PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " multiple_statements", 20); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // body + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- body:", 9); + if (cast->body == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->body, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + pm_location_t *location = &cast->opening_loc; + 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); + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + pm_location_t *location = &cast->closing_loc; + 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); + } + + break; + } + case PM_PINNED_EXPRESSION_NODE: { + pm_pinned_expression_node_t *cast = (pm_pinned_expression_node_t *) node; + pm_buffer_append_string(output_buffer, "@ PinnedExpressionNode (location: ", 34); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // expression + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- expression:", 15); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->expression, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + + // lparen_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- lparen_loc:", 15); + pm_location_t *location = &cast->lparen_loc; + 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); + } + + // rparen_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- rparen_loc:", 15); + pm_location_t *location = &cast->rparen_loc; + 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); + } + + break; + } + case PM_PINNED_VARIABLE_NODE: { + pm_pinned_variable_node_t *cast = (pm_pinned_variable_node_t *) node; + pm_buffer_append_string(output_buffer, "@ PinnedVariableNode (location: ", 32); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // variable + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- variable:", 13); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->variable, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + + break; + } + case PM_POST_EXECUTION_NODE: { + pm_post_execution_node_t *cast = (pm_post_execution_node_t *) node; + pm_buffer_append_string(output_buffer, "@ PostExecutionNode (location: ", 31); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // statements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- statements:", 15); + if (cast->statements == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->statements, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- keyword_loc:", 16); + pm_location_t *location = &cast->keyword_loc; + 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); + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + pm_location_t *location = &cast->opening_loc; + 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); + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + pm_location_t *location = &cast->closing_loc; + 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); + } + + break; + } + case PM_PRE_EXECUTION_NODE: { + pm_pre_execution_node_t *cast = (pm_pre_execution_node_t *) node; + pm_buffer_append_string(output_buffer, "@ PreExecutionNode (location: ", 30); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // statements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- statements:", 15); + if (cast->statements == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->statements, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- keyword_loc:", 16); + pm_location_t *location = &cast->keyword_loc; + 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); + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + pm_location_t *location = &cast->opening_loc; + 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); + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + pm_location_t *location = &cast->closing_loc; + 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); + } + + break; + } + case PM_PROGRAM_NODE: { + pm_program_node_t *cast = (pm_program_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ProgramNode (location: ", 25); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // locals + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- locals:", 11); + pm_buffer_append_string(output_buffer, " [", 2); + for (uint32_t index = 0; index < cast->locals.size; index++) { + if (index != 0) pm_buffer_append_string(output_buffer, ", ", 2); + prettyprint_constant(output_buffer, parser, cast->locals.ids[index]); + } + pm_buffer_append_string(output_buffer, "]\n", 2); + } + + // statements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- statements:", 15); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->statements, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_RANGE_NODE: { + pm_range_node_t *cast = (pm_range_node_t *) node; + pm_buffer_append_string(output_buffer, "@ RangeNode (location: ", 23); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // RangeFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- RangeFlags:", 15); + bool found = false; + if (cast->base.flags & PM_RANGE_FLAGS_EXCLUDE_END) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " exclude_end", 12); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // left + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- left:", 9); + if (cast->left == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->left, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // right + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- right:", 10); + if (cast->right == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->right, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + + break; + } + case PM_RATIONAL_NODE: { + pm_rational_node_t *cast = (pm_rational_node_t *) node; + pm_buffer_append_string(output_buffer, "@ RationalNode (location: ", 26); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // IntegerBaseFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- IntegerBaseFlags:", 21); + bool found = false; + if (cast->base.flags & PM_INTEGER_BASE_FLAGS_BINARY) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " binary", 7); + found = true; + } + if (cast->base.flags & PM_INTEGER_BASE_FLAGS_DECIMAL) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " decimal", 8); + found = true; + } + if (cast->base.flags & PM_INTEGER_BASE_FLAGS_OCTAL) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " octal", 6); + found = true; + } + if (cast->base.flags & PM_INTEGER_BASE_FLAGS_HEXADECIMAL) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " hexadecimal", 12); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // numerator + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- numerator:", 14); + const pm_integer_t *integer = &cast->numerator; + pm_buffer_append_byte(output_buffer, ' '); + pm_integer_string(output_buffer, integer); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // denominator + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- denominator:", 16); + const pm_integer_t *integer = &cast->denominator; + pm_buffer_append_byte(output_buffer, ' '); + pm_integer_string(output_buffer, integer); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_REDO_NODE: { + pm_buffer_append_string(output_buffer, "@ RedoNode (location: ", 22); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + break; + } + case PM_REGULAR_EXPRESSION_NODE: { + pm_regular_expression_node_t *cast = (pm_regular_expression_node_t *) node; + pm_buffer_append_string(output_buffer, "@ RegularExpressionNode (location: ", 35); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // RegularExpressionFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- RegularExpressionFlags:", 27); + bool found = false; + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " ignore_case", 12); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_EXTENDED) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " extended", 9); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " multi_line", 11); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_ONCE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " once", 5); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_EUC_JP) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " euc_jp", 7); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " ascii_8bit", 11); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " windows_31j", 12); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_UTF_8) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " utf_8", 6); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_FORCED_UTF8_ENCODING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " forced_utf8_encoding", 21); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_FORCED_BINARY_ENCODING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " forced_binary_encoding", 23); + found = true; + } + if (cast->base.flags & PM_REGULAR_EXPRESSION_FLAGS_FORCED_US_ASCII_ENCODING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " forced_us_ascii_encoding", 25); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + pm_location_t *location = &cast->opening_loc; + 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); + } + + // content_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- content_loc:", 16); + pm_location_t *location = &cast->content_loc; + 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); + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + pm_location_t *location = &cast->closing_loc; + 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); + } + + // unescaped + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- unescaped:", 14); + pm_buffer_append_string(output_buffer, " \"", 2); + pm_buffer_append_source(output_buffer, pm_string_source(&cast->unescaped), pm_string_length(&cast->unescaped), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_REQUIRED_KEYWORD_PARAMETER_NODE: { + pm_required_keyword_parameter_node_t *cast = (pm_required_keyword_parameter_node_t *) node; + pm_buffer_append_string(output_buffer, "@ RequiredKeywordParameterNode (location: ", 42); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // ParameterFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ParameterFlags:", 19); + bool found = false; + if (cast->base.flags & PM_PARAMETER_FLAGS_REPEATED_PARAMETER) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " repeated_parameter", 19); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name_loc:", 13); + pm_location_t *location = &cast->name_loc; + 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); + } + + break; + } + case PM_REQUIRED_PARAMETER_NODE: { + pm_required_parameter_node_t *cast = (pm_required_parameter_node_t *) node; + pm_buffer_append_string(output_buffer, "@ RequiredParameterNode (location: ", 35); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // ParameterFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ParameterFlags:", 19); + bool found = false; + if (cast->base.flags & PM_PARAMETER_FLAGS_REPEATED_PARAMETER) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " repeated_parameter", 19); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + + break; + } + case PM_RESCUE_MODIFIER_NODE: { + pm_rescue_modifier_node_t *cast = (pm_rescue_modifier_node_t *) node; + pm_buffer_append_string(output_buffer, "@ RescueModifierNode (location: ", 32); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // expression + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- expression:", 15); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->expression, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- keyword_loc:", 16); + pm_location_t *location = &cast->keyword_loc; + 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); + } + + // rescue_expression + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- rescue_expression:", 22); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->rescue_expression, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_RESCUE_NODE: { + pm_rescue_node_t *cast = (pm_rescue_node_t *) node; + pm_buffer_append_string(output_buffer, "@ RescueNode (location: ", 24); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- keyword_loc:", 16); + pm_location_t *location = &cast->keyword_loc; + 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); + } + + // exceptions + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- exceptions:", 15); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->exceptions.size)); + + size_t last_index = cast->exceptions.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 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->exceptions.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + } + + // reference + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- reference:", 14); + if (cast->reference == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->reference, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // then_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- then_keyword_loc:", 21); + pm_location_t *location = &cast->then_keyword_loc; + 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); + } + } + + // statements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- statements:", 15); + if (cast->statements == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->statements, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // subsequent + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- subsequent:", 15); + if (cast->subsequent == 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, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->subsequent, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + break; + } + case PM_REST_PARAMETER_NODE: { + pm_rest_parameter_node_t *cast = (pm_rest_parameter_node_t *) node; + pm_buffer_append_string(output_buffer, "@ RestParameterNode (location: ", 31); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // ParameterFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ParameterFlags:", 19); + bool found = false; + if (cast->base.flags & PM_PARAMETER_FLAGS_REPEATED_PARAMETER) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " repeated_parameter", 19); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // name + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name:", 9); + if (cast->name == 0) { + pm_buffer_append_string(output_buffer, " nil\n", 5); + } else { + pm_buffer_append_byte(output_buffer, ' '); + prettyprint_constant(output_buffer, parser, cast->name); + pm_buffer_append_byte(output_buffer, '\n'); + } + } + + // name_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- name_loc:", 13); + pm_location_t *location = &cast->name_loc; + 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); + } + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + + break; + } + case PM_RETRY_NODE: { + pm_buffer_append_string(output_buffer, "@ RetryNode (location: ", 23); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + break; + } + case PM_RETURN_NODE: { + pm_return_node_t *cast = (pm_return_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ReturnNode (location: ", 24); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- keyword_loc:", 16); + pm_location_t *location = &cast->keyword_loc; + 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); + } + + // arguments + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- arguments:", 14); + if (cast->arguments == 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, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->arguments, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + break; + } + case PM_SELF_NODE: { + pm_buffer_append_string(output_buffer, "@ SelfNode (location: ", 22); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + break; + } + case PM_SHAREABLE_CONSTANT_NODE: { + pm_shareable_constant_node_t *cast = (pm_shareable_constant_node_t *) node; + pm_buffer_append_string(output_buffer, "@ ShareableConstantNode (location: ", 35); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // ShareableConstantNodeFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- ShareableConstantNodeFlags:", 31); + bool found = false; + if (cast->base.flags & PM_SHAREABLE_CONSTANT_NODE_FLAGS_LITERAL) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " literal", 8); + found = true; + } + if (cast->base.flags & PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_EVERYTHING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " experimental_everything", 24); + found = true; + } + if (cast->base.flags & PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_COPY) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " experimental_copy", 18); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // write + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- write:", 10); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->write, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + break; + } + case PM_SINGLETON_CLASS_NODE: { + pm_singleton_class_node_t *cast = (pm_singleton_class_node_t *) node; + pm_buffer_append_string(output_buffer, "@ SingletonClassNode (location: ", 32); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // locals + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- locals:", 11); + pm_buffer_append_string(output_buffer, " [", 2); + for (uint32_t index = 0; index < cast->locals.size; index++) { + if (index != 0) pm_buffer_append_string(output_buffer, ", ", 2); + prettyprint_constant(output_buffer, parser, cast->locals.ids[index]); + } + pm_buffer_append_string(output_buffer, "]\n", 2); + } + + // class_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- class_keyword_loc:", 22); + pm_location_t *location = &cast->class_keyword_loc; + 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); + } + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + + // expression + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- expression:", 15); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->expression, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // body + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- body:", 9); + if (cast->body == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->body, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // end_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- end_keyword_loc:", 20); + pm_location_t *location = &cast->end_keyword_loc; + 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); + } + + break; + } + case PM_SOURCE_ENCODING_NODE: { + pm_buffer_append_string(output_buffer, "@ SourceEncodingNode (location: ", 32); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + break; + } + case PM_SOURCE_FILE_NODE: { + pm_source_file_node_t *cast = (pm_source_file_node_t *) node; + pm_buffer_append_string(output_buffer, "@ SourceFileNode (location: ", 28); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // StringFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- StringFlags:", 16); + bool found = false; + if (cast->base.flags & PM_STRING_FLAGS_FORCED_UTF8_ENCODING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " forced_utf8_encoding", 21); + found = true; + } + if (cast->base.flags & PM_STRING_FLAGS_FORCED_BINARY_ENCODING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " forced_binary_encoding", 23); + found = true; + } + if (cast->base.flags & PM_STRING_FLAGS_FROZEN) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " frozen", 7); + found = true; + } + if (cast->base.flags & PM_STRING_FLAGS_MUTABLE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " mutable", 8); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // filepath + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- filepath:", 13); + pm_buffer_append_string(output_buffer, " \"", 2); + pm_buffer_append_source(output_buffer, pm_string_source(&cast->filepath), pm_string_length(&cast->filepath), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_SOURCE_LINE_NODE: { + pm_buffer_append_string(output_buffer, "@ SourceLineNode (location: ", 28); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + break; + } + case PM_SPLAT_NODE: { + pm_splat_node_t *cast = (pm_splat_node_t *) node; + pm_buffer_append_string(output_buffer, "@ SplatNode (location: ", 23); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // operator_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- operator_loc:", 17); + pm_location_t *location = &cast->operator_loc; + 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); + } + + // expression + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- expression:", 15); + if (cast->expression == 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, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->expression, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + break; + } + case PM_STATEMENTS_NODE: { + pm_statements_node_t *cast = (pm_statements_node_t *) node; + pm_buffer_append_string(output_buffer, "@ StatementsNode (location: ", 28); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // body + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- body:", 9); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->body.size)); + + size_t last_index = cast->body.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, " ", 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->body.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + break; + } + case PM_STRING_NODE: { + pm_string_node_t *cast = (pm_string_node_t *) node; + pm_buffer_append_string(output_buffer, "@ StringNode (location: ", 24); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // StringFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- StringFlags:", 16); + bool found = false; + if (cast->base.flags & PM_STRING_FLAGS_FORCED_UTF8_ENCODING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " forced_utf8_encoding", 21); + found = true; + } + if (cast->base.flags & PM_STRING_FLAGS_FORCED_BINARY_ENCODING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " forced_binary_encoding", 23); + found = true; + } + if (cast->base.flags & PM_STRING_FLAGS_FROZEN) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " frozen", 7); + found = true; + } + if (cast->base.flags & PM_STRING_FLAGS_MUTABLE) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " mutable", 8); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + pm_location_t *location = &cast->opening_loc; + 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); + } + } + + // content_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- content_loc:", 16); + pm_location_t *location = &cast->content_loc; + 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); + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + pm_location_t *location = &cast->closing_loc; + 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); + } + } + + // unescaped + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- unescaped:", 14); + pm_buffer_append_string(output_buffer, " \"", 2); + pm_buffer_append_source(output_buffer, pm_string_source(&cast->unescaped), pm_string_length(&cast->unescaped), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_SUPER_NODE: { + pm_super_node_t *cast = (pm_super_node_t *) node; + pm_buffer_append_string(output_buffer, "@ SuperNode (location: ", 23); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- keyword_loc:", 16); + pm_location_t *location = &cast->keyword_loc; + 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); + } + + // lparen_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- lparen_loc:", 15); + pm_location_t *location = &cast->lparen_loc; + 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); + } + } + + // arguments + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- arguments:", 14); + if (cast->arguments == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->arguments, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // rparen_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- rparen_loc:", 15); + pm_location_t *location = &cast->rparen_loc; + 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); + } + } + + // block + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- block:", 10); + if (cast->block == 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, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->block, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + break; + } + case PM_SYMBOL_NODE: { + pm_symbol_node_t *cast = (pm_symbol_node_t *) node; + pm_buffer_append_string(output_buffer, "@ SymbolNode (location: ", 24); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // SymbolFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- SymbolFlags:", 16); + bool found = false; + if (cast->base.flags & PM_SYMBOL_FLAGS_FORCED_UTF8_ENCODING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " forced_utf8_encoding", 21); + found = true; + } + if (cast->base.flags & PM_SYMBOL_FLAGS_FORCED_BINARY_ENCODING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " forced_binary_encoding", 23); + found = true; + } + if (cast->base.flags & PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " forced_us_ascii_encoding", 25); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + pm_location_t *location = &cast->opening_loc; + 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); + } + } + + // value_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- value_loc:", 14); + pm_location_t *location = &cast->value_loc; + 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); + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + pm_location_t *location = &cast->closing_loc; + 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); + } + } + + // unescaped + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- unescaped:", 14); + pm_buffer_append_string(output_buffer, " \"", 2); + pm_buffer_append_source(output_buffer, pm_string_source(&cast->unescaped), pm_string_length(&cast->unescaped), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_TRUE_NODE: { + pm_buffer_append_string(output_buffer, "@ TrueNode (location: ", 22); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + break; + } + case PM_UNDEF_NODE: { + pm_undef_node_t *cast = (pm_undef_node_t *) node; + pm_buffer_append_string(output_buffer, "@ UndefNode (location: ", 23); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // names + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- names:", 10); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->names.size)); + + size_t last_index = cast->names.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 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->names.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- keyword_loc:", 16); + pm_location_t *location = &cast->keyword_loc; + 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); + } + + break; + } + case PM_UNLESS_NODE: { + pm_unless_node_t *cast = (pm_unless_node_t *) node; + pm_buffer_append_string(output_buffer, "@ UnlessNode (location: ", 24); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- keyword_loc:", 16); + pm_location_t *location = &cast->keyword_loc; + 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); + } + + // predicate + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- predicate:", 14); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->predicate, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // then_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- then_keyword_loc:", 21); + pm_location_t *location = &cast->then_keyword_loc; + 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); + } + } + + // statements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- statements:", 15); + if (cast->statements == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->statements, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // else_clause + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- else_clause:", 16); + if (cast->else_clause == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->else_clause, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // end_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- end_keyword_loc:", 20); + pm_location_t *location = &cast->end_keyword_loc; + 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); + } + } + + break; + } + case PM_UNTIL_NODE: { + pm_until_node_t *cast = (pm_until_node_t *) node; + pm_buffer_append_string(output_buffer, "@ UntilNode (location: ", 23); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // LoopFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- LoopFlags:", 14); + bool found = false; + if (cast->base.flags & PM_LOOP_FLAGS_BEGIN_MODIFIER) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " begin_modifier", 15); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- keyword_loc:", 16); + pm_location_t *location = &cast->keyword_loc; + 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); + } + + // do_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- do_keyword_loc:", 19); + pm_location_t *location = &cast->do_keyword_loc; + 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); + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + pm_location_t *location = &cast->closing_loc; + 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); + } + } + + // predicate + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- predicate:", 14); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->predicate, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // statements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- statements:", 15); + if (cast->statements == 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, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->statements, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + break; + } + case PM_WHEN_NODE: { + pm_when_node_t *cast = (pm_when_node_t *) node; + pm_buffer_append_string(output_buffer, "@ WhenNode (location: ", 22); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- keyword_loc:", 16); + pm_location_t *location = &cast->keyword_loc; + 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); + } + + // conditions + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- conditions:", 15); + pm_buffer_append_format(output_buffer, " (length: %lu)\n", (unsigned long) (cast->conditions.size)); + + size_t last_index = cast->conditions.size; + for (uint32_t index = 0; index < last_index; index++) { + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 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->conditions.nodes[index], prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // then_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- then_keyword_loc:", 21); + pm_location_t *location = &cast->then_keyword_loc; + 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); + } + } + + // statements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- statements:", 15); + if (cast->statements == 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, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->statements, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + break; + } + case PM_WHILE_NODE: { + pm_while_node_t *cast = (pm_while_node_t *) node; + pm_buffer_append_string(output_buffer, "@ WhileNode (location: ", 23); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // LoopFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- LoopFlags:", 14); + bool found = false; + if (cast->base.flags & PM_LOOP_FLAGS_BEGIN_MODIFIER) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " begin_modifier", 15); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- keyword_loc:", 16); + pm_location_t *location = &cast->keyword_loc; + 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); + } + + // do_keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- do_keyword_loc:", 19); + pm_location_t *location = &cast->do_keyword_loc; + 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); + } + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + pm_location_t *location = &cast->closing_loc; + 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); + } + } + + // predicate + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- predicate:", 14); + pm_buffer_append_byte(output_buffer, '\n'); + + size_t prefix_length = prefix_buffer->length; + pm_buffer_append_string(prefix_buffer, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->predicate, prefix_buffer); + prefix_buffer->length = prefix_length; + } + + // statements + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- statements:", 15); + if (cast->statements == 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, " ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->statements, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + break; + } + case PM_X_STRING_NODE: { + pm_x_string_node_t *cast = (pm_x_string_node_t *) node; + pm_buffer_append_string(output_buffer, "@ XStringNode (location: ", 25); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // EncodingFlags + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- EncodingFlags:", 18); + bool found = false; + if (cast->base.flags & PM_ENCODING_FLAGS_FORCED_UTF8_ENCODING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " forced_utf8_encoding", 21); + found = true; + } + if (cast->base.flags & PM_ENCODING_FLAGS_FORCED_BINARY_ENCODING) { + if (found) pm_buffer_append_byte(output_buffer, ','); + pm_buffer_append_string(output_buffer, " forced_binary_encoding", 23); + found = true; + } + if (!found) pm_buffer_append_string(output_buffer, " nil", 4); + pm_buffer_append_byte(output_buffer, '\n'); + } + + // opening_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- opening_loc:", 16); + pm_location_t *location = &cast->opening_loc; + 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); + } + + // content_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- content_loc:", 16); + pm_location_t *location = &cast->content_loc; + 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); + } + + // closing_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- closing_loc:", 16); + pm_location_t *location = &cast->closing_loc; + 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); + } + + // unescaped + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- unescaped:", 14); + pm_buffer_append_string(output_buffer, " \"", 2); + pm_buffer_append_source(output_buffer, pm_string_source(&cast->unescaped), pm_string_length(&cast->unescaped), PM_BUFFER_ESCAPING_RUBY); + pm_buffer_append_string(output_buffer, "\"\n", 2); + } + + break; + } + case PM_YIELD_NODE: { + pm_yield_node_t *cast = (pm_yield_node_t *) node; + pm_buffer_append_string(output_buffer, "@ YieldNode (location: ", 23); + prettyprint_location(output_buffer, parser, &node->location); + pm_buffer_append_string(output_buffer, ")\n", 2); + + // keyword_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- keyword_loc:", 16); + pm_location_t *location = &cast->keyword_loc; + 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); + } + + // lparen_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- lparen_loc:", 15); + pm_location_t *location = &cast->lparen_loc; + 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); + } + } + + // arguments + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- arguments:", 14); + if (cast->arguments == 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, "| ", 4); + pm_buffer_concat(output_buffer, prefix_buffer); + prettyprint_node(output_buffer, parser, (pm_node_t *) cast->arguments, prefix_buffer); + prefix_buffer->length = prefix_length; + } + } + + // rparen_loc + { + pm_buffer_concat(output_buffer, prefix_buffer); + pm_buffer_append_string(output_buffer, "+-- rparen_loc:", 15); + pm_location_t *location = &cast->rparen_loc; + 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); + } + } + + break; + } + } +} + +/** + * 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/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/prism.c b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/prism.c new file mode 100644 index 0000000..b158e50 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/prism.c @@ -0,0 +1,22679 @@ +#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 + +// Macros for min/max. +#define MIN(a,b) (((a)<(b))?(a):(b)) +#define MAX(a,b) (((a)>(b))?(a):(b)) + +/******************************************************************************/ +/* Helpful AST-related macros */ +/******************************************************************************/ + +#define FL PM_NODE_FLAGS +#define UP PM_NODE_UPCAST + +#define PM_TOKEN_START(token_) ((token_)->start) +#define PM_TOKEN_END(token_) ((token_)->end) + +#define PM_NODE_START(node_) (UP(node_)->location.start) +#define PM_NODE_END(node_) (UP(node_)->location.end) + +#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 = PM_TOKEN_START(token_), .end = PM_TOKEN_END(token_) }) +#define PM_LOCATION_NODE_VALUE(node_) ((pm_location_t) { .start = PM_NODE_START(node_), .end = PM_NODE_END(node_) }) +#define PM_OPTIONAL_LOCATION_TOKEN_VALUE(token) ((token)->type == PM_TOKEN_NOT_PROVIDED ? ((pm_location_t) { 0 }) : PM_LOCATION_TOKEN_VALUE(token)) + +/******************************************************************************/ +/* 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; + } + + parser->explicit_encoding = NULL; + 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(const 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_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; +} + +#ifndef PM_DEBUG_LOGGING +/** + * Debugging logging will print additional information to stdout whenever the + * lexer state changes. + */ +#define PM_DEBUG_LOGGING 0 +#endif + +#if PM_DEBUG_LOGGING +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"); +} + +static 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__) + +/** + * Add an error for an expected heredoc terminator. This is a special function + * only because it grabs its location off of a lex mode instead of a node or a + * token. + */ +static void +pm_parser_err_heredoc_term(pm_parser_t *parser, const uint8_t *ident_start, size_t ident_length) { + PM_PARSER_ERR_FORMAT( + parser, + ident_start, + ident_start + ident_length, + PM_ERR_HEREDOC_TERM, + (int) ident_length, + (const char *) ident_start + ); +} + +/******************************************************************************/ +/* 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, + .implicit_parameters = { 0 }, + .shareable_constant = 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; +} + +typedef enum { + PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_PASS, + PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_CONFLICT, + PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_FAIL +} pm_scope_forwarding_param_check_result_t; + +static pm_scope_forwarding_param_check_result_t +pm_parser_scope_forwarding_param_check(pm_parser_t *parser, const uint8_t mask) { + pm_scope_t *scope = parser->current_scope; + bool conflict = false; + + while (scope != NULL) { + if (scope->parameters & mask) { + if (scope->closed) { + if (conflict) { + return PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_CONFLICT; + } else { + return PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_PASS; + } + } + + conflict = true; + } + + if (scope->closed) break; + scope = scope->previous; + } + + return PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_FAIL; +} + +static void +pm_parser_scope_forwarding_block_check(pm_parser_t *parser, const pm_token_t * token) { + switch (pm_parser_scope_forwarding_param_check(parser, PM_SCOPE_PARAMETERS_FORWARDING_BLOCK)) { + case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_PASS: + // Pass. + break; + case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_CONFLICT: + pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_CONFLICT_AMPERSAND); + break; + case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_FAIL: + pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_NO_FORWARDING_AMPERSAND); + break; + } +} + +static void +pm_parser_scope_forwarding_positionals_check(pm_parser_t *parser, const pm_token_t * token) { + switch (pm_parser_scope_forwarding_param_check(parser, PM_SCOPE_PARAMETERS_FORWARDING_POSITIONALS)) { + case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_PASS: + // Pass. + break; + case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_CONFLICT: + pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_CONFLICT_STAR); + break; + case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_FAIL: + pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_NO_FORWARDING_STAR); + break; + } +} + +static void +pm_parser_scope_forwarding_all_check(pm_parser_t *parser, const pm_token_t *token) { + switch (pm_parser_scope_forwarding_param_check(parser, PM_SCOPE_PARAMETERS_FORWARDING_ALL)) { + case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_PASS: + // Pass. + break; + case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_CONFLICT: + // This shouldn't happen, because ... is not allowed in the + // declaration of blocks. If we get here, we assume we already have + // an error for this. + break; + case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_FAIL: + pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES); + break; + } +} + +static void +pm_parser_scope_forwarding_keywords_check(pm_parser_t *parser, const pm_token_t * token) { + switch (pm_parser_scope_forwarding_param_check(parser, PM_SCOPE_PARAMETERS_FORWARDING_KEYWORDS)) { + case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_PASS: + // Pass. + break; + case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_CONFLICT: + pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_CONFLICT_STAR_STAR); + break; + case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_FAIL: + pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_NO_FORWARDING_STAR_STAR); + break; + } +} + +/** + * 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 && ((parser->start_line >= 0) || (pm_newline_list_line(&parser->newline_list, local->location.start, parser->start_line) >= 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_parser_t *parser, 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; + + if (cast->ensure_clause != NULL) { + if (cast->rescue_clause != NULL) { + pm_node_t *vn = pm_check_value_expression(parser, UP(cast->rescue_clause)); + if (vn != NULL) return vn; + } + + if (cast->statements != NULL) { + pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements)); + if (vn != NULL) return vn; + } + + node = UP(cast->ensure_clause); + } else if (cast->rescue_clause != NULL) { + if (cast->statements == NULL) return NULL; + + pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements)); + if (vn == NULL) return NULL; + if (void_node == NULL) void_node = vn; + + for (pm_rescue_node_t *rescue_clause = cast->rescue_clause; rescue_clause != NULL; rescue_clause = rescue_clause->subsequent) { + pm_node_t *vn = pm_check_value_expression(parser, UP(rescue_clause->statements)); + if (vn == NULL) { + void_node = NULL; + break; + } + if (void_node == NULL) { + void_node = vn; + } + } + + if (cast->else_clause != NULL) { + node = UP(cast->else_clause); + } else { + return void_node; + } + } else { + node = UP(cast->statements); + } + + break; + } + case PM_ENSURE_NODE: { + pm_ensure_node_t *cast = (pm_ensure_node_t *) node; + node = UP(cast->statements); + break; + } + case PM_PARENTHESES_NODE: { + pm_parentheses_node_t *cast = (pm_parentheses_node_t *) node; + node = UP(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->subsequent == NULL) { + return NULL; + } + pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements)); + if (vn == NULL) { + return NULL; + } + if (void_node == NULL) { + void_node = vn; + } + node = cast->subsequent; + break; + } + case PM_UNLESS_NODE: { + pm_unless_node_t *cast = (pm_unless_node_t *) node; + if (cast->statements == NULL || cast->else_clause == NULL) { + return NULL; + } + pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements)); + if (vn == NULL) { + return NULL; + } + if (void_node == NULL) { + void_node = vn; + } + node = UP(cast->else_clause); + break; + } + case PM_ELSE_NODE: { + pm_else_node_t *cast = (pm_else_node_t *) node; + node = UP(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; + } + case PM_LOCAL_VARIABLE_WRITE_NODE: { + pm_local_variable_write_node_t *cast = (pm_local_variable_write_node_t *) node; + + pm_scope_t *scope = parser->current_scope; + for (uint32_t depth = 0; depth < cast->depth; depth++) scope = scope->previous; + + pm_locals_read(&scope->locals, cast->name); + return NULL; + } + 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(parser, 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, bool last_value) { + assert(node->body.size > 0); + const size_t size = node->body.size - (last_value ? 1 : 0); + for (size_t index = 0; index < size; 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: { + if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) return true; + + const pm_array_node_t *cast = (const pm_array_node_t *) node; + for (size_t index = 0; index < cast->elements.size; index++) { + if (!pm_conditional_predicate_warn_write_literal_p(cast->elements.nodes[index])) return false; + } + + return true; + } + case PM_HASH_NODE: { + if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) return true; + + const pm_hash_node_t *cast = (const pm_hash_node_t *) node; + for (size_t index = 0; index < cast->elements.size; index++) { + const pm_node_t *element = cast->elements.nodes[index]; + if (!PM_NODE_TYPE_P(element, PM_ASSOC_NODE)) return false; + + const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) element; + if (!pm_conditional_predicate_warn_write_literal_p(assoc->key) || !pm_conditional_predicate_warn_write_literal_p(assoc->value)) 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 ? PM_WARN_EQUAL_IN_CONDITIONAL_3_3 : 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 }; +} + +/** + * 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, UP(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, ptrdiff_t n) { + if (n <= 0) return 0; + + if (parser->encoding_changed) { + size_t width; + + if ((width = parser->encoding->alpha_char(b, n)) != 0) { + return width; + } else if (*b == '_') { + return 1; + } else if (*b >= 0x80) { + return parser->encoding->char_width(b, n); + } 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, n); + } +} + +/** + * 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, ptrdiff_t n) { + if (n <= 0) { + return 0; + } else 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, n); + } +} + +/** + * 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(const pm_parser_t *parser, const uint8_t *b, ptrdiff_t n) { + if (n <= 0) { + return 0; + } else if (parser->encoding_changed) { + size_t width; + + if ((width = parser->encoding->alnum_char(b, n)) != 0) { + return width; + } else if (*b == '_') { + return 1; + } else if (*b >= 0x80) { + return parser->encoding->char_width(b, n); + } else { + return 0; + } + } else { + return char_is_identifier_utf8(b, n); + } +} + +// 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_BRACKET_LEFT_RIGHT_EQUAL) || + ((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, bool newline); + +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_node_alloc(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_NODE_ALLOC(parser_, type_) (type_ *) pm_node_alloc(parser_, sizeof(type_)) +#define PM_NODE_INIT(parser_, type_, flags_, start_, end_) (pm_node_t) { \ + .type = (type_), \ + .flags = (flags_), \ + .node_id = ++(parser_)->node_id, \ + .location = { .start = (start_), .end = (end_) } \ +} + +#define PM_NODE_INIT_UNSET(parser_, type_, flags_) PM_NODE_INIT(parser_, type_, flags_, NULL, NULL) +#define PM_NODE_INIT_BASE(parser_, type_, flags_) PM_NODE_INIT(parser_, type_, flags_, (parser_)->start, (parser_)->start) +#define PM_NODE_INIT_TOKEN(parser_, type_, flags_, token_) PM_NODE_INIT(parser_, type_, flags_, PM_TOKEN_START(token_), PM_TOKEN_END(token_)) +#define PM_NODE_INIT_NODE(parser_, type_, flags_, node_) PM_NODE_INIT(parser_, type_, flags_, PM_NODE_START(node_), PM_NODE_END(node_)) + +#define PM_NODE_INIT_TOKENS(parser_, type_, flags_, left_, right_) PM_NODE_INIT(parser_, type_, flags_, PM_TOKEN_START(left_), PM_TOKEN_END(right_)) +#define PM_NODE_INIT_NODES(parser_, type_, flags_, left_, right_) PM_NODE_INIT(parser_, type_, flags_, PM_NODE_START(left_), PM_NODE_END(right_)) +#define PM_NODE_INIT_TOKEN_NODE(parser_, type_, flags_, token_, node_) PM_NODE_INIT(parser_, type_, flags_, PM_TOKEN_START(token_), PM_NODE_END(node_)) +#define PM_NODE_INIT_NODE_TOKEN(parser_, type_, flags_, node_, token_) PM_NODE_INIT(parser_, type_, flags_, PM_NODE_START(node_), PM_TOKEN_END(token_)) + +/** + * 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_NODE_ALLOC(parser, pm_missing_node_t); + + *node = (pm_missing_node_t) { + .base = PM_NODE_INIT(parser, PM_MISSING_NODE, 0, start, 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_NODE_ALLOC(parser, pm_alias_global_variable_node_t); + + *node = (pm_alias_global_variable_node_t) { + .base = PM_NODE_INIT_TOKEN_NODE(parser, PM_ALIAS_GLOBAL_VARIABLE_NODE, 0, keyword, old_name), + .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_NODE_ALLOC(parser, pm_alias_method_node_t); + + *node = (pm_alias_method_node_t) { + .base = PM_NODE_INIT_TOKEN_NODE(parser, PM_ALIAS_METHOD_NODE, 0, keyword, old_name), + .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_NODE_ALLOC(parser, pm_alternation_pattern_node_t); + + *node = (pm_alternation_pattern_node_t) { + .base = PM_NODE_INIT_NODES(parser, PM_ALTERNATION_PATTERN_NODE, 0, left, right), + .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_NODE_ALLOC(parser, pm_and_node_t); + + *node = (pm_and_node_t) { + .base = PM_NODE_INIT_NODES(parser, PM_AND_NODE, 0, left, right), + .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_NODE_ALLOC(parser, pm_arguments_node_t); + + *node = (pm_arguments_node_t) { + .base = PM_NODE_INIT_BASE(parser, PM_ARGUMENTS_NODE, 0), + .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; + } + + if (node->base.location.end < argument->location.end) { + node->base.location.end = argument->location.end; + } + + pm_node_list_append(&node->arguments, argument); + + if (PM_NODE_TYPE_P(argument, PM_SPLAT_NODE)) { + if (PM_NODE_FLAG_P(node, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_SPLAT)) { + pm_node_flag_set(UP(node), PM_ARGUMENTS_NODE_FLAGS_CONTAINS_MULTIPLE_SPLATS); + } else { + pm_node_flag_set(UP(node), PM_ARGUMENTS_NODE_FLAGS_CONTAINS_SPLAT); + } + } +} + +/** + * 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_NODE_ALLOC(parser, pm_array_node_t); + + *node = (pm_array_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_ARRAY_NODE, PM_NODE_FLAG_STATIC_LITERAL, opening), + .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening), + .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening), + .elements = { 0 } + }; + + return node; +} + +/** + * 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(UP(node), PM_NODE_FLAG_STATIC_LITERAL); + } + + if (PM_NODE_TYPE_P(element, PM_SPLAT_NODE)) { + pm_node_flag_set(UP(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_NODE_ALLOC(parser, pm_array_pattern_node_t); + + *node = (pm_array_pattern_node_t) { + .base = PM_NODE_INIT_NODES(parser, PM_ARRAY_PATTERN_NODE, 0, nodes->nodes[0], nodes->nodes[nodes->size - 1]), + .constant = NULL, + .rest = NULL, + .requireds = { 0 }, + .posts = { 0 }, + .opening_loc = { 0 }, + .closing_loc = { 0 } + }; + + // 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_NODE_ALLOC(parser, pm_array_pattern_node_t); + + *node = (pm_array_pattern_node_t) { + .base = PM_NODE_INIT_NODE(parser, PM_ARRAY_PATTERN_NODE, 0, rest), + .constant = NULL, + .rest = rest, + .requireds = { 0 }, + .posts = { 0 }, + .opening_loc = { 0 }, + .closing_loc = { 0 } + }; + + 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_NODE_ALLOC(parser, pm_array_pattern_node_t); + + *node = (pm_array_pattern_node_t) { + .base = PM_NODE_INIT_NODE_TOKEN(parser, PM_ARRAY_PATTERN_NODE, 0, constant, closing), + .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_NODE_ALLOC(parser, pm_array_pattern_node_t); + + *node = (pm_array_pattern_node_t) { + .base = PM_NODE_INIT_TOKENS(parser, PM_ARRAY_PATTERN_NODE, 0, opening, closing), + .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_NODE_ALLOC(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 ( + !PM_NODE_TYPE_P(key, PM_ARRAY_NODE) && !PM_NODE_TYPE_P(key, PM_HASH_NODE) && !PM_NODE_TYPE_P(key, PM_RANGE_NODE) && + 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) { + .base = PM_NODE_INIT(parser, PM_ASSOC_NODE, flags, key->location.start, 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_NODE_ALLOC(parser, pm_assoc_splat_node_t); + + *node = (pm_assoc_splat_node_t) { + .base = ( + (value == NULL) + ? PM_NODE_INIT_TOKEN(parser, PM_ASSOC_SPLAT_NODE, 0, operator) + : PM_NODE_INIT_TOKEN_NODE(parser, PM_ASSOC_SPLAT_NODE, 0, operator, value) + ), + .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_NODE_ALLOC(parser, pm_back_reference_read_node_t); + + *node = (pm_back_reference_read_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_BACK_REFERENCE_READ_NODE, 0, 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_NODE_ALLOC(parser, pm_begin_node_t); + + *node = (pm_begin_node_t) { + .base = ( + (statements == NULL) + ? PM_NODE_INIT_TOKEN(parser, PM_BEGIN_NODE, 0, begin_keyword) + : PM_NODE_INIT_TOKEN_NODE(parser, PM_BEGIN_NODE, 0, begin_keyword, statements) + ), + .begin_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(begin_keyword), + .statements = statements, + .end_keyword_loc = { 0 } + }; + + 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_NODE_ALLOC(parser, pm_block_argument_node_t); + + *node = (pm_block_argument_node_t) { + .base = ( + (expression == NULL) + ? PM_NODE_INIT_TOKEN(parser, PM_BLOCK_ARGUMENT_NODE, 0, operator) + : PM_NODE_INIT_TOKEN_NODE(parser, PM_BLOCK_ARGUMENT_NODE, 0, operator, expression) + ), + .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_NODE_ALLOC(parser, pm_block_node_t); + + *node = (pm_block_node_t) { + .base = PM_NODE_INIT_TOKENS(parser, PM_BLOCK_NODE, 0, opening, closing), + .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_NODE_ALLOC(parser, pm_block_parameter_node_t); + + *node = (pm_block_parameter_node_t) { + .base = ( + (name->type == PM_TOKEN_NOT_PROVIDED) + ? PM_NODE_INIT_TOKEN(parser, PM_BLOCK_PARAMETER_NODE, 0, operator) + : PM_NODE_INIT_TOKENS(parser, PM_BLOCK_PARAMETER_NODE, 0, operator, name) + ), + .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_NODE_ALLOC(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) { + .base = PM_NODE_INIT(parser, PM_BLOCK_PARAMETERS_NODE, 0, start, end), + .parameters = parameters, + .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening), + .closing_loc = { 0 }, + .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_NODE_ALLOC(parser, pm_block_local_variable_node_t); + + *node = (pm_block_local_variable_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_BLOCK_LOCAL_VARIABLE_NODE, 0, 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, UP(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_NODE_ALLOC(parser, pm_break_node_t); + + *node = (pm_break_node_t) { + .base = ( + (arguments == NULL) + ? PM_NODE_INIT_TOKEN(parser, PM_BREAK_NODE, 0, keyword) + : PM_NODE_INIT_TOKEN_NODE(parser, PM_BREAK_NODE, 0, keyword, arguments) + ), + .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_WRITE_NODE_FLAGS_IMPLICIT_ARRAY = (1 << 2); + +static const pm_node_flags_t PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY = ((PM_CALL_NODE_FLAGS_LAST - 1) << 1); +static const pm_node_flags_t PM_CALL_NODE_FLAGS_COMPARISON = ((PM_CALL_NODE_FLAGS_LAST - 1) << 2); +static const pm_node_flags_t PM_CALL_NODE_FLAGS_INDEX = ((PM_CALL_NODE_FLAGS_LAST - 1) << 3); + +/** + * 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_NODE_ALLOC(parser, pm_call_node_t); + + *node = (pm_call_node_t) { + .base = PM_NODE_INIT_BASE(parser, PM_CALL_NODE, flags), + .receiver = NULL, + .call_operator_loc = { 0 }, + .message_loc = { 0 }, + .opening_loc = { 0 }, + .arguments = NULL, + .closing_loc = { 0 }, + .equal_loc = { 0 }, + .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; +} + +static const uint8_t * parse_operator_symbol_name(const pm_token_t *); + +/** + * 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(UP(node), PM_CALL_NODE_FLAGS_SAFE_NAVIGATION); + } + + /** + * If the final character is `@` as is the case for `foo.~@`, + * we should ignore the @ in the same way we do for symbols. + */ + node->name = pm_parser_constant_id_location(parser, message->start, parse_operator_symbol_name(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 = PM_LOCATION_NULL_VALUE(parser); + 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(UP(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, parser->end - 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_NODE_ALLOC(parser, pm_call_and_write_node_t); + + *node = (pm_call_and_write_node_t) { + .base = PM_NODE_INIT_NODES(parser, PM_CALL_AND_WRITE_NODE, FL(target), target, value), + .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; +} + +/** + * Validate that index expressions do not have keywords or blocks if we are + * parsing as Ruby 3.4+. + */ +static void +pm_index_arguments_check(pm_parser_t *parser, const pm_arguments_node_t *arguments, const pm_node_t *block) { + if (parser->version >= PM_OPTIONS_VERSION_CRUBY_3_4) { + if (arguments != NULL && PM_NODE_FLAG_P(arguments, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS)) { + pm_node_t *node; + PM_NODE_LIST_FOREACH(&arguments->arguments, index, node) { + if (PM_NODE_TYPE_P(node, PM_KEYWORD_HASH_NODE)) { + pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_INDEX_KEYWORDS); + break; + } + } + } + + if (block != NULL) { + pm_parser_err_node(parser, block, PM_ERR_UNEXPECTED_INDEX_BLOCK); + } + } +} + +/** + * 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_NODE_ALLOC(parser, pm_index_and_write_node_t); + + pm_index_arguments_check(parser, target->arguments, target->block); + + assert(!target->block || PM_NODE_TYPE_P(target->block, PM_BLOCK_ARGUMENT_NODE)); + *node = (pm_index_and_write_node_t) { + .base = PM_NODE_INIT_NODES(parser, PM_INDEX_AND_WRITE_NODE, FL(target), target, value), + .receiver = target->receiver, + .call_operator_loc = target->call_operator_loc, + .opening_loc = target->opening_loc, + .arguments = target->arguments, + .closing_loc = target->closing_loc, + .block = (pm_block_argument_node_t *) 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_NODE_ALLOC(parser, pm_call_operator_write_node_t); + + *node = (pm_call_operator_write_node_t) { + .base = PM_NODE_INIT_NODES(parser, PM_CALL_OPERATOR_WRITE_NODE, FL(target), target, value), + .receiver = target->receiver, + .call_operator_loc = target->call_operator_loc, + .message_loc = target->message_loc, + .read_name = 0, + .write_name = target->name, + .binary_operator = pm_parser_constant_id_location(parser, operator->start, operator->end - 1), + .binary_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_NODE_ALLOC(parser, pm_index_operator_write_node_t); + + pm_index_arguments_check(parser, target->arguments, target->block); + + assert(!target->block || PM_NODE_TYPE_P(target->block, PM_BLOCK_ARGUMENT_NODE)); + *node = (pm_index_operator_write_node_t) { + .base = PM_NODE_INIT_NODES(parser, PM_INDEX_OPERATOR_WRITE_NODE, FL(target), target, value), + .receiver = target->receiver, + .call_operator_loc = target->call_operator_loc, + .opening_loc = target->opening_loc, + .arguments = target->arguments, + .closing_loc = target->closing_loc, + .block = (pm_block_argument_node_t *) target->block, + .binary_operator = pm_parser_constant_id_location(parser, operator->start, operator->end - 1), + .binary_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_NODE_ALLOC(parser, pm_call_or_write_node_t); + + *node = (pm_call_or_write_node_t) { + .base = PM_NODE_INIT_NODES(parser, PM_CALL_OR_WRITE_NODE, FL(target), target, value), + .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_NODE_ALLOC(parser, pm_index_or_write_node_t); + + pm_index_arguments_check(parser, target->arguments, target->block); + + assert(!target->block || PM_NODE_TYPE_P(target->block, PM_BLOCK_ARGUMENT_NODE)); + *node = (pm_index_or_write_node_t) { + .base = PM_NODE_INIT_NODES(parser, PM_INDEX_OR_WRITE_NODE, FL(target), target, value), + .receiver = target->receiver, + .call_operator_loc = target->call_operator_loc, + .opening_loc = target->opening_loc, + .arguments = target->arguments, + .closing_loc = target->closing_loc, + .block = (pm_block_argument_node_t *) 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_NODE_ALLOC(parser, pm_call_target_node_t); + + *node = (pm_call_target_node_t) { + .base = PM_NODE_INIT_NODE(parser, PM_CALL_TARGET_NODE, FL(target), target), + .receiver = target->receiver, + .call_operator_loc = target->call_operator_loc, + .name = target->name, + .message_loc = target->message_loc + }; + + /* It is possible to get here where we have parsed an invalid syntax tree + * where the call operator was not present. In that case we will have a + * problem because it is a required location. In this case we need to fill + * it in with a fake location so that the syntax tree remains valid. */ + if (node->call_operator_loc.start == NULL) { + node->call_operator_loc = (pm_location_t) { + .start = target->base.location.start, + .end = target->base.location.start + }; + } + + // 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_NODE_ALLOC(parser, pm_index_target_node_t); + + pm_index_arguments_check(parser, target->arguments, target->block); + assert(!target->block || PM_NODE_TYPE_P(target->block, PM_BLOCK_ARGUMENT_NODE)); + + *node = (pm_index_target_node_t) { + .base = PM_NODE_INIT_NODE(parser, PM_INDEX_TARGET_NODE, FL(target) | PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE, target), + .receiver = target->receiver, + .opening_loc = target->opening_loc, + .arguments = target->arguments, + .closing_loc = target->closing_loc, + .block = (pm_block_argument_node_t *) 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_local_variable_target_node_t *target, const pm_token_t *operator) { + pm_capture_pattern_node_t *node = PM_NODE_ALLOC(parser, pm_capture_pattern_node_t); + + *node = (pm_capture_pattern_node_t) { + .base = PM_NODE_INIT_NODES(parser, PM_CAPTURE_PATTERN_NODE, 0, value, target), + .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_NODE_ALLOC(parser, pm_case_node_t); + + *node = (pm_case_node_t) { + .base = PM_NODE_INIT_TOKENS(parser, PM_CASE_NODE, 0, case_keyword, end_keyword), + .predicate = predicate, + .else_clause = 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 else clause of a CaseNode node. + */ +static void +pm_case_node_else_clause_set(pm_case_node_t *node, pm_else_node_t *else_clause) { + node->else_clause = else_clause; + node->base.location.end = else_clause->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_NODE_ALLOC(parser, pm_case_match_node_t); + + *node = (pm_case_match_node_t) { + .base = PM_NODE_INIT_TOKENS(parser, PM_CASE_MATCH_NODE, 0, case_keyword, end_keyword), + .predicate = predicate, + .else_clause = 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 else clause of a CaseMatchNode node. + */ +static void +pm_case_match_node_else_clause_set(pm_case_match_node_t *node, pm_else_node_t *else_clause) { + node->else_clause = else_clause; + node->base.location.end = else_clause->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_NODE_ALLOC(parser, pm_class_node_t); + + *node = (pm_class_node_t) { + .base = PM_NODE_INIT_TOKENS(parser, PM_CLASS_NODE, 0, class_keyword, end_keyword), + .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_NODE_ALLOC(parser, pm_class_variable_and_write_node_t); + + *node = (pm_class_variable_and_write_node_t) { + .base = PM_NODE_INIT_NODES(parser, PM_CLASS_VARIABLE_AND_WRITE_NODE, 0, target, value), + .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_NODE_ALLOC(parser, pm_class_variable_operator_write_node_t); + + *node = (pm_class_variable_operator_write_node_t) { + .base = PM_NODE_INIT_NODES(parser, PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE, 0, target, value), + .name = target->name, + .name_loc = target->base.location, + .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .value = value, + .binary_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_NODE_ALLOC(parser, pm_class_variable_or_write_node_t); + + *node = (pm_class_variable_or_write_node_t) { + .base = PM_NODE_INIT_NODES(parser, PM_CLASS_VARIABLE_OR_WRITE_NODE, 0, target, value), + .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_NODE_ALLOC(parser, pm_class_variable_read_node_t); + + *node = (pm_class_variable_read_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_CLASS_VARIABLE_READ_NODE, 0, token), + .name = pm_parser_constant_id_token(parser, token) + }; + + return node; +} + +/** + * True if the given node is an implicit array node on a write, as in: + * + * a = *b + * a = 1, 2, 3 + */ +static inline pm_node_flags_t +pm_implicit_array_write_flags(const pm_node_t *node, pm_node_flags_t flags) { + if (PM_NODE_TYPE_P(node, PM_ARRAY_NODE) && ((const pm_array_node_t *) node)->opening_loc.start == NULL) { + return flags; + } + return 0; +} + +/** + * 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_NODE_ALLOC(parser, pm_class_variable_write_node_t); + pm_node_flags_t flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY); + + *node = (pm_class_variable_write_node_t) { + .base = PM_NODE_INIT_NODES(parser, PM_CLASS_VARIABLE_WRITE_NODE, flags, read_node, value), + .name = read_node->name, + .name_loc = PM_LOCATION_NODE_VALUE(UP(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_NODE_ALLOC(parser, pm_constant_path_and_write_node_t); + + *node = (pm_constant_path_and_write_node_t) { + .base = PM_NODE_INIT_NODES(parser, PM_CONSTANT_PATH_AND_WRITE_NODE, 0, target, value), + .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_NODE_ALLOC(parser, pm_constant_path_operator_write_node_t); + + *node = (pm_constant_path_operator_write_node_t) { + .base = PM_NODE_INIT_NODES(parser, PM_CONSTANT_PATH_OPERATOR_WRITE_NODE, 0, target, value), + .target = target, + .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .value = value, + .binary_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_NODE_ALLOC(parser, pm_constant_path_or_write_node_t); + + *node = (pm_constant_path_or_write_node_t) { + .base = PM_NODE_INIT_NODES(parser, PM_CONSTANT_PATH_OR_WRITE_NODE, 0, target, value), + .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, const pm_token_t *name_token) { + pm_assert_value_expression(parser, parent); + pm_constant_path_node_t *node = PM_NODE_ALLOC(parser, pm_constant_path_node_t); + + pm_constant_id_t name = PM_CONSTANT_ID_UNSET; + if (name_token->type == PM_TOKEN_CONSTANT) { + name = pm_parser_constant_id_token(parser, name_token); + } + + if (parent == NULL) { + *node = (pm_constant_path_node_t) { + .base = PM_NODE_INIT_TOKENS(parser, PM_CONSTANT_PATH_NODE, 0, delimiter, name_token), + .parent = parent, + .name = name, + .delimiter_loc = PM_LOCATION_TOKEN_VALUE(delimiter), + .name_loc = PM_LOCATION_TOKEN_VALUE(name_token) + }; + } else { + *node = (pm_constant_path_node_t) { + .base = PM_NODE_INIT_NODE_TOKEN(parser, PM_CONSTANT_PATH_NODE, 0, parent, name_token), + .parent = parent, + .name = name, + .delimiter_loc = PM_LOCATION_TOKEN_VALUE(delimiter), + .name_loc = PM_LOCATION_TOKEN_VALUE(name_token) + }; + } + + 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_NODE_ALLOC(parser, pm_constant_path_write_node_t); + pm_node_flags_t flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY); + + *node = (pm_constant_path_write_node_t) { + .base = PM_NODE_INIT_NODES(parser, PM_CONSTANT_PATH_WRITE_NODE, flags, target, value), + .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_NODE_ALLOC(parser, pm_constant_and_write_node_t); + + *node = (pm_constant_and_write_node_t) { + .base = PM_NODE_INIT_NODES(parser, PM_CONSTANT_AND_WRITE_NODE, 0, target, value), + .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_NODE_ALLOC(parser, pm_constant_operator_write_node_t); + + *node = (pm_constant_operator_write_node_t) { + .base = PM_NODE_INIT_NODES(parser, PM_CONSTANT_OPERATOR_WRITE_NODE, 0, target, value), + .name = target->name, + .name_loc = target->base.location, + .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .value = value, + .binary_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_NODE_ALLOC(parser, pm_constant_or_write_node_t); + + *node = (pm_constant_or_write_node_t) { + .base = PM_NODE_INIT_NODES(parser, PM_CONSTANT_OR_WRITE_NODE, 0, target, value), + .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_NODE_ALLOC(parser, pm_constant_read_node_t); + + *node = (pm_constant_read_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_CONSTANT_READ_NODE, 0, 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_NODE_ALLOC(parser, pm_constant_write_node_t); + pm_node_flags_t flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY); + + *node = (pm_constant_write_node_t) { + .base = PM_NODE_INIT_NODES(parser, PM_CONSTANT_WRITE_NODE, flags, target, value), + .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, UP(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; + } +} + +/** + * 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_NODE_ALLOC(parser, pm_def_node_t); + + if (receiver != NULL) { + pm_def_node_receiver_check(parser, receiver); + } + + *node = (pm_def_node_t) { + .base = ( + (end_keyword->type == PM_TOKEN_NOT_PROVIDED) + ? PM_NODE_INIT_TOKEN_NODE(parser, PM_DEF_NODE, 0, def_keyword, body) + : PM_NODE_INIT_TOKENS(parser, PM_DEF_NODE, 0, def_keyword, end_keyword) + ), + .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_token_t *keyword) { + pm_defined_node_t *node = PM_NODE_ALLOC(parser, pm_defined_node_t); + + *node = (pm_defined_node_t) { + .base = ( + (rparen->type == PM_TOKEN_NOT_PROVIDED) + ? PM_NODE_INIT_TOKEN_NODE(parser, PM_DEFINED_NODE, 0, keyword, value) + : PM_NODE_INIT_TOKENS(parser, PM_DEFINED_NODE, 0, keyword, rparen) + ), + .lparen_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(lparen), + .value = value, + .rparen_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(rparen), + .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword) + }; + + 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_NODE_ALLOC(parser, pm_else_node_t); + + *node = (pm_else_node_t) { + .base = ( + ((end_keyword->type == PM_TOKEN_NOT_PROVIDED) && (statements != NULL)) + ? PM_NODE_INIT_TOKEN_NODE(parser, PM_ELSE_NODE, 0, else_keyword, statements) + : PM_NODE_INIT_TOKENS(parser, PM_ELSE_NODE, 0, else_keyword, end_keyword) + ), + .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_NODE_ALLOC(parser, pm_embedded_statements_node_t); + + *node = (pm_embedded_statements_node_t) { + .base = PM_NODE_INIT_TOKENS(parser, PM_EMBEDDED_STATEMENTS_NODE, 0, opening, closing), + .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_NODE_ALLOC(parser, pm_embedded_variable_node_t); + + *node = (pm_embedded_variable_node_t) { + .base = PM_NODE_INIT_TOKEN_NODE(parser, PM_EMBEDDED_VARIABLE_NODE, 0, operator, variable), + .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_NODE_ALLOC(parser, pm_ensure_node_t); + + *node = (pm_ensure_node_t) { + .base = PM_NODE_INIT_TOKENS(parser, PM_ENSURE_NODE, 0, ensure_keyword, end_keyword), + .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_NODE_ALLOC(parser, pm_false_node_t); + + *node = (pm_false_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_FALSE_NODE, PM_NODE_FLAG_STATIC_LITERAL, 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_NODE_ALLOC(parser, pm_find_pattern_node_t); + + pm_node_t *left = nodes->nodes[0]; + assert(PM_NODE_TYPE_P(left, PM_SPLAT_NODE)); + pm_splat_node_t *left_splat_node = (pm_splat_node_t *) left; + + pm_node_t *right; + + if (nodes->size == 1) { + right = UP(pm_missing_node_create(parser, left->location.end, left->location.end)); + } else { + right = nodes->nodes[nodes->size - 1]; + assert(PM_NODE_TYPE_P(right, PM_SPLAT_NODE)); + } + +#if PRISM_SERIALIZE_ONLY_SEMANTICS_FIELDS + // FindPatternNode#right is typed as SplatNode in this case, so replace the potential MissingNode with a SplatNode. + // The resulting AST will anyway be ignored, but this file still needs to compile. + pm_splat_node_t *right_splat_node = PM_NODE_TYPE_P(right, PM_SPLAT_NODE) ? (pm_splat_node_t *) right : left_splat_node; +#else + pm_node_t *right_splat_node = right; +#endif + *node = (pm_find_pattern_node_t) { + .base = PM_NODE_INIT_NODES(parser, PM_FIND_PATTERN_NODE, 0, left, right), + .constant = NULL, + .left = left_splat_node, + .right = right_splat_node, + .requireds = { 0 }, + .opening_loc = { 0 }, + .closing_loc = { 0 } + }; + + // 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 && PRISM_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_NODE_ALLOC(parser, pm_float_node_t); + + *node = (pm_float_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_FLOAT_NODE, PM_NODE_FLAG_STATIC_LITERAL, 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_NODE_ALLOC(parser, pm_imaginary_node_t); + *node = (pm_imaginary_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_IMAGINARY_NODE, PM_NODE_FLAG_STATIC_LITERAL, token), + .numeric = UP(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 RationalNode 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_NODE_ALLOC(parser, pm_rational_node_t); + *node = (pm_rational_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_RATIONAL_NODE, PM_INTEGER_BASE_FLAGS_DECIMAL | PM_NODE_FLAG_STATIC_LITERAL, token), + .numerator = { 0 }, + .denominator = { 0 } + }; + + const uint8_t *start = token->start; + const uint8_t *end = token->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); + if (length == 1) { + node->denominator.value = 1; + return node; + } + + const uint8_t *point = memchr(start, '.', length); + assert(point && "should have a decimal point"); + + uint8_t *digits = xmalloc(length); + if (digits == NULL) { + fputs("[pm_float_node_rational_create] Failed to allocate memory", stderr); + abort(); + } + + memcpy(digits, start, (unsigned long) (point - start)); + memcpy(digits + (point - start), point + 1, (unsigned long) (end - point - 1)); + pm_integer_parse(&node->numerator, PM_INTEGER_BASE_DEFAULT, digits, digits + length - 1); + + size_t fract_length = 0; + for (const uint8_t *fract = point; fract < end; ++fract) { + if (*fract != '_') ++fract_length; + } + digits[0] = '1'; + if (fract_length > 1) memset(digits + 1, '0', fract_length - 1); + pm_integer_parse(&node->denominator, PM_INTEGER_BASE_DEFAULT, digits, digits + fract_length); + xfree(digits); + + pm_integers_reduce(&node->numerator, &node->denominator); + 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_NODE_ALLOC(parser, pm_imaginary_node_t); + *node = (pm_imaginary_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_IMAGINARY_NODE, PM_NODE_FLAG_STATIC_LITERAL, token), + .numeric = UP(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_NODE_ALLOC(parser, pm_for_node_t); + + *node = (pm_for_node_t) { + .base = PM_NODE_INIT_TOKENS(parser, PM_FOR_NODE, 0, for_keyword, end_keyword), + .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_NODE_ALLOC(parser, pm_forwarding_arguments_node_t); + + *node = (pm_forwarding_arguments_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_FORWARDING_ARGUMENTS_NODE, 0, 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_NODE_ALLOC(parser, pm_forwarding_parameter_node_t); + + *node = (pm_forwarding_parameter_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_FORWARDING_PARAMETER_NODE, 0, 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_NODE_ALLOC(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) { + .base = ( + (block == NULL) + ? PM_NODE_INIT_TOKEN(parser, PM_FORWARDING_SUPER_NODE, 0, token) + : PM_NODE_INIT_TOKEN_NODE(parser, PM_FORWARDING_SUPER_NODE, 0, token, block) + ), + .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_NODE_ALLOC(parser, pm_hash_pattern_node_t); + + *node = (pm_hash_pattern_node_t) { + .base = PM_NODE_INIT_TOKENS(parser, PM_HASH_PATTERN_NODE, 0, opening, closing), + .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_NODE_ALLOC(parser, pm_hash_pattern_node_t); + + const uint8_t *start; + const uint8_t *end; + + if (elements->size > 0) { + if (rest) { + start = MIN(rest->location.start, elements->nodes[0]->location.start); + end = MAX(rest->location.end, elements->nodes[elements->size - 1]->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) { + .base = PM_NODE_INIT(parser, PM_HASH_PATTERN_NODE, 0, start, end), + .constant = NULL, + .elements = { 0 }, + .rest = rest, + .opening_loc = { 0 }, + .closing_loc = { 0 } + }; + + pm_node_list_concat(&node->elements, elements); + 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_NODE_ALLOC(parser, pm_global_variable_and_write_node_t); + + *node = (pm_global_variable_and_write_node_t) { + .base = PM_NODE_INIT_NODES(parser, PM_GLOBAL_VARIABLE_AND_WRITE_NODE, 0, target, value), + .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_NODE_ALLOC(parser, pm_global_variable_operator_write_node_t); + + *node = (pm_global_variable_operator_write_node_t) { + .base = PM_NODE_INIT_NODES(parser, PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE, 0, target, value), + .name = pm_global_variable_write_name(parser, target), + .name_loc = target->location, + .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .value = value, + .binary_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_NODE_ALLOC(parser, pm_global_variable_or_write_node_t); + + *node = (pm_global_variable_or_write_node_t) { + .base = PM_NODE_INIT_NODES(parser, PM_GLOBAL_VARIABLE_OR_WRITE_NODE, 0, target, value), + .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_NODE_ALLOC(parser, pm_global_variable_read_node_t); + + *node = (pm_global_variable_read_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_GLOBAL_VARIABLE_READ_NODE, 0, 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_NODE_ALLOC(parser, pm_global_variable_read_node_t); + + *node = (pm_global_variable_read_node_t) { + .base = PM_NODE_INIT_BASE(parser, PM_GLOBAL_VARIABLE_READ_NODE, 0), + .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_NODE_ALLOC(parser, pm_global_variable_write_node_t); + pm_node_flags_t flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY); + + *node = (pm_global_variable_write_node_t) { + .base = PM_NODE_INIT_NODES(parser, PM_GLOBAL_VARIABLE_WRITE_NODE, flags, target, value), + .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_NODE_ALLOC(parser, pm_global_variable_write_node_t); + + *node = (pm_global_variable_write_node_t) { + .base = PM_NODE_INIT_BASE(parser, PM_GLOBAL_VARIABLE_WRITE_NODE, 0), + .name = name, + .name_loc = PM_LOCATION_NULL_VALUE(parser), + .operator_loc = PM_LOCATION_NULL_VALUE(parser), + .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_NODE_ALLOC(parser, pm_hash_node_t); + + *node = (pm_hash_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_HASH_NODE, PM_NODE_FLAG_STATIC_LITERAL, 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(UP(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 *subsequent, + const pm_token_t *end_keyword +) { + pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL); + pm_if_node_t *node = PM_NODE_ALLOC(parser, pm_if_node_t); + + const uint8_t *end; + if (end_keyword->type != PM_TOKEN_NOT_PROVIDED) { + end = end_keyword->end; + } else if (subsequent != NULL) { + end = subsequent->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) { + .base = PM_NODE_INIT(parser, PM_IF_NODE, PM_NODE_FLAG_NEWLINE, if_keyword->start, end), + .if_keyword_loc = PM_LOCATION_TOKEN_VALUE(if_keyword), + .predicate = predicate, + .then_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(then_keyword), + .statements = statements, + .subsequent = subsequent, + .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_NODE_ALLOC(parser, pm_if_node_t); + + pm_statements_node_t *statements = pm_statements_node_create(parser); + pm_statements_node_body_append(parser, statements, statement, true); + + *node = (pm_if_node_t) { + .base = PM_NODE_INIT_NODES(parser, PM_IF_NODE, PM_NODE_FLAG_NEWLINE, statement, predicate), + .if_keyword_loc = PM_LOCATION_TOKEN_VALUE(if_keyword), + .predicate = predicate, + .then_keyword_loc = { 0 }, + .statements = statements, + .subsequent = NULL, + .end_keyword_loc = { 0 } + }; + + 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, true); + + pm_statements_node_t *else_statements = pm_statements_node_create(parser); + pm_statements_node_body_append(parser, else_statements, false_expression, true); + + 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_NODE_ALLOC(parser, pm_if_node_t); + + *node = (pm_if_node_t) { + .base = PM_NODE_INIT_NODES(parser, PM_IF_NODE, PM_NODE_FLAG_NEWLINE, predicate, false_expression), + .if_keyword_loc = { 0 }, + .predicate = predicate, + .then_keyword_loc = PM_LOCATION_TOKEN_VALUE(qmark), + .statements = if_statements, + .subsequent = UP(else_node), + .end_keyword_loc = { 0 } + }; + + 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_NODE_ALLOC(parser, pm_implicit_node_t); + + *node = (pm_implicit_node_t) { + .base = PM_NODE_INIT_NODE(parser, PM_IMPLICIT_NODE, 0, value), + .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_NODE_ALLOC(parser, pm_implicit_rest_node_t); + + *node = (pm_implicit_rest_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_IMPLICIT_REST_NODE, 0, 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_NODE_ALLOC(parser, pm_integer_node_t); + + *node = (pm_integer_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_INTEGER_NODE, base | PM_NODE_FLAG_STATIC_LITERAL, 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_NODE_ALLOC(parser, pm_imaginary_node_t); + *node = (pm_imaginary_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_IMAGINARY_NODE, PM_NODE_FLAG_STATIC_LITERAL, token), + .numeric = UP(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 RationalNode 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_NODE_ALLOC(parser, pm_rational_node_t); + *node = (pm_rational_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_RATIONAL_NODE, base | PM_NODE_FLAG_STATIC_LITERAL, token), + .numerator = { 0 }, + .denominator = { .value = 1, 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->numerator, integer_base, token->start, 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_NODE_ALLOC(parser, pm_imaginary_node_t); + *node = (pm_imaginary_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_IMAGINARY_NODE, PM_NODE_FLAG_STATIC_LITERAL, token), + .numeric = UP(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_NODE_ALLOC(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) { + .base = PM_NODE_INIT(parser, PM_IN_NODE, 0, in_keyword->start, 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_NODE_ALLOC(parser, pm_instance_variable_and_write_node_t); + + *node = (pm_instance_variable_and_write_node_t) { + .base = PM_NODE_INIT_NODES(parser, PM_INSTANCE_VARIABLE_AND_WRITE_NODE, 0, target, value), + .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_NODE_ALLOC(parser, pm_instance_variable_operator_write_node_t); + + *node = (pm_instance_variable_operator_write_node_t) { + .base = PM_NODE_INIT_NODES(parser, PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE, 0, target, value), + .name = target->name, + .name_loc = target->base.location, + .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .value = value, + .binary_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_NODE_ALLOC(parser, pm_instance_variable_or_write_node_t); + + *node = (pm_instance_variable_or_write_node_t) { + .base = PM_NODE_INIT_NODES(parser, PM_INSTANCE_VARIABLE_OR_WRITE_NODE, 0, target, value), + .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_NODE_ALLOC(parser, pm_instance_variable_read_node_t); + + *node = (pm_instance_variable_read_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_INSTANCE_VARIABLE_READ_NODE, 0, 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_NODE_ALLOC(parser, pm_instance_variable_write_node_t); + pm_node_flags_t flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY); + + *node = (pm_instance_variable_write_node_t) { + .base = PM_NODE_INIT_NODES(parser, PM_INSTANCE_VARIABLE_WRITE_NODE, flags, read_node, value), + .name = read_node->name, + .name_loc = PM_LOCATION_NODE_VALUE(read_node), + .operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator), + .value = value + }; + + return node; +} + +/** + * Append a part into a list of string parts. Importantly this handles nested + * interpolated strings by not necessarily removing the marker for static + * literals. + */ +static void +pm_interpolated_node_append(pm_node_t *node, pm_node_list_t *parts, pm_node_t *part) { + switch (PM_NODE_TYPE(part)) { + case PM_STRING_NODE: + pm_node_flag_set(part, PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN); + break; + case PM_EMBEDDED_STATEMENTS_NODE: { + pm_embedded_statements_node_t *cast = (pm_embedded_statements_node_t *) part; + pm_node_t *embedded = (cast->statements != NULL && cast->statements->body.size == 1) ? cast->statements->body.nodes[0] : NULL; + + if (embedded == NULL) { + // If there are no statements or more than one statement, then + // we lose the static literal flag. + pm_node_flag_unset(node, PM_NODE_FLAG_STATIC_LITERAL); + } else if (PM_NODE_TYPE_P(embedded, PM_STRING_NODE)) { + // If the embedded statement is a string, then we can keep the + // static literal flag and mark the string as frozen. + pm_node_flag_set(embedded, PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN); + } else if (PM_NODE_TYPE_P(embedded, PM_INTERPOLATED_STRING_NODE) && PM_NODE_FLAG_P(embedded, PM_NODE_FLAG_STATIC_LITERAL)) { + // If the embedded statement is an interpolated string and it's + // a static literal, then we can keep the static literal flag. + } else { + // Otherwise we lose the static literal flag. + pm_node_flag_unset(node, PM_NODE_FLAG_STATIC_LITERAL); + } + + break; + } + case PM_EMBEDDED_VARIABLE_NODE: + pm_node_flag_unset(UP(node), PM_NODE_FLAG_STATIC_LITERAL); + break; + default: + assert(false && "unexpected node type"); + break; + } + + pm_node_list_append(parts, part); +} + +/** + * 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_NODE_ALLOC(parser, pm_interpolated_regular_expression_node_t); + + *node = (pm_interpolated_regular_expression_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_INTERPOLATED_REGULAR_EXPRESSION_NODE, PM_NODE_FLAG_STATIC_LITERAL, opening), + .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; + } + + pm_interpolated_node_append(UP(node), &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(UP(node), pm_regular_expression_flags_create(parser, closing)); +} + +/** + * Append a part to an InterpolatedStringNode node. + * + * This has some somewhat complicated semantics, because we need to update + * multiple flags that have somewhat confusing interactions. + * + * PM_NODE_FLAG_STATIC_LITERAL indicates that the node should be treated as a + * single static literal string that can be pushed onto the stack on its own. + * Note that this doesn't necessarily mean that the string will be frozen or + * not; the instructions in CRuby will be either putobject or putstring, + * depending on the combination of `--enable-frozen-string-literal`, + * `# frozen_string_literal: true`, and whether or not there is interpolation. + * + * PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN indicates that the string should be + * explicitly frozen. This will only happen if the string is comprised entirely + * of string parts that are themselves static literals and frozen. + * + * PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE indicates that the string should + * be explicitly marked as mutable. This will happen from + * `--disable-frozen-string-literal` or `# frozen_string_literal: false`. This + * is necessary to indicate that the string should be left up to the runtime, + * which could potentially use a chilled string otherwise. + */ +static inline void +pm_interpolated_string_node_append(pm_interpolated_string_node_t *node, pm_node_t *part) { +#define CLEAR_FLAGS(node) \ + node->base.flags = (pm_node_flags_t) (FL(node) & ~(PM_NODE_FLAG_STATIC_LITERAL | PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN | PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE)) + +#define MUTABLE_FLAGS(node) \ + node->base.flags = (pm_node_flags_t) ((FL(node) | PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE) & ~PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN); + + if (node->parts.size == 0 && node->opening_loc.start == NULL) { + node->base.location.start = part->location.start; + } + + node->base.location.end = MAX(node->base.location.end, part->location.end); + + switch (PM_NODE_TYPE(part)) { + case PM_STRING_NODE: + // If inner string is not frozen, it stops being a static literal. We should *not* clear other flags, + // because concatenating two frozen strings (`'foo' 'bar'`) is still frozen. This holds true for + // as long as this interpolation only consists of other string literals. + if (!PM_NODE_FLAG_P(part, PM_STRING_FLAGS_FROZEN)) { + pm_node_flag_unset(UP(node), PM_NODE_FLAG_STATIC_LITERAL); + } + part->flags = (pm_node_flags_t) ((part->flags | PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN) & ~PM_STRING_FLAGS_MUTABLE); + break; + case PM_INTERPOLATED_STRING_NODE: + if (PM_NODE_FLAG_P(part, PM_NODE_FLAG_STATIC_LITERAL)) { + // If the string that we're concatenating is a static literal, + // then we can keep the static literal flag for this string. + } else { + // Otherwise, we lose the static literal flag here and we should + // also clear the mutability flags. + CLEAR_FLAGS(node); + } + break; + case PM_EMBEDDED_STATEMENTS_NODE: { + pm_embedded_statements_node_t *cast = (pm_embedded_statements_node_t *) part; + pm_node_t *embedded = (cast->statements != NULL && cast->statements->body.size == 1) ? cast->statements->body.nodes[0] : NULL; + + if (embedded == NULL) { + // If we're embedding multiple statements or no statements, then + // the string is not longer a static literal. + CLEAR_FLAGS(node); + } else if (PM_NODE_TYPE_P(embedded, PM_STRING_NODE)) { + // If the embedded statement is a string, then we can make that + // string as frozen and static literal, and not touch the static + // literal status of this string. + embedded->flags = (pm_node_flags_t) ((embedded->flags | PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN) & ~PM_STRING_FLAGS_MUTABLE); + + if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) { + MUTABLE_FLAGS(node); + } + } else if (PM_NODE_TYPE_P(embedded, PM_INTERPOLATED_STRING_NODE) && PM_NODE_FLAG_P(embedded, PM_NODE_FLAG_STATIC_LITERAL)) { + // If the embedded statement is an interpolated string, but that + // string is marked as static literal, then we can keep our + // static literal status for this string. + if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) { + MUTABLE_FLAGS(node); + } + } else { + // In all other cases, we lose the static literal flag here and + // become mutable. + CLEAR_FLAGS(node); + } + + break; + } + case PM_EMBEDDED_VARIABLE_NODE: + // Embedded variables clear static literal, which means we also + // should clear the mutability flags. + CLEAR_FLAGS(node); + break; + case PM_X_STRING_NODE: + case PM_INTERPOLATED_X_STRING_NODE: + case PM_SYMBOL_NODE: + case PM_INTERPOLATED_SYMBOL_NODE: + // These will only happen in error cases. But we want to handle it + // here so that we don't fail the assertion. + CLEAR_FLAGS(node); + break; + default: + assert(false && "unexpected node type"); + break; + } + + pm_node_list_append(&node->parts, part); + +#undef CLEAR_FLAGS +#undef MUTABLE_FLAGS +} + +/** + * 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_NODE_ALLOC(parser, pm_interpolated_string_node_t); + pm_node_flags_t flags = PM_NODE_FLAG_STATIC_LITERAL; + + switch (parser->frozen_string_literal) { + case PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED: + flags |= PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE; + break; + case PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED: + flags |= PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN; + break; + } + + *node = (pm_interpolated_string_node_t) { + .base = PM_NODE_INIT_TOKENS(parser, PM_INTERPOLATED_STRING_NODE, flags, opening, closing), + .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(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; + } + + pm_interpolated_node_append(UP(node), &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_NODE_ALLOC(parser, pm_interpolated_symbol_node_t); + + *node = (pm_interpolated_symbol_node_t) { + .base = PM_NODE_INIT_TOKENS(parser, PM_INTERPOLATED_SYMBOL_NODE, PM_NODE_FLAG_STATIC_LITERAL, opening, closing), + .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_NODE_ALLOC(parser, pm_interpolated_x_string_node_t); + + *node = (pm_interpolated_x_string_node_t) { + .base = PM_NODE_INIT_TOKENS(parser, PM_INTERPOLATED_X_STRING_NODE, 0, opening, closing), + .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) { + pm_interpolated_node_append(UP(node), &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; +} + +/** + * Create a local variable read that is reading the implicit 'it' variable. + */ +static pm_it_local_variable_read_node_t * +pm_it_local_variable_read_node_create(pm_parser_t *parser, const pm_token_t *name) { + pm_it_local_variable_read_node_t *node = PM_NODE_ALLOC(parser, pm_it_local_variable_read_node_t); + + *node = (pm_it_local_variable_read_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_IT_LOCAL_VARIABLE_READ_NODE, 0, name), + }; + + return node; +} + +/** + * 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_NODE_ALLOC(parser, pm_it_parameters_node_t); + + *node = (pm_it_parameters_node_t) { + .base = PM_NODE_INIT_TOKENS(parser, PM_IT_PARAMETERS_NODE, 0, opening, closing), + }; + + 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_NODE_ALLOC(parser, pm_keyword_hash_node_t); + + *node = (pm_keyword_hash_node_t) { + .base = PM_NODE_INIT_UNSET(parser, PM_KEYWORD_HASH_NODE, 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(UP(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_NODE_ALLOC(parser, pm_required_keyword_parameter_node_t); + + *node = (pm_required_keyword_parameter_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_REQUIRED_KEYWORD_PARAMETER_NODE, 0, name), + .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_NODE_ALLOC(parser, pm_optional_keyword_parameter_node_t); + + *node = (pm_optional_keyword_parameter_node_t) { + .base = PM_NODE_INIT_TOKEN_NODE(parser, PM_OPTIONAL_KEYWORD_PARAMETER_NODE, 0, name, value), + .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_NODE_ALLOC(parser, pm_keyword_rest_parameter_node_t); + + *node = (pm_keyword_rest_parameter_node_t) { + .base = ( + (name->type == PM_TOKEN_NOT_PROVIDED) + ? PM_NODE_INIT_TOKEN(parser, PM_KEYWORD_REST_PARAMETER_NODE, 0, operator) + : PM_NODE_INIT_TOKENS(parser, PM_KEYWORD_REST_PARAMETER_NODE, 0, operator, name) + ), + .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_NODE_ALLOC(parser, pm_lambda_node_t); + + *node = (pm_lambda_node_t) { + .base = PM_NODE_INIT_TOKENS(parser, PM_LAMBDA_NODE, 0, operator, closing), + .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_IT_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_NODE_ALLOC(parser, pm_local_variable_and_write_node_t); + + *node = (pm_local_variable_and_write_node_t) { + .base = PM_NODE_INIT_NODES(parser, PM_LOCAL_VARIABLE_AND_WRITE_NODE, 0, target, value), + .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_NODE_ALLOC(parser, pm_local_variable_operator_write_node_t); + + *node = (pm_local_variable_operator_write_node_t) { + .base = PM_NODE_INIT_NODES(parser, PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE, 0, target, value), + .name_loc = target->location, + .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator), + .value = value, + .name = name, + .binary_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_IT_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_NODE_ALLOC(parser, pm_local_variable_or_write_node_t); + + *node = (pm_local_variable_or_write_node_t) { + .base = PM_NODE_INIT_NODES(parser, PM_LOCAL_VARIABLE_OR_WRITE_NODE, 0, target, value), + .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_NODE_ALLOC(parser, pm_local_variable_read_node_t); + + *node = (pm_local_variable_read_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_LOCAL_VARIABLE_READ_NODE, 0, 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_NODE_ALLOC(parser, pm_local_variable_write_node_t); + pm_node_flags_t flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY); + + *node = (pm_local_variable_write_node_t) { + .base = PM_NODE_INIT_TOKEN_NODE(parser, PM_LOCAL_VARIABLE_WRITE_NODE, flags, name_loc, value), + .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 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_NODE_ALLOC(parser, pm_local_variable_target_node_t); + + *node = (pm_local_variable_target_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_LOCAL_VARIABLE_TARGET_NODE, 0, 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_NODE_ALLOC(parser, pm_match_predicate_node_t); + + *node = (pm_match_predicate_node_t) { + .base = PM_NODE_INIT_NODES(parser, PM_MATCH_PREDICATE_NODE, 0, value, pattern), + .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_NODE_ALLOC(parser, pm_match_required_node_t); + + *node = (pm_match_required_node_t) { + .base = PM_NODE_INIT_NODES(parser, PM_MATCH_REQUIRED_NODE, 0, value, pattern), + .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_NODE_ALLOC(parser, pm_match_write_node_t); + + *node = (pm_match_write_node_t) { + .base = PM_NODE_INIT_NODE(parser, PM_MATCH_WRITE_NODE, 0, call), + .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_NODE_ALLOC(parser, pm_module_node_t); + + *node = (pm_module_node_t) { + .base = PM_NODE_INIT_TOKENS(parser, PM_MODULE_NODE, 0, module_keyword, end_keyword), + .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_NODE_ALLOC(parser, pm_multi_target_node_t); + + *node = (pm_multi_target_node_t) { + .base = PM_NODE_INIT_UNSET(parser, PM_MULTI_TARGET_NODE, 0), + .lefts = { 0 }, + .rest = NULL, + .rights = { 0 }, + .lparen_loc = { 0 }, + .rparen_loc = { 0 } + }; + + 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_NODE_ALLOC(parser, pm_multi_write_node_t); + pm_node_flags_t flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY); + + *node = (pm_multi_write_node_t) { + .base = PM_NODE_INIT_NODES(parser, PM_MULTI_WRITE_NODE, flags, target, value), + .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_NODE_ALLOC(parser, pm_next_node_t); + + *node = (pm_next_node_t) { + .base = ( + (arguments == NULL) + ? PM_NODE_INIT_TOKEN(parser, PM_NEXT_NODE, 0, keyword) + : PM_NODE_INIT_TOKEN_NODE(parser, PM_NEXT_NODE, 0, keyword, arguments) + ), + .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_NODE_ALLOC(parser, pm_nil_node_t); + + *node = (pm_nil_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_NIL_NODE, PM_NODE_FLAG_STATIC_LITERAL, 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_NODE_ALLOC(parser, pm_no_keywords_parameter_node_t); + + *node = (pm_no_keywords_parameter_node_t) { + .base = PM_NODE_INIT_TOKENS(parser, PM_NO_KEYWORDS_PARAMETER_NODE, 0, operator, keyword), + .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_NODE_ALLOC(parser, pm_numbered_parameters_node_t); + + *node = (pm_numbered_parameters_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_NUMBERED_PARAMETERS_NODE, 0, 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); +#if PTRDIFF_MAX > SIZE_MAX + assert(diff < (ptrdiff_t) SIZE_MAX); +#endif + 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')) { + pm_parser_err(parser, start, end, PM_ERR_INVALID_NUMBER_DECIMAL); + value = 0; + } + + xfree(digits); + + if ((errno == ERANGE) || (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_NODE_ALLOC(parser, pm_numbered_reference_read_node_t); + + *node = (pm_numbered_reference_read_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_NUMBERED_REFERENCE_READ_NODE, 0, 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_NODE_ALLOC(parser, pm_optional_parameter_node_t); + + *node = (pm_optional_parameter_node_t) { + .base = PM_NODE_INIT_TOKEN_NODE(parser, PM_OPTIONAL_PARAMETER_NODE, 0, name, value), + .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_NODE_ALLOC(parser, pm_or_node_t); + + *node = (pm_or_node_t) { + .base = PM_NODE_INIT_NODES(parser, PM_OR_NODE, 0, left, right), + .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_NODE_ALLOC(parser, pm_parameters_node_t); + + *node = (pm_parameters_node_t) { + .base = PM_NODE_INIT_UNSET(parser, PM_PARAMETERS_NODE, 0), + .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(¶ms->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, UP(param)); + pm_node_list_append(¶ms->optionals, UP(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(¶ms->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(¶ms->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, UP(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_NODE_ALLOC(parser, pm_program_node_t); + + *node = (pm_program_node_t) { + .base = PM_NODE_INIT_NODE(parser, PM_PROGRAM_NODE, 0, statements), + .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_node_flags_t flags) { + pm_parentheses_node_t *node = PM_NODE_ALLOC(parser, pm_parentheses_node_t); + + *node = (pm_parentheses_node_t) { + .base = PM_NODE_INIT_TOKENS(parser, PM_PARENTHESES_NODE, flags, opening, closing), + .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_NODE_ALLOC(parser, pm_pinned_expression_node_t); + + *node = (pm_pinned_expression_node_t) { + .base = PM_NODE_INIT_TOKENS(parser, PM_PINNED_EXPRESSION_NODE, 0, operator, rparen), + .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_NODE_ALLOC(parser, pm_pinned_variable_node_t); + + *node = (pm_pinned_variable_node_t) { + .base = PM_NODE_INIT_TOKEN_NODE(parser, PM_PINNED_VARIABLE_NODE, 0, operator, variable), + .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_NODE_ALLOC(parser, pm_post_execution_node_t); + + *node = (pm_post_execution_node_t) { + .base = PM_NODE_INIT_TOKENS(parser, PM_POST_EXECUTION_NODE, 0, keyword, closing), + .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_NODE_ALLOC(parser, pm_pre_execution_node_t); + + *node = (pm_pre_execution_node_t) { + .base = PM_NODE_INIT_TOKENS(parser, PM_PRE_EXECUTION_NODE, 0, keyword, closing), + .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_NODE_ALLOC(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) { + .base = PM_NODE_INIT(parser, PM_RANGE_NODE, flags, (left == NULL ? operator->start : left->location.start), (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_NODE_ALLOC(parser, pm_redo_node_t); + + *node = (pm_redo_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_REDO_NODE, 0, 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_NODE_ALLOC(parser, pm_regular_expression_node_t); + pm_node_flags_t flags = pm_regular_expression_flags_create(parser, closing) | PM_NODE_FLAG_STATIC_LITERAL; + + *node = (pm_regular_expression_node_t) { + .base = PM_NODE_INIT_TOKENS(parser, PM_REGULAR_EXPRESSION_NODE, flags, opening, closing), + .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_NODE_ALLOC(parser, pm_required_parameter_node_t); + + *node = (pm_required_parameter_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_REQUIRED_PARAMETER_NODE, 0, 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_NODE_ALLOC(parser, pm_rescue_modifier_node_t); + + *node = (pm_rescue_modifier_node_t) { + .base = PM_NODE_INIT_NODES(parser, PM_RESCUE_MODIFIER_NODE, 0, expression, rescue_expression), + .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_NODE_ALLOC(parser, pm_rescue_node_t); + + *node = (pm_rescue_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_RESCUE_NODE, 0, keyword), + .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), + .operator_loc = { 0 }, + .then_keyword_loc = { 0 }, + .reference = NULL, + .statements = NULL, + .subsequent = 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 subsequent of a rescue node, and update the location. + */ +static void +pm_rescue_node_subsequent_set(pm_rescue_node_t *node, pm_rescue_node_t *subsequent) { + node->subsequent = subsequent; + node->base.location.end = subsequent->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_NODE_ALLOC(parser, pm_rest_parameter_node_t); + + *node = (pm_rest_parameter_node_t) { + .base = ( + (name->type == PM_TOKEN_NOT_PROVIDED) + ? PM_NODE_INIT_TOKEN(parser, PM_REST_PARAMETER_NODE, 0, operator) + : PM_NODE_INIT_TOKENS(parser, PM_REST_PARAMETER_NODE, 0, operator, name) + ), + .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_NODE_ALLOC(parser, pm_retry_node_t); + + *node = (pm_retry_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_RETRY_NODE, 0, 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_NODE_ALLOC(parser, pm_return_node_t); + + *node = (pm_return_node_t) { + .base = ( + (arguments == NULL) + ? PM_NODE_INIT_TOKEN(parser, PM_RETURN_NODE, 0, keyword) + : PM_NODE_INIT_TOKEN_NODE(parser, PM_RETURN_NODE, 0, keyword, arguments) + ), + .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_NODE_ALLOC(parser, pm_self_node_t); + + *node = (pm_self_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_SELF_NODE, 0, 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_NODE_ALLOC(parser, pm_shareable_constant_node_t); + + *node = (pm_shareable_constant_node_t) { + .base = PM_NODE_INIT_NODE(parser, PM_SHAREABLE_CONSTANT_NODE, (pm_node_flags_t) 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_NODE_ALLOC(parser, pm_singleton_class_node_t); + + *node = (pm_singleton_class_node_t) { + .base = PM_NODE_INIT_TOKENS(parser, PM_SINGLETON_CLASS_NODE, 0, class_keyword, end_keyword), + .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_NODE_ALLOC(parser, pm_source_encoding_node_t); + + *node = (pm_source_encoding_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_SOURCE_ENCODING_NODE, PM_NODE_FLAG_STATIC_LITERAL, 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_NODE_ALLOC(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) { + .base = PM_NODE_INIT_TOKEN(parser, PM_SOURCE_FILE_NODE, flags, 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_NODE_ALLOC(parser, pm_source_line_node_t); + + *node = (pm_source_line_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_SOURCE_LINE_NODE, PM_NODE_FLAG_STATIC_LITERAL, 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_NODE_ALLOC(parser, pm_splat_node_t); + + *node = (pm_splat_node_t) { + .base = ( + (expression == NULL) + ? PM_NODE_INIT_TOKEN(parser, PM_SPLAT_NODE, 0, operator) + : PM_NODE_INIT_TOKEN_NODE(parser, PM_SPLAT_NODE, 0, operator, expression) + ), + .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_NODE_ALLOC(parser, pm_statements_node_t); + + *node = (pm_statements_node_t) { + .base = PM_NODE_INIT_BASE(parser, PM_STATEMENTS_NODE, 0), + .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, bool newline) { + 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, statement, PM_WARN_UNREACHABLE_STATEMENT); + break; + default: + break; + } + } + + pm_node_list_append(&node->body, statement); + if (newline) 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_NODE_ALLOC(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; + } + + const uint8_t *start = (opening->type == PM_TOKEN_NOT_PROVIDED ? content->start : opening->start); + const uint8_t *end = (closing->type == PM_TOKEN_NOT_PROVIDED ? content->end : closing->end); + + *node = (pm_string_node_t) { + .base = PM_NODE_INIT(parser, PM_STRING_NODE, flags, start, 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_NODE_ALLOC(parser, pm_super_node_t); + + const uint8_t *end = pm_arguments_end(arguments); + if (end == NULL) { + assert(false && "unreachable"); + } + + *node = (pm_super_node_t) { + .base = PM_NODE_INIT(parser, PM_SUPER_NODE, 0, keyword->start, 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; +} + +/** + * Validate that the contents of the given symbol are all valid UTF-8. + */ +static void +parse_symbol_encoding_validate_utf8(pm_parser_t *parser, const pm_token_t *location, const pm_string_t *contents) { + for (const uint8_t *cursor = pm_string_source(contents), *end = cursor + pm_string_length(contents); cursor < end;) { + size_t width = pm_encoding_utf_8_char_width(cursor, end - cursor); + + if (width == 0) { + pm_parser_err(parser, location->start, location->end, PM_ERR_INVALID_SYMBOL); + break; + } + + cursor += width; + } +} + +/** + * Validate that the contents of the given symbol are all valid in the encoding + * of the parser. + */ +static void +parse_symbol_encoding_validate_other(pm_parser_t *parser, const pm_token_t *location, const pm_string_t *contents) { + const pm_encoding_t *encoding = parser->encoding; + + for (const uint8_t *cursor = pm_string_source(contents), *end = cursor + pm_string_length(contents); cursor < end;) { + size_t width = encoding->char_width(cursor, end - cursor); + + if (width == 0) { + pm_parser_err(parser, location->start, location->end, PM_ERR_INVALID_SYMBOL); + break; + } + + cursor += width; + } +} + +/** + * 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. + * + * If the validate flag is set, then it will check the contents of the symbol + * to ensure that all characters are valid in the encoding. + */ +static inline pm_node_flags_t +parse_symbol_encoding(pm_parser_t *parser, const pm_token_t *location, const pm_string_t *contents, bool validate) { + 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) { + if (validate) parse_symbol_encoding_validate_utf8(parser, location, contents); + 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 (validate) { + parse_symbol_encoding_validate_other(parser, location, contents); + } + } 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; + } else if (validate) { + parse_symbol_encoding_validate_other(parser, location, contents); + } + + 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_" 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_NODE_ALLOC(parser, pm_symbol_node_t); + + const uint8_t *start = (opening->type == PM_TOKEN_NOT_PROVIDED ? value->start : opening->start); + const uint8_t *end = (closing->type == PM_TOKEN_NOT_PROVIDED ? value->end : closing->end); + + *node = (pm_symbol_node_t) { + .base = PM_NODE_INIT(parser, PM_SYMBOL_NODE, PM_NODE_FLAG_STATIC_LITERAL | flags, start, 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, value, &parser->current_string, false)); + 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(UP(node), parse_symbol_encoding(parser, &label, &node->unescaped, false)); + + 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_NODE_ALLOC(parser, pm_symbol_node_t); + + *node = (pm_symbol_node_t) { + .base = PM_NODE_INIT_BASE(parser, PM_SYMBOL_NODE, PM_NODE_FLAG_STATIC_LITERAL | PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING), + .value_loc = PM_LOCATION_NULL_VALUE(parser), + .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_NODE_ALLOC(parser, pm_symbol_node_t); + + *new_node = (pm_symbol_node_t) { + .base = PM_NODE_INIT_TOKENS(parser, PM_SYMBOL_NODE, PM_NODE_FLAG_STATIC_LITERAL, opening, closing), + .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_token_t content = { .type = PM_TOKEN_IDENTIFIER, .start = node->content_loc.start, .end = node->content_loc.end }; + pm_node_flag_set(UP(new_node), parse_symbol_encoding(parser, &content, &node->unescaped, true)); + + // 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_NODE_ALLOC(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) { + .base = PM_NODE_INIT_NODE(parser, PM_STRING_NODE, flags, node), + .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_NODE_ALLOC(parser, pm_true_node_t); + + *node = (pm_true_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_TRUE_NODE, PM_NODE_FLAG_STATIC_LITERAL, 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_NODE_ALLOC(parser, pm_true_node_t); + + *node = (pm_true_node_t) { + .base = PM_NODE_INIT_BASE(parser, PM_TRUE_NODE, PM_NODE_FLAG_STATIC_LITERAL) + }; + + 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_NODE_ALLOC(parser, pm_undef_node_t); + + *node = (pm_undef_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_UNDEF_NODE, 0, 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_NODE_ALLOC(parser, pm_unless_node_t); + pm_node_t *end = statements == NULL ? predicate : UP(statements); + + *node = (pm_unless_node_t) { + .base = PM_NODE_INIT_TOKEN_NODE(parser, PM_UNLESS_NODE, PM_NODE_FLAG_NEWLINE, keyword, end), + .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), + .predicate = predicate, + .then_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(then_keyword), + .statements = statements, + .else_clause = NULL, + .end_keyword_loc = { 0 } + }; + + 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_NODE_ALLOC(parser, pm_unless_node_t); + + pm_statements_node_t *statements = pm_statements_node_create(parser); + pm_statements_node_body_append(parser, statements, statement, true); + + *node = (pm_unless_node_t) { + .base = PM_NODE_INIT_NODES(parser, PM_UNLESS_NODE, PM_NODE_FLAG_NEWLINE, statement, predicate), + .keyword_loc = PM_LOCATION_TOKEN_VALUE(unless_keyword), + .predicate = predicate, + .then_keyword_loc = { 0 }, + .statements = statements, + .else_clause = NULL, + .end_keyword_loc = { 0 } + }; + + 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; +} + +/** + * Loop modifiers could potentially modify an expression that contains block + * exits. In this case we need to loop through them and remove them from the + * list of block exits so that they do not later get marked as invalid. + */ +static void +pm_loop_modifier_block_exits(pm_parser_t *parser, pm_statements_node_t *statements) { + assert(parser->current_block_exits != NULL); + + // All of the block exits that we want to remove should be within the + // statements, and since we are modifying the statements, we shouldn't have + // to check the end location. + const uint8_t *start = statements->base.location.start; + + for (size_t index = parser->current_block_exits->size; index > 0; index--) { + pm_node_t *block_exit = parser->current_block_exits->nodes[index - 1]; + if (block_exit->location.start < start) break; + + // Implicitly remove from the list by lowering the size. + parser->current_block_exits->size--; + } +} + +/** + * 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 *do_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_NODE_ALLOC(parser, pm_until_node_t); + pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL); + + *node = (pm_until_node_t) { + .base = PM_NODE_INIT_TOKENS(parser, PM_UNTIL_NODE, flags, keyword, closing), + .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), + .do_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(do_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_NODE_ALLOC(parser, pm_until_node_t); + pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL); + pm_loop_modifier_block_exits(parser, statements); + + *node = (pm_until_node_t) { + .base = PM_NODE_INIT_NODES(parser, PM_UNTIL_NODE, flags, statements, predicate), + .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), + .do_keyword_loc = { 0 }, + .closing_loc = { 0 }, + .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_NODE_ALLOC(parser, pm_when_node_t); + + *node = (pm_when_node_t) { + .base = PM_NODE_INIT_TOKEN(parser, PM_WHEN_NODE, 0, keyword), + .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), + .statements = NULL, + .then_keyword_loc = { 0 }, + .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 *do_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_NODE_ALLOC(parser, pm_while_node_t); + pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL); + + *node = (pm_while_node_t) { + .base = PM_NODE_INIT_TOKENS(parser, PM_WHILE_NODE, flags, keyword, closing), + .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), + .do_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(do_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_NODE_ALLOC(parser, pm_while_node_t); + pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL); + pm_loop_modifier_block_exits(parser, statements); + + *node = (pm_while_node_t) { + .base = PM_NODE_INIT_NODES(parser, PM_WHILE_NODE, flags, statements, predicate), + .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), + .do_keyword_loc = { 0 }, + .closing_loc = { 0 }, + .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_NODE_ALLOC(parser, pm_while_node_t); + + *node = (pm_while_node_t) { + .base = PM_NODE_INIT_BASE(parser, PM_WHILE_NODE, 0), + .keyword_loc = PM_LOCATION_NULL_VALUE(parser), + .do_keyword_loc = PM_LOCATION_NULL_VALUE(parser), + .closing_loc = PM_LOCATION_NULL_VALUE(parser), + .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_NODE_ALLOC(parser, pm_x_string_node_t); + + *node = (pm_x_string_node_t) { + .base = PM_NODE_INIT_TOKENS(parser, PM_X_STRING_NODE, PM_STRING_FLAGS_FROZEN, opening, closing), + .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_NODE_ALLOC(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) { + .base = PM_NODE_INIT(parser, PM_YIELD_NODE, 0, keyword->start, end), + .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), + .lparen_loc = *lparen_loc, + .arguments = arguments, + .rparen_loc = *rparen_loc + }; + + return 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; +} + +/** + * 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); + pm_node_list_free(&scope->implicit_parameters); + 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(const 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(const 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); +} + +/** + * This is equivalent to the predicate of warn_balanced in CRuby. + */ +static inline bool +ambiguous_operator_p(const pm_parser_t *parser, bool space_seen) { + return !lex_state_p(parser, PM_LEX_STATE_CLASS | PM_LEX_STATE_DOT | PM_LEX_STATE_FNAME | PM_LEX_STATE_ENDFN) && space_seen && !pm_char_is_whitespace(peek(parser)); +} + +/** + * 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); + } +} + +typedef enum { + PM_MAGIC_COMMENT_BOOLEAN_VALUE_TRUE, + PM_MAGIC_COMMENT_BOOLEAN_VALUE_FALSE, + PM_MAGIC_COMMENT_BOOLEAN_VALUE_INVALID +} pm_magic_comment_boolean_value_t; + +/** + * Check if this is a magic comment that includes the frozen_string_literal + * pragma. If it does, set that field on the parser. + */ +static pm_magic_comment_boolean_value_t +parser_lex_magic_comment_boolean_value(const uint8_t *value_start, uint32_t value_length) { + if (value_length == 4 && pm_strncasecmp(value_start, (const uint8_t *) "true", 4) == 0) { + return PM_MAGIC_COMMENT_BOOLEAN_VALUE_TRUE; + } else if (value_length == 5 && pm_strncasecmp(value_start, (const uint8_t *) "false", 5) == 0) { + return PM_MAGIC_COMMENT_BOOLEAN_VALUE_FALSE; + } else { + return PM_MAGIC_COMMENT_BOOLEAN_VALUE_INVALID; + } +} + +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; + if (cursor < end && *cursor == '"') 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, '-', key_length, parser->encoding_changed, parser->encoding); + + if (dash == NULL) { + pm_string_shared_init(&key, key_start, key_end); + } else { + uint8_t *buffer = xmalloc(key_length); + if (buffer == NULL) break; + + memcpy(buffer, key_start, key_length); + 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, key_length); + } + + // 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); + uint32_t value_length = (uint32_t) (value_end - value_start); + + // 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 && !parser->encoding_locked) { + 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); + } + } + + if (key_length == 11) { + if (pm_strncasecmp(key_source, (const uint8_t *) "warn_indent", 11) == 0) { + switch (parser_lex_magic_comment_boolean_value(value_start, value_length)) { + case PM_MAGIC_COMMENT_BOOLEAN_VALUE_INVALID: + PM_PARSER_WARN_TOKEN_FORMAT( + parser, + parser->current, + PM_WARN_INVALID_MAGIC_COMMENT_VALUE, + (int) key_length, + (const char *) key_source, + (int) value_length, + (const char *) value_start + ); + break; + case PM_MAGIC_COMMENT_BOOLEAN_VALUE_FALSE: + parser->warn_mismatched_indentation = false; + break; + case PM_MAGIC_COMMENT_BOOLEAN_VALUE_TRUE: + parser->warn_mismatched_indentation = true; + break; + } + } + } else if (key_length == 21) { + if (pm_strncasecmp(key_source, (const uint8_t *) "frozen_string_literal", 21) == 0) { + // We only want to handle frozen string literal comments if it's + // before any semantic tokens have been seen. + if (semantic_token_seen) { + pm_parser_warn_token(parser, &parser->current, PM_WARN_IGNORED_FROZEN_STRING_LITERAL); + } else { + switch (parser_lex_magic_comment_boolean_value(value_start, value_length)) { + case PM_MAGIC_COMMENT_BOOLEAN_VALUE_INVALID: + PM_PARSER_WARN_TOKEN_FORMAT( + parser, + parser->current, + PM_WARN_INVALID_MAGIC_COMMENT_VALUE, + (int) key_length, + (const char *) key_source, + (int) value_length, + (const char *) value_start + ); + break; + case PM_MAGIC_COMMENT_BOOLEAN_VALUE_FALSE: + parser->frozen_string_literal = PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED; + break; + case PM_MAGIC_COMMENT_BOOLEAN_VALUE_TRUE: + parser->frozen_string_literal = PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED; + break; + } + } + } + } else if (key_length == 24) { + if (pm_strncasecmp(key_source, (const uint8_t *) "shareable_constant_value", 24) == 0) { + const uint8_t *cursor = parser->current.start; + while ((cursor > parser->start) && ((cursor[-1] == ' ') || (cursor[-1] == '\t'))) cursor--; + + if (!((cursor == parser->start) || (cursor[-1] == '\n'))) { + pm_parser_warn_token(parser, &parser->current, PM_WARN_SHAREABLE_CONSTANT_VALUE_LINE); + } else 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_MAGIC_COMMENT_VALUE, + (int) key_length, + (const char *) key_source, + (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 = value_length; + pm_list_append(&parser->magic_comment_list, (pm_list_node_t *) magic_comment); + } + } + + return result; +} + +/******************************************************************************/ +/* Context manipulations */ +/******************************************************************************/ + +static const uint32_t context_terminators[] = { + [PM_CONTEXT_NONE] = 0, + [PM_CONTEXT_BEGIN] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_BEGIN_ENSURE] = (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_BEGIN_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_BEGIN_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_BLOCK_BRACES] = (1U << PM_TOKEN_BRACE_RIGHT), + [PM_CONTEXT_BLOCK_KEYWORDS] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE), + [PM_CONTEXT_BLOCK_ENSURE] = (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_BLOCK_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_BLOCK_PARAMETERS] = (1U << PM_TOKEN_PIPE), + [PM_CONTEXT_BLOCK_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_CASE_WHEN] = (1U << PM_TOKEN_KEYWORD_WHEN) | (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_ELSE), + [PM_CONTEXT_CASE_IN] = (1U << PM_TOKEN_KEYWORD_IN) | (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_ELSE), + [PM_CONTEXT_CLASS] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE), + [PM_CONTEXT_CLASS_ENSURE] = (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_CLASS_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_CLASS_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_DEF] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE), + [PM_CONTEXT_DEF_ENSURE] = (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_DEF_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_DEF_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_DEF_PARAMS] = (1U << PM_TOKEN_EOF), + [PM_CONTEXT_DEFINED] = (1U << PM_TOKEN_EOF), + [PM_CONTEXT_DEFAULT_PARAMS] = (1U << PM_TOKEN_COMMA) | (1U << PM_TOKEN_PARENTHESIS_RIGHT), + [PM_CONTEXT_ELSE] = (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_ELSIF] = (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_ELSIF) | (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_EMBEXPR] = (1U << PM_TOKEN_EMBEXPR_END), + [PM_CONTEXT_FOR] = (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_FOR_INDEX] = (1U << PM_TOKEN_KEYWORD_IN), + [PM_CONTEXT_IF] = (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_ELSIF) | (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_LAMBDA_BRACES] = (1U << PM_TOKEN_BRACE_RIGHT), + [PM_CONTEXT_LAMBDA_DO_END] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE), + [PM_CONTEXT_LAMBDA_ENSURE] = (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_LAMBDA_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_LAMBDA_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_LOOP_PREDICATE] = (1U << PM_TOKEN_KEYWORD_DO) | (1U << PM_TOKEN_KEYWORD_THEN), + [PM_CONTEXT_MAIN] = (1U << PM_TOKEN_EOF), + [PM_CONTEXT_MODULE] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE), + [PM_CONTEXT_MODULE_ENSURE] = (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_MODULE_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_MODULE_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_MULTI_TARGET] = (1U << PM_TOKEN_EOF), + [PM_CONTEXT_PARENS] = (1U << PM_TOKEN_PARENTHESIS_RIGHT), + [PM_CONTEXT_POSTEXE] = (1U << PM_TOKEN_BRACE_RIGHT), + [PM_CONTEXT_PREDICATE] = (1U << PM_TOKEN_KEYWORD_THEN) | (1U << PM_TOKEN_NEWLINE) | (1U << PM_TOKEN_SEMICOLON), + [PM_CONTEXT_PREEXE] = (1U << PM_TOKEN_BRACE_RIGHT), + [PM_CONTEXT_RESCUE_MODIFIER] = (1U << PM_TOKEN_EOF), + [PM_CONTEXT_SCLASS] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE), + [PM_CONTEXT_SCLASS_ENSURE] = (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_SCLASS_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_SCLASS_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_TERNARY] = (1U << PM_TOKEN_EOF), + [PM_CONTEXT_UNLESS] = (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_UNTIL] = (1U << PM_TOKEN_KEYWORD_END), + [PM_CONTEXT_WHILE] = (1U << PM_TOKEN_KEYWORD_END), +}; + +static inline bool +context_terminator(pm_context_t context, pm_token_t *token) { + return token->type < 32 && (context_terminators[context] & (1U << token->type)); +} + +/** + * 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_BLOCK_PARAMETERS: return "'|'..'|' block parameter"; + 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_LOOP_PREDICATE: return "loop predicate"; + case PM_CONTEXT_MAIN: return "top level context"; + case PM_CONTEXT_MODULE: return "module definition"; + case PM_CONTEXT_MULTI_TARGET: return "multiple targets"; + 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 inline void +pm_strspn_number_validate(pm_parser_t *parser, const uint8_t *string, size_t length, const uint8_t *invalid) { + if (invalid != NULL) { + pm_diagnostic_id_t diag_id = (invalid == (string + length - 1)) ? PM_ERR_INVALID_NUMBER_UNDERSCORE_TRAILING : PM_ERR_INVALID_NUMBER_UNDERSCORE_INNER; + pm_parser_err(parser, invalid, invalid + 1, diag_id); + } +} + +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, string, length, 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, string, length, 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, string, length, 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, string, length, 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); + } + + // At this point we have a completed number, but we want to provide the user + // with a good experience if they put an additional .xxx fractional + // component on the end, so we'll check for that here. + if (peek_offset(parser, 0) == '.' && pm_char_is_decimal_digit(peek_offset(parser, 1))) { + const uint8_t *fraction_start = parser->current.end; + const uint8_t *fraction_end = parser->current.end + 2; + fraction_end += pm_strspn_decimal_digit(fraction_end, parser->end - fraction_end); + pm_parser_err(parser, fraction_start, fraction_end, PM_ERR_INVALID_NUMBER_FRACTION); + } + + 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; + } + + // True if multiple characters are allowed after the declaration of the + // global variable. Not true when it starts with "$-". + bool allow_multiple = true; + + 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 ((width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end)) > 0) { + do { + parser->current.end += width; + } while ((width = char_is_identifier(parser, parser->current.end, parser->end - 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 ? PM_ERR_INVALID_VARIABLE_GLOBAL_3_3 : 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++; + allow_multiple = false; + PRISM_FALLTHROUGH + default: { + size_t width; + + if ((width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end)) > 0) { + do { + parser->current.end += width; + } while (allow_multiple && (width = char_is_identifier(parser, parser->current.end, parser->end - 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 ? PM_ERR_INVALID_VARIABLE_GLOBAL_3_3 : PM_ERR_INVALID_VARIABLE_GLOBAL; + const uint8_t *end = parser->current.end + parser->encoding->char_width(parser->current.end, parser->end - parser->current.end); + PM_PARSER_ERR_FORMAT(parser, parser->current.start, end, diag_id, (int) (end - 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 ((width = char_is_identifier(parser, current_end, end - current_end)) > 0) { + current_end += width; + } + } else { + while ((width = char_is_identifier_utf8(current_end, end - current_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, parser->end - 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, parser->end - 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(pm_parser_t *parser, const uint8_t *string, size_t length, const pm_location_t *error_location) { + uint32_t value = 0; + for (size_t index = 0; index < length; index++) { + if (index != 0) value <<= 4; + value |= escape_hexadecimal_digit(string[index]); + } + + // Here we're going to verify that the value is actually a valid Unicode + // codepoint and not a surrogate pair. + if (value >= 0xD800 && value <= 0xDFFF) { + if (error_location != NULL) { + pm_parser_err(parser, error_location->start, error_location->end, PM_ERR_ESCAPE_INVALID_UNICODE); + } else { + pm_parser_err(parser, string, string + length, PM_ERR_ESCAPE_INVALID_UNICODE); + } + return 0xFFFD; + } + + 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 &= 0x9f; + 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 (!pm_buffer_append_unicode_codepoint(buffer, value)) { + 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); +} + +/** + * 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_format(regular_expression_buffer, "\\x%02X", byte); + } + + escape_write_byte_encoded(parser, 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, pm_buffer_t *regular_expression_buffer, uint8_t flags) { + 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); + } + + if (width == 1) { + escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(*parser->current.end++, flags)); + } else if (width > 1) { + // Valid multibyte character. Just ignore escape. + pm_buffer_t *b = (flags & PM_ESCAPE_FLAG_REGEXP) ? regular_expression_buffer : buffer; + pm_buffer_append_bytes(b, parser->current.end, width); + parser->current.end += width; + } else { + // Assume the next character wasn't meant to be part of this escape + // sequence since it is invalid. Add an error and move on. + parser->current.end++; + pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL); + } +} + +/** + * 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) { + uint8_t peeked = peek(parser); + switch (peeked) { + case '\\': { + parser->current.end++; + escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\\', flags)); + return; + } + case '\'': { + parser->current.end++; + escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\'', flags)); + return; + } + case 'a': { + parser->current.end++; + escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\a', flags)); + return; + } + case 'b': { + parser->current.end++; + escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\b', flags)); + return; + } + case 'e': { + parser->current.end++; + escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\033', flags)); + return; + } + case 'f': { + parser->current.end++; + escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\f', flags)); + return; + } + case 'n': { + parser->current.end++; + escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\n', flags)); + return; + } + case 'r': { + parser->current.end++; + escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\r', flags)); + return; + } + case 's': { + parser->current.end++; + escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(' ', flags)); + return; + } + case 't': { + parser->current.end++; + escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\t', flags)); + return; + } + case 'v': { + parser->current.end++; + escape_write_byte(parser, buffer, regular_expression_buffer, flags, 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++; + } + } + + value = escape_byte(value, flags); + escape_write_byte(parser, buffer, regular_expression_buffer, flags, 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++; + } + + value = escape_byte(value, flags); + if (flags & PM_ESCAPE_FLAG_REGEXP) { + if (flags & (PM_ESCAPE_FLAG_CONTROL | PM_ESCAPE_FLAG_META)) { + pm_buffer_append_format(regular_expression_buffer, "\\x%02X", value); + } else { + 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 == parser->end) { + const uint8_t *start = parser->current.end - 2; + PM_PARSER_ERR_FORMAT(parser, start, parser->current.end, PM_ERR_ESCAPE_INVALID_UNICODE_SHORT, 2, start); + } else if (peek(parser) == '{') { + const uint8_t *unicode_codepoints_start = parser->current.end - 2; + parser->current.end++; + + size_t whitespace; + while (true) { + if ((whitespace = pm_strspn_inline_whitespace(parser->current.end, parser->end - parser->current.end)) > 0) { + parser->current.end += whitespace; + } else if (peek(parser) == '\\' && peek_offset(parser, 1) == 'n') { + // This is super hacky, but it gets us nicer error + // messages because we can still pass it off to the + // regular expression engine even if we hit an + // unterminated regular expression. + parser->current.end += 2; + } else { + break; + } + } + + 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 + + if (flags & PM_ESCAPE_FLAG_REGEXP) { + // If this is a regular expression, we are going to + // let the regular expression engine handle this + // error instead of us. + pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start)); + } else { + pm_parser_err(parser, parser->current.end, parser->current.end, PM_ERR_ESCAPE_INVALID_UNICODE); + pm_parser_err(parser, parser->current.end, parser->current.end, PM_ERR_ESCAPE_INVALID_UNICODE_TERM); + } + + 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(parser, unicode_start, hexadecimal_length, NULL); + escape_write_unicode(parser, buffer, flags, unicode_start, parser->current.end, value); + + parser->current.end += pm_strspn_inline_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 (parser->current.end == parser->end) { + PM_PARSER_ERR_FORMAT(parser, start, parser->current.end, PM_ERR_ESCAPE_INVALID_UNICODE_LIST, (int) (parser->current.end - start), start); + } else if (peek(parser) == '}') { + parser->current.end++; + } else { + if (flags & PM_ESCAPE_FLAG_REGEXP) { + // If this is a regular expression, we are going to let + // the regular expression engine handle this error + // instead of us. + pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start)); + } 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 { + size_t length = pm_strspn_hexadecimal_digit(parser->current.end, MIN(parser->end - parser->current.end, 4)); + + if (length == 0) { + if (flags & PM_ESCAPE_FLAG_REGEXP) { + pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start)); + } else { + const uint8_t *start = parser->current.end - 2; + PM_PARSER_ERR_FORMAT(parser, start, parser->current.end, PM_ERR_ESCAPE_INVALID_UNICODE_SHORT, 2, start); + } + } else if (length == 4) { + uint32_t value = escape_unicode(parser, parser->current.end, 4, NULL); + + 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 { + parser->current.end += length; + + if (flags & PM_ESCAPE_FLAG_REGEXP) { + // If this is a regular expression, we are going to let + // the regular expression engine handle this error + // instead of us. + pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start)); + } else { + pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_UNICODE); + } + } + } + + return; + } + case 'c': { + parser->current.end++; + if (flags & PM_ESCAPE_FLAG_CONTROL) { + pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL_REPEAT); + } + + 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 '\\': + parser->current.end++; + + if (match(parser, 'u') || match(parser, 'U')) { + pm_parser_err(parser, parser->current.start, parser->current.end, PM_ERR_INVALID_ESCAPE_CHARACTER); + return; + } + + 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 (flags & PM_ESCAPE_FLAG_CONTROL) { + pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL_REPEAT); + } + + if (peek(parser) != '-') { + size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end); + pm_parser_err(parser, parser->current.start, parser->current.end + width, 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 '\\': + parser->current.end++; + + if (match(parser, 'u') || match(parser, 'U')) { + pm_parser_err(parser, parser->current.start, parser->current.end, PM_ERR_INVALID_ESCAPE_CHARACTER); + return; + } + + 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)) { + size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end); + pm_parser_err(parser, parser->current.start, parser->current.end + width, 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 (flags & PM_ESCAPE_FLAG_META) { + pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_META_REPEAT); + } + + if (peek(parser) != '-') { + size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end); + pm_parser_err(parser, parser->current.start, parser->current.end + width, 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 '\\': + parser->current.end++; + + if (match(parser, 'u') || match(parser, 'U')) { + pm_parser_err(parser, parser->current.start, parser->current.end, PM_ERR_INVALID_ESCAPE_CHARACTER); + return; + } + + 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)) { + size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end); + pm_parser_err(parser, parser->current.start, parser->current.end + width, 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; + } + PRISM_FALLTHROUGH + } + default: { + if ((flags & (PM_ESCAPE_FLAG_CONTROL | PM_ESCAPE_FLAG_META)) && !char_is_ascii_printable(peeked)) { + size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end); + pm_parser_err(parser, parser->current.start, parser->current.end + width, PM_ERR_ESCAPE_INVALID_META); + return; + } + if (parser->current.end < parser->end) { + escape_write_escape_encoded(parser, buffer, regular_expression_buffer, flags); + } else { + pm_parser_err_current(parser, PM_ERR_INVALID_ESCAPE_CHARACTER); + } + 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, parser->end - (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; + const uint8_t *end = parser->end; + + size_t width; + if ((width = char_is_identifier_start(parser, parser->current.end, end - parser->current.end)) > 0) { + parser->current.end += width; + + while ((width = char_is_identifier(parser, parser->current.end, end - parser->current.end)) > 0) { + parser->current.end += width; + } + } else if (parser->current.end < 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) { + diag_id = (type == PM_TOKEN_CLASS_VARIABLE) ? PM_ERR_INCOMPLETE_VARIABLE_CLASS_3_3 : PM_ERR_INCOMPLETE_VARIABLE_INSTANCE_3_3; + } + + size_t width = parser->encoding->char_width(parser->current.end, 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; + assert(end >= start); + 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); + } + + uint8_t delimiter = *parser->current.end; + + // If our delimiter is \r\n, we want to treat it as if it's \n. + // For example, %\r\nfoo\r\n should be "foo" + if (eol_length == 2) { + delimiter = *(parser->current.end + 1); + } + + 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) { + // If we hit EOF, but the EOF came immediately after a newline, + // set the start of the token to the newline. This way any EOF + // errors will be reported as happening on that line rather than + // a line after. For example "foo(\n" should report an error + // on line 1 even though EOF technically occurs on line 2. + if (parser->current.start > parser->start && (*(parser->current.start - 1) == '\n')) { + parser->current.start -= 1; + } + 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->encoding_locked) { + parser_lex_magic_comment_encoding(parser); + } + } + + lexed_comment = true; + } + PRISM_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; + } + PRISM_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, we will lex the + // ignored newline. + if (lex_state_ignored_p(parser)) { + if (!lexed_comment) parser_lex_ignored_newline(parser); + lexed_comment = false; + goto lex_next_token; + } + + // If we hit a '.' or a '&.' we will lex the ignored + // newline. + if (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 are parsing as CRuby 4.0 or later and we + // hit a '&&' or a '||' then we will lex the ignored + // newline. + if ( + (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_0) && + following && ( + (peek_at(parser, following) == '&' && peek_at(parser, following + 1) == '&') || + (peek_at(parser, following) == '|' && peek_at(parser, following + 1) == '|') || + (peek_at(parser, following) == 'a' && peek_at(parser, following + 1) == 'n' && peek_at(parser, following + 2) == 'd' && !char_is_identifier(parser, following + 3, parser->end - (following + 3))) || + (peek_at(parser, following) == 'o' && peek_at(parser, following + 1) == 'r' && !char_is_identifier(parser, following + 2, parser->end - (following + 2))) + ) + ) { + 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); + } + + if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_0) { + // If we hit an && then we are in a logical chain + // and we need to return the logical 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_BEG); + parser->current.start = next_content; + parser->current.end = next_content + 2; + parser->next_start = NULL; + LEX(PM_TOKEN_AMPERSAND_AMPERSAND); + } + + // If we hit a || then we are in a logical chain and + // we need to return the logical 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_BEG); + parser->current.start = next_content; + parser->current.end = next_content + 2; + parser->next_start = NULL; + LEX(PM_TOKEN_PIPE_PIPE); + } + + // If we hit an 'and' then we are in a logical chain + // and we need to return the logical operator. + if ( + peek_at(parser, next_content) == 'a' && + peek_at(parser, next_content + 1) == 'n' && + peek_at(parser, next_content + 2) == 'd' && + !char_is_identifier(parser, next_content + 3, parser->end - (next_content + 3)) + ) { + if (!lexed_comment) parser_lex_ignored_newline(parser); + lex_state_set(parser, PM_LEX_STATE_BEG); + parser->current.start = next_content; + parser->current.end = next_content + 3; + parser->next_start = NULL; + parser->command_start = true; + LEX(PM_TOKEN_KEYWORD_AND); + } + + // If we hit a 'or' then we are in a logical chain + // and we need to return the logical operator. + if ( + peek_at(parser, next_content) == 'o' && + peek_at(parser, next_content + 1) == 'r' && + !char_is_identifier(parser, next_content + 2, parser->end - (next_content + 2)) + ) { + if (!lexed_comment) parser_lex_ignored_newline(parser); + lex_state_set(parser, PM_LEX_STATE_BEG); + parser->current.start = next_content; + parser->current.end = next_content + 2; + parser->next_start = NULL; + parser->command_start = true; + LEX(PM_TOKEN_KEYWORD_OR); + } + } + } + + // 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 ',': + if ((parser->previous.type == PM_TOKEN_COMMA) && (parser->enclosure_nesting > 0)) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_ARRAY_TERM, pm_token_type_human(parser->current.type)); + } + + 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; + } else if (ambiguous_operator_p(parser, space_seen)) { + PM_PARSER_WARN_TOKEN_FORMAT(parser, parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "**", "argument prefix"); + } + + 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; + } else if (ambiguous_operator_p(parser, space_seen)) { + PM_PARSER_WARN_TOKEN_FORMAT(parser, parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "*", "argument prefix"); + } + + 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, parser->end - parser->current.end)) == 0) { + parser->current.end = end; + } else { + if (quote == PM_HEREDOC_QUOTE_NONE) { + parser->current.end += width; + + while ((width = char_is_identifier(parser, parser->current.end, parser->end - 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)) { + if (*parser->current.end == '\r' || *parser->current.end == '\n') break; + parser->current.end++; + } + } + + size_t ident_length = (size_t) (parser->current.end - ident_start); + bool ident_error = false; + + if (quote != PM_HEREDOC_QUOTE_NONE && !match(parser, (uint8_t) quote)) { + pm_parser_err(parser, ident_start, ident_start + ident_length, PM_ERR_HEREDOC_IDENTIFIER); + ident_error = true; + } + + parser->explicit_encoding = NULL; + lex_mode_push(parser, (pm_lex_mode_t) { + .mode = PM_LEX_HEREDOC, + .as.heredoc = { + .base = { + .ident_start = ident_start, + .ident_length = ident_length, + .quote = quote, + .indent = indent + }, + .next_start = parser->current.end, + .common_whitespace = NULL, + .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. + if (!ident_error) pm_parser_err_heredoc_term(parser, ident_start, ident_length); + 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 (ambiguous_operator_p(parser, space_seen)) { + PM_PARSER_WARN_TOKEN_FORMAT(parser, parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "<<", "here document"); + } + + 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)) { + if ((peek(parser) != ':') || (peek_offset(parser, 1) == '\0')) { + pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_PREFIX_AMPERSAND); + } else { + const uint8_t delim = peek_offset(parser, 1); + + if ((delim != '\'') && (delim != '"') && !char_is_identifier(parser, parser->current.end + 1, parser->end - (parser->current.end + 1))) { + 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; + } else if (ambiguous_operator_p(parser, space_seen)) { + PM_PARSER_WARN_TOKEN_FORMAT(parser, parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "&", "argument prefix"); + } + + 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); + } + + if (ambiguous_operator_p(parser, space_seen)) { + PM_PARSER_WARN_TOKEN_FORMAT(parser, parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "+", "unary operator"); + } + + 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); + } + + if (ambiguous_operator_p(parser, space_seen)) { + PM_PARSER_WARN_TOKEN_FORMAT(parser, parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "-", "unary operator"); + } + + 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 (ambiguous_operator_p(parser, space_seen)) { + PM_PARSER_WARN_TOKEN_FORMAT(parser, parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "/", "regexp literal"); + } + + 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_EOF); + 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)); + 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; + } + } + + if (ambiguous_operator_p(parser, space_seen)) { + PM_PARSER_WARN_TOKEN_FORMAT(parser, parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "%", "string literal"); + } + + 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, parser->end - 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; + } + PRISM_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; + } + PRISM_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) { + uint8_t term = lex_mode->as.regexp.terminator; + bool is_terminator = (*breakpoint == term); + + // If the terminator is newline, we need to consider \r\n _also_ a newline + // For example: `%\nfoo\r\n` + // The string should be "foo", not "foo\r" + if (*breakpoint == '\r' && peek_at(parser, breakpoint + 1) == '\n') { + if (term == '\n') { + is_terminator = true; + } + + // If the terminator is a CR, but we see a CRLF, we need to + // treat the CRLF as a newline, meaning this is _not_ the + // terminator + if (term == '\r') { + is_terminator = false; + } + } + + // If we hit the terminator, we need to determine what kind of + // token to return. + if (is_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; + + // Track the newline if we're not in a heredoc that + // would have already have added the newline to the + // list. + if (parser->heredoc_end == NULL) { + 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; + + PRISM_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; + } + PRISM_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; + } + + uint8_t term = lex_mode->as.string.terminator; + bool is_terminator = (*breakpoint == term); + + // If the terminator is newline, we need to consider \r\n _also_ a newline + // For example: `%r\nfoo\r\n` + // The string should be /foo/, not /foo\r/ + if (*breakpoint == '\r' && peek_at(parser, breakpoint + 1) == '\n') { + if (term == '\n') { + is_terminator = true; + } + + // If the terminator is a CR, but we see a CRLF, we need to + // treat the CRLF as a newline, meaning this is _not_ the + // terminator + if (term == '\r') { + is_terminator = false; + } + } + + // 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 (is_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; + + // Track the newline if we're not in a heredoc that + // would have already have added the newline to the + // list. + if (parser->heredoc_end == NULL) { + 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); + } + + // When the delimiter itself is a newline, we won't + // get a chance to flush heredocs in the usual places since + // the newline is already consumed. + if (term == '\n' && parser->heredoc_end) { + parser_flush_heredoc_end(parser); + } + + 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; + + PRISM_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; + } + PRISM_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; + pm_heredoc_lex_mode_t *heredoc_lex_mode = &lex_mode->as.heredoc.base; + + 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_heredoc_term(parser, heredoc_lex_mode->ident_start, heredoc_lex_mode->ident_length); + parser->next_start = lex_mode->as.heredoc.next_start; + parser->heredoc_end = parser->current.end; + lex_state_set(parser, PM_LEX_STATE_END); + lex_mode_pop(parser); + LEX(PM_TOKEN_HEREDOC_END); + } + + const uint8_t *ident_start = heredoc_lex_mode->ident_start; + size_t ident_length = heredoc_lex_mode->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 (current_token_starts_line(parser)) { + const uint8_t *start = parser->current.start; + + if (!line_continuation && (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 (heredoc_lex_mode->indent == PM_HEREDOC_INDENT_DASH || heredoc_lex_mode->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_mode_pop(parser); + LEX(PM_TOKEN_HEREDOC_END); + } + } + + size_t whitespace = pm_heredoc_strspn_inline_whitespace(parser, &start, heredoc_lex_mode->indent); + if ( + heredoc_lex_mode->indent == PM_HEREDOC_INDENT_TILDE && + lex_mode->as.heredoc.common_whitespace != NULL && + (*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 = heredoc_lex_mode->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; + + PRISM_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 (heredoc_lex_mode->indent == PM_HEREDOC_INDENT_DASH || heredoc_lex_mode->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.base.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.base.indent == PM_HEREDOC_INDENT_TILDE) { + if ((lex_mode->as.heredoc.common_whitespace != NULL) && (*lex_mode->as.heredoc.common_whitespace > whitespace) && peek_at(parser, start) != '\n') { + *lex_mode->as.heredoc.common_whitespace = whitespace; + } + + parser->current.end = breakpoint + 1; + 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; + } + PRISM_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; + } + PRISM_FALLTHROUGH + case '\n': + // If we are in a tilde here, we should + // break out of the loop and return the + // string content. + if (heredoc_lex_mode->indent == PM_HEREDOC_INDENT_TILDE) { + const uint8_t *end = parser->current.end; + + if (parser->heredoc_end == NULL) { + 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] = { PM_BINDING_POWER_MODIFIER_RESCUE, PM_BINDING_POWER_COMPOSITION, true, false }, + + // 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] = RIGHT_ASSOCIATIVE_UNARY(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 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; +} + +/** + * 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; +} + +/** + * A special expect1 that expects a heredoc terminator and handles popping the + * lex mode accordingly. + */ +static void +expect1_heredoc_term(pm_parser_t *parser, const uint8_t *ident_start, size_t ident_length) { + if (match1(parser, PM_TOKEN_HEREDOC_END)) { + parser_lex(parser); + } else { + pm_parser_err_heredoc_term(parser, ident_start, ident_length); + parser->previous.start = parser->previous.end; + parser->previous.type = PM_TOKEN_MISSING; + } +} + +/** + * A special expect1 that attaches the error to the opening token location + * rather than the current position. This is useful for errors about missing + * closing tokens, where we want to point to the line with the opening token + * (e.g., `def`, `class`, `if`, `{`) rather than the end of the file. + */ +static void +expect1_opening(pm_parser_t *parser, pm_token_type_t type, pm_diagnostic_id_t diag_id, const pm_token_t *opening) { + if (accept1(parser, type)) return; + + pm_parser_err(parser, opening->start, opening->end, diag_id); + + parser->previous.start = parser->previous.end; + 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, bool accepts_label, pm_diagnostic_id_t diag_id, uint16_t depth); + +/** + * 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, bool accepts_label, pm_diagnostic_id_t diag_id, uint16_t depth) { + pm_node_t *node = parse_expression(parser, binding_power, accepts_command_call, accepts_label, diag_id, depth); + 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, uint16_t depth) { + if (accept1(parser, PM_TOKEN_USTAR)) { + pm_token_t operator = parser->previous; + pm_node_t *expression = parse_value_expression(parser, binding_power, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR, (uint16_t) (depth + 1)); + return UP(pm_splat_node_create(parser, &operator, expression)); + } + + return parse_value_expression(parser, binding_power, accepts_command_call, false, diag_id, depth); +} + +static bool +pm_node_unreference_each(const pm_node_t *node, void *data) { + switch (PM_NODE_TYPE(node)) { + /* When we are about to destroy a set of nodes that could potentially + * contain block exits for the current scope, we need to check if they + * are contained in the list of block exits and remove them if they are. + */ + case PM_BREAK_NODE: + case PM_NEXT_NODE: + case PM_REDO_NODE: { + pm_parser_t *parser = (pm_parser_t *) data; + size_t index = 0; + + while (index < parser->current_block_exits->size) { + pm_node_t *block_exit = parser->current_block_exits->nodes[index]; + + if (block_exit == node) { + if (index + 1 < parser->current_block_exits->size) { + memmove( + &parser->current_block_exits->nodes[index], + &parser->current_block_exits->nodes[index + 1], + (parser->current_block_exits->size - index - 1) * sizeof(pm_node_t *) + ); + } + parser->current_block_exits->size--; + + /* Note returning true here because these nodes could have + * arguments that are themselves block exits. */ + return true; + } + + index++; + } + + return true; + } + /* When an implicit local variable is written to or targeted, it becomes + * a regular, named local variable. This branch removes it from the list + * of implicit parameters when that happens. */ + case PM_LOCAL_VARIABLE_READ_NODE: + case PM_IT_LOCAL_VARIABLE_READ_NODE: { + pm_parser_t *parser = (pm_parser_t *) data; + pm_node_list_t *implicit_parameters = &parser->current_scope->implicit_parameters; + + for (size_t index = 0; index < implicit_parameters->size; index++) { + if (implicit_parameters->nodes[index] == node) { + /* If the node is not the last one in the list, we need to + * shift the remaining nodes down to fill the gap. This is + * extremely unlikely to happen. */ + if (index != implicit_parameters->size - 1) { + memmove(&implicit_parameters->nodes[index], &implicit_parameters->nodes[index + 1], (implicit_parameters->size - index - 1) * sizeof(pm_node_t *)); + } + + implicit_parameters->size--; + break; + } + } + + return false; + } + default: + return true; + } +} + +/** + * When we are about to destroy a set of nodes that could potentially be + * referenced by one or more lists on the parser, then remove them from those + * lists so we don't get a use-after-free. + */ +static void +pm_node_unreference(pm_parser_t *parser, const pm_node_t *node) { + pm_visit_node(node, pm_node_unreference_each, parser); +} + +/** + * 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); +} + +/** + * Certain expressions are not targetable, 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_target(pm_parser_t *parser, pm_node_t *target) { + switch (PM_NODE_TYPE(target)) { + case PM_SOURCE_ENCODING_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_ENCODING); break; + case PM_FALSE_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_FALSE); break; + case PM_SOURCE_FILE_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_FILE); break; + case PM_SOURCE_LINE_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_LINE); break; + case PM_NIL_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_NIL); break; + case PM_SELF_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_SELF); break; + case PM_TRUE_NODE: pm_parser_err_node(parser, target, 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_target_node_t *result = pm_local_variable_target_node_create(parser, &target->location, name, 0); + + pm_node_destroy(parser, target); + return UP(result); +} + +/** + * Convert the given node into a valid target node. + * + * @param multiple Whether or not this target is part of a larger set of + * targets. If it is, then the &. operator is not allowed. + * @param splat Whether or not this target is a child of a splat target. If it + * is, then fewer patterns are allowed. + */ +static pm_node_t * +parse_target(pm_parser_t *parser, pm_node_t *target, bool multiple, bool splat_parent) { + switch (PM_NODE_TYPE(target)) { + case PM_MISSING_NODE: + return target; + 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. + return parse_unwriteable_target(parser, 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: + if (context_def_p(parser)) { + pm_parser_err_node(parser, target, PM_ERR_WRITE_TARGET_IN_METHOD); + } + + 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: + if (context_def_p(parser)) { + pm_parser_err_node(parser, target, PM_ERR_WRITE_TARGET_IN_METHOD); + } + + 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: { + if (pm_token_is_numbered_parameter(target->location.start, target->location.end)) { + PM_PARSER_ERR_FORMAT(parser, target->location.start, target->location.end, PM_ERR_PARAMETER_NUMBERED_RESERVED, target->location.start); + pm_node_unreference(parser, target); + } + + 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_IT_LOCAL_VARIABLE_READ_NODE: { + pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2); + pm_node_t *node = UP(pm_local_variable_target_node_create(parser, &target->location, name, 0)); + + pm_node_unreference(parser, target); + pm_node_destroy(parser, target); + + return node; + } + 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: + if (splat_parent) { + // Multi target is not accepted in all positions. If this is one + // of them, then we need to add an error. + pm_parser_err_node(parser, target, PM_ERR_WRITE_TARGET_UNEXPECTED); + } + + 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, multiple, true); + } + + return UP(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 UP(pm_local_variable_target_node_create(parser, &message_loc, name, 0)); + } + + if (peek_at(parser, call->message_loc.start) == '_' || parser->encoding->alnum_char(call->message_loc.start, call->message_loc.end - call->message_loc.start)) { + if (multiple && PM_NODE_FLAG_P(call, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) { + pm_parser_err_node(parser, (const pm_node_t *) call, PM_ERR_UNEXPECTED_SAFE_NAVIGATION); + } + + parse_write_name(parser, &call->name); + return UP(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 UP(pm_index_target_node_create(parser, call)); + } + } + PRISM_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, bool multiple) { + pm_node_t *result = parse_target(parser, target, multiple, false); + + // 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 UP(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 UP(node); + } + case PM_CONSTANT_PATH_NODE: { + pm_node_t *node = UP(pm_constant_path_write_node_create(parser, (pm_constant_path_node_t *) target, operator, value)); + + if (context_def_p(parser)) { + pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_IN_METHOD); + } + + return parse_shareable_constant_write(parser, node); + } + case PM_CONSTANT_READ_NODE: { + pm_node_t *node = UP(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); + PRISM_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 UP(node); + } + case PM_LOCAL_VARIABLE_READ_NODE: { + pm_local_variable_read_node_t *local_read = (pm_local_variable_read_node_t *) target; + + pm_constant_id_t name = local_read->name; + pm_location_t name_loc = target->location; + + uint32_t depth = local_read->depth; + pm_scope_t *scope = pm_parser_scope_find(parser, depth); + + if (pm_token_is_numbered_parameter(target->location.start, target->location.end)) { + pm_diagnostic_id_t diag_id = (scope->parameters & PM_SCOPE_PARAMETERS_NUMBERED_FOUND) ? PM_ERR_EXPRESSION_NOT_WRITABLE_NUMBERED : PM_ERR_PARAMETER_NUMBERED_RESERVED; + PM_PARSER_ERR_FORMAT(parser, target->location.start, target->location.end, diag_id, target->location.start); + pm_node_unreference(parser, target); + } + + pm_locals_unread(&scope->locals, name); + pm_node_destroy(parser, target); + + return UP(pm_local_variable_write_node_create(parser, name, depth, value, &name_loc, operator)); + } + case PM_IT_LOCAL_VARIABLE_READ_NODE: { + pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2); + pm_node_t *node = UP(pm_local_variable_write_node_create(parser, name, 0, value, &target->location, operator)); + + pm_node_unreference(parser, target); + pm_node_destroy(parser, target); + + return node; + } + case PM_INSTANCE_VARIABLE_READ_NODE: { + pm_node_t *write_node = UP(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 UP(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, UP(splat)); + + return UP(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 = UP(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, parser->end - 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; + call->equal_loc = PM_LOCATION_TOKEN_VALUE(operator); + + parse_write_name(parser, &call->name); + pm_node_flag_set(UP(call), PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE | pm_implicit_array_write_flags(value, PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY)); + + return UP(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); + call->equal_loc = PM_LOCATION_TOKEN_VALUE(operator); + + // Ensure that the arguments for []= don't contain keywords + pm_index_arguments_check(parser, call->arguments, call->block); + pm_node_flag_set(UP(call), PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE | pm_implicit_array_write_flags(value, PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY)); + + 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. + // + // Since it is possible for the value to contain an implicit + // parameter somewhere in its subtree, we need to walk it and remove + // any implicit parameters from the list of implicit parameters for + // the current scope. + pm_node_unreference(parser, value); + pm_node_destroy(parser, value); + } + PRISM_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_local_add_location(parser, target->location.start, target->location.end, 1); + 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 UP(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, uint16_t depth) { + 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, true, false)); + + 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, false, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR, (uint16_t) (depth + 1)); + name = parse_target(parser, name, true, true); + } + + pm_node_t *splat = UP(pm_splat_node_create(parser, &star_operator, name)); + pm_multi_target_node_targets_append(parser, result, splat); + has_rest = true; + } else if (match1(parser, PM_TOKEN_PARENTHESIS_LEFT)) { + context_push(parser, PM_CONTEXT_MULTI_TARGET); + pm_node_t *target = parse_expression(parser, binding_power, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA, (uint16_t) (depth + 1)); + target = parse_target(parser, target, true, false); + + pm_multi_target_node_targets_append(parser, result, target); + context_pop(parser); + } else if (token_begins_expression_p(parser->current.type)) { + pm_node_t *target = parse_expression(parser, binding_power, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA, (uint16_t) (depth + 1)); + target = parse_target(parser, target, true, false); + + 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 = UP(pm_implicit_rest_node_create(parser, &parser->previous)); + pm_multi_target_node_targets_append(parser, result, rest); + break; + } + } + + return UP(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, uint16_t depth) { + pm_node_t *result = parse_targets(parser, first_target, binding_power, depth); + 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, uint16_t depth) { + // 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, false, PM_ERR_CANNOT_PARSE_EXPRESSION, (uint16_t) (depth + 1)); + pm_statements_node_body_append(parser, statements, node, true); + + // 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); + + // If we are at the end of the file, then we need to stop parsing + // the statements entirely at this point. Mark the parser as + // recovering, as we know that EOF closes the top-level context, and + // then break out of the loop. + if (match1(parser, PM_TOKEN_EOF)) { + parser->recovering = true; + break; + } + + while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)); + if (context_terminator(context, &parser->current)) break; + } else if (!accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_EOF)) { + // 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); + bool last_value = true; + switch (context) { + case PM_CONTEXT_BEGIN_ENSURE: + case PM_CONTEXT_DEF_ENSURE: + last_value = false; + break; + default: + break; + } + pm_void_statements_check(parser, statements, last_value); + + 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, true); + + 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) { + pm_node_t *previous; + + if ((previous = pm_static_literals_add(&parser->newline_list, parser->start_line, literals, node, false)) != 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, + pm_newline_list_line_column(&parser->newline_list, previous->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, uint16_t depth) { + 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, false, PM_ERR_EXPECT_EXPRESSION_AFTER_SPLAT_HASH, (uint16_t) (depth + 1)); + } else if (token_begins_expression_p(parser->current.type)) { + value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_SPLAT_HASH, (uint16_t) (depth + 1)); + } else { + pm_parser_scope_forwarding_keywords_check(parser, &operator); + } + + element = UP(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 = UP(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, false, PM_ERR_HASH_EXPRESSION_AFTER_LABEL, (uint16_t) (depth + 1)); + } 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 = UP(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 = UP(pm_call_node_variable_call_create(parser, &identifier)); + } else { + value = UP(pm_local_variable_read_node_create(parser, &identifier, (uint32_t) depth)); + } + } + + value->location.end++; + value = UP(pm_implicit_node_create(parser, value)); + } + + element = UP(pm_assoc_node_create(parser, key, &operator, value)); + break; + } + default: { + pm_node_t *key = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, true, PM_ERR_HASH_KEY, (uint16_t) (depth + 1)); + + // 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, false, PM_ERR_HASH_VALUE, (uint16_t) (depth + 1)); + element = UP(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; +} + +static inline bool +argument_allowed_for_bare_hash(pm_parser_t *parser, pm_node_t *argument) { + if (pm_symbol_node_label_p(argument)) { + return true; + } + + switch (PM_NODE_TYPE(argument)) { + case PM_CALL_NODE: { + pm_call_node_t *cast = (pm_call_node_t *) argument; + if (cast->opening_loc.start == NULL && cast->arguments != NULL) { + if (PM_NODE_FLAG_P(cast->arguments, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS | PM_ARGUMENTS_NODE_FLAGS_CONTAINS_SPLAT)) { + return false; + } + if (cast->block != NULL) { + return false; + } + } + break; + } + default: break; + } + return accept1(parser, PM_TOKEN_EQUAL_GREATER); +} + +/** + * 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, uint16_t depth) { + 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_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 = UP(hash); + + pm_static_literals_t hash_keys = { 0 }; + bool contains_keyword_splat = parse_assocs(parser, &hash_keys, UP(hash), (uint16_t) (depth + 1)); + + parse_arguments_append(parser, arguments, argument); + + pm_node_flags_t flags = PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS; + if (contains_keyword_splat) flags |= PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT; + pm_node_flag_set(UP(arguments->arguments), flags); + + 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, false, PM_ERR_EXPECT_ARGUMENT, (uint16_t) (depth + 1)); + } else { + pm_parser_scope_forwarding_block_check(parser, &operator); + } + + argument = UP(pm_block_argument_node_create(parser, &operator, expression)); + if (parsed_block_argument) { + parse_arguments_append(parser, arguments, argument); + } else { + arguments->block = argument; + } + + if (match1(parser, PM_TOKEN_COMMA)) { + pm_parser_err_current(parser, PM_ERR_ARGUMENT_AFTER_BLOCK); + } + + 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 = UP(pm_splat_node_create(parser, &operator, NULL)); + if (parsed_bare_hash) { + pm_parser_err_previous(parser, PM_ERR_ARGUMENT_SPLAT_AFTER_ASSOC_SPLAT); + } + } else { + pm_node_t *expression = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_SPLAT, (uint16_t) (depth + 1)); + + if (parsed_bare_hash) { + pm_parser_err(parser, operator.start, expression->location.end, PM_ERR_ARGUMENT_SPLAT_AFTER_ASSOC_SPLAT); + } + + argument = UP(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, false, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); + + // If we parse a range, we need to validate that we + // didn't accidentally violate the nonassoc rules of the + // ... operator. + if (PM_NODE_TYPE_P(right, PM_RANGE_NODE)) { + pm_range_node_t *range = (pm_range_node_t *) right; + pm_parser_err(parser, range->operator_loc.start, range->operator_loc.end, PM_ERR_UNEXPECTED_RANGE_OPERATOR); + } + + argument = UP(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 = UP(pm_forwarding_arguments_node_create(parser, &parser->previous)); + parse_arguments_append(parser, arguments, argument); + pm_node_flag_set(UP(arguments->arguments), PM_ARGUMENTS_NODE_FLAGS_CONTAINS_FORWARDING); + arguments->has_forwarding = true; + parsed_forwarding_arguments = true; + break; + } + } + } + PRISM_FALLTHROUGH + default: { + if (argument == NULL) { + argument = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, !parsed_first_argument, true, PM_ERR_EXPECT_ARGUMENT, (uint16_t) (depth + 1)); + } + + bool contains_keywords = false; + bool contains_keyword_splat = false; + + if (argument_allowed_for_bare_hash(parser, argument)){ + 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); + contains_keywords = true; + + // 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, false, PM_ERR_HASH_VALUE, (uint16_t) (depth + 1)); + argument = UP(pm_assoc_node_create(parser, argument, &operator, value)); + + pm_keyword_hash_node_elements_append(bare_hash, argument); + argument = UP(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, UP(bare_hash), (uint16_t) (depth + 1)); + } + + pm_static_literals_free(&hash_keys); + parsed_bare_hash = true; + } + + parse_arguments_append(parser, arguments, argument); + + pm_node_flags_t flags = 0; + if (contains_keywords) flags |= PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS; + if (contains_keyword_splat) flags |= PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT; + pm_node_flag_set(UP(arguments->arguments), flags); + + 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. + bool accepted_newline = false; + if (terminator != PM_TOKEN_EOF) { + accepted_newline = 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 (accept1(parser, PM_TOKEN_COMMA)) { + // If there was a comma, then we need to check if we also accepted a + // newline. If we did, then this is a syntax error. + if (accepted_newline) { + pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA); + } + + // If this is a command call and an argument takes a block, + // there can be no further arguments. For example, + // `foo(bar 1 do end, 2)` should be rejected. + if (PM_NODE_TYPE_P(argument, PM_CALL_NODE)) { + pm_call_node_t *call = (pm_call_node_t *) argument; + if (call->opening_loc.start == NULL && call->arguments != NULL && call->block != NULL) { + pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA); + break; + } + } + } 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. + 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 = UP(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 = UP(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 = UP(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 = UP(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 = UP(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. + * + * It returns true if it was successful, and false otherwise. + */ +static bool +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 true; + + // 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 true; + } else if (*current == PM_PARAMETERS_ORDER_AFTER_OPTIONAL && state == PM_PARAMETERS_ORDER_NAMED) { + return true; + } + + if (token->type == PM_TOKEN_USTAR && *current == PM_PARAMETERS_ORDER_AFTER_OPTIONAL) { + pm_parser_err_token(parser, token, PM_ERR_PARAMETER_STAR); + return false; + } else if (token->type == PM_TOKEN_UDOT_DOT_DOT && (*current >= PM_PARAMETERS_ORDER_KEYWORDS_REST && *current <= PM_PARAMETERS_ORDER_AFTER_OPTIONAL)) { + pm_parser_err_token(parser, token, *current == PM_PARAMETERS_ORDER_AFTER_OPTIONAL ? PM_ERR_PARAMETER_FORWARDING_AFTER_REST : PM_ERR_PARAMETER_ORDER); + return false; + } else 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); + return false; + } + + if (state < *current) *current = state; + return true; +} + +/** + * 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, + bool accepts_blocks_in_defaults, + bool in_block, + uint16_t depth +) { + pm_do_loop_stack_push(parser, false); + + pm_parameters_node_t *params = pm_parameters_node_create(parser); + pm_parameters_order_t order = PM_PARAMETERS_ORDER_NONE; + + while (true) { + bool parsing = true; + + switch (parser->current.type) { + case PM_TOKEN_PARENTHESIS_LEFT: { + update_parameter_state(parser, &parser->current, &order); + pm_node_t *param = UP(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(UP(param)); + } + if (params->block == NULL) { + pm_parameters_node_block_set(params, param); + } else { + pm_parser_err_node(parser, UP(param), PM_ERR_PARAMETER_BLOCK_MULTI); + pm_parameters_node_posts_append(params, UP(param)); + } + + break; + } + case PM_TOKEN_UDOT_DOT_DOT: { + if (!allows_forwarding_parameters) { + pm_parser_err_current(parser, PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES); + } + + bool succeeded = 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); + if (succeeded) pm_parser_err_previous(parser, PM_ERR_PARAMETER_UNEXPECTED_FWD); + params->keyword_rest = NULL; + } + + pm_parameters_node_keyword_rest_set(params, UP(param)); + 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 (match1(parser, PM_TOKEN_EQUAL)) { + pm_token_t operator = parser->current; + context_push(parser, PM_CONTEXT_DEFAULT_PARAMS); + parser_lex(parser); + + pm_constant_id_t name_id = pm_parser_constant_id_token(parser, &name); + uint32_t reads = parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 ? pm_locals_reads(&parser->current_scope->locals, name_id) : 0; + + if (accepts_blocks_in_defaults) pm_accepts_block_stack_push(parser, true); + pm_node_t *value = parse_value_expression(parser, binding_power, false, false, PM_ERR_PARAMETER_NO_DEFAULT, (uint16_t) (depth + 1)); + if (accepts_blocks_in_defaults) pm_accepts_block_stack_pop(parser); + + pm_optional_parameter_node_t *param = pm_optional_parameter_node_create(parser, &name, &operator, value); + + if (repeated) { + pm_node_flag_set_repeated_parameter(UP(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 ((parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3) && (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) { + parsing = 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(UP(param)); + } + pm_parameters_node_requireds_append(params, UP(param)); + } else { + pm_required_parameter_node_t *param = pm_required_parameter_node_create(parser, &name); + if (repeated) { + pm_node_flag_set_repeated_parameter(UP(param)); + } + pm_parameters_node_posts_append(params, UP(param)); + } + + break; + } + case PM_TOKEN_LABEL: { + if (!uses_parentheses && !in_block) parser->in_keyword_arg = true; + update_parameter_state(parser, &parser->current, &order); + + context_push(parser, PM_CONTEXT_DEFAULT_PARAMS); + parser_lex(parser); + + pm_token_t name = parser->previous; + pm_token_t local = name; + local.end -= 1; + + if (parser->encoding_changed ? parser->encoding->isupper_char(local.start, local.end - local.start) : pm_encoding_utf_8_isupper_char(local.start, local.end - local.start)) { + pm_parser_err(parser, local.start, local.end, PM_ERR_ARGUMENT_FORMAL_CONSTANT); + } else if (local.end[-1] == '!' || local.end[-1] == '?') { + PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, local, PM_ERR_INVALID_LOCAL_VARIABLE_WRITE); + } + + 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: { + context_pop(parser); + + pm_node_t *param = UP(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: { + context_pop(parser); + + if (uses_parentheses) { + parsing = false; + break; + } + + pm_node_t *param = UP(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)) { + pm_constant_id_t name_id = pm_parser_constant_id_token(parser, &local); + uint32_t reads = parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 ? pm_locals_reads(&parser->current_scope->locals, name_id) : 0; + + if (accepts_blocks_in_defaults) pm_accepts_block_stack_push(parser, true); + pm_node_t *value = parse_value_expression(parser, binding_power, false, false, PM_ERR_PARAMETER_NO_DEFAULT_KW, (uint16_t) (depth + 1)); + if (accepts_blocks_in_defaults) pm_accepts_block_stack_pop(parser); + + if (parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 && (pm_locals_reads(&parser->current_scope->locals, name_id) != reads)) { + PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, local, PM_ERR_PARAMETER_CIRCULAR); + } + + param = UP(pm_optional_keyword_parameter_node_create(parser, &name, value)); + } + else { + param = UP(pm_required_keyword_parameter_node_create(parser, &name)); + } + + if (repeated) { + pm_node_flag_set_repeated_parameter(param); + } + + context_pop(parser); + 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) { + parsing = 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 = UP(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: { + pm_parameters_order_t previous_order = order; + 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)) { + if (previous_order <= PM_PARAMETERS_ORDER_KEYWORDS) { + pm_parser_err_previous(parser, PM_ERR_PARAMETER_UNEXPECTED_NO_KW); + } + + param = UP(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 = UP(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 && order >= PM_PARAMETERS_ORDER_NAMED) { + // If we get here, then we have a trailing comma in a + // block parameter list. + pm_node_t *param = UP(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, UP(param), PM_ERR_PARAMETER_SPLAT_MULTI); + pm_parameters_node_posts_append(params, UP(param)); + } + } else { + pm_parser_err_previous(parser, PM_ERR_PARAMETER_WILD_LOOSE_COMMA); + } + } + + parsing = false; + break; + } + + // If we hit some kind of issue while parsing the parameter, this would + // have been set to false. In that case, we need to break out of the + // loop. + if (!parsing) break; + + bool accepted_newline = false; + if (uses_parentheses) { + accepted_newline = accept1(parser, PM_TOKEN_NEWLINE); + } + + if (accept1(parser, PM_TOKEN_COMMA)) { + // If there was a comma, but we also accepted a newline, then this + // is a syntax error. + if (accepted_newline) { + pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA); + } + } else { + // If there was no comma, then we're done parsing parameters. + break; + } + } + + 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, UP(params)); + return NULL; + } + + return params; +} + +/** + * Accepts a parser returns the index of the last newline in the file that was + * ecorded before the current token within the newline list. + */ +static size_t +token_newline_index(const pm_parser_t *parser) { + if (parser->heredoc_end == NULL) { + // This is the common case. In this case we can look at the previously + // recorded newline in the newline list and subtract from the current + // offset. + return parser->newline_list.size - 1; + } else { + // This is unlikely. This is the case that we have already parsed the + // start of a heredoc, so we cannot rely on looking at the previous + // offset of the newline list, and instead must go through the whole + // process of a binary search for the line number. + return (size_t) pm_newline_list_line(&parser->newline_list, parser->current.start, 0); + } +} + +/** + * Accepts a parser, a newline index, and a token and returns the column. The + * important piece of this is that it expands tabs out to the next tab stop. + */ +static int64_t +token_column(const pm_parser_t *parser, size_t newline_index, const pm_token_t *token, bool break_on_non_space) { + const uint8_t *cursor = parser->start + parser->newline_list.offsets[newline_index]; + const uint8_t *end = token->start; + + // Skip over the BOM if it is present. + if ( + newline_index == 0 && + parser->start[0] == 0xef && + parser->start[1] == 0xbb && + parser->start[2] == 0xbf + ) cursor += 3; + + int64_t column = 0; + for (; cursor < end; cursor++) { + switch (*cursor) { + case '\t': + column = ((column / PM_TAB_WHITESPACE_SIZE) + 1) * PM_TAB_WHITESPACE_SIZE; + break; + case ' ': + column++; + break; + default: + column++; + if (break_on_non_space) return -1; + break; + } + } + + return column; +} + +/** + * Accepts a parser, two newline indices, and pointers to two tokens. This + * function warns if the indentation of the two tokens does not match. + */ +static void +parser_warn_indentation_mismatch(pm_parser_t *parser, size_t opening_newline_index, const pm_token_t *opening_token, bool if_after_else, bool allow_indent) { + // If these warnings are disabled (unlikely), then we can just return. + if (!parser->warn_mismatched_indentation) return; + + // If the tokens are on the same line, we do not warn. + size_t closing_newline_index = token_newline_index(parser); + if (opening_newline_index == closing_newline_index) return; + + // If the opening token has anything other than spaces or tabs before it, + // then we do not warn. This is unless we are matching up an `if`/`end` pair + // and the `if` immediately follows an `else` keyword. + int64_t opening_column = token_column(parser, opening_newline_index, opening_token, !if_after_else); + if (!if_after_else && (opening_column == -1)) return; + + // Get a reference to the closing token off the current parser. This assumes + // that the caller has placed this in the correct position. + pm_token_t *closing_token = &parser->current; + + // If the tokens are at the same indentation, we do not warn. + int64_t closing_column = token_column(parser, closing_newline_index, closing_token, true); + if ((closing_column == -1) || (opening_column == closing_column)) return; + + // If the closing column is greater than the opening column and we are + // allowing indentation, then we do not warn. + if (allow_indent && (closing_column > opening_column)) return; + + // Otherwise, add a warning. + PM_PARSER_WARN_FORMAT( + parser, + closing_token->start, + closing_token->end, + PM_WARN_INDENTATION_MISMATCH, + (int) (closing_token->end - closing_token->start), + (const char *) closing_token->start, + (int) (opening_token->end - opening_token->start), + (const char *) opening_token->start, + ((int32_t) opening_newline_index) + parser->start_line + ); +} + +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, size_t opening_newline_index, const pm_token_t *opening, pm_begin_node_t *parent_node, pm_rescues_type_t type, uint16_t depth) { + pm_rescue_node_t *current = NULL; + + while (match1(parser, PM_TOKEN_KEYWORD_RESCUE)) { + if (opening != NULL) parser_warn_indentation_mismatch(parser, opening_newline_index, opening, false, false); + parser_lex(parser); + + 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, false, PM_ERR_RESCUE_VARIABLE, (uint16_t) (depth + 1)); + reference = parse_target(parser, reference, false, false); + + 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, (uint16_t) (depth + 1)); + 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, false, PM_ERR_RESCUE_VARIABLE, (uint16_t) (depth + 1)); + reference = parse_target(parser, reference, false, false); + + pm_rescue_node_reference_set(rescue, reference); + break; + } + } while (accept1(parser, PM_TOKEN_COMMA)); + } + } + } + + if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) { + if (accept1(parser, PM_TOKEN_KEYWORD_THEN)) { + rescue->then_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(&parser->previous); + } + } else { + expect1(parser, PM_TOKEN_KEYWORD_THEN, PM_ERR_RESCUE_TERM); + rescue->then_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(&parser->previous); + } + + 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, (uint16_t) (depth + 1)); + 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_subsequent_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 subsequent + // clauses. This sets the end location on all rescues once we know it. + if (current != NULL) { + const uint8_t *end_to_set = current->base.location.end; + pm_rescue_node_t *clause = parent_node->rescue_clause; + + while (clause != NULL) { + clause->base.location.end = end_to_set; + clause = clause->subsequent; + } + } + + pm_token_t else_keyword; + if (match1(parser, PM_TOKEN_KEYWORD_ELSE)) { + if (opening != NULL) parser_warn_indentation_mismatch(parser, opening_newline_index, opening, false, false); + opening_newline_index = token_newline_index(parser); + + else_keyword = parser->current; + opening = &else_keyword; + + parser_lex(parser); + 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_ELSE; break; + } + + else_statements = parse_statements(parser, context, (uint16_t) (depth + 1)); + 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 we don't have a `current` rescue node, then this is a dangling + // else, and it's an error. + if (current == NULL) pm_parser_err_node(parser, UP(else_clause), PM_ERR_BEGIN_LONELY_ELSE); + } + + if (match1(parser, PM_TOKEN_KEYWORD_ENSURE)) { + if (opening != NULL) parser_warn_indentation_mismatch(parser, opening_newline_index, opening, false, false); + pm_token_t ensure_keyword = parser->current; + + parser_lex(parser); + 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, (uint16_t) (depth + 1)); + 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 (match1(parser, PM_TOKEN_KEYWORD_END)) { + if (opening != NULL) parser_warn_indentation_mismatch(parser, opening_newline_index, opening, false, false); + 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, size_t opening_newline_index, const pm_token_t *opening, const uint8_t *start, pm_statements_node_t *statements, pm_rescues_type_t type, uint16_t depth) { + pm_token_t begin_keyword = not_provided(parser); + pm_begin_node_t *node = pm_begin_node_create(parser, &begin_keyword, statements); + + parse_rescues(parser, opening_newline_index, opening, node, type, (uint16_t) (depth + 1)); + 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, + bool accepts_blocks_in_defaults, + uint16_t depth +) { + pm_parameters_node_t *parameters = NULL; + if (!match1(parser, PM_TOKEN_SEMICOLON)) { + if (!is_lambda_literal) { + context_push(parser, PM_CONTEXT_BLOCK_PARAMETERS); + } + parameters = parse_parameters( + parser, + is_lambda_literal ? PM_BINDING_POWER_DEFINED : PM_BINDING_POWER_INDEX, + false, + allows_trailing_comma, + false, + accepts_blocks_in_defaults, + true, + (uint16_t) (depth + 1) + ); + if (!is_lambda_literal) { + context_pop(parser); + } + } + + 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(UP(local)); + + pm_block_parameters_node_append_local(block_parameters, local); + } while (accept1(parser, PM_TOKEN_COMMA)); + } + } + + return block_parameters; +} + +/** + * 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->parameters & PM_SCOPE_PARAMETERS_NUMBERED_FOUND) 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" +}; + +/** + * 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) { + pm_node_list_t *implicit_parameters = &parser->current_scope->implicit_parameters; + + // If we have ordinary parameters, then we will return them as the set of + // parameters. + if (parameters != NULL) { + // If we also have implicit parameters, then this is an error. + if (implicit_parameters->size > 0) { + pm_node_t *node = implicit_parameters->nodes[0]; + + if (PM_NODE_TYPE_P(node, PM_LOCAL_VARIABLE_READ_NODE)) { + pm_parser_err_node(parser, node, PM_ERR_NUMBERED_PARAMETER_ORDINARY); + } else if (PM_NODE_TYPE_P(node, PM_IT_LOCAL_VARIABLE_READ_NODE)) { + pm_parser_err_node(parser, node, PM_ERR_IT_NOT_ALLOWED_ORDINARY); + } else { + assert(false && "unreachable"); + } + } + + return parameters; + } + + // If we don't have any implicit parameters, then the set of parameters is + // NULL. + if (implicit_parameters->size == 0) { + return NULL; + } + + // If we don't have ordinary parameters, then we now must validate our set + // of implicit parameters. We can only have numbered parameters or it, but + // they cannot be mixed. + uint8_t numbered_parameter = 0; + bool it_parameter = false; + + for (size_t index = 0; index < implicit_parameters->size; index++) { + pm_node_t *node = implicit_parameters->nodes[index]; + + if (PM_NODE_TYPE_P(node, PM_LOCAL_VARIABLE_READ_NODE)) { + if (it_parameter) { + pm_parser_err_node(parser, node, PM_ERR_NUMBERED_PARAMETER_IT); + } else if (outer_scope_using_numbered_parameters_p(parser)) { + pm_parser_err_node(parser, node, PM_ERR_NUMBERED_PARAMETER_OUTER_BLOCK); + } else if (parser->current_scope->parameters & PM_SCOPE_PARAMETERS_NUMBERED_INNER) { + pm_parser_err_node(parser, node, PM_ERR_NUMBERED_PARAMETER_INNER_BLOCK); + } else if (pm_token_is_numbered_parameter(node->location.start, node->location.end)) { + numbered_parameter = MAX(numbered_parameter, (uint8_t) (node->location.start[1] - '0')); + } else { + assert(false && "unreachable"); + } + } else if (PM_NODE_TYPE_P(node, PM_IT_LOCAL_VARIABLE_READ_NODE)) { + if (numbered_parameter > 0) { + pm_parser_err_node(parser, node, PM_ERR_IT_NOT_ALLOWED_NUMBERED); + } else { + it_parameter = true; + } + } + } + + if (numbered_parameter > 0) { + // Go through the parent scopes and mark them as being disallowed from + // using numbered parameters because this inner scope is using them. + for (pm_scope_t *scope = parser->current_scope->previous; scope != NULL && !scope->closed; scope = scope->previous) { + scope->parameters |= PM_SCOPE_PARAMETERS_NUMBERED_INNER; + } + + const pm_location_t location = { .start = opening->start, .end = closing->end }; + return UP(pm_numbered_parameters_node_create(parser, &location, numbered_parameter)); + } + + if (it_parameter) { + return UP(pm_it_parameters_node_create(parser, opening, closing)); + } + + return NULL; +} + +/** + * Parse a block. + */ +static pm_block_node_t * +parse_block(pm_parser_t *parser, uint16_t depth) { + 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)) { + 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, true, (uint16_t) (depth + 1)); + 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 = UP(parse_statements(parser, PM_CONTEXT_BLOCK_BRACES, (uint16_t) (depth + 1))); + } + + expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_BLOCK_TERM_BRACE, &opening); + } 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 = UP(parse_statements(parser, PM_CONTEXT_BLOCK_KEYWORDS, (uint16_t) (depth + 1))); + 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 = UP(parse_rescues_implicit_begin(parser, 0, NULL, opening.start, (pm_statements_node_t *) statements, PM_RESCUES_BLOCK, (uint16_t) (depth + 1))); + } + } + + expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_BLOCK_TERM_END, &opening); + } + + 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, UP(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, uint16_t depth) { + 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, accepts_block, PM_TOKEN_PARENTHESIS_RIGHT, (uint16_t) (depth + 1)); + + 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, accepts_block, PM_TOKEN_EOF, (uint16_t) (depth + 1)); + + // 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_TOKEN_FORMAT(parser, parser->previous, PM_ERR_EXPECT_ARGUMENT, pm_token_type_human(parser->current.type)); + } + + 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, (uint16_t) (depth + 1)); + 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, (uint16_t) (depth + 1)); + } + + if (block != NULL) { + if (arguments->block == NULL && !arguments->has_forwarding) { + arguments->block = UP(block); + } else { + pm_parser_err_node(parser, UP(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 = UP(block); + } + } + } + + return found; +} + +/** + * Check that the return is allowed in the current context. If it isn't, add an + * error to the parser. + */ +static void +parse_return(pm_parser_t *parser, pm_node_t *node) { + bool in_sclass = false; + for (pm_context_node_t *context_node = parser->current_context; context_node != NULL; context_node = context_node->prev) { + switch (context_node->context) { + case PM_CONTEXT_BEGIN_ELSE: + case PM_CONTEXT_BEGIN_ENSURE: + case PM_CONTEXT_BEGIN_RESCUE: + case PM_CONTEXT_BEGIN: + case PM_CONTEXT_CASE_IN: + case PM_CONTEXT_CASE_WHEN: + case PM_CONTEXT_DEFAULT_PARAMS: + case PM_CONTEXT_DEFINED: + 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_LOOP_PREDICATE: + case PM_CONTEXT_MAIN: + case PM_CONTEXT_MULTI_TARGET: + 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: + // Keep iterating up the lists of contexts, because returns can + // see through these. + continue; + case PM_CONTEXT_SCLASS_ELSE: + case PM_CONTEXT_SCLASS_ENSURE: + case PM_CONTEXT_SCLASS_RESCUE: + case PM_CONTEXT_SCLASS: + in_sclass = true; + continue; + case PM_CONTEXT_CLASS_ELSE: + case PM_CONTEXT_CLASS_ENSURE: + case PM_CONTEXT_CLASS_RESCUE: + case PM_CONTEXT_CLASS: + case PM_CONTEXT_MODULE_ELSE: + case PM_CONTEXT_MODULE_ENSURE: + case PM_CONTEXT_MODULE_RESCUE: + case PM_CONTEXT_MODULE: + // These contexts are invalid for a return. + pm_parser_err_node(parser, node, PM_ERR_RETURN_INVALID); + return; + case PM_CONTEXT_BLOCK_BRACES: + case PM_CONTEXT_BLOCK_ELSE: + case PM_CONTEXT_BLOCK_ENSURE: + case PM_CONTEXT_BLOCK_KEYWORDS: + case PM_CONTEXT_BLOCK_RESCUE: + case PM_CONTEXT_BLOCK_PARAMETERS: + case PM_CONTEXT_DEF_ELSE: + case PM_CONTEXT_DEF_ENSURE: + case PM_CONTEXT_DEF_PARAMS: + case PM_CONTEXT_DEF_RESCUE: + case PM_CONTEXT_DEF: + 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: + // These contexts are valid for a return, and we should not + // continue to loop. + return; + case PM_CONTEXT_NONE: + // This case should never happen. + assert(false && "unreachable"); + break; + } + } + if (in_sclass && parser->version >= PM_OPTIONS_VERSION_CRUBY_3_4) { + pm_parser_err_node(parser, node, PM_ERR_RETURN_INVALID); + } +} + +/** + * 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) { + for (pm_context_node_t *context_node = parser->current_context; context_node != NULL; context_node = context_node->prev) { + 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_PARAMETERS: + 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_LOOP_PREDICATE: + 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 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); + return; + case PM_CONTEXT_BEGIN_ELSE: + case PM_CONTEXT_BEGIN_ENSURE: + case PM_CONTEXT_BEGIN_RESCUE: + case PM_CONTEXT_BEGIN: + case PM_CONTEXT_CASE_IN: + case PM_CONTEXT_CASE_WHEN: + case PM_CONTEXT_CLASS_ELSE: + case PM_CONTEXT_CLASS_ENSURE: + case PM_CONTEXT_CLASS_RESCUE: + case PM_CONTEXT_CLASS: + 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_IF: + case PM_CONTEXT_MODULE_ELSE: + case PM_CONTEXT_MODULE_ENSURE: + case PM_CONTEXT_MODULE_RESCUE: + case PM_CONTEXT_MODULE: + case PM_CONTEXT_MULTI_TARGET: + case PM_CONTEXT_PARENS: + case PM_CONTEXT_PREDICATE: + case PM_CONTEXT_RESCUE_MODIFIER: + case PM_CONTEXT_TERNARY: + case PM_CONTEXT_UNLESS: + // In these contexts we should continue walking up the list of + // contexts. + break; + case PM_CONTEXT_NONE: + // This case should never happen. + assert(false && "unreachable"); + break; + } + } +} + +/** + * 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; +} + +/** + * 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. + */ +static void +flush_block_exits(pm_parser_t *parser, pm_node_list_t *previous_block_exits) { + 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; +} + +/** + * 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. + parser->current_block_exits = previous_block_exits; + } 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); + parser->current_block_exits = previous_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. + flush_block_exits(parser, 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, uint16_t depth) { + 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, false, error_id, (uint16_t) (depth + 1)); + + // 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, size_t opening_newline_index, bool if_after_else, uint16_t depth) { + pm_node_list_t current_block_exits = { 0 }; + pm_node_list_t *previous_block_exits = push_block_exits(parser, ¤t_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, (uint16_t) (depth + 1)); + 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, (uint16_t) (depth + 1)); + 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 = UP(pm_if_node_create(parser, &keyword, predicate, &then_keyword, statements, NULL, &end_keyword)); + break; + case PM_CONTEXT_UNLESS: + parent = UP(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); + } + + parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, false, false); + 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, (uint16_t) (depth + 1)); + pm_accepts_block_stack_push(parser, true); + + pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_ELSIF, (uint16_t) (depth + 1)); + pm_accepts_block_stack_pop(parser); + accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON); + + pm_node_t *elsif = UP(pm_if_node_create(parser, &elsif_keyword, predicate, &then_keyword, statements, NULL, &end_keyword)); + ((pm_if_node_t *) current)->subsequent = elsif; + current = elsif; + } + } + + if (match1(parser, PM_TOKEN_KEYWORD_ELSE)) { + parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, false, false); + opening_newline_index = token_newline_index(parser); + + 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, (uint16_t) (depth + 1)); + pm_accepts_block_stack_pop(parser); + + accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON); + parser_warn_indentation_mismatch(parser, opening_newline_index, &else_keyword, false, false); + expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CONDITIONAL_TERM_ELSE, &keyword); + + 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)->subsequent = UP(else_node); + break; + case PM_CONTEXT_UNLESS: + ((pm_unless_node_t *) parent)->else_clause = else_node; + break; + default: + assert(false && "unreachable"); + break; + } + } else { + parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, if_after_else, false); + expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CONDITIONAL_TERM, &keyword); + } + + // 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)->subsequent; + 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(¤t_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: case PM_IT_LOCAL_VARIABLE_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, uint16_t depth) { + 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 = UP(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: { + // Ruby disallows seeing encoding around interpolation in strings, + // even though it is known at parse time. + parser->explicit_encoding = NULL; + + 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, (uint16_t) (depth + 1)); + 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; + + // If this set of embedded statements only contains a single + // statement, then Ruby does not consider it as a possible statement + // that could emit a line event. + if (statements != NULL && statements->body.size == 1) { + pm_node_flag_unset(statements->body.nodes[0], PM_NODE_FLAG_NEWLINE); + } + + return UP(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: { + // Ruby disallows seeing encoding around interpolation in strings, + // even though it is known at parse time. + parser->explicit_encoding = NULL; + + 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 = UP(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 = UP(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 = UP(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 = UP(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 = UP(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 = UP(pm_missing_node_create(parser, parser->current.start, parser->current.end)); + break; + } + + return UP(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; + PRISM_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(UP(symbol), PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING); + + return UP(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, uint16_t depth) { + 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(UP(symbol), parse_symbol_encoding(parser, &parser->previous, &symbol->unescaped, false)); + + return UP(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 UP(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, (uint16_t) (depth + 1)); + + // 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 UP(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, (uint16_t) (depth + 1))) != 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 UP(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: + // + // <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 UP(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 UP(pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped, parse_symbol_encoding(parser, &content, &unescaped, false))); +} + +/** + * 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, uint16_t depth) { + 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(UP(symbol), parse_symbol_encoding(parser, &parser->previous, &symbol->unescaped, false)); + + return UP(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, (uint16_t) (depth + 1)); + } + default: + pm_parser_err_current(parser, PM_ERR_UNDEF_ARGUMENT); + return UP(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, uint16_t depth) { + 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(UP(symbol), parse_symbol_encoding(parser, &parser->previous, &symbol->unescaped, false)); + + return UP(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, (uint16_t) (depth + 1)); + } + case PM_TOKEN_BACK_REFERENCE: + parser_lex(parser); + return UP(pm_back_reference_read_node_create(parser, &parser->previous)); + case PM_TOKEN_NUMBERED_REFERENCE: + parser_lex(parser); + return UP(pm_numbered_reference_read_node_create(parser, &parser->previous)); + case PM_TOKEN_GLOBAL_VARIABLE: + parser_lex(parser); + return UP(pm_global_variable_read_node_create(parser, &parser->previous)); + default: + pm_parser_err_current(parser, PM_ERR_ALIAS_ARGUMENT); + return UP(pm_missing_node_create(parser, parser->current.start, parser->current.end)); + } +} + +/** + * Parse an identifier into either a local variable read. If the local variable + * is not found, it returns NULL instead. + */ +static pm_node_t * +parse_variable(pm_parser_t *parser) { + pm_constant_id_t name_id = pm_parser_constant_id_token(parser, &parser->previous); + int depth; + bool is_numbered_param = pm_token_is_numbered_parameter(parser->previous.start, parser->previous.end); + + if (!is_numbered_param && ((depth = pm_parser_local_depth_constant_id(parser, name_id)) != -1)) { + return UP(pm_local_variable_read_node_create_constant_id(parser, &parser->previous, name_id, (uint32_t) depth, false)); + } + + pm_scope_t *current_scope = parser->current_scope; + if (!current_scope->closed && !(current_scope->parameters & PM_SCOPE_PARAMETERS_IMPLICIT_DISALLOWED)) { + if (is_numbered_param) { + // 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. + uint8_t maximum = (uint8_t) (parser->previous.start[1] - '0'); + for (uint8_t number = 1; number <= maximum; number++) { + pm_parser_local_add_constant(parser, pm_numbered_parameter_names[number - 1], 2); + } + + if (!match1(parser, PM_TOKEN_EQUAL)) { + parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_NUMBERED_FOUND; + } + + pm_node_t *node = UP(pm_local_variable_read_node_create_constant_id(parser, &parser->previous, name_id, 0, false)); + pm_node_list_append(¤t_scope->implicit_parameters, node); + + return node; + } else if ((parser->version >= PM_OPTIONS_VERSION_CRUBY_3_4) && pm_token_is_it(parser->previous.start, parser->previous.end)) { + pm_node_t *node = UP(pm_it_local_variable_read_node_create(parser, &parser->previous)); + pm_node_list_append(¤t_scope->implicit_parameters, node); + + return node; + } + } + + 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_node_t *node = parse_variable(parser); + if (node != NULL) return 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(UP(node), flags); + + return UP(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; +} + +/** + * 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, bool accepts_label, uint16_t depth) { + assert(parser->current.type == PM_TOKEN_STRING_BEGIN); + bool concating = false; + + 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; + bool label_allowed = lex_mode->as.string.label_allowed && accepts_label; + + 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 = UP(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 = UP(symbol); + + if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL); + } 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 = UP(pm_string_node_create_unescaped(parser, &delimiters, &content, &delimiters, &unescaped)); + pm_node_list_append(&parts, part); + + do { + part = UP(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 = UP(pm_interpolated_string_node_create(parser, &opening, &parts, &parser->previous)); + + pm_node_list_free(&parts); + } else if (accept1(parser, PM_TOKEN_LABEL_END)) { + node = UP(pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped, parse_symbol_encoding(parser, &content, &unescaped, true))); + if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL); + } else if (match1(parser, PM_TOKEN_EOF)) { + pm_parser_err_token(parser, &opening, PM_ERR_STRING_LITERAL_EOF); + node = UP(pm_string_node_create_unescaped(parser, &opening, &content, &parser->current, &unescaped)); + } else if (accept1(parser, PM_TOKEN_STRING_END)) { + node = UP(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 = UP(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 = UP(pm_string_node_create_unescaped(parser, &opening, &content, &parser->current, &unescaped)); + pm_node_flag_set(node, parse_unescaped_encoding(parser)); + + // Kind of odd behavior, but basically if we have an + // unterminated string and it ends in a newline, we back up one + // character so that the error message is on the last line of + // content in the string. + if (!accept1(parser, PM_TOKEN_STRING_END)) { + const uint8_t *location = parser->previous.end; + if (location > parser->start && location[-1] == '\n') location--; + pm_parser_err(parser, location, location, PM_ERR_STRING_LITERAL_EOF); + + parser->previous.start = parser->previous.end; + parser->previous.type = PM_TOKEN_MISSING; + } + } else if (accept1(parser, PM_TOKEN_LABEL_END)) { + node = UP(pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped, parse_symbol_encoding(parser, &content, &unescaped, true))); + if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL); + } 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 = UP(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, (uint16_t) (depth + 1))) != NULL) { + pm_node_list_append(&parts, part); + } + } + + if (accept1(parser, PM_TOKEN_LABEL_END)) { + node = UP(pm_interpolated_symbol_node_create(parser, &opening, &parts, &parser->previous)); + if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL); + } else if (match1(parser, PM_TOKEN_EOF)) { + pm_parser_err_token(parser, &opening, PM_ERR_STRING_INTERPOLATED_TERM); + node = UP(pm_interpolated_string_node_create(parser, &opening, &parts, &parser->current)); + } else { + expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_INTERPOLATED_TERM); + node = UP(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, (uint16_t) (depth + 1))) != NULL) { + pm_node_list_append(&parts, part); + } + } + + if (accept1(parser, PM_TOKEN_LABEL_END)) { + node = UP(pm_interpolated_symbol_node_create(parser, &opening, &parts, &parser->previous)); + if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL); + } else if (match1(parser, PM_TOKEN_EOF)) { + pm_parser_err_token(parser, &opening, PM_ERR_STRING_INTERPOLATED_TERM); + node = UP(pm_interpolated_string_node_create(parser, &opening, &parts, &parser->current)); + } else { + expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_INTERPOLATED_TERM); + node = UP(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) { + if (!PM_NODE_TYPE_P(current, PM_STRING_NODE) && !PM_NODE_TYPE_P(current, PM_INTERPOLATED_STRING_NODE)) { + pm_parser_err_node(parser, current, PM_ERR_STRING_CONCATENATION); + } + + 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(container, current); + current = UP(container); + } + + pm_interpolated_string_node_append((pm_interpolated_string_node_t *) current, node); + } + } + + return current; +} + +#define PM_PARSE_PATTERN_SINGLE 0 +#define PM_PARSE_PATTERN_TOP 1 +#define PM_PARSE_PATTERN_MULTI 2 + +static pm_node_t * +parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth); + +/** + * 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 (peek_at(parser, 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, uint16_t depth) { + // 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); + node = UP(pm_constant_path_node_create(parser, node, &delimiter, &parser->previous)); + } + + // 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, PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI, PM_ERR_PATTERN_EXPRESSION_AFTER_BRACKET, (uint16_t) (depth + 1)); + accept1(parser, PM_TOKEN_NEWLINE); + expect1_opening(parser, PM_TOKEN_BRACKET_RIGHT, PM_ERR_PATTERN_TERM_BRACKET, &opening); + } + + 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, PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI, PM_ERR_PATTERN_EXPRESSION_AFTER_PAREN, (uint16_t) (depth + 1)); + accept1(parser, PM_TOKEN_NEWLINE); + expect1_opening(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_PATTERN_TERM_PAREN, &opening); + } + + 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 UP(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 UP(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 UP(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 UP(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 UP(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 = UP(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 UP(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 = UP(pm_local_variable_target_node_create( + parser, + &PM_LOCATION_TOKEN_VALUE(&parser->previous), + constant_id, + (uint32_t) (depth == -1 ? 0 : depth) + )); + } + + return UP(pm_assoc_splat_node_create(parser, value, &operator)); +} + +/** + * Check that the slice of the source given by the bounds parameters constitutes + * a valid local variable name. + */ +static bool +pm_slice_is_valid_local(const pm_parser_t *parser, const uint8_t *start, const uint8_t *end) { + ptrdiff_t length = end - start; + if (length == 0) return false; + + // First ensure that it starts with a valid identifier starting character. + size_t width = char_is_identifier_start(parser, start, end - start); + if (width == 0) return false; + + // Next, ensure that it's not an uppercase character. + if (parser->encoding_changed) { + if (parser->encoding->isupper_char(start, length)) return false; + } else { + if (pm_encoding_utf_8_isupper_char(start, 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 = start + width; + while ((width = char_is_identifier(parser, cursor, end - cursor))) cursor += width; + return cursor == end; +} + +/** + * 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 (pm_slice_is_valid_local(parser, value_loc->start, value_loc->end)) { + depth = pm_parser_local_depth_constant_id(parser, constant_id); + } else { + pm_parser_err(parser, key->base.location.start, key->base.location.end, PM_ERR_PATTERN_HASH_KEY_LOCALS); + + if ((value_loc->end > value_loc->start) && ((value_loc->end[-1] == '!') || (value_loc->end[-1] == '?'))) { + 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); + } + } + + 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 UP(pm_implicit_node_create(parser, UP(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, true) != 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, uint16_t depth) { + 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 (match8(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, PM_TOKEN_EOF)) { + // 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, PM_PARSE_PATTERN_SINGLE, PM_ERR_PATTERN_EXPRESSION_AFTER_KEY, (uint16_t) (depth + 1)); + } + + pm_token_t operator = not_provided(parser); + pm_node_t *assoc = UP(pm_assoc_node_create(parser, first_node, &operator, value)); + + pm_node_list_append(&assocs, assoc); + break; + } + } + PRISM_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_diagnostic_id_t diag_id = PM_NODE_TYPE_P(first_node, PM_INTERPOLATED_SYMBOL_NODE) ? PM_ERR_PATTERN_HASH_KEY_INTERPOLATED : PM_ERR_PATTERN_HASH_KEY_LABEL; + pm_parser_err_node(parser, first_node, diag_id); + + pm_token_t operator = not_provided(parser); + pm_node_t *value = UP(pm_missing_node_create(parser, first_node->location.start, first_node->location.end)); + pm_node_t *assoc = UP(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 (match7(parser, PM_TOKEN_KEYWORD_THEN, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_TOKEN_EOF)) { + // Trailing commas are not allowed to follow a rest pattern. + if (rest != NULL) { + pm_parser_err_token(parser, &parser->current, PM_ERR_PATTERN_EXPRESSION_AFTER_REST); + } + + 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 { + pm_node_t *key; + + if (match1(parser, PM_TOKEN_STRING_BEGIN)) { + key = parse_strings(parser, NULL, true, (uint16_t) (depth + 1)); + + if (PM_NODE_TYPE_P(key, PM_INTERPOLATED_SYMBOL_NODE)) { + pm_parser_err_node(parser, key, PM_ERR_PATTERN_HASH_KEY_INTERPOLATED); + } else if (!pm_symbol_node_label_p(key)) { + pm_parser_err_node(parser, key, PM_ERR_PATTERN_LABEL_AFTER_COMMA); + } + } else { + expect1(parser, PM_TOKEN_LABEL, PM_ERR_PATTERN_LABEL_AFTER_COMMA); + key = UP(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)) { + if (PM_NODE_TYPE_P(key, PM_SYMBOL_NODE)) { + value = parse_pattern_hash_implicit_value(parser, captures, (pm_symbol_node_t *) key); + } else { + value = UP(pm_missing_node_create(parser, key->location.end, key->location.end)); + } + } else { + value = parse_pattern(parser, captures, PM_PARSE_PATTERN_SINGLE, PM_ERR_PATTERN_EXPRESSION_AFTER_KEY, (uint16_t) (depth + 1)); + } + + pm_token_t operator = not_provided(parser); + pm_node_t *assoc = UP(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, uint16_t depth) { + 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 UP(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 UP(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, PM_PARSE_PATTERN_MULTI, PM_ERR_PATTERN_EXPRESSION_AFTER_BRACKET, (uint16_t) (depth + 1)); + + accept1(parser, PM_TOKEN_NEWLINE); + expect1_opening(parser, PM_TOKEN_BRACKET_RIGHT, PM_ERR_PATTERN_TERM_BRACKET, &opening); + 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 UP(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 UP(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 UP(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 = UP(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, true, PM_ERR_PATTERN_HASH_KEY_LABEL, (uint16_t) (depth + 1)); + break; + default: { + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_PATTERN_HASH_KEY, pm_token_type_human(parser->current.type)); + parser_lex(parser); + + first_node = UP(pm_missing_node_create(parser, parser->previous.start, parser->previous.end)); + break; + } + } + + node = parse_pattern_hash(parser, captures, first_node, (uint16_t) (depth + 1)); + + accept1(parser, PM_TOKEN_NEWLINE); + expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_PATTERN_TERM_BRACE, &opening); + 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 UP(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, false, PM_ERR_PATTERN_EXPRESSION_AFTER_RANGE, (uint16_t) (depth + 1)); + return UP(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 = UP(pm_missing_node_create(parser, operator.start, operator.end)); + return UP(pm_range_node_create(parser, NULL, &operator, right)); + } + } + } + case PM_CASE_PRIMITIVE: { + pm_node_t *node = parse_expression(parser, PM_BINDING_POWER_MAX, false, true, diag_id, (uint16_t) (depth + 1)); + + // If we found a label, we need to immediately return to the caller. + if (pm_symbol_node_label_p(node)) return node; + + // Call nodes (arithmetic operations) are not allowed in patterns + if (PM_NODE_TYPE(node) == PM_CALL_NODE) { + pm_parser_err_node(parser, node, diag_id); + pm_missing_node_t *missing_node = pm_missing_node_create(parser, node->location.start, node->location.end); + + pm_node_unreference(parser, node); + pm_node_destroy(parser, node); + return UP(missing_node); + } + + // 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, false, PM_ERR_PATTERN_EXPRESSION_AFTER_RANGE, (uint16_t) (depth + 1)); + return UP(pm_range_node_create(parser, node, &operator, right)); + } + default: + return UP(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 = UP(parse_variable(parser)); + + if (variable == NULL) { + PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, parser->previous, PM_ERR_NO_LOCAL_VARIABLE); + variable = UP(pm_local_variable_read_node_missing_create(parser, &parser->previous, 0)); + } + + return UP(pm_pinned_variable_node_create(parser, &operator, variable)); + } + case PM_TOKEN_INSTANCE_VARIABLE: { + parser_lex(parser); + pm_node_t *variable = UP(pm_instance_variable_read_node_create(parser, &parser->previous)); + + return UP(pm_pinned_variable_node_create(parser, &operator, variable)); + } + case PM_TOKEN_CLASS_VARIABLE: { + parser_lex(parser); + pm_node_t *variable = UP(pm_class_variable_read_node_create(parser, &parser->previous)); + + return UP(pm_pinned_variable_node_create(parser, &operator, variable)); + } + case PM_TOKEN_GLOBAL_VARIABLE: { + parser_lex(parser); + pm_node_t *variable = UP(pm_global_variable_read_node_create(parser, &parser->previous)); + + return UP(pm_pinned_variable_node_create(parser, &operator, variable)); + } + case PM_TOKEN_NUMBERED_REFERENCE: { + parser_lex(parser); + pm_node_t *variable = UP(pm_numbered_reference_read_node_create(parser, &parser->previous)); + + return UP(pm_pinned_variable_node_create(parser, &operator, variable)); + } + case PM_TOKEN_BACK_REFERENCE: { + parser_lex(parser); + pm_node_t *variable = UP(pm_back_reference_read_node_create(parser, &parser->previous)); + + return UP(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, false, PM_ERR_PATTERN_EXPRESSION_AFTER_PIN, (uint16_t) (depth + 1)); + parser->pattern_matching_newlines = previous_pattern_matching_newlines; + + accept1(parser, PM_TOKEN_NEWLINE); + expect1_opening(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_PATTERN_TERM_PAREN, &lparen); + return UP(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 = UP(pm_missing_node_create(parser, operator.start, operator.end)); + return UP(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_constant_path_node_t *node = pm_constant_path_node_create(parser, NULL, &delimiter, &parser->previous); + + return parse_pattern_constant_path(parser, captures, UP(node), (uint16_t) (depth + 1)); + } + case PM_TOKEN_CONSTANT: { + pm_token_t constant = parser->current; + parser_lex(parser); + + pm_node_t *node = UP(pm_constant_read_node_create(parser, &constant)); + return parse_pattern_constant_path(parser, captures, node, (uint16_t) (depth + 1)); + } + default: + pm_parser_err_current(parser, diag_id); + return UP(pm_missing_node_create(parser, parser->current.start, parser->current.end)); + } +} + +static bool +parse_pattern_alternation_error_each(const pm_node_t *node, void *data) { + switch (PM_NODE_TYPE(node)) { + case PM_LOCAL_VARIABLE_TARGET_NODE: + pm_parser_err((pm_parser_t *) data, node->location.start, node->location.end, PM_ERR_PATTERN_CAPTURE_IN_ALTERNATIVE); + return false; + default: + return true; + } +} + +/** + * When we get here, we know that we already have a syntax error, because we + * know we have captured a variable and that we are in an alternation. + */ +static void +parse_pattern_alternation_error(pm_parser_t *parser, const pm_node_t *node) { + pm_visit_node(node, parse_pattern_alternation_error_each, parser); +} + +/** + * 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_node_t *first_node, pm_diagnostic_id_t diag_id, uint16_t depth) { + pm_node_t *node = first_node; + bool alternation = false; + + while ((node == NULL) || (alternation = accept1(parser, PM_TOKEN_PIPE))) { + if (alternation && !PM_NODE_TYPE_P(node, PM_ALTERNATION_PATTERN_NODE) && captures->size) { + parse_pattern_alternation_error(parser, node); + } + + 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 (!alternation) { + node = parse_pattern_primitive(parser, captures, diag_id, (uint16_t) (depth + 1)); + } else { + pm_token_t operator = parser->previous; + pm_node_t *right = parse_pattern_primitive(parser, captures, PM_ERR_PATTERN_EXPRESSION_AFTER_PIPE, (uint16_t) (depth + 1)); + + if (captures->size) parse_pattern_alternation_error(parser, right); + node = UP(pm_alternation_pattern_node_create(parser, node, right, &operator)); + } + + break; + } + case PM_TOKEN_PARENTHESIS_LEFT: + case PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES: { + pm_token_t operator = parser->previous; + pm_token_t opening = parser->current; + parser_lex(parser); + + pm_node_t *body = parse_pattern(parser, captures, PM_PARSE_PATTERN_SINGLE, PM_ERR_PATTERN_EXPRESSION_AFTER_PAREN, (uint16_t) (depth + 1)); + accept1(parser, PM_TOKEN_NEWLINE); + expect1_opening(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_PATTERN_TERM_PAREN, &opening); + pm_node_t *right = UP(pm_parentheses_node_create(parser, &opening, body, &parser->previous, 0)); + + if (!alternation) { + node = right; + } else { + if (captures->size) parse_pattern_alternation_error(parser, right); + node = UP(pm_alternation_pattern_node_create(parser, node, right, &operator)); + } + + break; + } + default: { + pm_parser_err_current(parser, diag_id); + pm_node_t *right = UP(pm_missing_node_create(parser, parser->current.start, parser->current.end)); + + if (!alternation) { + node = right; + } else { + if (captures->size) parse_pattern_alternation_error(parser, right); + node = UP(pm_alternation_pattern_node_create(parser, node, right, &parser->previous)); + } + + break; + } + } + } + + // 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_local_variable_target_node_t *target = pm_local_variable_target_node_create( + parser, + &PM_LOCATION_TOKEN_VALUE(&parser->previous), + constant_id, + (uint32_t) (depth == -1 ? 0 : depth) + ); + + node = UP(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, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth) { + 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 = UP(pm_symbol_node_label_create(parser, &parser->previous)); + node = UP(parse_pattern_hash(parser, captures, key, (uint16_t) (depth + 1))); + + if (!(flags & PM_PARSE_PATTERN_TOP)) { + pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_IMPLICIT); + } + + return node; + } + case PM_TOKEN_USTAR_STAR: { + node = parse_pattern_keyword_rest(parser, captures); + node = UP(parse_pattern_hash(parser, captures, node, (uint16_t) (depth + 1))); + + if (!(flags & PM_PARSE_PATTERN_TOP)) { + pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_IMPLICIT); + } + + return node; + } + case PM_TOKEN_STRING_BEGIN: { + // We need special handling for string beginnings because they could + // be dynamic symbols leading to hash patterns. + node = parse_pattern_primitive(parser, captures, diag_id, (uint16_t) (depth + 1)); + + if (pm_symbol_node_label_p(node)) { + node = UP(parse_pattern_hash(parser, captures, node, (uint16_t) (depth + 1))); + + if (!(flags & PM_PARSE_PATTERN_TOP)) { + pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_IMPLICIT); + } + + return node; + } + + node = parse_pattern_primitives(parser, captures, node, diag_id, (uint16_t) (depth + 1)); + break; + } + case PM_TOKEN_USTAR: { + if (flags & (PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI)) { + parser_lex(parser); + node = UP(parse_pattern_rest(parser, captures)); + leading_rest = true; + break; + } + } + PRISM_FALLTHROUGH + default: + node = parse_pattern_primitives(parser, captures, NULL, diag_id, (uint16_t) (depth + 1)); + 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 UP(parse_pattern_hash(parser, captures, node, (uint16_t) (depth + 1))); + } + + if ((flags & PM_PARSE_PATTERN_MULTI) && 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 (match7(parser, PM_TOKEN_KEYWORD_THEN, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_SEMICOLON, PM_TOKEN_KEYWORD_AND, PM_TOKEN_KEYWORD_OR)) { + node = UP(pm_implicit_rest_node_create(parser, &parser->previous)); + pm_node_list_append(&nodes, node); + trailing_rest = true; + break; + } + + if (accept1(parser, PM_TOKEN_USTAR)) { + node = UP(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, NULL, PM_ERR_PATTERN_EXPRESSION_AFTER_COMMA, (uint16_t) (depth + 1)); + } + + 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 (leading_rest && PM_NODE_TYPE_P(nodes.nodes[nodes.size - 1], PM_SPLAT_NODE)) { + node = UP(pm_find_pattern_node_create(parser, &nodes)); + + if (nodes.size == 2) { + pm_parser_err_node(parser, node, PM_ERR_PATTERN_FIND_MISSING_INNER); + } + } else { + node = UP(pm_array_pattern_node_node_list_create(parser, &nodes)); + + if (leading_rest && trailing_rest) { + pm_parser_err_node(parser, node, PM_ERR_PATTERN_ARRAY_MULTIPLE_RESTS); + } + } + + 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 = UP(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: { + pm_rational_node_t *cast = (pm_rational_node_t *) node; + cast->base.location.start--; + cast->numerator.negative = true; + break; + } + case PM_IMAGINARY_NODE: + node->location.start--; + parse_negative_numeric(((pm_imaginary_node_t *) node)->numeric); + break; + default: + assert(false && "unreachable"); + break; + } +} + +/** + * 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_HASH_VALUE: + case PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR: { + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, diag_id, pm_token_type_human(parser->current.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; + } + case PM_ERR_UNARY_DISALLOWED: + case PM_ERR_EXPECT_ARGUMENT: { + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, diag_id, pm_token_type_human(parser->current.type)); + 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) { +#define CONTEXT_NONE 0 +#define CONTEXT_THROUGH_ENSURE 1 +#define CONTEXT_THROUGH_ELSE 2 + + pm_context_node_t *context_node = parser->current_context; + int context = CONTEXT_NONE; + + 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. + if (context == CONTEXT_NONE) { + pm_parser_err_node(parser, node, PM_ERR_INVALID_RETRY_WITHOUT_RESCUE); + } else if (context == CONTEXT_THROUGH_ENSURE) { + pm_parser_err_node(parser, node, PM_ERR_INVALID_RETRY_AFTER_ENSURE); + } else if (context == CONTEXT_THROUGH_ELSE) { + pm_parser_err_node(parser, node, PM_ERR_INVALID_RETRY_AFTER_ELSE); + } + 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. + context = CONTEXT_THROUGH_ELSE; + break; + 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. + context = CONTEXT_THROUGH_ENSURE; + break; + 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_BLOCK_PARAMETERS: + 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_LOOP_PREDICATE: + case PM_CONTEXT_MULTI_TARGET: + 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; + } + +#undef CONTEXT_NONE +#undef CONTEXT_ENSURE +#undef CONTEXT_ELSE +} + +/** + * 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_PARAMETERS: + 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_LOOP_PREDICATE: + case PM_CONTEXT_MULTI_TARGET: + 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; + } +} + +/** + * This struct is used to pass information between the regular expression parser + * and the error callback. + */ +typedef struct { + /** The parser that we are parsing the regular expression for. */ + pm_parser_t *parser; + + /** The start of the regular expression. */ + const uint8_t *start; + + /** The end of the regular expression. */ + const uint8_t *end; + + /** + * Whether or not the source of the regular expression is shared. This + * impacts the location of error messages, because if it is shared then we + * can use the location directly and if it is not, then we use the bounds of + * the regular expression itself. + */ + bool shared; +} parse_regular_expression_error_data_t; + +/** + * This callback is called when the regular expression parser encounters a + * syntax error. + */ +static void +parse_regular_expression_error(const uint8_t *start, const uint8_t *end, const char *message, void *data) { + parse_regular_expression_error_data_t *callback_data = (parse_regular_expression_error_data_t *) data; + pm_location_t location; + + if (callback_data->shared) { + location = (pm_location_t) { .start = start, .end = end }; + } else { + location = (pm_location_t) { .start = callback_data->start, .end = callback_data->end }; + } + + PM_PARSER_ERR_FORMAT(callback_data->parser, location.start, location.end, PM_ERR_REGEXP_PARSE_ERROR, message); +} + +/** + * Parse the errors for the regular expression and add them to the parser. + */ +static void +parse_regular_expression_errors(pm_parser_t *parser, pm_regular_expression_node_t *node) { + const pm_string_t *unescaped = &node->unescaped; + parse_regular_expression_error_data_t error_data = { + .parser = parser, + .start = node->base.location.start, + .end = node->base.location.end, + .shared = unescaped->type == PM_STRING_SHARED + }; + + pm_regexp_parse(parser, pm_string_source(unescaped), pm_string_length(unescaped), PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_EXTENDED), NULL, NULL, parse_regular_expression_error, &error_data); +} + +/** + * 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, bool accepts_label, pm_diagnostic_id_t diag_id, uint16_t depth) { + 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)) { + bool accepted_newline = accept1(parser, PM_TOKEN_NEWLINE); + + // Handle the case where we don't have a comma and we have a + // newline followed by a right bracket. + if (accepted_newline && match1(parser, PM_TOKEN_BRACKET_RIGHT)) { + break; + } + + // Ensure that we have a comma between elements in the array. + if (array->elements.size > 0) { + if (accept1(parser, PM_TOKEN_COMMA)) { + // If there was a comma but we also accepts a newline, + // then this is a syntax error. + if (accepted_newline) { + pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA); + } + } else { + // If there was no comma, then we need to add a syntax + // error. + const uint8_t *location = parser->previous.end; + PM_PARSER_ERR_FORMAT(parser, location, location, PM_ERR_ARRAY_SEPARATOR, pm_token_type_human(parser->current.type)); + + parser->previous.start = location; + parser->previous.type = PM_TOKEN_MISSING; + } + } + + // 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, false, PM_ERR_ARRAY_EXPRESSION_AFTER_STAR, (uint16_t) (depth + 1)); + } + + element = UP(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 = UP(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, (uint16_t) (depth + 1)); + } + + pm_static_literals_free(&hash_keys); + parsed_bare_hash = true; + } else { + element = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, true, PM_ERR_ARRAY_EXPRESSION, (uint16_t) (depth + 1)); + + 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, false, PM_ERR_HASH_VALUE, (uint16_t) (depth + 1)); + pm_node_t *assoc = UP(pm_assoc_node_create(parser, element, &operator, value)); + pm_keyword_hash_node_elements_append(hash, assoc); + + element = UP(hash); + if (accept1(parser, PM_TOKEN_COMMA) && !match1(parser, PM_TOKEN_BRACKET_RIGHT)) { + parse_assocs(parser, &hash_keys, element, (uint16_t) (depth + 1)); + } + + 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); + + if (!accept1(parser, PM_TOKEN_BRACKET_RIGHT)) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_ARRAY_TERM, pm_token_type_human(parser->current.type)); + parser->previous.start = parser->previous.end; + parser->previous.type = PM_TOKEN_MISSING; + } + + pm_array_node_close_set(array, &parser->previous); + pm_accepts_block_stack_pop(parser); + + return UP(array); + } + case PM_TOKEN_PARENTHESIS_LEFT: + case PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES: { + pm_token_t opening = parser->current; + pm_node_flags_t flags = 0; + + pm_node_list_t current_block_exits = { 0 }; + pm_node_list_t *previous_block_exits = push_block_exits(parser, ¤t_block_exits); + + parser_lex(parser); + while (true) { + if (accept1(parser, PM_TOKEN_SEMICOLON)) { + flags |= PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS; + } else if (!accept1(parser, PM_TOKEN_NEWLINE)) { + break; + } + } + + // 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(¤t_block_exits); + + return UP(pm_parentheses_node_create(parser, &opening, NULL, &parser->previous, flags)); + } + + // 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, false, PM_ERR_CANNOT_PARSE_EXPRESSION, (uint16_t) (depth + 1)); + 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 = false; + + if (accept1(parser, PM_TOKEN_SEMICOLON)) { + terminator_found = true; + flags |= PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS; + } else if (accept1(parser, PM_TOKEN_NEWLINE)) { + terminator_found = true; + } + + if (terminator_found) { + while (true) { + if (accept1(parser, PM_TOKEN_SEMICOLON)) { + flags |= PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS; + } else if (!accept1(parser, PM_TOKEN_NEWLINE)) { + break; + } + } + } + + // 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(¤t_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; + + pm_node_t *result; + if (match1(parser, PM_TOKEN_COMMA) && (binding_power == PM_BINDING_POWER_STATEMENT)) { + result = parse_targets(parser, UP(multi_target), PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1)); + accept1(parser, PM_TOKEN_NEWLINE); + } else { + result = UP(multi_target); + } + + if (context_p(parser, PM_CONTEXT_MULTI_TARGET)) { + // All set, this is explicitly allowed by the parent + // context. + } else if (context_p(parser, PM_CONTEXT_FOR_INDEX) && match1(parser, PM_TOKEN_KEYWORD_IN)) { + // All set, we're inside a for loop and we're parsing + // multiple targets. + } else if (binding_power != PM_BINDING_POWER_STATEMENT) { + // Multi targets are not allowed when it's not a + // statement level. + pm_parser_err_node(parser, result, PM_ERR_WRITE_TARGET_UNEXPECTED); + } else if (!match2(parser, PM_TOKEN_EQUAL, PM_TOKEN_PARENTHESIS_RIGHT)) { + // Multi targets must be followed by an equal sign in + // order to be valid (or a right parenthesis if they are + // nested). + pm_parser_err_node(parser, result, PM_ERR_WRITE_TARGET_UNEXPECTED); + } + + return result; + } + + // 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, true); + + return UP(pm_parentheses_node_create(parser, &opening, UP(statements), &parser->previous, flags)); + } + + // 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); + flags |= PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS; + + pm_statements_node_t *statements = pm_statements_node_create(parser); + pm_statements_node_body_append(parser, statements, statement, true); + + // If we didn't find a terminator and we didn't find a right + // parenthesis, then this is a syntax error. + if (!terminator_found && !match1(parser, PM_TOKEN_EOF)) { + 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, false, PM_ERR_CANNOT_PARSE_EXPRESSION, (uint16_t) (depth + 1)); + pm_statements_node_body_append(parser, statements, node, true); + + // 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 if (!match1(parser, PM_TOKEN_EOF)) { + // If we're at the end of the file, then we're going to add + // an error after this for the ) anyway. + 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); + + // When we're parsing multi targets, we allow them to be followed by + // a right parenthesis if they are at the statement level. This is + // only possible if they are the final statement in a parentheses. + // We need to explicitly reject that here. + { + pm_node_t *statement = statements->body.nodes[statements->body.size - 1]; + + if (PM_NODE_TYPE_P(statement, 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, statement); + + statement = UP(multi_target); + statements->body.nodes[statements->body.size - 1] = statement; + } + + if (PM_NODE_TYPE_P(statement, PM_MULTI_TARGET_NODE)) { + const uint8_t *offset = statement->location.end; + pm_token_t operator = { .type = PM_TOKEN_EQUAL, .start = offset, .end = offset }; + pm_node_t *value = UP(pm_missing_node_create(parser, offset, offset)); + + statement = UP(pm_multi_write_node_create(parser, (pm_multi_target_node_t *) statement, &operator, value)); + statements->body.nodes[statements->body.size - 1] = statement; + + pm_parser_err_node(parser, statement, PM_ERR_WRITE_TARGET_UNEXPECTED); + } + } + + pop_block_exits(parser, previous_block_exits); + pm_node_list_free(¤t_block_exits); + + pm_void_statements_check(parser, statements, true); + return UP(pm_parentheses_node_create(parser, &opening, UP(statements), &parser->previous, flags)); + } + 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_token_t opening = parser->previous; + pm_hash_node_t *node = pm_hash_node_create(parser, &opening); + + if (!match2(parser, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_EOF)) { + if (current_hash_keys != NULL) { + parse_assocs(parser, current_hash_keys, UP(node), (uint16_t) (depth + 1)); + } else { + pm_static_literals_t hash_keys = { 0 }; + parse_assocs(parser, &hash_keys, UP(node), (uint16_t) (depth + 1)); + pm_static_literals_free(&hash_keys); + } + + accept1(parser, PM_TOKEN_NEWLINE); + } + + pm_accepts_block_stack_pop(parser); + expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_HASH_TERM, &opening); + pm_hash_node_closing_loc_set(node, &parser->previous); + + return UP(node); + } + case PM_TOKEN_CHARACTER_LITERAL: { + pm_token_t closing = not_provided(parser); + pm_node_t *node = UP(pm_string_node_create_current_string( + parser, + &(pm_token_t) { + .type = PM_TOKEN_STRING_BEGIN, + .start = parser->current.start, + .end = parser->current.start + 1 + }, + &(pm_token_t) { + .type = PM_TOKEN_STRING_CONTENT, + .start = parser->current.start + 1, + .end = parser->current.end + }, + &closing + )); + + pm_node_flag_set(node, parse_unescaped_encoding(parser)); + + // Skip past the character literal here, since now we have handled + // parser->explicit_encoding correctly. + parser_lex(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, false, (uint16_t) (depth + 1)); + } + + return node; + } + case PM_TOKEN_CLASS_VARIABLE: { + parser_lex(parser); + pm_node_t *node = UP(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, (uint16_t) (depth + 1)); + } + + 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, (uint16_t) (depth + 1)); + return UP(pm_call_node_fcall_create(parser, &constant, &arguments)); + } + + pm_node_t *node = UP(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, (uint16_t) (depth + 1)); + } + + 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 *node = UP(pm_constant_path_node_create(parser, NULL, &delimiter, &parser->previous)); + + if ((binding_power == PM_BINDING_POWER_STATEMENT) && match1(parser, PM_TOKEN_COMMA)) { + node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1)); + } + + 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, false, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); + + // Unary .. and ... are special because these are non-associative + // operators that can also be unary operators. In this case we need + // to explicitly reject code that has a .. or ... that follows this + // expression. + if (match2(parser, PM_TOKEN_DOT_DOT, PM_TOKEN_DOT_DOT_DOT)) { + pm_parser_err_current(parser, PM_ERR_UNEXPECTED_RANGE_OPERATOR); + } + + return UP(pm_range_node_create(parser, NULL, &operator, right)); + } + case PM_TOKEN_FLOAT: + parser_lex(parser); + return UP(pm_float_node_create(parser, &parser->previous)); + case PM_TOKEN_FLOAT_IMAGINARY: + parser_lex(parser); + return UP(pm_float_node_imaginary_create(parser, &parser->previous)); + case PM_TOKEN_FLOAT_RATIONAL: + parser_lex(parser); + return UP(pm_float_node_rational_create(parser, &parser->previous)); + case PM_TOKEN_FLOAT_RATIONAL_IMAGINARY: + parser_lex(parser); + return UP(pm_float_node_rational_imaginary_create(parser, &parser->previous)); + case PM_TOKEN_NUMBERED_REFERENCE: { + parser_lex(parser); + pm_node_t *node = UP(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, (uint16_t) (depth + 1)); + } + + return node; + } + case PM_TOKEN_GLOBAL_VARIABLE: { + parser_lex(parser); + pm_node_t *node = UP(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, (uint16_t) (depth + 1)); + } + + return node; + } + case PM_TOKEN_BACK_REFERENCE: { + parser_lex(parser); + pm_node_t *node = UP(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, (uint16_t) (depth + 1)); + } + + 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, (uint16_t) (depth + 1))) { + // Since we found arguments, we need to turn off the + // variable call bit in the flags. + pm_node_flag_unset(UP(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; + + const uint8_t *end = pm_arguments_end(&arguments); + if (!end) { + end = call->message_loc.end; + } + call->base.location.end = 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, (uint16_t) (depth + 1)); + pm_call_node_t *fcall = pm_call_node_fcall_create(parser, &identifier, &arguments); + + if (PM_NODE_TYPE_P(node, PM_IT_LOCAL_VARIABLE_READ_NODE)) { + // If we're about to convert an 'it' implicit local + // variable read into a method call, we need to remove + // it from the list of implicit local variables. + pm_node_unreference(parser, node); + } else { + // Otherwise, we're about to convert a regular local + // variable read into a method call, in which case we + // need to indicate that this was not a read for the + // purposes of warnings. + assert(PM_NODE_TYPE_P(node, PM_LOCAL_VARIABLE_READ_NODE)); + + if (pm_token_is_numbered_parameter(identifier.start, identifier.end)) { + pm_node_unreference(parser, node); + } else { + pm_local_variable_read_node_t *cast = (pm_local_variable_read_node_t *) node; + pm_locals_unread(&pm_parser_scope_find(parser, cast->depth)->locals, cast->name); + } + } + + pm_node_destroy(parser, node); + return UP(fcall); + } + } + + if ((binding_power == PM_BINDING_POWER_STATEMENT) && match1(parser, PM_TOKEN_COMMA)) { + node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1)); + } + + 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. + assert(parser->lex_modes.current->mode == PM_LEX_HEREDOC); + pm_heredoc_lex_mode_t lex_mode = parser->lex_modes.current->as.heredoc.base; + + size_t common_whitespace = (size_t) -1; + parser->lex_modes.current->as.heredoc.common_whitespace = &common_whitespace; + + 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. + expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length); + pm_token_t content = parse_strings_empty_content(parser->previous.start); + + if (lex_mode.quote == PM_HEREDOC_QUOTE_BACKTICK) { + node = UP(pm_xstring_node_create_unescaped(parser, &opening, &content, &parser->previous, &PM_STRING_EMPTY)); + } else { + node = UP(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, (uint16_t) (depth + 1))) == 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 = UP(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 (lex_mode.quote == PM_HEREDOC_QUOTE_BACKTICK) { + assert(sizeof(pm_string_node_t) == sizeof(pm_x_string_node_t)); + cast->base.type = PM_X_STRING_NODE; + } + + if (lex_mode.indent == PM_HEREDOC_INDENT_TILDE && (common_whitespace != (size_t) -1) && (common_whitespace != 0)) { + parse_heredoc_dedent_string(&cast->unescaped, common_whitespace); + } + + node = UP(cast); + expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length); + } 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, (uint16_t) (depth + 1))) != NULL) { + pm_node_list_append(&parts, part); + } + } + + // Now that we have all of the parts, create the correct type of + // interpolated node. + if (lex_mode.quote == PM_HEREDOC_QUOTE_BACKTICK) { + pm_interpolated_x_string_node_t *cast = pm_interpolated_xstring_node_create(parser, &opening, &opening); + cast->parts = parts; + + expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length); + pm_interpolated_xstring_node_closing_set(cast, &parser->previous); + + cast->base.location = cast->opening_loc; + node = UP(cast); + } else { + pm_interpolated_string_node_t *cast = pm_interpolated_string_node_create(parser, &opening, &parts, &opening); + pm_node_list_free(&parts); + + expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length); + pm_interpolated_string_node_closing_set(cast, &parser->previous); + + cast->base.location = cast->opening_loc; + node = UP(cast); + } + + // If this is a heredoc that is indented with a ~, then we need + // to dedent each line by the common leading whitespace. + if (lex_mode.indent == PM_HEREDOC_INDENT_TILDE && (common_whitespace != (size_t) -1) && (common_whitespace != 0)) { + pm_node_list_t *nodes; + if (lex_mode.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, false, (uint16_t) (depth + 1)); + } + + return node; + } + case PM_TOKEN_INSTANCE_VARIABLE: { + parser_lex(parser); + pm_node_t *node = UP(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, (uint16_t) (depth + 1)); + } + + return node; + } + case PM_TOKEN_INTEGER: { + pm_node_flags_t base = parser->integer_base; + parser_lex(parser); + return UP(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 UP(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 UP(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 UP(pm_integer_node_rational_imaginary_create(parser, base, &parser->previous)); + } + case PM_TOKEN_KEYWORD___ENCODING__: + parser_lex(parser); + return UP(pm_source_encoding_node_create(parser, &parser->previous)); + case PM_TOKEN_KEYWORD___FILE__: + parser_lex(parser); + return UP(pm_source_file_node_create(parser, &parser->previous)); + case PM_TOKEN_KEYWORD___LINE__: + parser_lex(parser); + return UP(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, (uint16_t) (depth + 1)); + pm_node_t *old_name = parse_alias_argument(parser, false, (uint16_t) (depth + 1)); + + 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 UP(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); + } + } + PRISM_FALLTHROUGH + default: + return UP(pm_alias_method_node_create(parser, &keyword, new_name, old_name)); + } + } + case PM_TOKEN_KEYWORD_CASE: { + size_t opening_newline_index = token_newline_index(parser); + 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, ¤t_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, false, PM_ERR_CASE_EXPRESSION_AFTER_CASE, (uint16_t) (depth + 1)); + while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)); + } + + if (match1(parser, PM_TOKEN_KEYWORD_END)) { + parser_warn_indentation_mismatch(parser, opening_newline_index, &case_keyword, false, false); + parser_lex(parser); + + pop_block_exits(parser, previous_block_exits); + pm_node_list_free(¤t_block_exits); + + pm_parser_err_token(parser, &case_keyword, PM_ERR_CASE_MISSING_CONDITIONS); + return UP(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 (match1(parser, PM_TOKEN_KEYWORD_WHEN)) { + parser_warn_indentation_mismatch(parser, opening_newline_index, &case_keyword, false, true); + parser_lex(parser); + + 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, false, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR, (uint16_t) (depth + 1)); + + pm_splat_node_t *splat_node = pm_splat_node_create(parser, &operator, expression); + pm_when_node_conditions_append(when_node, UP(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, false, PM_ERR_CASE_EXPRESSION_AFTER_WHEN, (uint16_t) (depth + 1)); + 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); + } else if (PM_NODE_TYPE_P(condition, PM_SOURCE_FILE_NODE)) { + pm_node_flag_set(condition, 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, (uint16_t) (depth + 1)); + if (statements != NULL) { + pm_when_node_statements_set(when_node, statements); + } + } + + pm_case_node_condition_append(case_node, UP(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 = UP(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)) { + parser_warn_indentation_mismatch(parser, opening_newline_index, &case_keyword, false, true); + + 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, PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI, PM_ERR_PATTERN_EXPRESSION_AFTER_IN, (uint16_t) (depth + 1)); + + 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, false, PM_ERR_CONDITIONAL_IF_PREDICATE, (uint16_t) (depth + 1)); + pattern = UP(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, false, PM_ERR_CONDITIONAL_UNLESS_PREDICATE, (uint16_t) (depth + 1)); + pattern = UP(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_IN_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, (uint16_t) (depth + 1)); + } + + // 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 = UP(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 = UP(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, (uint16_t) (depth + 1)), &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_else_clause_set((pm_case_node_t *) node, else_node); + } else { + pm_case_match_node_else_clause_set((pm_case_match_node_t *) node, else_node); + } + } + + parser_warn_indentation_mismatch(parser, opening_newline_index, &case_keyword, false, false); + expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CASE_TERM, &case_keyword); + + 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(¤t_block_exits); + + return node; + } + case PM_TOKEN_KEYWORD_BEGIN: { + size_t opening_newline_index = token_newline_index(parser); + 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, ¤t_block_exits); + pm_statements_node_t *begin_statements = NULL; + + if (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) { + pm_accepts_block_stack_push(parser, true); + begin_statements = parse_statements(parser, PM_CONTEXT_BEGIN, (uint16_t) (depth + 1)); + 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, opening_newline_index, &begin_keyword, begin_node, PM_RESCUES_BEGIN, (uint16_t) (depth + 1)); + expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_BEGIN_TERM, &begin_keyword); + + begin_node->base.location.end = parser->previous.end; + pm_begin_node_end_keyword_set(begin_node, &parser->previous); + + pop_block_exits(parser, previous_block_exits); + pm_node_list_free(¤t_block_exits); + + return UP(begin_node); + } + case PM_TOKEN_KEYWORD_BEGIN_UPCASE: { + pm_node_list_t current_block_exits = { 0 }; + pm_node_list_t *previous_block_exits = push_block_exits(parser, ¤t_block_exits); + + 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, (uint16_t) (depth + 1)); + + expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_BEGIN_UPCASE_TERM, &opening); + 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); + } + + flush_block_exits(parser, previous_block_exits); + pm_node_list_free(¤t_block_exits); + + return UP(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) { + pm_token_t next = parser->current; + parse_arguments(parser, &arguments, false, PM_TOKEN_EOF, (uint16_t) (depth + 1)); + + // Reject `foo && return bar`. + if (!accepts_command_call && arguments.arguments != NULL) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, next, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_type_human(next.type)); + } + } + } + + switch (keyword.type) { + case PM_TOKEN_KEYWORD_BREAK: { + pm_node_t *node = UP(pm_break_node_create(parser, &keyword, arguments.arguments)); + if (!parser->partial_script) parse_block_exit(parser, node); + return node; + } + case PM_TOKEN_KEYWORD_NEXT: { + pm_node_t *node = UP(pm_next_node_create(parser, &keyword, arguments.arguments)); + if (!parser->partial_script) parse_block_exit(parser, node); + return node; + } + case PM_TOKEN_KEYWORD_RETURN: { + pm_node_t *node = UP(pm_return_node_create(parser, &keyword, arguments.arguments)); + parse_return(parser, node); + return node; + } + default: + assert(false && "unreachable"); + return UP(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, (uint16_t) (depth + 1)); + + if ( + arguments.opening_loc.start == NULL && + arguments.arguments == NULL && + ((arguments.block == NULL) || PM_NODE_TYPE_P(arguments.block, PM_BLOCK_NODE)) + ) { + return UP(pm_forwarding_super_node_create(parser, &keyword, &arguments)); + } + + return UP(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, (uint16_t) (depth + 1)); + + // 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_unreference(parser, arguments.block); + pm_node_destroy(parser, arguments.block); + arguments.block = NULL; + } + + pm_node_t *node = UP(pm_yield_node_create(parser, &keyword, &arguments.opening_loc, arguments.arguments, &arguments.closing_loc)); + if (!parser->parsing_eval && !parser->partial_script) parse_yield(parser, node); + + return node; + } + case PM_TOKEN_KEYWORD_CLASS: { + size_t opening_newline_index = token_newline_index(parser); + parser_lex(parser); + + pm_token_t class_keyword = parser->previous; + pm_do_loop_stack_push(parser, false); + + pm_node_list_t current_block_exits = { 0 }; + pm_node_list_t *previous_block_exits = push_block_exits(parser, ¤t_block_exits); + + if (accept1(parser, PM_TOKEN_LESS_LESS)) { + pm_token_t operator = parser->previous; + pm_node_t *expression = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, true, false, PM_ERR_EXPECT_EXPRESSION_AFTER_LESS_LESS, (uint16_t) (depth + 1)); + + pm_parser_scope_push(parser, true); + if (!match2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_EXPECT_SINGLETON_CLASS_DELIMITER, pm_token_type_human(parser->current.type)); + } + + pm_node_t *statements = NULL; + if (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) { + pm_accepts_block_stack_push(parser, true); + statements = UP(parse_statements(parser, PM_CONTEXT_SCLASS, (uint16_t) (depth + 1))); + 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 = UP(parse_rescues_implicit_begin(parser, opening_newline_index, &class_keyword, class_keyword.start, (pm_statements_node_t *) statements, PM_RESCUES_SCLASS, (uint16_t) (depth + 1))); + } else { + parser_warn_indentation_mismatch(parser, opening_newline_index, &class_keyword, false, false); + } + + expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CLASS_TERM, &class_keyword); + + 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); + + flush_block_exits(parser, previous_block_exits); + pm_node_list_free(¤t_block_exits); + + return UP(pm_singleton_class_node_create(parser, &locals, &class_keyword, &operator, expression, statements, &parser->previous)); + } + + pm_node_t *constant_path = parse_expression(parser, PM_BINDING_POWER_INDEX, false, false, PM_ERR_CLASS_NAME, (uint16_t) (depth + 1)); + 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, false, PM_ERR_CLASS_SUPERCLASS, (uint16_t) (depth + 1)); + } 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 (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) { + pm_accepts_block_stack_push(parser, true); + statements = UP(parse_statements(parser, PM_CONTEXT_CLASS, (uint16_t) (depth + 1))); + 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 = UP(parse_rescues_implicit_begin(parser, opening_newline_index, &class_keyword, class_keyword.start, (pm_statements_node_t *) statements, PM_RESCUES_CLASS, (uint16_t) (depth + 1))); + } else { + parser_warn_indentation_mismatch(parser, opening_newline_index, &class_keyword, false, false); + } + + expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CLASS_TERM, &class_keyword); + + 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(¤t_block_exits); + + return UP(pm_class_node_create(parser, &locals, &class_keyword, constant_path, &name, &inheritance_operator, superclass, statements, &parser->previous)); + } + case PM_TOKEN_KEYWORD_DEF: { + pm_node_list_t current_block_exits = { 0 }; + pm_node_list_t *previous_block_exits = push_block_exits(parser, ¤t_block_exits); + + pm_token_t def_keyword = parser->current; + size_t opening_newline_index = token_newline_index(parser); + + 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); + + // This will be false if the method name is not a valid identifier + // but could be followed by an operator. + bool valid_name = true; + + 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); + + 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_INSTANCE_VARIABLE: + case PM_TOKEN_CLASS_VARIABLE: + case PM_TOKEN_GLOBAL_VARIABLE: + valid_name = false; + PRISM_FALLTHROUGH + case PM_TOKEN_CONSTANT: + 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 = UP(pm_constant_read_node_create(parser, &identifier)); + break; + case PM_TOKEN_INSTANCE_VARIABLE: + receiver = UP(pm_instance_variable_read_node_create(parser, &identifier)); + break; + case PM_TOKEN_CLASS_VARIABLE: + receiver = UP(pm_class_variable_read_node_create(parser, &identifier)); + break; + case PM_TOKEN_GLOBAL_VARIABLE: + receiver = UP(pm_global_variable_read_node_create(parser, &identifier)); + break; + case PM_TOKEN_KEYWORD_NIL: + receiver = UP(pm_nil_node_create(parser, &identifier)); + break; + case PM_TOKEN_KEYWORD_SELF: + receiver = UP(pm_self_node_create(parser, &identifier)); + break; + case PM_TOKEN_KEYWORD_TRUE: + receiver = UP(pm_true_node_create(parser, &identifier)); + break; + case PM_TOKEN_KEYWORD_FALSE: + receiver = UP(pm_false_node_create(parser, &identifier)); + break; + case PM_TOKEN_KEYWORD___FILE__: + receiver = UP(pm_source_file_node_create(parser, &identifier)); + break; + case PM_TOKEN_KEYWORD___LINE__: + receiver = UP(pm_source_line_node_create(parser, &identifier)); + break; + case PM_TOKEN_KEYWORD___ENCODING__: + receiver = UP(pm_source_encoding_node_create(parser, &identifier)); + break; + default: + break; + } + + name = parse_method_definition_name(parser); + } else { + if (!valid_name) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, identifier, PM_ERR_DEF_NAME, pm_token_type_human(identifier.type)); + } + + 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_COMPOSITION, true, false, PM_ERR_DEF_RECEIVER, (uint16_t) (depth + 1)); + + 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 = UP(pm_parentheses_node_create(parser, &lparen, expression, &rparen, 0)); + + // 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; + + bool accept_endless_def = true; + 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, true, false, (uint16_t) (depth + 1)); + } + + lex_state_set(parser, PM_LEX_STATE_BEG); + parser->command_start = true; + + context_pop(parser); + if (!accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_DEF_PARAMS_TERM_PAREN, pm_token_type_human(parser->current.type)); + parser->previous.start = parser->previous.end; + parser->previous.type = PM_TOKEN_MISSING; + } + + 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, true, false, (uint16_t) (depth + 1)); + + // Reject `def * = 1` and similar. We have to specifically check + // for them because they create ambiguity with optional arguments. + accept_endless_def = false; + + context_pop(parser); + break; + } + default: { + lparen = not_provided(parser); + rparen = not_provided(parser); + params = NULL; + + context_pop(parser); + break; + } + } + + 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); + } + if (!accept_endless_def) { + pm_parser_err_previous(parser, PM_ERR_DEF_ENDLESS_PARAMETERS); + } + if ( + parser->current_context->context == PM_CONTEXT_DEFAULT_PARAMS && + parser->current_context->prev->context == PM_CONTEXT_BLOCK_PARAMETERS + ) { + PM_PARSER_ERR_FORMAT(parser, def_keyword.start, parser->previous.end, PM_ERR_UNEXPECTED_PARAMETER_DEFAULT_VALUE, "endless method definition"); + } + equal = parser->previous; + + context_push(parser, PM_CONTEXT_DEF); + pm_do_loop_stack_push(parser, false); + statements = UP(pm_statements_node_create(parser)); + + bool allow_command_call; + if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_0) { + allow_command_call = accepts_command_call; + } else { + // Allow `def foo = puts "Hello"` but not `private def foo = puts "Hello"` + allow_command_call = binding_power == PM_BINDING_POWER_ASSIGNMENT || binding_power < PM_BINDING_POWER_COMPOSITION; + } + + pm_node_t *statement = parse_expression(parser, PM_BINDING_POWER_DEFINED + 1, allow_command_call, false, PM_ERR_DEF_ENDLESS, (uint16_t) (depth + 1)); + + 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, pm_binding_powers[PM_TOKEN_KEYWORD_RESCUE_MODIFIER].right, false, false, PM_ERR_RESCUE_MODIFIER_VALUE, (uint16_t) (depth + 1)); + context_pop(parser); + + statement = UP(pm_rescue_modifier_node_create(parser, statement, &rescue_keyword, value)); + } + + pm_statements_node_body_append(parser, (pm_statements_node_t *) statements, statement, false); + 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 (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) { + pm_accepts_block_stack_push(parser, true); + statements = UP(parse_statements(parser, PM_CONTEXT_DEF, (uint16_t) (depth + 1))); + pm_accepts_block_stack_pop(parser); + } + + if (match3(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE)) { + assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE)); + statements = UP(parse_rescues_implicit_begin(parser, opening_newline_index, &def_keyword, def_keyword.start, (pm_statements_node_t *) statements, PM_RESCUES_DEF, (uint16_t) (depth + 1))); + } else { + parser_warn_indentation_mismatch(parser, opening_newline_index, &def_keyword, false, false); + } + + pm_accepts_block_stack_pop(parser); + pm_do_loop_stack_pop(parser); + + expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_DEF_TERM, &def_keyword); + 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)); + + flush_block_exits(parser, previous_block_exits); + pm_node_list_free(¤t_block_exits); + + return UP(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); + bool newline = accept1(parser, PM_TOKEN_NEWLINE); + + if (accept1(parser, PM_TOKEN_PARENTHESIS_LEFT)) { + lparen = parser->previous; + + if (newline && accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) { + expression = UP(pm_parentheses_node_create(parser, &lparen, NULL, &parser->previous, 0)); + lparen = not_provided(parser); + rparen = not_provided(parser); + } else { + expression = parse_expression(parser, PM_BINDING_POWER_COMPOSITION, true, false, PM_ERR_DEFINED_EXPRESSION, (uint16_t) (depth + 1)); + + 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, false, PM_ERR_DEFINED_EXPRESSION, (uint16_t) (depth + 1)); + } + + context_pop(parser); + return UP(pm_defined_node_create( + parser, + &lparen, + expression, + &rparen, + &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, (uint16_t) (depth + 1)); + + expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_END_UPCASE_TERM, &opening); + return UP(pm_post_execution_node_create(parser, &keyword, &opening, statements, &parser->previous)); + } + case PM_TOKEN_KEYWORD_FALSE: + parser_lex(parser); + return UP(pm_false_node_create(parser, &parser->previous)); + case PM_TOKEN_KEYWORD_FOR: { + size_t opening_newline_index = token_newline_index(parser); + 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, false, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR, (uint16_t) (depth + 1)); + } + + index = UP(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, false, PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA, (uint16_t) (depth + 1)); + } else { + pm_parser_err_token(parser, &for_keyword, PM_ERR_FOR_INDEX); + index = UP(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, (uint16_t) (depth + 1)); + } else { + index = parse_target(parser, index, false, false); + } + + 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, false, PM_ERR_FOR_COLLECTION, (uint16_t) (depth + 1)); + 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); + if (!match2(parser, PM_TOKEN_SEMICOLON, PM_TOKEN_NEWLINE)) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_EXPECT_FOR_DELIMITER, pm_token_type_human(parser->current.type)); + } + } + + pm_statements_node_t *statements = NULL; + if (!match1(parser, PM_TOKEN_KEYWORD_END)) { + statements = parse_statements(parser, PM_CONTEXT_FOR, (uint16_t) (depth + 1)); + } + + parser_warn_indentation_mismatch(parser, opening_newline_index, &for_keyword, false, false); + expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_FOR_TERM, &for_keyword); + + return UP(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); + } + + size_t opening_newline_index = token_newline_index(parser); + bool if_after_else = parser->previous.type == PM_TOKEN_KEYWORD_ELSE; + parser_lex(parser); + + return parse_conditional(parser, PM_CONTEXT_IF, opening_newline_index, if_after_else, (uint16_t) (depth + 1)); + 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, (uint16_t) (depth + 1)); + + 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, (uint16_t) (depth + 1)); + + if (PM_NODE_TYPE_P(name, PM_MISSING_NODE)) { + pm_node_destroy(parser, name); + break; + } + + pm_undef_node_append(undef, name); + } + } + + return UP(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; + + // If we do not accept a command call, then we also do not accept a + // not without parentheses. In this case we need to reject this + // syntax. + if (!accepts_command_call && !match1(parser, PM_TOKEN_PARENTHESIS_LEFT)) { + if (match1(parser, PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES)) { + pm_parser_err(parser, parser->previous.end, parser->previous.end + 1, PM_ERR_EXPECT_LPAREN_AFTER_NOT_LPAREN); + } else { + accept1(parser, PM_TOKEN_NEWLINE); + pm_parser_err_current(parser, PM_ERR_EXPECT_LPAREN_AFTER_NOT_OTHER); + } + + return UP(pm_missing_node_create(parser, parser->current.start, parser->current.end)); + } + + accept1(parser, PM_TOKEN_NEWLINE); + + if (accept1(parser, PM_TOKEN_PARENTHESIS_LEFT)) { + pm_token_t lparen = parser->previous; + + if (accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) { + receiver = UP(pm_parentheses_node_create(parser, &lparen, NULL, &parser->previous, 0)); + } else { + arguments.opening_loc = PM_LOCATION_TOKEN_VALUE(&lparen); + receiver = parse_expression(parser, PM_BINDING_POWER_COMPOSITION, true, false, PM_ERR_NOT_EXPRESSION, (uint16_t) (depth + 1)); + + 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, false, PM_ERR_NOT_EXPRESSION, (uint16_t) (depth + 1)); + } + + return UP(pm_call_node_not_create(parser, receiver, &message, &arguments)); + } + case PM_TOKEN_KEYWORD_UNLESS: { + size_t opening_newline_index = token_newline_index(parser); + parser_lex(parser); + + return parse_conditional(parser, PM_CONTEXT_UNLESS, opening_newline_index, false, (uint16_t) (depth + 1)); + } + case PM_TOKEN_KEYWORD_MODULE: { + pm_node_list_t current_block_exits = { 0 }; + pm_node_list_t *previous_block_exits = push_block_exits(parser, ¤t_block_exits); + + size_t opening_newline_index = token_newline_index(parser); + parser_lex(parser); + pm_token_t module_keyword = parser->previous; + + pm_node_t *constant_path = parse_expression(parser, PM_BINDING_POWER_INDEX, false, false, PM_ERR_MODULE_NAME, (uint16_t) (depth + 1)); + 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(¤t_block_exits); + + pm_token_t missing = (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end }; + return UP(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); + constant_path = UP(pm_constant_path_node_create(parser, constant_path, &double_colon, &parser->previous)); + } + + // 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 (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) { + pm_accepts_block_stack_push(parser, true); + statements = UP(parse_statements(parser, PM_CONTEXT_MODULE, (uint16_t) (depth + 1))); + pm_accepts_block_stack_pop(parser); + } + + if (match3(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE)) { + assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE)); + statements = UP(parse_rescues_implicit_begin(parser, opening_newline_index, &module_keyword, module_keyword.start, (pm_statements_node_t *) statements, PM_RESCUES_MODULE, (uint16_t) (depth + 1))); + } else { + parser_warn_indentation_mismatch(parser, opening_newline_index, &module_keyword, false, false); + } + + pm_constant_id_list_t locals; + pm_locals_order(parser, &parser->current_scope->locals, &locals, false); + + pm_parser_scope_pop(parser); + expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_MODULE_TERM, &module_keyword); + + 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(¤t_block_exits); + + return UP(pm_module_node_create(parser, &locals, &module_keyword, constant_path, &name, statements, &parser->previous)); + } + case PM_TOKEN_KEYWORD_NIL: + parser_lex(parser); + return UP(pm_nil_node_create(parser, &parser->previous)); + case PM_TOKEN_KEYWORD_REDO: { + parser_lex(parser); + + pm_node_t *node = UP(pm_redo_node_create(parser, &parser->previous)); + if (!parser->partial_script) parse_block_exit(parser, node); + + return node; + } + case PM_TOKEN_KEYWORD_RETRY: { + parser_lex(parser); + + pm_node_t *node = UP(pm_retry_node_create(parser, &parser->previous)); + parse_retry(parser, node); + + return node; + } + case PM_TOKEN_KEYWORD_SELF: + parser_lex(parser); + return UP(pm_self_node_create(parser, &parser->previous)); + case PM_TOKEN_KEYWORD_TRUE: + parser_lex(parser); + return UP(pm_true_node_create(parser, &parser->previous)); + case PM_TOKEN_KEYWORD_UNTIL: { + size_t opening_newline_index = token_newline_index(parser); + + context_push(parser, PM_CONTEXT_LOOP_PREDICATE); + 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, false, PM_ERR_CONDITIONAL_UNTIL_PREDICATE, (uint16_t) (depth + 1)); + + pm_do_loop_stack_pop(parser); + context_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); + expect2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_ERR_CONDITIONAL_UNTIL_PREDICATE); + } + + pm_statements_node_t *statements = NULL; + if (!match1(parser, PM_TOKEN_KEYWORD_END)) { + pm_accepts_block_stack_push(parser, true); + statements = parse_statements(parser, PM_CONTEXT_UNTIL, (uint16_t) (depth + 1)); + pm_accepts_block_stack_pop(parser); + accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON); + } + + parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, false, false); + expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_UNTIL_TERM, &keyword); + + return UP(pm_until_node_create(parser, &keyword, &do_keyword, &parser->previous, predicate, statements, 0)); + } + case PM_TOKEN_KEYWORD_WHILE: { + size_t opening_newline_index = token_newline_index(parser); + + context_push(parser, PM_CONTEXT_LOOP_PREDICATE); + 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, false, PM_ERR_CONDITIONAL_WHILE_PREDICATE, (uint16_t) (depth + 1)); + + pm_do_loop_stack_pop(parser); + context_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); + expect2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_ERR_CONDITIONAL_WHILE_PREDICATE); + } + + pm_statements_node_t *statements = NULL; + if (!match1(parser, PM_TOKEN_KEYWORD_END)) { + pm_accepts_block_stack_push(parser, true); + statements = parse_statements(parser, PM_CONTEXT_WHILE, (uint16_t) (depth + 1)); + pm_accepts_block_stack_pop(parser); + accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON); + } + + parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, false, false); + expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_WHILE_TERM, &keyword); + + return UP(pm_while_node_create(parser, &keyword, &do_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); + pm_node_t *current = NULL; + + while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) { + accept1(parser, PM_TOKEN_WORDS_SEP); + if (match1(parser, PM_TOKEN_STRING_END)) break; + + // Interpolation is not possible but nested heredocs can still lead to + // consecutive (disjoint) string tokens when the final newline is escaped. + while (match1(parser, PM_TOKEN_STRING_CONTENT)) { + pm_token_t opening = not_provided(parser); + pm_token_t closing = not_provided(parser); + + // Record the string node, moving to interpolation if needed. + if (current == NULL) { + current = UP(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)) { + pm_node_t *string = UP(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)) { + 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 = UP(pm_string_node_create_unescaped(parser, &bounds, &content, &bounds, &cast->unescaped)); + pm_node_t *second_string = UP(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 = UP(interpolated); + } else { + assert(false && "unreachable"); + } + } + + if (current) { + pm_array_node_elements_append(array, current); + current = NULL; + } else { + 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 UP(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 = UP(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 = UP(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 = UP(pm_string_node_create_unescaped(parser, &bounds, &content, &bounds, &cast->unescaped)); + pm_node_t *second_string = UP(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 = UP(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 = UP(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 = UP(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 = UP(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, (uint16_t) (depth + 1)); + 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 = UP(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 = UP(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 = UP(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, (uint16_t) (depth + 1)); + 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 UP(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); + pm_node_t *current = NULL; + + while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) { + accept1(parser, PM_TOKEN_WORDS_SEP); + if (match1(parser, PM_TOKEN_STRING_END)) break; + + // Interpolation is not possible but nested heredocs can still lead to + // consecutive (disjoint) string tokens when the final newline is escaped. + while (match1(parser, PM_TOKEN_STRING_CONTENT)) { + pm_token_t opening = not_provided(parser); + pm_token_t closing = not_provided(parser); + + pm_node_t *string = UP(pm_string_node_create_current_string(parser, &opening, &parser->current, &closing)); + + // Record the string node, moving to interpolation if needed. + if (current == NULL) { + current = string; + } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_STRING_NODE)) { + pm_interpolated_string_node_append((pm_interpolated_string_node_t *) current, string); + } else if (PM_NODE_TYPE_P(current, PM_STRING_NODE)) { + pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, &opening, NULL, &closing); + pm_interpolated_string_node_append(interpolated, current); + pm_interpolated_string_node_append(interpolated, string); + current = UP(interpolated); + } else { + assert(false && "unreachable"); + } + parser_lex(parser); + } + + if (current) { + pm_array_node_elements_append(array, current); + current = NULL; + } else { + 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 UP(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 = UP(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((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(interpolated, current); + pm_interpolated_string_node_append(interpolated, string); + current = UP(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 = UP(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(interpolated, current); + current = UP(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, (uint16_t) (depth + 1)); + pm_interpolated_string_node_append((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 = UP(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(interpolated, current); + current = UP(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, (uint16_t) (depth + 1)); + pm_interpolated_string_node_append((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 UP(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 = UP(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_regular_expression_node_t *node = (pm_regular_expression_node_t *) pm_regular_expression_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped); + + // If we're not immediately followed by a =~, then we want + // to parse all of the errors at this point. If it is + // followed by a =~, then it will get parsed higher up while + // parsing the named captures as well. + if (!match1(parser, PM_TOKEN_EQUAL_TILDE)) { + parse_regular_expression_errors(parser, node); + } + + pm_node_flag_set(UP(node), parse_and_validate_regular_expression_encoding(parser, &unescaped, ascii_only, FL(node))); + return UP(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 = UP(pm_string_node_create_unescaped(parser, &opening, &parser->previous, &closing, &unescaped)); + + if (parser->encoding == PM_ENCODING_US_ASCII_ENTRY) { + // This is extremely strange, but the first string part of a + // regular expression will always be tagged as binary if we + // are in a US-ASCII file, no matter its contents. + pm_node_flag_set(part, PM_STRING_FLAGS_FORCED_BINARY_ENCODING); + } + + 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, (uint16_t) (depth + 1))) != 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 UP(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 UP(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 = UP(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 = UP(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, (uint16_t) (depth + 1))) != 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 UP(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 UP(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, false, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR, (uint16_t) (depth + 1)); + } + + pm_node_t *splat = UP(pm_splat_node_create(parser, &operator, name)); + + if (match1(parser, PM_TOKEN_COMMA)) { + return parse_targets_validate(parser, splat, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1)); + } else { + return parse_target_validate(parser, splat, true); + } + } + case PM_TOKEN_BANG: { + if (binding_power > PM_BINDING_POWER_UNARY) { + pm_parser_err_prefix(parser, PM_ERR_UNARY_DISALLOWED); + } + + 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, false, PM_ERR_UNARY_RECEIVER, (uint16_t) (depth + 1)); + pm_call_node_t *node = pm_call_node_unary_create(parser, &operator, receiver, "!"); + + pm_conditional_predicate(parser, receiver, PM_CONDITIONAL_PREDICATE_TYPE_NOT); + return UP(node); + } + case PM_TOKEN_TILDE: { + if (binding_power > PM_BINDING_POWER_UNARY) { + pm_parser_err_prefix(parser, PM_ERR_UNARY_DISALLOWED); + } + parser_lex(parser); + + pm_token_t operator = parser->previous; + pm_node_t *receiver = parse_expression(parser, pm_binding_powers[parser->previous.type].right, false, false, PM_ERR_UNARY_RECEIVER, (uint16_t) (depth + 1)); + pm_call_node_t *node = pm_call_node_unary_create(parser, &operator, receiver, "~"); + + return UP(node); + } + case PM_TOKEN_UMINUS: { + if (binding_power > PM_BINDING_POWER_UNARY) { + pm_parser_err_prefix(parser, PM_ERR_UNARY_DISALLOWED); + } + parser_lex(parser); + + pm_token_t operator = parser->previous; + pm_node_t *receiver = parse_expression(parser, pm_binding_powers[parser->previous.type].right, false, false, PM_ERR_UNARY_RECEIVER, (uint16_t) (depth + 1)); + pm_call_node_t *node = pm_call_node_unary_create(parser, &operator, receiver, "-@"); + + return UP(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, false, PM_ERR_UNARY_RECEIVER, (uint16_t) (depth + 1)); + + 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, false, PM_ERR_EXPECT_ARGUMENT, (uint16_t) (depth + 1)); + node = UP(pm_call_node_binary_create(parser, node, &exponent_operator, exponent, 0)); + node = UP(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 = UP(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; + + size_t opening_newline_index = token_newline_index(parser); + 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: { + 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, true, (uint16_t) (depth + 1)); + } + + 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: { + pm_accepts_block_stack_push(parser, false); + pm_token_t opening = not_provided(parser); + block_parameters = parse_block_parameters(parser, false, &opening, true, false, (uint16_t) (depth + 1)); + 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 (!match1(parser, PM_TOKEN_BRACE_RIGHT)) { + body = UP(parse_statements(parser, PM_CONTEXT_LAMBDA_BRACES, (uint16_t) (depth + 1))); + } + + parser_warn_indentation_mismatch(parser, opening_newline_index, &operator, false, false); + expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_LAMBDA_TERM_BRACE, &opening); + } 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 = UP(parse_statements(parser, PM_CONTEXT_LAMBDA_DO_END, (uint16_t) (depth + 1))); + 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 = UP(parse_rescues_implicit_begin(parser, opening_newline_index, &operator, opening.start, (pm_statements_node_t *) body, PM_RESCUES_LAMBDA, (uint16_t) (depth + 1))); + } else { + parser_warn_indentation_mismatch(parser, opening_newline_index, &operator, false, false); + } + + expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_LAMBDA_TERM_END, &operator); + } + + 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, UP(block_parameters), &operator, &parser->previous); + + pm_parser_scope_pop(parser); + pm_accepts_block_stack_pop(parser); + + return UP(pm_lambda_node_create(parser, &locals, &operator, &opening, &parser->previous, parameters, body)); + } + case PM_TOKEN_UPLUS: { + if (binding_power > PM_BINDING_POWER_UNARY) { + pm_parser_err_prefix(parser, PM_ERR_UNARY_DISALLOWED); + } + parser_lex(parser); + + pm_token_t operator = parser->previous; + pm_node_t *receiver = parse_expression(parser, pm_binding_powers[parser->previous.type].right, false, false, PM_ERR_UNARY_RECEIVER, (uint16_t) (depth + 1)); + pm_call_node_t *node = pm_call_node_unary_create(parser, &operator, receiver, "+@"); + + return UP(node); + } + case PM_TOKEN_STRING_BEGIN: + return parse_strings(parser, NULL, accepts_label, (uint16_t) (depth + 1)); + 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, (uint16_t) (depth + 1)); + } + 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 UP(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, uint16_t depth) { + 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, false, diag_id, (uint16_t) (depth + 1)); + + // 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, pm_binding_powers[PM_TOKEN_KEYWORD_RESCUE_MODIFIER].right, false, false, PM_ERR_RESCUE_MODIFIER_VALUE, (uint16_t) (depth + 1)); + context_pop(parser); + + return UP(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, uint16_t depth) { + bool permitted = true; + if (previous_binding_power != PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_USTAR)) permitted = false; + + 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_MODIFIER, diag_id, (uint16_t) (depth + 1)); + if (!permitted) pm_parser_err_node(parser, value, PM_ERR_UNEXPECTED_MULTI_WRITE); + + 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 = UP(array); + + while (accept1(parser, PM_TOKEN_COMMA)) { + pm_node_t *element = parse_starred_expression(parser, binding_power, false, PM_ERR_ARRAY_ELEMENT, (uint16_t) (depth + 1)); + + 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, pm_binding_powers[PM_TOKEN_KEYWORD_RESCUE_MODIFIER].right, accepts_command_call_inner, false, PM_ERR_RESCUE_MODIFIER_VALUE, (uint16_t) (depth + 1)); + context_pop(parser); + + return UP(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_unreference(parser, UP(call_node->arguments)); + pm_node_destroy(parser, UP(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_unreference(parser, UP(call_node->block)); + pm_node_destroy(parser, UP(call_node->block)); + call_node->block = NULL; + } +} + +/** + * This struct is used to pass information between the regular expression parser + * and the named capture callback. + */ +typedef struct { + /** The parser that is parsing the regular expression. */ + pm_parser_t *parser; + + /** The call node wrapping the regular expression node. */ + pm_call_node_t *call; + + /** The match write node that is being created. */ + pm_match_write_node_t *match; + + /** The list of names that have been parsed. */ + pm_constant_id_list_t names; + + /** + * Whether the content of the regular expression is shared. This impacts + * whether or not we used owned constants or shared constants in the + * constant pool for the names of the captures. + */ + bool shared; +} parse_regular_expression_named_capture_data_t; + +static inline const uint8_t * +pm_named_capture_escape_hex(pm_buffer_t *unescaped, const uint8_t *cursor, const uint8_t *end) { + cursor++; + + if (cursor < end && pm_char_is_hexadecimal_digit(*cursor)) { + uint8_t value = escape_hexadecimal_digit(*cursor); + cursor++; + + if (cursor < end && pm_char_is_hexadecimal_digit(*cursor)) { + value = (uint8_t) ((value << 4) | escape_hexadecimal_digit(*cursor)); + cursor++; + } + + pm_buffer_append_byte(unescaped, value); + } else { + pm_buffer_append_string(unescaped, "\\x", 2); + } + + return cursor; +} + +static inline const uint8_t * +pm_named_capture_escape_octal(pm_buffer_t *unescaped, const uint8_t *cursor, const uint8_t *end) { + uint8_t value = (uint8_t) (*cursor - '0'); + cursor++; + + if (cursor < end && pm_char_is_octal_digit(*cursor)) { + value = ((uint8_t) (value << 3)) | ((uint8_t) (*cursor - '0')); + cursor++; + + if (cursor < end && pm_char_is_octal_digit(*cursor)) { + value = ((uint8_t) (value << 3)) | ((uint8_t) (*cursor - '0')); + cursor++; + } + } + + pm_buffer_append_byte(unescaped, value); + return cursor; +} + +static inline const uint8_t * +pm_named_capture_escape_unicode(pm_parser_t *parser, pm_buffer_t *unescaped, const uint8_t *cursor, const uint8_t *end, const pm_location_t *error_location) { + const uint8_t *start = cursor - 1; + cursor++; + + if (cursor >= end) { + pm_buffer_append_string(unescaped, "\\u", 2); + return cursor; + } + + if (*cursor != '{') { + size_t length = pm_strspn_hexadecimal_digit(cursor, MIN(end - cursor, 4)); + uint32_t value = escape_unicode(parser, cursor, length, error_location); + + if (!pm_buffer_append_unicode_codepoint(unescaped, value)) { + pm_buffer_append_string(unescaped, (const char *) start, (size_t) ((cursor + length) - start)); + } + + return cursor + length; + } + + cursor++; + for (;;) { + while (cursor < end && *cursor == ' ') cursor++; + + if (cursor >= end) break; + if (*cursor == '}') { + cursor++; + break; + } + + size_t length = pm_strspn_hexadecimal_digit(cursor, end - cursor); + if (length == 0) { + break; + } + uint32_t value = escape_unicode(parser, cursor, length, error_location); + + (void) pm_buffer_append_unicode_codepoint(unescaped, value); + cursor += length; + } + + return cursor; +} + +static void +pm_named_capture_escape(pm_parser_t *parser, pm_buffer_t *unescaped, const uint8_t *source, const size_t length, const uint8_t *cursor, const pm_location_t *error_location) { + const uint8_t *end = source + length; + pm_buffer_append_string(unescaped, (const char *) source, (size_t) (cursor - source)); + + for (;;) { + if (++cursor >= end) { + pm_buffer_append_byte(unescaped, '\\'); + return; + } + + switch (*cursor) { + case 'x': + cursor = pm_named_capture_escape_hex(unescaped, cursor, end); + break; + case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': + cursor = pm_named_capture_escape_octal(unescaped, cursor, end); + break; + case 'u': + cursor = pm_named_capture_escape_unicode(parser, unescaped, cursor, end, error_location); + break; + default: + pm_buffer_append_byte(unescaped, '\\'); + break; + } + + const uint8_t *next_cursor = pm_memchr(cursor, '\\', (size_t) (end - cursor), parser->encoding_changed, parser->encoding); + if (next_cursor == NULL) break; + + pm_buffer_append_string(unescaped, (const char *) cursor, (size_t) (next_cursor - cursor)); + cursor = next_cursor; + } + + pm_buffer_append_string(unescaped, (const char *) cursor, (size_t) (end - cursor)); +} + +/** + * This callback is called when the regular expression parser encounters a named + * capture group. + */ +static void +parse_regular_expression_named_capture(const pm_string_t *capture, void *data) { + parse_regular_expression_named_capture_data_t *callback_data = (parse_regular_expression_named_capture_data_t *) data; + + pm_parser_t *parser = callback_data->parser; + pm_call_node_t *call = callback_data->call; + pm_constant_id_list_t *names = &callback_data->names; + + const uint8_t *source = pm_string_source(capture); + size_t length = pm_string_length(capture); + pm_buffer_t unescaped = { 0 }; + + // First, we need to handle escapes within the name of the capture group. + // This is because regular expressions have three different representations + // in prism. The first is the plain source code. The second is the + // representation that will be sent to the regular expression engine, which + // is the value of the "unescaped" field. This is poorly named, because it + // actually still contains escapes, just a subset of them that the regular + // expression engine knows how to handle. The third representation is fully + // unescaped, which is what we need. + const uint8_t *cursor = pm_memchr(source, '\\', length, parser->encoding_changed, parser->encoding); + if (PRISM_UNLIKELY(cursor != NULL)) { + pm_named_capture_escape(parser, &unescaped, source, length, cursor, callback_data->shared ? NULL : &call->receiver->location); + source = (const uint8_t *) pm_buffer_value(&unescaped); + length = pm_buffer_length(&unescaped); + } + + 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 (!pm_slice_is_valid_local(parser, source, source + length)) { + pm_buffer_free(&unescaped); + return; + } + + if (callback_data->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 = (pm_location_t) { .start = call->receiver->location.start, .end = call->receiver->location.end }; + + void *memory = xmalloc(length); + if (memory == NULL) abort(); + + memcpy(memory, source, length); + name = pm_parser_constant_id_owned(parser, (uint8_t *) memory, length); + } + + // Add this name to the list of constants if it is valid, not duplicated, + // and not a keyword. + if (name != 0 && !pm_constant_id_list_includes(names, name)) { + pm_constant_id_list_append(names, name); + + int depth; + if ((depth = pm_parser_local_depth_constant_id(parser, name)) == -1) { + // If the local is not already a local but it is a keyword, then we + // do not want to add a capture for this. + if (pm_local_is_keyword((const char *) source, length)) { + pm_buffer_free(&unescaped); + return; + } + + // If the identifier is not already a local, then we will add it to + // the local table. + 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 (callback_data->match == NULL) { + callback_data->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 = UP(pm_local_variable_target_node_create(parser, &location, name, depth == -1 ? 0 : (uint32_t) depth)); + pm_node_list_append(&callback_data->match->targets, target); + } + + pm_buffer_free(&unescaped); +} + +/** + * 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, bool extended_mode) { + parse_regular_expression_named_capture_data_t callback_data = { + .parser = parser, + .call = call, + .names = { 0 }, + .shared = content->type == PM_STRING_SHARED + }; + + parse_regular_expression_error_data_t error_data = { + .parser = parser, + .start = call->receiver->location.start, + .end = call->receiver->location.end, + .shared = content->type == PM_STRING_SHARED + }; + + pm_regexp_parse(parser, pm_string_source(content), pm_string_length(content), extended_mode, parse_regular_expression_named_capture, &callback_data, parse_regular_expression_error, &error_data); + pm_constant_id_list_free(&callback_data.names); + + if (callback_data.match != NULL) { + return UP(callback_data.match); + } else { + return UP(call); + } +} + +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, uint16_t depth) { + 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); + } + } + PRISM_FALLTHROUGH + case PM_CASE_WRITABLE: { + // When we have `it = value`, we need to add `it` as a local + // variable before parsing the value, in case the value + // references the variable. + if (PM_NODE_TYPE_P(node, PM_IT_LOCAL_VARIABLE_READ_NODE)) { + pm_parser_local_add_location(parser, node->location.start, node->location.end, 0); + } + + 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, (uint16_t) (depth + 1)); + + if (PM_NODE_TYPE_P(node, PM_MULTI_TARGET_NODE) && previous_binding_power != PM_BINDING_POWER_STATEMENT) { + pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_MULTI_WRITE); + } + + 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, (uint16_t) (depth + 1)); + return parse_write(parser, UP(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, (uint16_t) (depth + 1)); + 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); + PRISM_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, (uint16_t) (depth + 1)); + pm_node_t *result = UP(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, (uint16_t) (depth + 1)); + pm_node_t *result = UP(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, (uint16_t) (depth + 1)); + pm_node_t *write = UP(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, (uint16_t) (depth + 1)); + pm_node_t *write = UP(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, (uint16_t) (depth + 1)); + pm_node_t *result = UP(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_IT_LOCAL_VARIABLE_READ_NODE: { + pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2); + 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, (uint16_t) (depth + 1)); + pm_node_t *result = UP(pm_local_variable_and_write_node_create(parser, node, &token, value, name, 0)); + + pm_node_unreference(parser, node); + pm_node_destroy(parser, node); + return result; + } + case PM_LOCAL_VARIABLE_READ_NODE: { + if (pm_token_is_numbered_parameter(node->location.start, node->location.end)) { + PM_PARSER_ERR_FORMAT(parser, node->location.start, node->location.end, PM_ERR_PARAMETER_NUMBERED_RESERVED, node->location.start); + pm_node_unreference(parser, 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, (uint16_t) (depth + 1)); + pm_node_t *result = UP(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: { + 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); + 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, (uint16_t) (depth + 1)); + pm_node_t *result = UP(pm_local_variable_and_write_node_create(parser, UP(cast), &token, value, constant_id, 0)); + + pm_node_destroy(parser, UP(cast)); + return result; + } + + // Move past the token here so that we have already added + // the local variable by this point. + parser_lex(parser); + + // 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, (uint16_t) (depth + 1)); + return UP(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, (uint16_t) (depth + 1)); + return UP(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); + PRISM_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, (uint16_t) (depth + 1)); + pm_node_t *result = UP(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, (uint16_t) (depth + 1)); + pm_node_t *result = UP(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, (uint16_t) (depth + 1)); + pm_node_t *write = UP(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, (uint16_t) (depth + 1)); + pm_node_t *write = UP(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, (uint16_t) (depth + 1)); + pm_node_t *result = UP(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_IT_LOCAL_VARIABLE_READ_NODE: { + pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2); + 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, (uint16_t) (depth + 1)); + pm_node_t *result = UP(pm_local_variable_or_write_node_create(parser, node, &token, value, name, 0)); + + pm_node_unreference(parser, node); + pm_node_destroy(parser, node); + return result; + } + case PM_LOCAL_VARIABLE_READ_NODE: { + if (pm_token_is_numbered_parameter(node->location.start, node->location.end)) { + PM_PARSER_ERR_FORMAT(parser, node->location.start, node->location.end, PM_ERR_PARAMETER_NUMBERED_RESERVED, node->location.start); + pm_node_unreference(parser, 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, (uint16_t) (depth + 1)); + pm_node_t *result = UP(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: { + 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); + 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, (uint16_t) (depth + 1)); + pm_node_t *result = UP(pm_local_variable_or_write_node_create(parser, UP(cast), &token, value, constant_id, 0)); + + pm_node_destroy(parser, UP(cast)); + return result; + } + + // Move past the token here so that we have already added + // the local variable by this point. + parser_lex(parser); + + // 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, (uint16_t) (depth + 1)); + return UP(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, (uint16_t) (depth + 1)); + return UP(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); + PRISM_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, (uint16_t) (depth + 1)); + pm_node_t *result = UP(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, (uint16_t) (depth + 1)); + pm_node_t *result = UP(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, (uint16_t) (depth + 1)); + pm_node_t *write = UP(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, (uint16_t) (depth + 1)); + pm_node_t *write = UP(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, (uint16_t) (depth + 1)); + pm_node_t *result = UP(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_IT_LOCAL_VARIABLE_READ_NODE: { + pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2); + 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, (uint16_t) (depth + 1)); + pm_node_t *result = UP(pm_local_variable_operator_write_node_create(parser, node, &token, value, name, 0)); + + pm_node_unreference(parser, node); + pm_node_destroy(parser, node); + return result; + } + case PM_LOCAL_VARIABLE_READ_NODE: { + if (pm_token_is_numbered_parameter(node->location.start, node->location.end)) { + PM_PARSER_ERR_FORMAT(parser, node->location.start, node->location.end, PM_ERR_PARAMETER_NUMBERED_RESERVED, node->location.start); + pm_node_unreference(parser, 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, (uint16_t) (depth + 1)); + pm_node_t *result = UP(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, (uint16_t) (depth + 1)); + pm_node_t *result = UP(pm_local_variable_operator_write_node_create(parser, UP(cast), &token, value, constant_id, 0)); + + pm_node_destroy(parser, UP(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, (uint16_t) (depth + 1)); + return UP(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, (uint16_t) (depth + 1)); + return UP(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_TOKEN_FORMAT(parser, parser->previous, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, pm_token_type_human(parser->current.type)); + 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, false, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); + return UP(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, false, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); + return UP(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, + // + // /(?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, false, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); + + // 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 = UP(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_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_EXTENDED)); + 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, PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_EXTENDED)); + } + + 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_token_t operator = parser->previous; + switch (PM_NODE_TYPE(node)) { + case PM_RESCUE_MODIFIER_NODE: { + pm_rescue_modifier_node_t *cast = (pm_rescue_modifier_node_t *) node; + if (PM_NODE_TYPE_P(cast->rescue_expression, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->rescue_expression, PM_MATCH_REQUIRED_NODE)) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_type_human(operator.type)); + } + break; + } + case PM_AND_NODE: { + pm_and_node_t *cast = (pm_and_node_t *) node; + if (PM_NODE_TYPE_P(cast->right, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->right, PM_MATCH_REQUIRED_NODE)) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_type_human(operator.type)); + } + break; + } + case PM_OR_NODE: { + pm_or_node_t *cast = (pm_or_node_t *) node; + if (PM_NODE_TYPE_P(cast->right, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->right, PM_MATCH_REQUIRED_NODE)) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_type_human(operator.type)); + } + break; + } + default: + break; + } + + pm_node_t *argument = parse_expression(parser, binding_power, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); + return UP(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, false, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); + return UP(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, (uint16_t) (depth + 1)); + return UP(pm_call_node_shorthand_create(parser, node, &operator, &arguments)); + } + + switch (PM_NODE_TYPE(node)) { + case PM_RESCUE_MODIFIER_NODE: { + pm_rescue_modifier_node_t *cast = (pm_rescue_modifier_node_t *) node; + if (PM_NODE_TYPE_P(cast->rescue_expression, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->rescue_expression, PM_MATCH_REQUIRED_NODE)) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_type_human(operator.type)); + } + break; + } + case PM_AND_NODE: { + pm_and_node_t *cast = (pm_and_node_t *) node; + if (PM_NODE_TYPE_P(cast->right, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->right, PM_MATCH_REQUIRED_NODE)) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_type_human(operator.type)); + } + break; + } + case PM_OR_NODE: { + pm_or_node_t *cast = (pm_or_node_t *) node; + if (PM_NODE_TYPE_P(cast->right, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->right, PM_MATCH_REQUIRED_NODE)) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_type_human(operator.type)); + } + break; + } + default: + break; + } + + 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, (uint16_t) (depth + 1)); + 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, UP(call), PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1)); + } else { + return UP(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, false, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); + } + + return UP(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, false, PM_ERR_CONDITIONAL_IF_PREDICATE, (uint16_t) (depth + 1)); + return UP(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, false, PM_ERR_CONDITIONAL_UNLESS_PREDICATE, (uint16_t) (depth + 1)); + return UP(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, true); + + pm_node_t *predicate = parse_value_expression(parser, binding_power, true, false, PM_ERR_CONDITIONAL_UNTIL_PREDICATE, (uint16_t) (depth + 1)); + return UP(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, true); + + pm_node_t *predicate = parse_value_expression(parser, binding_power, true, false, PM_ERR_CONDITIONAL_WHILE_PREDICATE, (uint16_t) (depth + 1)); + return UP(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, ¤t_block_exits); + + pm_token_t qmark = parser->current; + parser_lex(parser); + + pm_node_t *true_expression = parse_expression(parser, PM_BINDING_POWER_DEFINED, false, false, PM_ERR_TERNARY_EXPRESSION_TRUE, (uint16_t) (depth + 1)); + + 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 = UP(pm_missing_node_create(parser, colon.start, colon.end)); + + context_pop(parser); + pop_block_exits(parser, previous_block_exits); + pm_node_list_free(¤t_block_exits); + + return UP(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, false, PM_ERR_TERNARY_EXPRESSION_FALSE, (uint16_t) (depth + 1)); + + context_pop(parser); + pop_block_exits(parser, previous_block_exits); + pm_node_list_free(¤t_block_exits); + + return UP(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) || + (accepts_command_call && (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, (uint16_t) (depth + 1)); + path = UP(pm_call_node_call_create(parser, node, &delimiter, &message, &arguments)); + } else { + // Otherwise, this is a constant path. That would look like Foo::Bar. + path = UP(pm_constant_path_node_create(parser, node, &delimiter, &parser->previous)); + } + + // 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, (uint16_t) (depth + 1)); + } + + 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, (uint16_t) (depth + 1)); + 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, UP(call), PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1)); + } + + return UP(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, (uint16_t) (depth + 1)); + + return UP(pm_call_node_shorthand_create(parser, node, &delimiter, &arguments)); + } + default: { + expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT); + return UP(pm_constant_path_node_create(parser, node, &delimiter, &parser->previous)); + } + } + } + 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, false, PM_ERR_RESCUE_MODIFIER_VALUE, (uint16_t) (depth + 1)); + context_pop(parser); + + return UP(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, (uint16_t) (depth + 1)); + 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, UP(aref), PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1)); + } + + // 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, (uint16_t) (depth + 1)); + 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, (uint16_t) (depth + 1)); + } + + if (block != NULL) { + if (arguments.block != NULL) { + pm_parser_err_node(parser, UP(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 = UP(block); + } + + return UP(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, PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI, PM_ERR_PATTERN_EXPRESSION_AFTER_IN, (uint16_t) (depth + 1)); + + parser->pattern_matching_newlines = previous_pattern_matching_newlines; + pm_constant_id_list_free(&captures); + + return UP(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, PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI, PM_ERR_PATTERN_EXPRESSION_AFTER_HROCKET, (uint16_t) (depth + 1)); + + parser->pattern_matching_newlines = previous_pattern_matching_newlines; + pm_constant_id_list_free(&captures); + + return UP(pm_match_required_node_create(parser, node, pattern, &operator)); + } + default: + assert(false && "unreachable"); + return NULL; + } +} + +#undef PM_PARSE_PATTERN_SINGLE +#undef PM_PARSE_PATTERN_TOP +#undef PM_PARSE_PATTERN_MULTI + +/** + * Determine if a given call node looks like a "command", which means it has + * arguments but does not have parentheses. + */ +static inline bool +pm_call_node_command_p(const pm_call_node_t *node) { + return ( + (node->opening_loc.start == NULL) && + (node->block == NULL || PM_NODE_TYPE_P(node->block, PM_BLOCK_ARGUMENT_NODE)) && + (node->arguments != NULL || node->block != 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, bool accepts_label, pm_diagnostic_id_t diag_id, uint16_t depth) { + if (PRISM_UNLIKELY(depth >= PRISM_DEPTH_MAXIMUM)) { + pm_parser_err_current(parser, PM_ERR_NESTING_TOO_DEEP); + return UP(pm_missing_node_create(parser, parser->current.start, parser->current.end)); + } + + pm_node_t *node = parse_expression_prefix(parser, binding_power, accepts_command_call, accepts_label, diag_id, depth); + + 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_MULTI_WRITE_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_CALL_NODE: + // If we have a call node, then we need to check if it looks like a + // method call without parentheses that contains arguments. If it + // does, then it has different rules for parsing infix operators, + // namely that it only accepts composition (and/or) and modifiers + // (if/unless/etc.). + if ((pm_binding_powers[parser->current.type].left > PM_BINDING_POWER_COMPOSITION) && pm_call_node_command_p((pm_call_node_t *) node)) { + return node; + } + break; + case PM_SYMBOL_NODE: + // If we have a symbol node that is being parsed as a label, then we + // need to immediately return, because there should never be an + // infix operator following this node. + if (pm_symbol_node_label_p(node)) { + 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; + pm_token_type_t current_token_type; + + while ( + current_token_type = parser->current.type, + current_binding_powers = pm_binding_powers[current_token_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, (uint16_t) (depth + 1)); + + if (context_terminator(parser->current_context->context, &parser->current)) { + // If this token terminates the current context, then we need to + // stop parsing the expression, as it has become a statement. + return node; + } + + switch (PM_NODE_TYPE(node)) { + case PM_MULTI_WRITE_NODE: + // Multi-write nodes 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_CLASS_VARIABLE_WRITE_NODE: + case PM_CONSTANT_PATH_WRITE_NODE: + case PM_CONSTANT_WRITE_NODE: + case PM_GLOBAL_VARIABLE_WRITE_NODE: + case PM_INSTANCE_VARIABLE_WRITE_NODE: + case PM_LOCAL_VARIABLE_WRITE_NODE: + // These expressions are statements, by virtue of the right-hand + // side of their write being an implicit array. + if (PM_NODE_FLAG_P(node, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY) && pm_binding_powers[parser->current.type].left > PM_BINDING_POWER_MODIFIER) { + return node; + } + break; + case PM_CALL_NODE: + // These expressions are also statements, by virtue of the + // right-hand side of the expression (i.e., the last argument to + // the call node) being an implicit array. + if (PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY) && pm_binding_powers[parser->current.type].left > PM_BINDING_POWER_MODIFIER) { + return node; + } + break; + default: + break; + } + + // If the operator is nonassoc and we should not be able to parse the + // upcoming infix operator, break. + if (current_binding_powers.nonassoc) { + // If this is a non-assoc operator and we are about to parse the + // exact same operator, then we need to add an error. + if (match1(parser, current_token_type)) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_NON_ASSOCIATIVE_OPERATOR, pm_token_type_human(parser->current.type), pm_token_type_human(current_token_type)); + break; + } + + // If this is an endless range, then we need to reject a couple of + // additional operators because it violates the normal operator + // precedence rules. Those patterns are: + // + // 1.. & 2 + // 1.. * 2 + // + if (PM_NODE_TYPE_P(node, PM_RANGE_NODE) && ((pm_range_node_t *) node)->right == NULL) { + if (match4(parser, PM_TOKEN_UAMPERSAND, PM_TOKEN_USTAR, PM_TOKEN_DOT, PM_TOKEN_AMPERSAND_DOT)) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_NON_ASSOCIATIVE_OPERATOR, pm_token_type_human(parser->current.type), pm_token_type_human(current_token_type)); + break; + } + + if (PM_BINDING_POWER_TERM <= pm_binding_powers[parser->current.type].left) { + break; + } + } else if (current_binding_powers.left <= pm_binding_powers[parser->current.type].left) { + 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)) { + if (statements == NULL) { + statements = pm_statements_node_create(parser); + } + + pm_arguments_node_t *arguments = pm_arguments_node_create(parser); + pm_arguments_node_arguments_append( + arguments, + UP(pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$_", 2))) + ); + + pm_statements_node_body_append(parser, statements, UP(pm_call_node_fcall_synthesized_create( + parser, + arguments, + pm_parser_constant_id_constant(parser, "print", 5) + )), true); + } + + if (PM_PARSER_COMMAND_LINE_OPTION_N(parser)) { + if (PM_PARSER_COMMAND_LINE_OPTION_A(parser)) { + if (statements == NULL) { + statements = pm_statements_node_create(parser); + } + + pm_arguments_node_t *arguments = pm_arguments_node_create(parser); + pm_arguments_node_arguments_append( + arguments, + UP(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, UP(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), + UP(call) + ); + + pm_statements_node_body_prepend(statements, UP(write)); + } + + pm_arguments_node_t *arguments = pm_arguments_node_create(parser); + pm_arguments_node_arguments_append( + arguments, + UP(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, UP(pm_assoc_node_create( + parser, + UP(pm_symbol_node_synthesized_create(parser, "chomp")), + &(pm_token_t) { .type = PM_TOKEN_NOT_PROVIDED, .start = parser->start, .end = parser->start }, + UP(pm_true_node_synthesized_create(parser)) + ))); + + pm_arguments_node_arguments_append(arguments, UP(keywords)); + pm_node_flag_set(UP(arguments), PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS); + } + + pm_statements_node_t *wrapped_statements = pm_statements_node_create(parser); + pm_statements_node_body_append(parser, wrapped_statements, UP(pm_while_node_synthesized_create( + parser, + UP(pm_call_node_fcall_synthesized_create(parser, arguments, pm_parser_constant_id_constant(parser, "gets", 4))), + statements + )), true); + + 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); + } + + pm_node_list_t current_block_exits = { 0 }; + pm_node_list_t *previous_block_exits = push_block_exits(parser, ¤t_block_exits); + + parser_lex(parser); + pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_MAIN, 0); + + if (statements != NULL && !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); + + // 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); + } else { + flush_block_exits(parser, previous_block_exits); + } + + pm_node_list_free(¤t_block_exits); + + // 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 (statements == NULL) { + statements = pm_statements_node_create(parser); + pm_statements_node_location_set(statements, parser->start, parser->start); + } + + return UP(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 *max = big + big_length - little_length; big <= max; big++) { + if (*big == *little && memcmp(big, little, little_length) == 0) return big; + } + + return NULL; +} + +#ifdef _WIN32 +#define pm_parser_warn_shebang_carriage_return(parser, start, length) ((void) 0) +#else +/** + * 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 - 2] == '\r' && start[length - 1] == '\n') { + pm_parser_warn(parser, start, start + length, PM_WARN_SHEBANG_CARRIAGE_RETURN); + } +} +#endif + +/** + * Process the shebang when initializing the parser. This function assumes that + * the shebang_callback option has already been checked for nullability. + */ +static void +pm_parser_init_shebang(pm_parser_t *parser, const pm_options_t *options, const char *engine, size_t length) { + const char *switches = pm_strnstr(engine, " -", length); + if (switches == NULL) return; + + pm_options_t next_options = *options; + options->shebang_callback( + &next_options, + (const uint8_t *) (switches + 1), + length - ((size_t) (switches - engine)) - 1, + options->shebang_callback_data + ); + + size_t encoding_length; + if ((encoding_length = pm_string_length(&next_options.encoding)) > 0) { + const uint8_t *encoding_source = pm_string_source(&next_options.encoding); + parser_lex_magic_comment_encoding_value(parser, encoding_source, encoding_source + encoding_length); + } + + parser->command_line = next_options.command_line; + parser->frozen_string_literal = next_options.frozen_string_literal; +} + +/** + * 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) { + .node_id = 0, + .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, + .partial_script = false, + .command_start = true, + .recovering = false, + .encoding_locked = 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, + .warn_mismatched_indentation = true + }; + + // 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); + } + + // encoding_locked option + parser->encoding_locked = options->encoding_locked; + + // 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; + + // partial_script + parser->partial_script = options->partial_script; + + // scopes option + parser->parsing_eval = options->scopes_count > 0; + if (parser->parsing_eval) parser->warn_mismatched_indentation = false; + + 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->parameters = ((pm_scope_parameters_t) scope->forwarding) | PM_SCOPE_PARAMETERS_IMPLICIT_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); + } + } + } + + // Now that we have established the user-provided options, check if + // a version was given and parse as the latest version otherwise. + if (parser->version == PM_OPTIONS_VERSION_UNSET) { + parser->version = PM_OPTIONS_VERSION_LATEST; + } + + 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 will do a bit + // of extra processing. + // + // First, we'll indicate that the encoding comment is at the end of the + // shebang. This means that when a shebang is present the encoding comment + // can begin on the second line. + // + // Second, we will check if the shebang includes "ruby". If it does, then we + // we will start parsing from there. We will also potentially warning the + // user if there is a carriage return at the end of the shebang. We will + // also potentially call the shebang callback if this is the main script to + // allow the caller to parse the shebang and find any command-line options. + // If the shebang does not include "ruby" and this is the main script being + // parsed, then we will start searching the file for a shebang that does + // contain "ruby" as if -x were passed on the command line. + const uint8_t *newline = next_newline(parser->current.end, parser->end - parser->current.end); + size_t length = (size_t) ((newline != NULL ? newline : parser->end) - parser->current.end); + + if (length > 2 && parser->current.end[0] == '#' && parser->current.end[1] == '!') { + const char *engine; + + if ((engine = pm_strnstr((const char *) parser->start, "ruby", length)) != NULL) { + if (newline != NULL) { + parser->encoding_comment_start = newline + 1; + + if (options == NULL || options->main_script) { + pm_parser_warn_shebang_carriage_return(parser, parser->start, length + 1); + } + } + + if (options != NULL && options->main_script && options->shebang_callback != NULL) { + pm_parser_init_shebang(parser, options, engine, length - ((size_t) (engine - (const char *) parser->start))); + } + + search_shebang = false; + } else if (options != NULL && options->main_script && !parser->parsing_eval) { + 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] == '!') { + const char *engine; + if ((engine = pm_strnstr((const char *) cursor, "ruby", length)) != NULL) { + found_shebang = true; + + if (newline != NULL) { + pm_parser_warn_shebang_carriage_return(parser, cursor, length + 1); + parser->encoding_comment_start = newline + 1; + } + + if (options != NULL && options->shebang_callback != NULL) { + pm_parser_init_shebang(parser, options, engine, length - ((size_t) (engine - (const char *) cursor))); + } + + 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 *stream_fgets, pm_parse_stream_feof_t *stream_feof) { +#define LINE_SIZE 4096 + char line[LINE_SIZE]; + + while (memset(line, '\n', LINE_SIZE), stream_fgets(line, LINE_SIZE, stream) != NULL) { + size_t length = LINE_SIZE; + while (length > 0 && line[length - 1] == '\n') length--; + + if (length == LINE_SIZE) { + // 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. + length--; + pm_buffer_append_string(buffer, line, length); + continue; + } + + // Append the line to the buffer. + length--; + 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; + } + + // All data should be read via gets. If the string returned by gets + // _doesn't_ end with a newline, then we assume we hit EOF condition. + if (stream_feof(stream)) { + 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 *stream_fgets, pm_parse_stream_feof_t *stream_feof, const pm_options_t *options) { + pm_buffer_init(buffer); + + bool eof = pm_parse_stream_read(buffer, stream, stream_fgets, stream_feof); + + 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, stream_fgets, stream_feof); + + 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 + +// 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 *stream_fgets, pm_parse_stream_feof_t *stream_feof, 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, stream_fgets, stream_feof, &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 + +/******************************************************************************/ +/* Slice queries for the Ruby API */ +/******************************************************************************/ + +/** The category of slice returned from pm_slice_type. */ +typedef enum { + /** Returned when the given encoding name is invalid. */ + PM_SLICE_TYPE_ERROR = -1, + + /** Returned when no other types apply to the slice. */ + PM_SLICE_TYPE_NONE, + + /** Returned when the slice is a valid local variable name. */ + PM_SLICE_TYPE_LOCAL, + + /** Returned when the slice is a valid constant name. */ + PM_SLICE_TYPE_CONSTANT, + + /** Returned when the slice is a valid method name. */ + PM_SLICE_TYPE_METHOD_NAME +} pm_slice_type_t; + +/** + * Check that the slice is a valid local variable name or constant. + */ +pm_slice_type_t +pm_slice_type(const uint8_t *source, size_t length, const char *encoding_name) { + // first, get the right encoding object + const pm_encoding_t *encoding = pm_encoding_find((const uint8_t *) encoding_name, (const uint8_t *) (encoding_name + strlen(encoding_name))); + if (encoding == NULL) return PM_SLICE_TYPE_ERROR; + + // check that there is at least one character + if (length == 0) return PM_SLICE_TYPE_NONE; + + size_t width; + if ((width = encoding->alpha_char(source, (ptrdiff_t) length)) != 0) { + // valid because alphabetical + } else if (*source == '_') { + // valid because underscore + width = 1; + } else if ((*source >= 0x80) && ((width = encoding->char_width(source, (ptrdiff_t) length)) > 0)) { + // valid because multibyte + } else { + // invalid because no match + return PM_SLICE_TYPE_NONE; + } + + // determine the type of the slice based on the first character + const uint8_t *end = source + length; + pm_slice_type_t result = encoding->isupper_char(source, end - source) ? PM_SLICE_TYPE_CONSTANT : PM_SLICE_TYPE_LOCAL; + + // next, iterate through all of the bytes of the string to ensure that they + // are all valid identifier characters + source += width; + + while (source < end) { + if ((width = encoding->alnum_char(source, end - source)) != 0) { + // valid because alphanumeric + source += width; + } else if (*source == '_') { + // valid because underscore + source++; + } else if ((*source >= 0x80) && ((width = encoding->char_width(source, end - source)) > 0)) { + // valid because multibyte + source += width; + } else { + // invalid because no match + break; + } + } + + // accept a ! or ? at the end of the slice as a method name + if (*source == '!' || *source == '?' || *source == '=') { + source++; + result = PM_SLICE_TYPE_METHOD_NAME; + } + + // valid if we are at the end of the slice + return source == end ? result : PM_SLICE_TYPE_NONE; +} + +/** + * Check that the slice is a valid local variable name. + */ +PRISM_EXPORTED_FUNCTION pm_string_query_t +pm_string_query_local(const uint8_t *source, size_t length, const char *encoding_name) { + switch (pm_slice_type(source, length, encoding_name)) { + case PM_SLICE_TYPE_ERROR: + return PM_STRING_QUERY_ERROR; + case PM_SLICE_TYPE_NONE: + case PM_SLICE_TYPE_CONSTANT: + case PM_SLICE_TYPE_METHOD_NAME: + return PM_STRING_QUERY_FALSE; + case PM_SLICE_TYPE_LOCAL: + return PM_STRING_QUERY_TRUE; + } + + assert(false && "unreachable"); + return PM_STRING_QUERY_FALSE; +} + +/** + * Check that the slice is a valid constant name. + */ +PRISM_EXPORTED_FUNCTION pm_string_query_t +pm_string_query_constant(const uint8_t *source, size_t length, const char *encoding_name) { + switch (pm_slice_type(source, length, encoding_name)) { + case PM_SLICE_TYPE_ERROR: + return PM_STRING_QUERY_ERROR; + case PM_SLICE_TYPE_NONE: + case PM_SLICE_TYPE_LOCAL: + case PM_SLICE_TYPE_METHOD_NAME: + return PM_STRING_QUERY_FALSE; + case PM_SLICE_TYPE_CONSTANT: + return PM_STRING_QUERY_TRUE; + } + + assert(false && "unreachable"); + return PM_STRING_QUERY_FALSE; +} + +/** + * Check that the slice is a valid method name. + */ +PRISM_EXPORTED_FUNCTION pm_string_query_t +pm_string_query_method_name(const uint8_t *source, size_t length, const char *encoding_name) { +#define B(p) ((p) ? PM_STRING_QUERY_TRUE : PM_STRING_QUERY_FALSE) +#define C1(c) (*source == c) +#define C2(s) (memcmp(source, s, 2) == 0) +#define C3(s) (memcmp(source, s, 3) == 0) + + switch (pm_slice_type(source, length, encoding_name)) { + case PM_SLICE_TYPE_ERROR: + return PM_STRING_QUERY_ERROR; + case PM_SLICE_TYPE_NONE: + break; + case PM_SLICE_TYPE_LOCAL: + // numbered parameters are not valid method names + return B((length != 2) || (source[0] != '_') || (source[1] == '0') || !pm_char_is_decimal_digit(source[1])); + case PM_SLICE_TYPE_CONSTANT: + // all constants are valid method names + case PM_SLICE_TYPE_METHOD_NAME: + // all method names are valid method names + return PM_STRING_QUERY_TRUE; + } + + switch (length) { + case 1: + return B(C1('&') || C1('`') || C1('!') || C1('^') || C1('>') || C1('<') || C1('-') || C1('%') || C1('|') || C1('+') || C1('/') || C1('*') || C1('~')); + case 2: + return B(C2("!=") || C2("!~") || C2("[]") || C2("==") || C2("=~") || C2(">=") || C2(">>") || C2("<=") || C2("<<") || C2("**")); + case 3: + return B(C3("===") || C3("<=>") || C3("[]=")); + default: + return PM_STRING_QUERY_FALSE; + } + +#undef B +#undef C1 +#undef C2 +#undef C3 +} diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/regexp.c b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/regexp.c new file mode 100644 index 0000000..dcc7476 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/regexp.c @@ -0,0 +1,790 @@ +#include "prism/regexp.h" + +#define PM_REGEXP_PARSE_DEPTH_MAX 4096 + +/** + * This is the parser that is going to handle parsing regular expressions. + */ +typedef struct { + /** The parser that is currently being used. */ + pm_parser_t *parser; + + /** 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; + + /** + * Whether or not the regular expression currently being parsed is in + * extended mode, wherein whitespace is ignored and comments are allowed. + */ + bool extended_mode; + + /** Whether the encoding has changed from the default. */ + bool encoding_changed; + + /** The encoding of the source. */ + const pm_encoding_t *encoding; + + /** The callback to call when a named capture group is found. */ + pm_regexp_name_callback_t name_callback; + + /** The data to pass to the name callback. */ + void *name_data; + + /** The callback to call when a parse error is found. */ + pm_regexp_error_callback_t error_callback; + + /** The data to pass to the error callback. */ + void *error_data; +} pm_regexp_parser_t; + +/** + * Append an error to the parser. + */ +static inline void +pm_regexp_parse_error(pm_regexp_parser_t *parser, const uint8_t *start, const uint8_t *end, const char *message) { + parser->error_callback(start, end, message, parser->error_data); +} + +/** + * This appends a new string to the list of named captures. This function + * assumes the caller has already checked the validity of the name callback. + */ +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); + parser->name_callback(&string, parser->name_data); + 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) { + if (parser->cursor >= parser->end) { + parser->cursor = savepoint; + return true; + } + + 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 + * | + * ; + */ +static bool +pm_regexp_parse_quantifier(pm_regexp_parser_t *parser) { + while (!pm_regexp_char_is_eof(parser)) { + switch (*parser->cursor) { + case '*': + case '+': + case '?': + parser->cursor++; + break; + case '{': + parser->cursor++; + if (!pm_regexp_parse_range_quantifier(parser)) return false; + break; + default: + // In this case there is no quantifier. + return true; + } + } + + 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, uint16_t depth); + +/** + * match-char-set : '[' '^'? (match-range | match-char)* ']' + * ; + */ +static bool +pm_regexp_parse_character_set(pm_regexp_parser_t *parser, uint16_t depth) { + pm_regexp_char_accept(parser, '^'); + + while (!pm_regexp_char_is_eof(parser) && *parser->cursor != ']') { + switch (*parser->cursor++) { + case '[': + pm_regexp_parse_lbracket(parser, (uint16_t) (depth + 1)); + 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, uint16_t depth) { + if (depth >= PM_REGEXP_PARSE_DEPTH_MAX) { + pm_regexp_parse_error(parser, parser->start, parser->end, "parse depth limit over"); + return false; + } + + if ((parser->cursor < parser->end) && parser->cursor[0] == ']') { + parser->cursor++; + pm_regexp_parse_error(parser, parser->cursor - 1, parser->cursor, "empty char-class"); + return true; + } + + 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, depth); +} + +// 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, uint16_t depth); + +/** + * 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; +} + +/** + * True if the given key is set in the options. + */ +static uint8_t +pm_regexp_options_state(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); + return options->values[key]; + } + + 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) - 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, uint16_t depth) { + const uint8_t *group_start = parser->cursor; + + pm_regexp_options_t options; + pm_regexp_options_init(&options); + + // First, parse any options for the group. + if (pm_regexp_char_accept(parser, '?')) { + if (pm_regexp_char_is_eof(parser)) { + pm_regexp_parse_error(parser, group_start, parser->cursor, "end pattern in group"); + return false; + } + + switch (*parser->cursor) { + case '#': { // inline comments + parser->cursor++; + if (pm_regexp_char_is_eof(parser)) { + pm_regexp_parse_error(parser, group_start, parser->cursor, "end pattern in group"); + return false; + } + + 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)) { + pm_regexp_parse_error(parser, group_start, parser->cursor, "end pattern with unmatched parenthesis"); + 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; + } + + if (parser->cursor - start == 1) { + pm_regexp_parse_error(parser, start, parser->cursor, "group name is empty"); + } + + if (parser->name_callback != NULL) { + 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; + } + + if (parser->name_callback != NULL) { + 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 are at the end of the group of options and there is no + // subexpression, then we are going to be setting the options + // for the parent group. In this case we are safe to return now. + if (*parser->cursor == ')') { + if (pm_regexp_options_state(&options, 'x') == PM_REGEXP_OPTION_STATE_ADDED) { + parser->extended_mode = true; + } + + parser->cursor++; + return true; + } + + // If we hit a -, then we're done parsing options. + if (*parser->cursor != '-') break; + + PRISM_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; + } + + // If we are at the end of the group of options and there is no + // subexpression, then we are going to be setting the options + // for the parent group. In this case we are safe to return now. + if (*parser->cursor == ')') { + switch (pm_regexp_options_state(&options, 'x')) { + case PM_REGEXP_OPTION_STATE_ADDED: + parser->extended_mode = true; + break; + case PM_REGEXP_OPTION_STATE_REMOVED: + parser->extended_mode = false; + break; + } + + parser->cursor++; + return true; + } + + break; + default: + parser->cursor++; + pm_regexp_parse_error(parser, parser->cursor - 1, parser->cursor, "undefined group option"); + break; + } + } + + bool extended_mode = parser->extended_mode; + switch (pm_regexp_options_state(&options, 'x')) { + case PM_REGEXP_OPTION_STATE_ADDED: + parser->extended_mode = true; + break; + case PM_REGEXP_OPTION_STATE_REMOVED: + parser->extended_mode = false; + break; + } + + // Now, parse the expressions within this group. + while (!pm_regexp_char_is_eof(parser) && *parser->cursor != ')') { + if (!pm_regexp_parse_expression(parser, (uint16_t) (depth + 1))) { + parser->extended_mode = extended_mode; + return false; + } + pm_regexp_char_accept(parser, '|'); + } + + // Finally, make sure we have a closing parenthesis. + parser->extended_mode = extended_mode; + if (pm_regexp_char_expect(parser, ')')) return true; + + pm_regexp_parse_error(parser, group_start, parser->cursor, "end pattern with unmatched parenthesis"); + return false; +} + +/** + * 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, uint16_t depth) { + switch (*parser->cursor) { + case '^': + case '$': + parser->cursor++; + return pm_regexp_parse_quantifier(parser); + 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, depth) && pm_regexp_parse_quantifier(parser); + case '[': + parser->cursor++; + return pm_regexp_parse_lbracket(parser, depth) && pm_regexp_parse_quantifier(parser); + case '*': + case '?': + case '+': + parser->cursor++; + pm_regexp_parse_error(parser, parser->cursor - 1, parser->cursor, "target of repeat operator is not specified"); + return true; + case ')': + parser->cursor++; + pm_regexp_parse_error(parser, parser->cursor - 1, parser->cursor, "unmatched close parenthesis"); + return true; + case '#': + if (parser->extended_mode) { + if (!pm_regexp_char_find(parser, '\n')) parser->cursor = parser->end; + return true; + } + PRISM_FALLTHROUGH + 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, uint16_t depth) { + if (depth >= PM_REGEXP_PARSE_DEPTH_MAX) { + pm_regexp_parse_error(parser, parser->start, parser->end, "parse depth limit over"); + return false; + } + + if (!pm_regexp_parse_item(parser, depth)) { + return false; + } + + while (!pm_regexp_char_is_eof(parser) && *parser->cursor != ')' && *parser->cursor != '|') { + if (!pm_regexp_parse_item(parser, depth)) { + return false; + } + } + + return true; +} + +/** + * pattern : EOF + * | expression EOF + * | expression '|' pattern + * ; + */ +static bool +pm_regexp_parse_pattern(pm_regexp_parser_t *parser) { + do { + if (pm_regexp_char_is_eof(parser)) return true; + if (!pm_regexp_parse_expression(parser, 0)) return false; + } while (pm_regexp_char_accept(parser, '|')); + + return pm_regexp_char_is_eof(parser); +} + +/** + * Parse a regular expression and extract the names of all of the named capture + * groups. + */ +PRISM_EXPORTED_FUNCTION void +pm_regexp_parse(pm_parser_t *parser, const uint8_t *source, size_t size, bool extended_mode, pm_regexp_name_callback_t name_callback, void *name_data, pm_regexp_error_callback_t error_callback, void *error_data) { + pm_regexp_parse_pattern(&(pm_regexp_parser_t) { + .parser = parser, + .start = source, + .cursor = source, + .end = source + size, + .extended_mode = extended_mode, + .encoding_changed = parser->encoding_changed, + .encoding = parser->encoding, + .name_callback = name_callback, + .name_data = name_data, + .error_callback = error_callback, + .error_data = error_data + }); +} diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/serialize.c b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/serialize.c new file mode 100644 index 0000000..08ffb81 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/serialize.c @@ -0,0 +1,2274 @@ +/* :markup: markdown */ + +/*----------------------------------------------------------------------------*/ +/* This file is generated by the templates/template.rb script and should not */ +/* be modified manually. See */ +/* templates/src/serialize.c.erb */ +/* if you are looking to modify the */ +/* template */ +/*----------------------------------------------------------------------------*/ + +#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 + +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_buffer_append_varuint(buffer, node->node_id); + 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; + case PM_ALIAS_GLOBAL_VARIABLE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_node(parser, (pm_node_t *)((pm_alias_global_variable_node_t *)node)->new_name, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_alias_global_variable_node_t *)node)->old_name, buffer); + pm_serialize_location(parser, &((pm_alias_global_variable_node_t *)node)->keyword_loc, buffer); + break; + } + case PM_ALIAS_METHOD_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_node(parser, (pm_node_t *)((pm_alias_method_node_t *)node)->new_name, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_alias_method_node_t *)node)->old_name, buffer); + pm_serialize_location(parser, &((pm_alias_method_node_t *)node)->keyword_loc, buffer); + break; + } + case PM_ALTERNATION_PATTERN_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_node(parser, (pm_node_t *)((pm_alternation_pattern_node_t *)node)->left, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_alternation_pattern_node_t *)node)->right, buffer); + pm_serialize_location(parser, &((pm_alternation_pattern_node_t *)node)->operator_loc, buffer); + break; + } + case PM_AND_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_node(parser, (pm_node_t *)((pm_and_node_t *)node)->left, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_and_node_t *)node)->right, buffer); + pm_serialize_location(parser, &((pm_and_node_t *)node)->operator_loc, buffer); + break; + } + case PM_ARGUMENTS_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + uint32_t arguments_size = pm_sizet_to_u32(((pm_arguments_node_t *)node)->arguments.size); + pm_buffer_append_varuint(buffer, arguments_size); + for (uint32_t index = 0; index < arguments_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_arguments_node_t *)node)->arguments.nodes[index], buffer); + } + break; + } + case PM_ARRAY_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + uint32_t elements_size = pm_sizet_to_u32(((pm_array_node_t *)node)->elements.size); + pm_buffer_append_varuint(buffer, elements_size); + for (uint32_t index = 0; index < elements_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_array_node_t *)node)->elements.nodes[index], buffer); + } + if (((pm_array_node_t *)node)->opening_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_array_node_t *)node)->opening_loc, buffer); + } + if (((pm_array_node_t *)node)->closing_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_array_node_t *)node)->closing_loc, buffer); + } + break; + } + case PM_ARRAY_PATTERN_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + if (((pm_array_pattern_node_t *)node)->constant == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_array_pattern_node_t *)node)->constant, buffer); + } + uint32_t requireds_size = pm_sizet_to_u32(((pm_array_pattern_node_t *)node)->requireds.size); + pm_buffer_append_varuint(buffer, requireds_size); + for (uint32_t index = 0; index < requireds_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_array_pattern_node_t *)node)->requireds.nodes[index], buffer); + } + if (((pm_array_pattern_node_t *)node)->rest == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_array_pattern_node_t *)node)->rest, buffer); + } + uint32_t posts_size = pm_sizet_to_u32(((pm_array_pattern_node_t *)node)->posts.size); + pm_buffer_append_varuint(buffer, posts_size); + for (uint32_t index = 0; index < posts_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_array_pattern_node_t *)node)->posts.nodes[index], buffer); + } + if (((pm_array_pattern_node_t *)node)->opening_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_array_pattern_node_t *)node)->opening_loc, buffer); + } + if (((pm_array_pattern_node_t *)node)->closing_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_array_pattern_node_t *)node)->closing_loc, buffer); + } + break; + } + case PM_ASSOC_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_node(parser, (pm_node_t *)((pm_assoc_node_t *)node)->key, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_assoc_node_t *)node)->value, buffer); + if (((pm_assoc_node_t *)node)->operator_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_assoc_node_t *)node)->operator_loc, buffer); + } + break; + } + case PM_ASSOC_SPLAT_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + if (((pm_assoc_splat_node_t *)node)->value == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_assoc_splat_node_t *)node)->value, buffer); + } + pm_serialize_location(parser, &((pm_assoc_splat_node_t *)node)->operator_loc, buffer); + break; + } + case PM_BACK_REFERENCE_READ_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_back_reference_read_node_t *)node)->name)); + break; + } + case PM_BEGIN_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + if (((pm_begin_node_t *)node)->begin_keyword_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_begin_node_t *)node)->begin_keyword_loc, buffer); + } + if (((pm_begin_node_t *)node)->statements == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_begin_node_t *)node)->statements, buffer); + } + if (((pm_begin_node_t *)node)->rescue_clause == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_begin_node_t *)node)->rescue_clause, buffer); + } + if (((pm_begin_node_t *)node)->else_clause == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_begin_node_t *)node)->else_clause, buffer); + } + if (((pm_begin_node_t *)node)->ensure_clause == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_begin_node_t *)node)->ensure_clause, buffer); + } + if (((pm_begin_node_t *)node)->end_keyword_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_begin_node_t *)node)->end_keyword_loc, buffer); + } + break; + } + case PM_BLOCK_ARGUMENT_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + if (((pm_block_argument_node_t *)node)->expression == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_block_argument_node_t *)node)->expression, buffer); + } + pm_serialize_location(parser, &((pm_block_argument_node_t *)node)->operator_loc, buffer); + break; + } + case PM_BLOCK_LOCAL_VARIABLE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_block_local_variable_node_t *)node)->name)); + break; + } + case PM_BLOCK_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + uint32_t locals_size = pm_sizet_to_u32(((pm_block_node_t *)node)->locals.size); + pm_buffer_append_varuint(buffer, locals_size); + for (uint32_t index = 0; index < locals_size; index++) { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_block_node_t *)node)->locals.ids[index])); + } + if (((pm_block_node_t *)node)->parameters == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_block_node_t *)node)->parameters, buffer); + } + if (((pm_block_node_t *)node)->body == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_block_node_t *)node)->body, buffer); + } + pm_serialize_location(parser, &((pm_block_node_t *)node)->opening_loc, buffer); + pm_serialize_location(parser, &((pm_block_node_t *)node)->closing_loc, buffer); + break; + } + case PM_BLOCK_PARAMETER_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_block_parameter_node_t *)node)->name)); + if (((pm_block_parameter_node_t *)node)->name_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_block_parameter_node_t *)node)->name_loc, buffer); + } + pm_serialize_location(parser, &((pm_block_parameter_node_t *)node)->operator_loc, buffer); + break; + } + case PM_BLOCK_PARAMETERS_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + if (((pm_block_parameters_node_t *)node)->parameters == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_block_parameters_node_t *)node)->parameters, buffer); + } + uint32_t locals_size = pm_sizet_to_u32(((pm_block_parameters_node_t *)node)->locals.size); + pm_buffer_append_varuint(buffer, locals_size); + for (uint32_t index = 0; index < locals_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_block_parameters_node_t *)node)->locals.nodes[index], buffer); + } + if (((pm_block_parameters_node_t *)node)->opening_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_block_parameters_node_t *)node)->opening_loc, buffer); + } + if (((pm_block_parameters_node_t *)node)->closing_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_block_parameters_node_t *)node)->closing_loc, buffer); + } + break; + } + case PM_BREAK_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + if (((pm_break_node_t *)node)->arguments == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_break_node_t *)node)->arguments, buffer); + } + pm_serialize_location(parser, &((pm_break_node_t *)node)->keyword_loc, buffer); + break; + } + case PM_CALL_AND_WRITE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + if (((pm_call_and_write_node_t *)node)->receiver == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_call_and_write_node_t *)node)->receiver, buffer); + } + if (((pm_call_and_write_node_t *)node)->call_operator_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_call_and_write_node_t *)node)->call_operator_loc, buffer); + } + if (((pm_call_and_write_node_t *)node)->message_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_call_and_write_node_t *)node)->message_loc, buffer); + } + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_call_and_write_node_t *)node)->read_name)); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_call_and_write_node_t *)node)->write_name)); + pm_serialize_location(parser, &((pm_call_and_write_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_call_and_write_node_t *)node)->value, buffer); + break; + } + case PM_CALL_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + if (((pm_call_node_t *)node)->receiver == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_call_node_t *)node)->receiver, buffer); + } + if (((pm_call_node_t *)node)->call_operator_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_call_node_t *)node)->call_operator_loc, buffer); + } + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_call_node_t *)node)->name)); + if (((pm_call_node_t *)node)->message_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_call_node_t *)node)->message_loc, buffer); + } + if (((pm_call_node_t *)node)->opening_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_call_node_t *)node)->opening_loc, buffer); + } + if (((pm_call_node_t *)node)->arguments == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_call_node_t *)node)->arguments, buffer); + } + if (((pm_call_node_t *)node)->closing_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_call_node_t *)node)->closing_loc, buffer); + } + if (((pm_call_node_t *)node)->equal_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_call_node_t *)node)->equal_loc, buffer); + } + if (((pm_call_node_t *)node)->block == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_call_node_t *)node)->block, buffer); + } + break; + } + case PM_CALL_OPERATOR_WRITE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + if (((pm_call_operator_write_node_t *)node)->receiver == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_call_operator_write_node_t *)node)->receiver, buffer); + } + if (((pm_call_operator_write_node_t *)node)->call_operator_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_call_operator_write_node_t *)node)->call_operator_loc, buffer); + } + if (((pm_call_operator_write_node_t *)node)->message_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_call_operator_write_node_t *)node)->message_loc, buffer); + } + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_call_operator_write_node_t *)node)->read_name)); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_call_operator_write_node_t *)node)->write_name)); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_call_operator_write_node_t *)node)->binary_operator)); + pm_serialize_location(parser, &((pm_call_operator_write_node_t *)node)->binary_operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_call_operator_write_node_t *)node)->value, buffer); + break; + } + case PM_CALL_OR_WRITE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + if (((pm_call_or_write_node_t *)node)->receiver == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_call_or_write_node_t *)node)->receiver, buffer); + } + if (((pm_call_or_write_node_t *)node)->call_operator_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_call_or_write_node_t *)node)->call_operator_loc, buffer); + } + if (((pm_call_or_write_node_t *)node)->message_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_call_or_write_node_t *)node)->message_loc, buffer); + } + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_call_or_write_node_t *)node)->read_name)); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_call_or_write_node_t *)node)->write_name)); + pm_serialize_location(parser, &((pm_call_or_write_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_call_or_write_node_t *)node)->value, buffer); + break; + } + case PM_CALL_TARGET_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_node(parser, (pm_node_t *)((pm_call_target_node_t *)node)->receiver, buffer); + pm_serialize_location(parser, &((pm_call_target_node_t *)node)->call_operator_loc, buffer); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_call_target_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_call_target_node_t *)node)->message_loc, buffer); + break; + } + case PM_CAPTURE_PATTERN_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_node(parser, (pm_node_t *)((pm_capture_pattern_node_t *)node)->value, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_capture_pattern_node_t *)node)->target, buffer); + pm_serialize_location(parser, &((pm_capture_pattern_node_t *)node)->operator_loc, buffer); + break; + } + case PM_CASE_MATCH_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + if (((pm_case_match_node_t *)node)->predicate == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_case_match_node_t *)node)->predicate, buffer); + } + uint32_t conditions_size = pm_sizet_to_u32(((pm_case_match_node_t *)node)->conditions.size); + pm_buffer_append_varuint(buffer, conditions_size); + for (uint32_t index = 0; index < conditions_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_case_match_node_t *)node)->conditions.nodes[index], buffer); + } + if (((pm_case_match_node_t *)node)->else_clause == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_case_match_node_t *)node)->else_clause, buffer); + } + pm_serialize_location(parser, &((pm_case_match_node_t *)node)->case_keyword_loc, buffer); + pm_serialize_location(parser, &((pm_case_match_node_t *)node)->end_keyword_loc, buffer); + break; + } + case PM_CASE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + if (((pm_case_node_t *)node)->predicate == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_case_node_t *)node)->predicate, buffer); + } + uint32_t conditions_size = pm_sizet_to_u32(((pm_case_node_t *)node)->conditions.size); + pm_buffer_append_varuint(buffer, conditions_size); + for (uint32_t index = 0; index < conditions_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_case_node_t *)node)->conditions.nodes[index], buffer); + } + if (((pm_case_node_t *)node)->else_clause == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_case_node_t *)node)->else_clause, buffer); + } + pm_serialize_location(parser, &((pm_case_node_t *)node)->case_keyword_loc, buffer); + pm_serialize_location(parser, &((pm_case_node_t *)node)->end_keyword_loc, buffer); + break; + } + case PM_CLASS_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + uint32_t locals_size = pm_sizet_to_u32(((pm_class_node_t *)node)->locals.size); + pm_buffer_append_varuint(buffer, locals_size); + for (uint32_t index = 0; index < locals_size; index++) { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_class_node_t *)node)->locals.ids[index])); + } + pm_serialize_location(parser, &((pm_class_node_t *)node)->class_keyword_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_class_node_t *)node)->constant_path, buffer); + if (((pm_class_node_t *)node)->inheritance_operator_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_class_node_t *)node)->inheritance_operator_loc, buffer); + } + if (((pm_class_node_t *)node)->superclass == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_class_node_t *)node)->superclass, buffer); + } + if (((pm_class_node_t *)node)->body == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_class_node_t *)node)->body, buffer); + } + pm_serialize_location(parser, &((pm_class_node_t *)node)->end_keyword_loc, buffer); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_class_node_t *)node)->name)); + break; + } + case PM_CLASS_VARIABLE_AND_WRITE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_class_variable_and_write_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_class_variable_and_write_node_t *)node)->name_loc, buffer); + pm_serialize_location(parser, &((pm_class_variable_and_write_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_class_variable_and_write_node_t *)node)->value, buffer); + break; + } + case PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_class_variable_operator_write_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_class_variable_operator_write_node_t *)node)->name_loc, buffer); + pm_serialize_location(parser, &((pm_class_variable_operator_write_node_t *)node)->binary_operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_class_variable_operator_write_node_t *)node)->value, buffer); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_class_variable_operator_write_node_t *)node)->binary_operator)); + break; + } + case PM_CLASS_VARIABLE_OR_WRITE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_class_variable_or_write_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_class_variable_or_write_node_t *)node)->name_loc, buffer); + pm_serialize_location(parser, &((pm_class_variable_or_write_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_class_variable_or_write_node_t *)node)->value, buffer); + break; + } + case PM_CLASS_VARIABLE_READ_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_class_variable_read_node_t *)node)->name)); + break; + } + case PM_CLASS_VARIABLE_TARGET_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_class_variable_target_node_t *)node)->name)); + break; + } + case PM_CLASS_VARIABLE_WRITE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_class_variable_write_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_class_variable_write_node_t *)node)->name_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_class_variable_write_node_t *)node)->value, buffer); + pm_serialize_location(parser, &((pm_class_variable_write_node_t *)node)->operator_loc, buffer); + break; + } + case PM_CONSTANT_AND_WRITE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_constant_and_write_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_constant_and_write_node_t *)node)->name_loc, buffer); + pm_serialize_location(parser, &((pm_constant_and_write_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_constant_and_write_node_t *)node)->value, buffer); + break; + } + case PM_CONSTANT_OPERATOR_WRITE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_constant_operator_write_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_constant_operator_write_node_t *)node)->name_loc, buffer); + pm_serialize_location(parser, &((pm_constant_operator_write_node_t *)node)->binary_operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_constant_operator_write_node_t *)node)->value, buffer); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_constant_operator_write_node_t *)node)->binary_operator)); + break; + } + case PM_CONSTANT_OR_WRITE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_constant_or_write_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_constant_or_write_node_t *)node)->name_loc, buffer); + pm_serialize_location(parser, &((pm_constant_or_write_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_constant_or_write_node_t *)node)->value, buffer); + break; + } + case PM_CONSTANT_PATH_AND_WRITE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_node(parser, (pm_node_t *)((pm_constant_path_and_write_node_t *)node)->target, buffer); + pm_serialize_location(parser, &((pm_constant_path_and_write_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_constant_path_and_write_node_t *)node)->value, buffer); + break; + } + case PM_CONSTANT_PATH_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + if (((pm_constant_path_node_t *)node)->parent == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_constant_path_node_t *)node)->parent, buffer); + } + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_constant_path_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_constant_path_node_t *)node)->delimiter_loc, buffer); + pm_serialize_location(parser, &((pm_constant_path_node_t *)node)->name_loc, buffer); + break; + } + case PM_CONSTANT_PATH_OPERATOR_WRITE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_node(parser, (pm_node_t *)((pm_constant_path_operator_write_node_t *)node)->target, buffer); + pm_serialize_location(parser, &((pm_constant_path_operator_write_node_t *)node)->binary_operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_constant_path_operator_write_node_t *)node)->value, buffer); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_constant_path_operator_write_node_t *)node)->binary_operator)); + break; + } + case PM_CONSTANT_PATH_OR_WRITE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_node(parser, (pm_node_t *)((pm_constant_path_or_write_node_t *)node)->target, buffer); + pm_serialize_location(parser, &((pm_constant_path_or_write_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_constant_path_or_write_node_t *)node)->value, buffer); + break; + } + case PM_CONSTANT_PATH_TARGET_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + if (((pm_constant_path_target_node_t *)node)->parent == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_constant_path_target_node_t *)node)->parent, buffer); + } + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_constant_path_target_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_constant_path_target_node_t *)node)->delimiter_loc, buffer); + pm_serialize_location(parser, &((pm_constant_path_target_node_t *)node)->name_loc, buffer); + break; + } + case PM_CONSTANT_PATH_WRITE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_node(parser, (pm_node_t *)((pm_constant_path_write_node_t *)node)->target, buffer); + pm_serialize_location(parser, &((pm_constant_path_write_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_constant_path_write_node_t *)node)->value, buffer); + break; + } + case PM_CONSTANT_READ_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_constant_read_node_t *)node)->name)); + break; + } + case PM_CONSTANT_TARGET_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_constant_target_node_t *)node)->name)); + break; + } + case PM_CONSTANT_WRITE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_constant_write_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_constant_write_node_t *)node)->name_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_constant_write_node_t *)node)->value, buffer); + pm_serialize_location(parser, &((pm_constant_write_node_t *)node)->operator_loc, buffer); + break; + } + case PM_DEF_NODE: { + // 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 */ + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_def_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_def_node_t *)node)->name_loc, buffer); + if (((pm_def_node_t *)node)->receiver == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_def_node_t *)node)->receiver, buffer); + } + if (((pm_def_node_t *)node)->parameters == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_def_node_t *)node)->parameters, buffer); + } + if (((pm_def_node_t *)node)->body == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_def_node_t *)node)->body, buffer); + } + uint32_t locals_size = pm_sizet_to_u32(((pm_def_node_t *)node)->locals.size); + pm_buffer_append_varuint(buffer, locals_size); + for (uint32_t index = 0; index < locals_size; index++) { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_def_node_t *)node)->locals.ids[index])); + } + pm_serialize_location(parser, &((pm_def_node_t *)node)->def_keyword_loc, buffer); + if (((pm_def_node_t *)node)->operator_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_def_node_t *)node)->operator_loc, buffer); + } + if (((pm_def_node_t *)node)->lparen_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_def_node_t *)node)->lparen_loc, buffer); + } + if (((pm_def_node_t *)node)->rparen_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_def_node_t *)node)->rparen_loc, buffer); + } + if (((pm_def_node_t *)node)->equal_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_def_node_t *)node)->equal_loc, buffer); + } + if (((pm_def_node_t *)node)->end_keyword_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_def_node_t *)node)->end_keyword_loc, buffer); + } + // serialize length + uint32_t length = pm_sizet_to_u32(buffer->length - offset - sizeof(uint32_t)); + memcpy(buffer->value + length_offset, &length, sizeof(uint32_t)); + break; + } + case PM_DEFINED_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + if (((pm_defined_node_t *)node)->lparen_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_defined_node_t *)node)->lparen_loc, buffer); + } + pm_serialize_node(parser, (pm_node_t *)((pm_defined_node_t *)node)->value, buffer); + if (((pm_defined_node_t *)node)->rparen_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_defined_node_t *)node)->rparen_loc, buffer); + } + pm_serialize_location(parser, &((pm_defined_node_t *)node)->keyword_loc, buffer); + break; + } + case PM_ELSE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_location(parser, &((pm_else_node_t *)node)->else_keyword_loc, buffer); + if (((pm_else_node_t *)node)->statements == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_else_node_t *)node)->statements, buffer); + } + if (((pm_else_node_t *)node)->end_keyword_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_else_node_t *)node)->end_keyword_loc, buffer); + } + break; + } + case PM_EMBEDDED_STATEMENTS_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_location(parser, &((pm_embedded_statements_node_t *)node)->opening_loc, buffer); + if (((pm_embedded_statements_node_t *)node)->statements == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_embedded_statements_node_t *)node)->statements, buffer); + } + pm_serialize_location(parser, &((pm_embedded_statements_node_t *)node)->closing_loc, buffer); + break; + } + case PM_EMBEDDED_VARIABLE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_location(parser, &((pm_embedded_variable_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_embedded_variable_node_t *)node)->variable, buffer); + break; + } + case PM_ENSURE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_location(parser, &((pm_ensure_node_t *)node)->ensure_keyword_loc, buffer); + if (((pm_ensure_node_t *)node)->statements == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_ensure_node_t *)node)->statements, buffer); + } + pm_serialize_location(parser, &((pm_ensure_node_t *)node)->end_keyword_loc, buffer); + break; + } + case PM_FALSE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + break; + } + case PM_FIND_PATTERN_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + if (((pm_find_pattern_node_t *)node)->constant == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_find_pattern_node_t *)node)->constant, buffer); + } + pm_serialize_node(parser, (pm_node_t *)((pm_find_pattern_node_t *)node)->left, buffer); + uint32_t requireds_size = pm_sizet_to_u32(((pm_find_pattern_node_t *)node)->requireds.size); + pm_buffer_append_varuint(buffer, requireds_size); + for (uint32_t index = 0; index < requireds_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_find_pattern_node_t *)node)->requireds.nodes[index], buffer); + } + pm_serialize_node(parser, (pm_node_t *)((pm_find_pattern_node_t *)node)->right, buffer); + if (((pm_find_pattern_node_t *)node)->opening_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_find_pattern_node_t *)node)->opening_loc, buffer); + } + if (((pm_find_pattern_node_t *)node)->closing_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_find_pattern_node_t *)node)->closing_loc, buffer); + } + break; + } + case PM_FLIP_FLOP_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + if (((pm_flip_flop_node_t *)node)->left == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_flip_flop_node_t *)node)->left, buffer); + } + if (((pm_flip_flop_node_t *)node)->right == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_flip_flop_node_t *)node)->right, buffer); + } + pm_serialize_location(parser, &((pm_flip_flop_node_t *)node)->operator_loc, buffer); + break; + } + case PM_FLOAT_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_buffer_append_double(buffer, ((pm_float_node_t *)node)->value); + break; + } + case PM_FOR_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_node(parser, (pm_node_t *)((pm_for_node_t *)node)->index, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_for_node_t *)node)->collection, buffer); + if (((pm_for_node_t *)node)->statements == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_for_node_t *)node)->statements, buffer); + } + pm_serialize_location(parser, &((pm_for_node_t *)node)->for_keyword_loc, buffer); + pm_serialize_location(parser, &((pm_for_node_t *)node)->in_keyword_loc, buffer); + if (((pm_for_node_t *)node)->do_keyword_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_for_node_t *)node)->do_keyword_loc, buffer); + } + pm_serialize_location(parser, &((pm_for_node_t *)node)->end_keyword_loc, buffer); + break; + } + case PM_FORWARDING_ARGUMENTS_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + break; + } + case PM_FORWARDING_PARAMETER_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + break; + } + case PM_FORWARDING_SUPER_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + if (((pm_forwarding_super_node_t *)node)->block == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_forwarding_super_node_t *)node)->block, buffer); + } + break; + } + case PM_GLOBAL_VARIABLE_AND_WRITE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_global_variable_and_write_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_global_variable_and_write_node_t *)node)->name_loc, buffer); + pm_serialize_location(parser, &((pm_global_variable_and_write_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_global_variable_and_write_node_t *)node)->value, buffer); + break; + } + case PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_global_variable_operator_write_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_global_variable_operator_write_node_t *)node)->name_loc, buffer); + pm_serialize_location(parser, &((pm_global_variable_operator_write_node_t *)node)->binary_operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_global_variable_operator_write_node_t *)node)->value, buffer); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_global_variable_operator_write_node_t *)node)->binary_operator)); + break; + } + case PM_GLOBAL_VARIABLE_OR_WRITE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_global_variable_or_write_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_global_variable_or_write_node_t *)node)->name_loc, buffer); + pm_serialize_location(parser, &((pm_global_variable_or_write_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_global_variable_or_write_node_t *)node)->value, buffer); + break; + } + case PM_GLOBAL_VARIABLE_READ_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_global_variable_read_node_t *)node)->name)); + break; + } + case PM_GLOBAL_VARIABLE_TARGET_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_global_variable_target_node_t *)node)->name)); + break; + } + case PM_GLOBAL_VARIABLE_WRITE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_global_variable_write_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_global_variable_write_node_t *)node)->name_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_global_variable_write_node_t *)node)->value, buffer); + pm_serialize_location(parser, &((pm_global_variable_write_node_t *)node)->operator_loc, buffer); + break; + } + case PM_HASH_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_location(parser, &((pm_hash_node_t *)node)->opening_loc, buffer); + uint32_t elements_size = pm_sizet_to_u32(((pm_hash_node_t *)node)->elements.size); + pm_buffer_append_varuint(buffer, elements_size); + for (uint32_t index = 0; index < elements_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_hash_node_t *)node)->elements.nodes[index], buffer); + } + pm_serialize_location(parser, &((pm_hash_node_t *)node)->closing_loc, buffer); + break; + } + case PM_HASH_PATTERN_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + if (((pm_hash_pattern_node_t *)node)->constant == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_hash_pattern_node_t *)node)->constant, buffer); + } + uint32_t elements_size = pm_sizet_to_u32(((pm_hash_pattern_node_t *)node)->elements.size); + pm_buffer_append_varuint(buffer, elements_size); + for (uint32_t index = 0; index < elements_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_hash_pattern_node_t *)node)->elements.nodes[index], buffer); + } + if (((pm_hash_pattern_node_t *)node)->rest == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_hash_pattern_node_t *)node)->rest, buffer); + } + if (((pm_hash_pattern_node_t *)node)->opening_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_hash_pattern_node_t *)node)->opening_loc, buffer); + } + if (((pm_hash_pattern_node_t *)node)->closing_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_hash_pattern_node_t *)node)->closing_loc, buffer); + } + break; + } + case PM_IF_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + if (((pm_if_node_t *)node)->if_keyword_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_if_node_t *)node)->if_keyword_loc, buffer); + } + pm_serialize_node(parser, (pm_node_t *)((pm_if_node_t *)node)->predicate, buffer); + if (((pm_if_node_t *)node)->then_keyword_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_if_node_t *)node)->then_keyword_loc, buffer); + } + if (((pm_if_node_t *)node)->statements == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_if_node_t *)node)->statements, buffer); + } + if (((pm_if_node_t *)node)->subsequent == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_if_node_t *)node)->subsequent, buffer); + } + if (((pm_if_node_t *)node)->end_keyword_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_if_node_t *)node)->end_keyword_loc, buffer); + } + break; + } + case PM_IMAGINARY_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_node(parser, (pm_node_t *)((pm_imaginary_node_t *)node)->numeric, buffer); + break; + } + case PM_IMPLICIT_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_node(parser, (pm_node_t *)((pm_implicit_node_t *)node)->value, buffer); + break; + } + case PM_IMPLICIT_REST_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + break; + } + case PM_IN_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_node(parser, (pm_node_t *)((pm_in_node_t *)node)->pattern, buffer); + if (((pm_in_node_t *)node)->statements == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_in_node_t *)node)->statements, buffer); + } + pm_serialize_location(parser, &((pm_in_node_t *)node)->in_loc, buffer); + if (((pm_in_node_t *)node)->then_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_in_node_t *)node)->then_loc, buffer); + } + break; + } + case PM_INDEX_AND_WRITE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + if (((pm_index_and_write_node_t *)node)->receiver == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_index_and_write_node_t *)node)->receiver, buffer); + } + if (((pm_index_and_write_node_t *)node)->call_operator_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_index_and_write_node_t *)node)->call_operator_loc, buffer); + } + pm_serialize_location(parser, &((pm_index_and_write_node_t *)node)->opening_loc, buffer); + if (((pm_index_and_write_node_t *)node)->arguments == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_index_and_write_node_t *)node)->arguments, buffer); + } + pm_serialize_location(parser, &((pm_index_and_write_node_t *)node)->closing_loc, buffer); + if (((pm_index_and_write_node_t *)node)->block == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_index_and_write_node_t *)node)->block, buffer); + } + pm_serialize_location(parser, &((pm_index_and_write_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_index_and_write_node_t *)node)->value, buffer); + break; + } + case PM_INDEX_OPERATOR_WRITE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + if (((pm_index_operator_write_node_t *)node)->receiver == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_index_operator_write_node_t *)node)->receiver, buffer); + } + if (((pm_index_operator_write_node_t *)node)->call_operator_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_index_operator_write_node_t *)node)->call_operator_loc, buffer); + } + pm_serialize_location(parser, &((pm_index_operator_write_node_t *)node)->opening_loc, buffer); + if (((pm_index_operator_write_node_t *)node)->arguments == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_index_operator_write_node_t *)node)->arguments, buffer); + } + pm_serialize_location(parser, &((pm_index_operator_write_node_t *)node)->closing_loc, buffer); + if (((pm_index_operator_write_node_t *)node)->block == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_index_operator_write_node_t *)node)->block, buffer); + } + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_index_operator_write_node_t *)node)->binary_operator)); + pm_serialize_location(parser, &((pm_index_operator_write_node_t *)node)->binary_operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_index_operator_write_node_t *)node)->value, buffer); + break; + } + case PM_INDEX_OR_WRITE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + if (((pm_index_or_write_node_t *)node)->receiver == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_index_or_write_node_t *)node)->receiver, buffer); + } + if (((pm_index_or_write_node_t *)node)->call_operator_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_index_or_write_node_t *)node)->call_operator_loc, buffer); + } + pm_serialize_location(parser, &((pm_index_or_write_node_t *)node)->opening_loc, buffer); + if (((pm_index_or_write_node_t *)node)->arguments == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_index_or_write_node_t *)node)->arguments, buffer); + } + pm_serialize_location(parser, &((pm_index_or_write_node_t *)node)->closing_loc, buffer); + if (((pm_index_or_write_node_t *)node)->block == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_index_or_write_node_t *)node)->block, buffer); + } + pm_serialize_location(parser, &((pm_index_or_write_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_index_or_write_node_t *)node)->value, buffer); + break; + } + case PM_INDEX_TARGET_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_node(parser, (pm_node_t *)((pm_index_target_node_t *)node)->receiver, buffer); + pm_serialize_location(parser, &((pm_index_target_node_t *)node)->opening_loc, buffer); + if (((pm_index_target_node_t *)node)->arguments == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_index_target_node_t *)node)->arguments, buffer); + } + pm_serialize_location(parser, &((pm_index_target_node_t *)node)->closing_loc, buffer); + if (((pm_index_target_node_t *)node)->block == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_index_target_node_t *)node)->block, buffer); + } + break; + } + case PM_INSTANCE_VARIABLE_AND_WRITE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_instance_variable_and_write_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_instance_variable_and_write_node_t *)node)->name_loc, buffer); + pm_serialize_location(parser, &((pm_instance_variable_and_write_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_instance_variable_and_write_node_t *)node)->value, buffer); + break; + } + case PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_instance_variable_operator_write_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_instance_variable_operator_write_node_t *)node)->name_loc, buffer); + pm_serialize_location(parser, &((pm_instance_variable_operator_write_node_t *)node)->binary_operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_instance_variable_operator_write_node_t *)node)->value, buffer); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_instance_variable_operator_write_node_t *)node)->binary_operator)); + break; + } + case PM_INSTANCE_VARIABLE_OR_WRITE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_instance_variable_or_write_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_instance_variable_or_write_node_t *)node)->name_loc, buffer); + pm_serialize_location(parser, &((pm_instance_variable_or_write_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_instance_variable_or_write_node_t *)node)->value, buffer); + break; + } + case PM_INSTANCE_VARIABLE_READ_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_instance_variable_read_node_t *)node)->name)); + break; + } + case PM_INSTANCE_VARIABLE_TARGET_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_instance_variable_target_node_t *)node)->name)); + break; + } + case PM_INSTANCE_VARIABLE_WRITE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_instance_variable_write_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_instance_variable_write_node_t *)node)->name_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_instance_variable_write_node_t *)node)->value, buffer); + pm_serialize_location(parser, &((pm_instance_variable_write_node_t *)node)->operator_loc, buffer); + break; + } + case PM_INTEGER_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_integer(&((pm_integer_node_t *)node)->value, buffer); + break; + } + case PM_INTERPOLATED_MATCH_LAST_LINE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_location(parser, &((pm_interpolated_match_last_line_node_t *)node)->opening_loc, buffer); + uint32_t parts_size = pm_sizet_to_u32(((pm_interpolated_match_last_line_node_t *)node)->parts.size); + pm_buffer_append_varuint(buffer, parts_size); + for (uint32_t index = 0; index < parts_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_interpolated_match_last_line_node_t *)node)->parts.nodes[index], buffer); + } + pm_serialize_location(parser, &((pm_interpolated_match_last_line_node_t *)node)->closing_loc, buffer); + break; + } + case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_location(parser, &((pm_interpolated_regular_expression_node_t *)node)->opening_loc, buffer); + uint32_t parts_size = pm_sizet_to_u32(((pm_interpolated_regular_expression_node_t *)node)->parts.size); + pm_buffer_append_varuint(buffer, parts_size); + for (uint32_t index = 0; index < parts_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_interpolated_regular_expression_node_t *)node)->parts.nodes[index], buffer); + } + pm_serialize_location(parser, &((pm_interpolated_regular_expression_node_t *)node)->closing_loc, buffer); + break; + } + case PM_INTERPOLATED_STRING_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + if (((pm_interpolated_string_node_t *)node)->opening_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_interpolated_string_node_t *)node)->opening_loc, buffer); + } + uint32_t parts_size = pm_sizet_to_u32(((pm_interpolated_string_node_t *)node)->parts.size); + pm_buffer_append_varuint(buffer, parts_size); + for (uint32_t index = 0; index < parts_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_interpolated_string_node_t *)node)->parts.nodes[index], buffer); + } + if (((pm_interpolated_string_node_t *)node)->closing_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_interpolated_string_node_t *)node)->closing_loc, buffer); + } + break; + } + case PM_INTERPOLATED_SYMBOL_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + if (((pm_interpolated_symbol_node_t *)node)->opening_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_interpolated_symbol_node_t *)node)->opening_loc, buffer); + } + uint32_t parts_size = pm_sizet_to_u32(((pm_interpolated_symbol_node_t *)node)->parts.size); + pm_buffer_append_varuint(buffer, parts_size); + for (uint32_t index = 0; index < parts_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_interpolated_symbol_node_t *)node)->parts.nodes[index], buffer); + } + if (((pm_interpolated_symbol_node_t *)node)->closing_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_interpolated_symbol_node_t *)node)->closing_loc, buffer); + } + break; + } + case PM_INTERPOLATED_X_STRING_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_location(parser, &((pm_interpolated_x_string_node_t *)node)->opening_loc, buffer); + uint32_t parts_size = pm_sizet_to_u32(((pm_interpolated_x_string_node_t *)node)->parts.size); + pm_buffer_append_varuint(buffer, parts_size); + for (uint32_t index = 0; index < parts_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_interpolated_x_string_node_t *)node)->parts.nodes[index], buffer); + } + pm_serialize_location(parser, &((pm_interpolated_x_string_node_t *)node)->closing_loc, buffer); + break; + } + case PM_IT_LOCAL_VARIABLE_READ_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + break; + } + case PM_IT_PARAMETERS_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + break; + } + case PM_KEYWORD_HASH_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + uint32_t elements_size = pm_sizet_to_u32(((pm_keyword_hash_node_t *)node)->elements.size); + pm_buffer_append_varuint(buffer, elements_size); + for (uint32_t index = 0; index < elements_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_keyword_hash_node_t *)node)->elements.nodes[index], buffer); + } + break; + } + case PM_KEYWORD_REST_PARAMETER_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_keyword_rest_parameter_node_t *)node)->name)); + if (((pm_keyword_rest_parameter_node_t *)node)->name_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_keyword_rest_parameter_node_t *)node)->name_loc, buffer); + } + pm_serialize_location(parser, &((pm_keyword_rest_parameter_node_t *)node)->operator_loc, buffer); + break; + } + case PM_LAMBDA_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + uint32_t locals_size = pm_sizet_to_u32(((pm_lambda_node_t *)node)->locals.size); + pm_buffer_append_varuint(buffer, locals_size); + for (uint32_t index = 0; index < locals_size; index++) { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_lambda_node_t *)node)->locals.ids[index])); + } + pm_serialize_location(parser, &((pm_lambda_node_t *)node)->operator_loc, buffer); + pm_serialize_location(parser, &((pm_lambda_node_t *)node)->opening_loc, buffer); + pm_serialize_location(parser, &((pm_lambda_node_t *)node)->closing_loc, buffer); + if (((pm_lambda_node_t *)node)->parameters == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_lambda_node_t *)node)->parameters, buffer); + } + if (((pm_lambda_node_t *)node)->body == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_lambda_node_t *)node)->body, buffer); + } + break; + } + case PM_LOCAL_VARIABLE_AND_WRITE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_location(parser, &((pm_local_variable_and_write_node_t *)node)->name_loc, buffer); + pm_serialize_location(parser, &((pm_local_variable_and_write_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_local_variable_and_write_node_t *)node)->value, buffer); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_local_variable_and_write_node_t *)node)->name)); + pm_buffer_append_varuint(buffer, ((pm_local_variable_and_write_node_t *)node)->depth); + break; + } + case PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_location(parser, &((pm_local_variable_operator_write_node_t *)node)->name_loc, buffer); + pm_serialize_location(parser, &((pm_local_variable_operator_write_node_t *)node)->binary_operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_local_variable_operator_write_node_t *)node)->value, buffer); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_local_variable_operator_write_node_t *)node)->name)); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_local_variable_operator_write_node_t *)node)->binary_operator)); + pm_buffer_append_varuint(buffer, ((pm_local_variable_operator_write_node_t *)node)->depth); + break; + } + case PM_LOCAL_VARIABLE_OR_WRITE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_location(parser, &((pm_local_variable_or_write_node_t *)node)->name_loc, buffer); + pm_serialize_location(parser, &((pm_local_variable_or_write_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_local_variable_or_write_node_t *)node)->value, buffer); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_local_variable_or_write_node_t *)node)->name)); + pm_buffer_append_varuint(buffer, ((pm_local_variable_or_write_node_t *)node)->depth); + break; + } + case PM_LOCAL_VARIABLE_READ_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_local_variable_read_node_t *)node)->name)); + pm_buffer_append_varuint(buffer, ((pm_local_variable_read_node_t *)node)->depth); + break; + } + case PM_LOCAL_VARIABLE_TARGET_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_local_variable_target_node_t *)node)->name)); + pm_buffer_append_varuint(buffer, ((pm_local_variable_target_node_t *)node)->depth); + break; + } + case PM_LOCAL_VARIABLE_WRITE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_local_variable_write_node_t *)node)->name)); + pm_buffer_append_varuint(buffer, ((pm_local_variable_write_node_t *)node)->depth); + pm_serialize_location(parser, &((pm_local_variable_write_node_t *)node)->name_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_local_variable_write_node_t *)node)->value, buffer); + pm_serialize_location(parser, &((pm_local_variable_write_node_t *)node)->operator_loc, buffer); + break; + } + case PM_MATCH_LAST_LINE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_location(parser, &((pm_match_last_line_node_t *)node)->opening_loc, buffer); + pm_serialize_location(parser, &((pm_match_last_line_node_t *)node)->content_loc, buffer); + pm_serialize_location(parser, &((pm_match_last_line_node_t *)node)->closing_loc, buffer); + pm_serialize_string(parser, &((pm_match_last_line_node_t *)node)->unescaped, buffer); + break; + } + case PM_MATCH_PREDICATE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_node(parser, (pm_node_t *)((pm_match_predicate_node_t *)node)->value, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_match_predicate_node_t *)node)->pattern, buffer); + pm_serialize_location(parser, &((pm_match_predicate_node_t *)node)->operator_loc, buffer); + break; + } + case PM_MATCH_REQUIRED_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_node(parser, (pm_node_t *)((pm_match_required_node_t *)node)->value, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_match_required_node_t *)node)->pattern, buffer); + pm_serialize_location(parser, &((pm_match_required_node_t *)node)->operator_loc, buffer); + break; + } + case PM_MATCH_WRITE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_node(parser, (pm_node_t *)((pm_match_write_node_t *)node)->call, buffer); + uint32_t targets_size = pm_sizet_to_u32(((pm_match_write_node_t *)node)->targets.size); + pm_buffer_append_varuint(buffer, targets_size); + for (uint32_t index = 0; index < targets_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_match_write_node_t *)node)->targets.nodes[index], buffer); + } + break; + } + case PM_MISSING_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + break; + } + case PM_MODULE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + uint32_t locals_size = pm_sizet_to_u32(((pm_module_node_t *)node)->locals.size); + pm_buffer_append_varuint(buffer, locals_size); + for (uint32_t index = 0; index < locals_size; index++) { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_module_node_t *)node)->locals.ids[index])); + } + pm_serialize_location(parser, &((pm_module_node_t *)node)->module_keyword_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_module_node_t *)node)->constant_path, buffer); + if (((pm_module_node_t *)node)->body == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_module_node_t *)node)->body, buffer); + } + pm_serialize_location(parser, &((pm_module_node_t *)node)->end_keyword_loc, buffer); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_module_node_t *)node)->name)); + break; + } + case PM_MULTI_TARGET_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + uint32_t lefts_size = pm_sizet_to_u32(((pm_multi_target_node_t *)node)->lefts.size); + pm_buffer_append_varuint(buffer, lefts_size); + for (uint32_t index = 0; index < lefts_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_multi_target_node_t *)node)->lefts.nodes[index], buffer); + } + if (((pm_multi_target_node_t *)node)->rest == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_multi_target_node_t *)node)->rest, buffer); + } + uint32_t rights_size = pm_sizet_to_u32(((pm_multi_target_node_t *)node)->rights.size); + pm_buffer_append_varuint(buffer, rights_size); + for (uint32_t index = 0; index < rights_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_multi_target_node_t *)node)->rights.nodes[index], buffer); + } + if (((pm_multi_target_node_t *)node)->lparen_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_multi_target_node_t *)node)->lparen_loc, buffer); + } + if (((pm_multi_target_node_t *)node)->rparen_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_multi_target_node_t *)node)->rparen_loc, buffer); + } + break; + } + case PM_MULTI_WRITE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + uint32_t lefts_size = pm_sizet_to_u32(((pm_multi_write_node_t *)node)->lefts.size); + pm_buffer_append_varuint(buffer, lefts_size); + for (uint32_t index = 0; index < lefts_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_multi_write_node_t *)node)->lefts.nodes[index], buffer); + } + if (((pm_multi_write_node_t *)node)->rest == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_multi_write_node_t *)node)->rest, buffer); + } + uint32_t rights_size = pm_sizet_to_u32(((pm_multi_write_node_t *)node)->rights.size); + pm_buffer_append_varuint(buffer, rights_size); + for (uint32_t index = 0; index < rights_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_multi_write_node_t *)node)->rights.nodes[index], buffer); + } + if (((pm_multi_write_node_t *)node)->lparen_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_multi_write_node_t *)node)->lparen_loc, buffer); + } + if (((pm_multi_write_node_t *)node)->rparen_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_multi_write_node_t *)node)->rparen_loc, buffer); + } + pm_serialize_location(parser, &((pm_multi_write_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_multi_write_node_t *)node)->value, buffer); + break; + } + case PM_NEXT_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + if (((pm_next_node_t *)node)->arguments == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_next_node_t *)node)->arguments, buffer); + } + pm_serialize_location(parser, &((pm_next_node_t *)node)->keyword_loc, buffer); + break; + } + case PM_NIL_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + break; + } + case PM_NO_KEYWORDS_PARAMETER_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_location(parser, &((pm_no_keywords_parameter_node_t *)node)->operator_loc, buffer); + pm_serialize_location(parser, &((pm_no_keywords_parameter_node_t *)node)->keyword_loc, buffer); + break; + } + case PM_NUMBERED_PARAMETERS_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_buffer_append_byte(buffer, ((pm_numbered_parameters_node_t *)node)->maximum); + break; + } + case PM_NUMBERED_REFERENCE_READ_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_buffer_append_varuint(buffer, ((pm_numbered_reference_read_node_t *)node)->number); + break; + } + case PM_OPTIONAL_KEYWORD_PARAMETER_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_optional_keyword_parameter_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_optional_keyword_parameter_node_t *)node)->name_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_optional_keyword_parameter_node_t *)node)->value, buffer); + break; + } + case PM_OPTIONAL_PARAMETER_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_optional_parameter_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_optional_parameter_node_t *)node)->name_loc, buffer); + pm_serialize_location(parser, &((pm_optional_parameter_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_optional_parameter_node_t *)node)->value, buffer); + break; + } + case PM_OR_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_node(parser, (pm_node_t *)((pm_or_node_t *)node)->left, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_or_node_t *)node)->right, buffer); + pm_serialize_location(parser, &((pm_or_node_t *)node)->operator_loc, buffer); + break; + } + case PM_PARAMETERS_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + uint32_t requireds_size = pm_sizet_to_u32(((pm_parameters_node_t *)node)->requireds.size); + pm_buffer_append_varuint(buffer, requireds_size); + for (uint32_t index = 0; index < requireds_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_parameters_node_t *)node)->requireds.nodes[index], buffer); + } + uint32_t optionals_size = pm_sizet_to_u32(((pm_parameters_node_t *)node)->optionals.size); + pm_buffer_append_varuint(buffer, optionals_size); + for (uint32_t index = 0; index < optionals_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_parameters_node_t *)node)->optionals.nodes[index], buffer); + } + if (((pm_parameters_node_t *)node)->rest == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_parameters_node_t *)node)->rest, buffer); + } + uint32_t posts_size = pm_sizet_to_u32(((pm_parameters_node_t *)node)->posts.size); + pm_buffer_append_varuint(buffer, posts_size); + for (uint32_t index = 0; index < posts_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_parameters_node_t *)node)->posts.nodes[index], buffer); + } + uint32_t keywords_size = pm_sizet_to_u32(((pm_parameters_node_t *)node)->keywords.size); + pm_buffer_append_varuint(buffer, keywords_size); + for (uint32_t index = 0; index < keywords_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_parameters_node_t *)node)->keywords.nodes[index], buffer); + } + if (((pm_parameters_node_t *)node)->keyword_rest == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_parameters_node_t *)node)->keyword_rest, buffer); + } + if (((pm_parameters_node_t *)node)->block == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_parameters_node_t *)node)->block, buffer); + } + break; + } + case PM_PARENTHESES_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + if (((pm_parentheses_node_t *)node)->body == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_parentheses_node_t *)node)->body, buffer); + } + pm_serialize_location(parser, &((pm_parentheses_node_t *)node)->opening_loc, buffer); + pm_serialize_location(parser, &((pm_parentheses_node_t *)node)->closing_loc, buffer); + break; + } + case PM_PINNED_EXPRESSION_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_node(parser, (pm_node_t *)((pm_pinned_expression_node_t *)node)->expression, buffer); + pm_serialize_location(parser, &((pm_pinned_expression_node_t *)node)->operator_loc, buffer); + pm_serialize_location(parser, &((pm_pinned_expression_node_t *)node)->lparen_loc, buffer); + pm_serialize_location(parser, &((pm_pinned_expression_node_t *)node)->rparen_loc, buffer); + break; + } + case PM_PINNED_VARIABLE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_node(parser, (pm_node_t *)((pm_pinned_variable_node_t *)node)->variable, buffer); + pm_serialize_location(parser, &((pm_pinned_variable_node_t *)node)->operator_loc, buffer); + break; + } + case PM_POST_EXECUTION_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + if (((pm_post_execution_node_t *)node)->statements == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_post_execution_node_t *)node)->statements, buffer); + } + pm_serialize_location(parser, &((pm_post_execution_node_t *)node)->keyword_loc, buffer); + pm_serialize_location(parser, &((pm_post_execution_node_t *)node)->opening_loc, buffer); + pm_serialize_location(parser, &((pm_post_execution_node_t *)node)->closing_loc, buffer); + break; + } + case PM_PRE_EXECUTION_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + if (((pm_pre_execution_node_t *)node)->statements == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_pre_execution_node_t *)node)->statements, buffer); + } + pm_serialize_location(parser, &((pm_pre_execution_node_t *)node)->keyword_loc, buffer); + pm_serialize_location(parser, &((pm_pre_execution_node_t *)node)->opening_loc, buffer); + pm_serialize_location(parser, &((pm_pre_execution_node_t *)node)->closing_loc, buffer); + break; + } + case PM_PROGRAM_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + uint32_t locals_size = pm_sizet_to_u32(((pm_program_node_t *)node)->locals.size); + pm_buffer_append_varuint(buffer, locals_size); + for (uint32_t index = 0; index < locals_size; index++) { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_program_node_t *)node)->locals.ids[index])); + } + pm_serialize_node(parser, (pm_node_t *)((pm_program_node_t *)node)->statements, buffer); + break; + } + case PM_RANGE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + if (((pm_range_node_t *)node)->left == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_range_node_t *)node)->left, buffer); + } + if (((pm_range_node_t *)node)->right == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_range_node_t *)node)->right, buffer); + } + pm_serialize_location(parser, &((pm_range_node_t *)node)->operator_loc, buffer); + break; + } + case PM_RATIONAL_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_integer(&((pm_rational_node_t *)node)->numerator, buffer); + pm_serialize_integer(&((pm_rational_node_t *)node)->denominator, buffer); + break; + } + case PM_REDO_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + break; + } + case PM_REGULAR_EXPRESSION_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_location(parser, &((pm_regular_expression_node_t *)node)->opening_loc, buffer); + pm_serialize_location(parser, &((pm_regular_expression_node_t *)node)->content_loc, buffer); + pm_serialize_location(parser, &((pm_regular_expression_node_t *)node)->closing_loc, buffer); + pm_serialize_string(parser, &((pm_regular_expression_node_t *)node)->unescaped, buffer); + break; + } + case PM_REQUIRED_KEYWORD_PARAMETER_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_required_keyword_parameter_node_t *)node)->name)); + pm_serialize_location(parser, &((pm_required_keyword_parameter_node_t *)node)->name_loc, buffer); + break; + } + case PM_REQUIRED_PARAMETER_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_required_parameter_node_t *)node)->name)); + break; + } + case PM_RESCUE_MODIFIER_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_node(parser, (pm_node_t *)((pm_rescue_modifier_node_t *)node)->expression, buffer); + pm_serialize_location(parser, &((pm_rescue_modifier_node_t *)node)->keyword_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_rescue_modifier_node_t *)node)->rescue_expression, buffer); + break; + } + case PM_RESCUE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_location(parser, &((pm_rescue_node_t *)node)->keyword_loc, buffer); + uint32_t exceptions_size = pm_sizet_to_u32(((pm_rescue_node_t *)node)->exceptions.size); + pm_buffer_append_varuint(buffer, exceptions_size); + for (uint32_t index = 0; index < exceptions_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_rescue_node_t *)node)->exceptions.nodes[index], buffer); + } + if (((pm_rescue_node_t *)node)->operator_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_rescue_node_t *)node)->operator_loc, buffer); + } + if (((pm_rescue_node_t *)node)->reference == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_rescue_node_t *)node)->reference, buffer); + } + if (((pm_rescue_node_t *)node)->then_keyword_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_rescue_node_t *)node)->then_keyword_loc, buffer); + } + if (((pm_rescue_node_t *)node)->statements == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_rescue_node_t *)node)->statements, buffer); + } + if (((pm_rescue_node_t *)node)->subsequent == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_rescue_node_t *)node)->subsequent, buffer); + } + break; + } + case PM_REST_PARAMETER_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_rest_parameter_node_t *)node)->name)); + if (((pm_rest_parameter_node_t *)node)->name_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_rest_parameter_node_t *)node)->name_loc, buffer); + } + pm_serialize_location(parser, &((pm_rest_parameter_node_t *)node)->operator_loc, buffer); + break; + } + case PM_RETRY_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + break; + } + case PM_RETURN_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_location(parser, &((pm_return_node_t *)node)->keyword_loc, buffer); + if (((pm_return_node_t *)node)->arguments == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_return_node_t *)node)->arguments, buffer); + } + break; + } + case PM_SELF_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + break; + } + case PM_SHAREABLE_CONSTANT_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_node(parser, (pm_node_t *)((pm_shareable_constant_node_t *)node)->write, buffer); + break; + } + case PM_SINGLETON_CLASS_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + uint32_t locals_size = pm_sizet_to_u32(((pm_singleton_class_node_t *)node)->locals.size); + pm_buffer_append_varuint(buffer, locals_size); + for (uint32_t index = 0; index < locals_size; index++) { + pm_buffer_append_varuint(buffer, pm_sizet_to_u32(((pm_singleton_class_node_t *)node)->locals.ids[index])); + } + pm_serialize_location(parser, &((pm_singleton_class_node_t *)node)->class_keyword_loc, buffer); + pm_serialize_location(parser, &((pm_singleton_class_node_t *)node)->operator_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_singleton_class_node_t *)node)->expression, buffer); + if (((pm_singleton_class_node_t *)node)->body == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_singleton_class_node_t *)node)->body, buffer); + } + pm_serialize_location(parser, &((pm_singleton_class_node_t *)node)->end_keyword_loc, buffer); + break; + } + case PM_SOURCE_ENCODING_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + break; + } + case PM_SOURCE_FILE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_string(parser, &((pm_source_file_node_t *)node)->filepath, buffer); + break; + } + case PM_SOURCE_LINE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + break; + } + case PM_SPLAT_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_location(parser, &((pm_splat_node_t *)node)->operator_loc, buffer); + if (((pm_splat_node_t *)node)->expression == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_splat_node_t *)node)->expression, buffer); + } + break; + } + case PM_STATEMENTS_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + uint32_t body_size = pm_sizet_to_u32(((pm_statements_node_t *)node)->body.size); + pm_buffer_append_varuint(buffer, body_size); + for (uint32_t index = 0; index < body_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_statements_node_t *)node)->body.nodes[index], buffer); + } + break; + } + case PM_STRING_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + if (((pm_string_node_t *)node)->opening_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_string_node_t *)node)->opening_loc, buffer); + } + pm_serialize_location(parser, &((pm_string_node_t *)node)->content_loc, buffer); + if (((pm_string_node_t *)node)->closing_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_string_node_t *)node)->closing_loc, buffer); + } + pm_serialize_string(parser, &((pm_string_node_t *)node)->unescaped, buffer); + break; + } + case PM_SUPER_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_location(parser, &((pm_super_node_t *)node)->keyword_loc, buffer); + if (((pm_super_node_t *)node)->lparen_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_super_node_t *)node)->lparen_loc, buffer); + } + if (((pm_super_node_t *)node)->arguments == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_super_node_t *)node)->arguments, buffer); + } + if (((pm_super_node_t *)node)->rparen_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_super_node_t *)node)->rparen_loc, buffer); + } + if (((pm_super_node_t *)node)->block == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_super_node_t *)node)->block, buffer); + } + break; + } + case PM_SYMBOL_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + if (((pm_symbol_node_t *)node)->opening_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_symbol_node_t *)node)->opening_loc, buffer); + } + if (((pm_symbol_node_t *)node)->value_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_symbol_node_t *)node)->value_loc, buffer); + } + if (((pm_symbol_node_t *)node)->closing_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_symbol_node_t *)node)->closing_loc, buffer); + } + pm_serialize_string(parser, &((pm_symbol_node_t *)node)->unescaped, buffer); + break; + } + case PM_TRUE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + break; + } + case PM_UNDEF_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + uint32_t names_size = pm_sizet_to_u32(((pm_undef_node_t *)node)->names.size); + pm_buffer_append_varuint(buffer, names_size); + for (uint32_t index = 0; index < names_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_undef_node_t *)node)->names.nodes[index], buffer); + } + pm_serialize_location(parser, &((pm_undef_node_t *)node)->keyword_loc, buffer); + break; + } + case PM_UNLESS_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_location(parser, &((pm_unless_node_t *)node)->keyword_loc, buffer); + pm_serialize_node(parser, (pm_node_t *)((pm_unless_node_t *)node)->predicate, buffer); + if (((pm_unless_node_t *)node)->then_keyword_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_unless_node_t *)node)->then_keyword_loc, buffer); + } + if (((pm_unless_node_t *)node)->statements == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_unless_node_t *)node)->statements, buffer); + } + if (((pm_unless_node_t *)node)->else_clause == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_unless_node_t *)node)->else_clause, buffer); + } + if (((pm_unless_node_t *)node)->end_keyword_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_unless_node_t *)node)->end_keyword_loc, buffer); + } + break; + } + case PM_UNTIL_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_location(parser, &((pm_until_node_t *)node)->keyword_loc, buffer); + if (((pm_until_node_t *)node)->do_keyword_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_until_node_t *)node)->do_keyword_loc, buffer); + } + if (((pm_until_node_t *)node)->closing_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_until_node_t *)node)->closing_loc, buffer); + } + pm_serialize_node(parser, (pm_node_t *)((pm_until_node_t *)node)->predicate, buffer); + if (((pm_until_node_t *)node)->statements == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_until_node_t *)node)->statements, buffer); + } + break; + } + case PM_WHEN_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_location(parser, &((pm_when_node_t *)node)->keyword_loc, buffer); + uint32_t conditions_size = pm_sizet_to_u32(((pm_when_node_t *)node)->conditions.size); + pm_buffer_append_varuint(buffer, conditions_size); + for (uint32_t index = 0; index < conditions_size; index++) { + pm_serialize_node(parser, (pm_node_t *) ((pm_when_node_t *)node)->conditions.nodes[index], buffer); + } + if (((pm_when_node_t *)node)->then_keyword_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_when_node_t *)node)->then_keyword_loc, buffer); + } + if (((pm_when_node_t *)node)->statements == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_when_node_t *)node)->statements, buffer); + } + break; + } + case PM_WHILE_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_location(parser, &((pm_while_node_t *)node)->keyword_loc, buffer); + if (((pm_while_node_t *)node)->do_keyword_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_while_node_t *)node)->do_keyword_loc, buffer); + } + if (((pm_while_node_t *)node)->closing_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_while_node_t *)node)->closing_loc, buffer); + } + pm_serialize_node(parser, (pm_node_t *)((pm_while_node_t *)node)->predicate, buffer); + if (((pm_while_node_t *)node)->statements == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_while_node_t *)node)->statements, buffer); + } + break; + } + case PM_X_STRING_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_location(parser, &((pm_x_string_node_t *)node)->opening_loc, buffer); + pm_serialize_location(parser, &((pm_x_string_node_t *)node)->content_loc, buffer); + pm_serialize_location(parser, &((pm_x_string_node_t *)node)->closing_loc, buffer); + pm_serialize_string(parser, &((pm_x_string_node_t *)node)->unescaped, buffer); + break; + } + case PM_YIELD_NODE: { + pm_buffer_append_varuint(buffer, (uint32_t) node->flags); + pm_serialize_location(parser, &((pm_yield_node_t *)node)->keyword_loc, buffer); + if (((pm_yield_node_t *)node)->lparen_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_yield_node_t *)node)->lparen_loc, buffer); + } + if (((pm_yield_node_t *)node)->arguments == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_serialize_node(parser, (pm_node_t *)((pm_yield_node_t *)node)->arguments, buffer); + } + if (((pm_yield_node_t *)node)->rparen_loc.start == NULL) { + pm_buffer_append_byte(buffer, 0); + } else { + pm_buffer_append_byte(buffer, 1); + pm_serialize_location(parser, &((pm_yield_node_t *)node)->rparen_loc, buffer); + } + break; + } + } +} + +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); + pm_serialize_comment_list(parser, &parser->comment_list, buffer); + 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 275 "prism/templates/src/serialize.c.erb" +/** + * 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 = 1U << 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/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/static_literals.c b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/static_literals.c new file mode 100644 index 0000000..9fa37b9 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/static_literals.c @@ -0,0 +1,617 @@ +#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; +} + +/** + * Hash the value of an integer and return it. + */ +static uint32_t +integer_hash(const pm_integer_t *integer) { + 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; +} + +/** + * 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_node_t *cast = (const pm_integer_node_t *) node; + return integer_hash(&cast->value); + } + 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 numerator and denominator. + const pm_rational_node_t *cast = (const pm_rational_node_t *) node; + return integer_hash(&cast->numerator) ^ integer_hash(&cast->denominator) ^ murmur_scramble((uint32_t) cast->base.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, bool replace, 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; + } else if (replace) { + 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: { + const pm_rational_node_t *left_rational = (const pm_rational_node_t *) left; + const pm_rational_node_t *right_rational = (const pm_rational_node_t *) right; + + int result = pm_integer_compare(&left_rational->denominator, &right_rational->denominator); + if (result != 0) return result; + + return pm_integer_compare(&left_rational->numerator, &right_rational->numerator); + } + 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, bool replace) { + 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, + replace, + 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, + replace, + 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, + replace, + 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, + replace, + 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, + replace, + 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, + replace, + pm_compare_string_nodes + ); + case PM_TRUE_NODE: { + pm_node_t *duplicated = literals->true_node; + if ((duplicated == NULL) || replace) literals->true_node = node; + return duplicated; + } + case PM_FALSE_NODE: { + pm_node_t *duplicated = literals->false_node; + if ((duplicated == NULL) || replace) literals->false_node = node; + return duplicated; + } + case PM_NIL_NODE: { + pm_node_t *duplicated = literals->nil_node; + if ((duplicated == NULL) || replace) literals->nil_node = node; + return duplicated; + } + case PM_SOURCE_ENCODING_NODE: { + pm_node_t *duplicated = literals->source_encoding_node; + if ((duplicated == NULL) || replace) 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 !((const pm_rational_node_t *) node)->numerator.negative; + case PM_IMAGINARY_NODE: + return pm_static_literal_positive_p(((const pm_imaginary_node_t *) node)->numeric); + default: + assert(false && "unreachable"); + return false; + } +} + +/** + * 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 (PRISM_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_rational_node_t *rational = (const pm_rational_node_t *) node; + pm_buffer_append_byte(buffer, '('); + pm_integer_string(buffer, &rational->numerator); + pm_buffer_append_byte(buffer, '/'); + pm_integer_string(buffer, &rational->denominator); + pm_buffer_append_byte(buffer, ')'); + 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, "#", 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. + */ +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/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/token_type.c b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/token_type.c new file mode 100644 index 0000000..32c20fe --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/token_type.c @@ -0,0 +1,703 @@ +/* :markup: markdown */ + +/*----------------------------------------------------------------------------*/ +/* This file is generated by the templates/template.rb script and should not */ +/* be modified manually. See */ +/* templates/src/token_type.c.erb */ +/* if you are looking to modify the */ +/* template */ +/*----------------------------------------------------------------------------*/ + +#include + +#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) { + case PM_TOKEN_EOF: + return "EOF"; + case PM_TOKEN_BRACE_RIGHT: + return "BRACE_RIGHT"; + case PM_TOKEN_COMMA: + return "COMMA"; + case PM_TOKEN_EMBEXPR_END: + return "EMBEXPR_END"; + case PM_TOKEN_KEYWORD_DO: + return "KEYWORD_DO"; + case PM_TOKEN_KEYWORD_ELSE: + return "KEYWORD_ELSE"; + case PM_TOKEN_KEYWORD_ELSIF: + return "KEYWORD_ELSIF"; + case PM_TOKEN_KEYWORD_END: + return "KEYWORD_END"; + case PM_TOKEN_KEYWORD_ENSURE: + return "KEYWORD_ENSURE"; + case PM_TOKEN_KEYWORD_IN: + return "KEYWORD_IN"; + case PM_TOKEN_KEYWORD_RESCUE: + return "KEYWORD_RESCUE"; + case PM_TOKEN_KEYWORD_THEN: + return "KEYWORD_THEN"; + case PM_TOKEN_KEYWORD_WHEN: + return "KEYWORD_WHEN"; + case PM_TOKEN_NEWLINE: + return "NEWLINE"; + case PM_TOKEN_PARENTHESIS_RIGHT: + return "PARENTHESIS_RIGHT"; + case PM_TOKEN_PIPE: + return "PIPE"; + case PM_TOKEN_SEMICOLON: + return "SEMICOLON"; + case PM_TOKEN_AMPERSAND: + return "AMPERSAND"; + case PM_TOKEN_AMPERSAND_AMPERSAND: + return "AMPERSAND_AMPERSAND"; + case PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL: + return "AMPERSAND_AMPERSAND_EQUAL"; + case PM_TOKEN_AMPERSAND_DOT: + return "AMPERSAND_DOT"; + case PM_TOKEN_AMPERSAND_EQUAL: + return "AMPERSAND_EQUAL"; + case PM_TOKEN_BACKTICK: + return "BACKTICK"; + case PM_TOKEN_BACK_REFERENCE: + return "BACK_REFERENCE"; + case PM_TOKEN_BANG: + return "BANG"; + case PM_TOKEN_BANG_EQUAL: + return "BANG_EQUAL"; + case PM_TOKEN_BANG_TILDE: + return "BANG_TILDE"; + case PM_TOKEN_BRACE_LEFT: + return "BRACE_LEFT"; + case PM_TOKEN_BRACKET_LEFT: + return "BRACKET_LEFT"; + case PM_TOKEN_BRACKET_LEFT_ARRAY: + return "BRACKET_LEFT_ARRAY"; + case PM_TOKEN_BRACKET_LEFT_RIGHT: + return "BRACKET_LEFT_RIGHT"; + case PM_TOKEN_BRACKET_LEFT_RIGHT_EQUAL: + return "BRACKET_LEFT_RIGHT_EQUAL"; + case PM_TOKEN_BRACKET_RIGHT: + return "BRACKET_RIGHT"; + case PM_TOKEN_CARET: + return "CARET"; + case PM_TOKEN_CARET_EQUAL: + return "CARET_EQUAL"; + case PM_TOKEN_CHARACTER_LITERAL: + return "CHARACTER_LITERAL"; + case PM_TOKEN_CLASS_VARIABLE: + return "CLASS_VARIABLE"; + case PM_TOKEN_COLON: + return "COLON"; + case PM_TOKEN_COLON_COLON: + return "COLON_COLON"; + case PM_TOKEN_COMMENT: + return "COMMENT"; + case PM_TOKEN_CONSTANT: + return "CONSTANT"; + case PM_TOKEN_DOT: + return "DOT"; + case PM_TOKEN_DOT_DOT: + return "DOT_DOT"; + case PM_TOKEN_DOT_DOT_DOT: + return "DOT_DOT_DOT"; + case PM_TOKEN_EMBDOC_BEGIN: + return "EMBDOC_BEGIN"; + case PM_TOKEN_EMBDOC_END: + return "EMBDOC_END"; + case PM_TOKEN_EMBDOC_LINE: + return "EMBDOC_LINE"; + case PM_TOKEN_EMBEXPR_BEGIN: + return "EMBEXPR_BEGIN"; + case PM_TOKEN_EMBVAR: + return "EMBVAR"; + case PM_TOKEN_EQUAL: + return "EQUAL"; + case PM_TOKEN_EQUAL_EQUAL: + return "EQUAL_EQUAL"; + case PM_TOKEN_EQUAL_EQUAL_EQUAL: + return "EQUAL_EQUAL_EQUAL"; + case PM_TOKEN_EQUAL_GREATER: + return "EQUAL_GREATER"; + case PM_TOKEN_EQUAL_TILDE: + return "EQUAL_TILDE"; + case PM_TOKEN_FLOAT: + return "FLOAT"; + case PM_TOKEN_FLOAT_IMAGINARY: + return "FLOAT_IMAGINARY"; + case PM_TOKEN_FLOAT_RATIONAL: + return "FLOAT_RATIONAL"; + case PM_TOKEN_FLOAT_RATIONAL_IMAGINARY: + return "FLOAT_RATIONAL_IMAGINARY"; + case PM_TOKEN_GLOBAL_VARIABLE: + return "GLOBAL_VARIABLE"; + case PM_TOKEN_GREATER: + return "GREATER"; + case PM_TOKEN_GREATER_EQUAL: + return "GREATER_EQUAL"; + case PM_TOKEN_GREATER_GREATER: + return "GREATER_GREATER"; + case PM_TOKEN_GREATER_GREATER_EQUAL: + return "GREATER_GREATER_EQUAL"; + case PM_TOKEN_HEREDOC_END: + return "HEREDOC_END"; + case PM_TOKEN_HEREDOC_START: + return "HEREDOC_START"; + case PM_TOKEN_IDENTIFIER: + return "IDENTIFIER"; + 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 "INTEGER_IMAGINARY"; + case PM_TOKEN_INTEGER_RATIONAL: + return "INTEGER_RATIONAL"; + case PM_TOKEN_INTEGER_RATIONAL_IMAGINARY: + return "INTEGER_RATIONAL_IMAGINARY"; + case PM_TOKEN_KEYWORD_ALIAS: + return "KEYWORD_ALIAS"; + case PM_TOKEN_KEYWORD_AND: + return "KEYWORD_AND"; + case PM_TOKEN_KEYWORD_BEGIN: + return "KEYWORD_BEGIN"; + case PM_TOKEN_KEYWORD_BEGIN_UPCASE: + return "KEYWORD_BEGIN_UPCASE"; + case PM_TOKEN_KEYWORD_BREAK: + return "KEYWORD_BREAK"; + case PM_TOKEN_KEYWORD_CASE: + return "KEYWORD_CASE"; + case PM_TOKEN_KEYWORD_CLASS: + return "KEYWORD_CLASS"; + case PM_TOKEN_KEYWORD_DEF: + return "KEYWORD_DEF"; + case PM_TOKEN_KEYWORD_DEFINED: + return "KEYWORD_DEFINED"; + case PM_TOKEN_KEYWORD_DO_LOOP: + return "KEYWORD_DO_LOOP"; + case PM_TOKEN_KEYWORD_END_UPCASE: + return "KEYWORD_END_UPCASE"; + case PM_TOKEN_KEYWORD_FALSE: + return "KEYWORD_FALSE"; + case PM_TOKEN_KEYWORD_FOR: + return "KEYWORD_FOR"; + case PM_TOKEN_KEYWORD_IF: + return "KEYWORD_IF"; + case PM_TOKEN_KEYWORD_IF_MODIFIER: + return "KEYWORD_IF_MODIFIER"; + case PM_TOKEN_KEYWORD_MODULE: + return "KEYWORD_MODULE"; + case PM_TOKEN_KEYWORD_NEXT: + return "KEYWORD_NEXT"; + case PM_TOKEN_KEYWORD_NIL: + return "KEYWORD_NIL"; + case PM_TOKEN_KEYWORD_NOT: + return "KEYWORD_NOT"; + case PM_TOKEN_KEYWORD_OR: + return "KEYWORD_OR"; + case PM_TOKEN_KEYWORD_REDO: + return "KEYWORD_REDO"; + case PM_TOKEN_KEYWORD_RESCUE_MODIFIER: + return "KEYWORD_RESCUE_MODIFIER"; + case PM_TOKEN_KEYWORD_RETRY: + return "KEYWORD_RETRY"; + case PM_TOKEN_KEYWORD_RETURN: + return "KEYWORD_RETURN"; + case PM_TOKEN_KEYWORD_SELF: + return "KEYWORD_SELF"; + case PM_TOKEN_KEYWORD_SUPER: + return "KEYWORD_SUPER"; + case PM_TOKEN_KEYWORD_TRUE: + return "KEYWORD_TRUE"; + case PM_TOKEN_KEYWORD_UNDEF: + return "KEYWORD_UNDEF"; + case PM_TOKEN_KEYWORD_UNLESS: + return "KEYWORD_UNLESS"; + case PM_TOKEN_KEYWORD_UNLESS_MODIFIER: + return "KEYWORD_UNLESS_MODIFIER"; + case PM_TOKEN_KEYWORD_UNTIL: + return "KEYWORD_UNTIL"; + case PM_TOKEN_KEYWORD_UNTIL_MODIFIER: + return "KEYWORD_UNTIL_MODIFIER"; + case PM_TOKEN_KEYWORD_WHILE: + return "KEYWORD_WHILE"; + case PM_TOKEN_KEYWORD_WHILE_MODIFIER: + return "KEYWORD_WHILE_MODIFIER"; + case PM_TOKEN_KEYWORD_YIELD: + return "KEYWORD_YIELD"; + case PM_TOKEN_KEYWORD___ENCODING__: + return "KEYWORD___ENCODING__"; + case PM_TOKEN_KEYWORD___FILE__: + return "KEYWORD___FILE__"; + case PM_TOKEN_KEYWORD___LINE__: + return "KEYWORD___LINE__"; + case PM_TOKEN_LABEL: + return "LABEL"; + case PM_TOKEN_LABEL_END: + return "LABEL_END"; + case PM_TOKEN_LAMBDA_BEGIN: + return "LAMBDA_BEGIN"; + case PM_TOKEN_LESS: + return "LESS"; + case PM_TOKEN_LESS_EQUAL: + return "LESS_EQUAL"; + case PM_TOKEN_LESS_EQUAL_GREATER: + return "LESS_EQUAL_GREATER"; + case PM_TOKEN_LESS_LESS: + return "LESS_LESS"; + case PM_TOKEN_LESS_LESS_EQUAL: + return "LESS_LESS_EQUAL"; + case PM_TOKEN_METHOD_NAME: + return "METHOD_NAME"; + case PM_TOKEN_MINUS: + return "MINUS"; + case PM_TOKEN_MINUS_EQUAL: + return "MINUS_EQUAL"; + case PM_TOKEN_MINUS_GREATER: + return "MINUS_GREATER"; + case PM_TOKEN_NUMBERED_REFERENCE: + return "NUMBERED_REFERENCE"; + case PM_TOKEN_PARENTHESIS_LEFT: + return "PARENTHESIS_LEFT"; + case PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES: + return "PARENTHESIS_LEFT_PARENTHESES"; + case PM_TOKEN_PERCENT: + return "PERCENT"; + case PM_TOKEN_PERCENT_EQUAL: + return "PERCENT_EQUAL"; + case PM_TOKEN_PERCENT_LOWER_I: + return "PERCENT_LOWER_I"; + case PM_TOKEN_PERCENT_LOWER_W: + return "PERCENT_LOWER_W"; + case PM_TOKEN_PERCENT_LOWER_X: + return "PERCENT_LOWER_X"; + case PM_TOKEN_PERCENT_UPPER_I: + return "PERCENT_UPPER_I"; + case PM_TOKEN_PERCENT_UPPER_W: + return "PERCENT_UPPER_W"; + case PM_TOKEN_PIPE_EQUAL: + return "PIPE_EQUAL"; + case PM_TOKEN_PIPE_PIPE: + return "PIPE_PIPE"; + case PM_TOKEN_PIPE_PIPE_EQUAL: + return "PIPE_PIPE_EQUAL"; + case PM_TOKEN_PLUS: + return "PLUS"; + case PM_TOKEN_PLUS_EQUAL: + return "PLUS_EQUAL"; + case PM_TOKEN_QUESTION_MARK: + return "QUESTION_MARK"; + case PM_TOKEN_REGEXP_BEGIN: + return "REGEXP_BEGIN"; + case PM_TOKEN_REGEXP_END: + return "REGEXP_END"; + case PM_TOKEN_SLASH: + return "SLASH"; + case PM_TOKEN_SLASH_EQUAL: + return "SLASH_EQUAL"; + case PM_TOKEN_STAR: + return "STAR"; + case PM_TOKEN_STAR_EQUAL: + return "STAR_EQUAL"; + case PM_TOKEN_STAR_STAR: + return "STAR_STAR"; + case PM_TOKEN_STAR_STAR_EQUAL: + return "STAR_STAR_EQUAL"; + case PM_TOKEN_STRING_BEGIN: + return "STRING_BEGIN"; + case PM_TOKEN_STRING_CONTENT: + return "STRING_CONTENT"; + case PM_TOKEN_STRING_END: + return "STRING_END"; + case PM_TOKEN_SYMBOL_BEGIN: + return "SYMBOL_BEGIN"; + case PM_TOKEN_TILDE: + return "TILDE"; + case PM_TOKEN_UAMPERSAND: + return "UAMPERSAND"; + case PM_TOKEN_UCOLON_COLON: + return "UCOLON_COLON"; + case PM_TOKEN_UDOT_DOT: + return "UDOT_DOT"; + case PM_TOKEN_UDOT_DOT_DOT: + return "UDOT_DOT_DOT"; + case PM_TOKEN_UMINUS: + return "UMINUS"; + case PM_TOKEN_UMINUS_NUM: + return "UMINUS_NUM"; + case PM_TOKEN_UPLUS: + return "UPLUS"; + case PM_TOKEN_USTAR: + return "USTAR"; + case PM_TOKEN_USTAR_STAR: + return "USTAR_STAR"; + case PM_TOKEN_WORDS_SEP: + return "WORDS_SEP"; + case PM_TOKEN___END__: + return "__END__"; + case PM_TOKEN_MISSING: + return "MISSING"; + case PM_TOKEN_NOT_PROVIDED: + return "NOT_PROVIDED"; + 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-input"; + 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/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/util/pm_buffer.c b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/util/pm_buffer.c new file mode 100644 index 0000000..2136a7c --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/util/pm_buffer.c @@ -0,0 +1,357 @@ +#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 unicode codepoint to the buffer. + */ +bool +pm_buffer_append_unicode_codepoint(pm_buffer_t *buffer, uint32_t value) { + if (value <= 0x7F) { + pm_buffer_append_byte(buffer, (uint8_t) value); // 0xxxxxxx + return true; + } else if (value <= 0x7FF) { + uint8_t bytes[] = { + (uint8_t) (0xC0 | ((value >> 6) & 0x3F)), // 110xxxxx + (uint8_t) (0x80 | (value & 0x3F)) // 10xxxxxx + }; + + pm_buffer_append_bytes(buffer, bytes, 2); + return true; + } else if (value <= 0xFFFF) { + uint8_t bytes[] = { + (uint8_t) (0xE0 | ((value >> 12) & 0x3F)), // 1110xxxx + (uint8_t) (0x80 | ((value >> 6) & 0x3F)), // 10xxxxxx + (uint8_t) (0x80 | (value & 0x3F)) // 10xxxxxx + }; + + pm_buffer_append_bytes(buffer, bytes, 3); + return true; + } else if (value <= 0x10FFFF) { + uint8_t bytes[] = { + (uint8_t) (0xF0 | ((value >> 18) & 0x3F)), // 11110xxx + (uint8_t) (0x80 | ((value >> 12) & 0x3F)), // 10xxxxxx + (uint8_t) (0x80 | ((value >> 6) & 0x3F)), // 10xxxxxx + (uint8_t) (0x80 | (value & 0x3F)) // 10xxxxxx + }; + + pm_buffer_append_bytes(buffer, bytes, 4); + return true; + } else { + return false; + } +} + +/** + * 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/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/util/pm_char.c b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/util/pm_char.c new file mode 100644 index 0000000..a51dc11 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/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 (size > 0 && 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/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/util/pm_constant_pool.c b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/util/pm_constant_pool.c new file mode 100644 index 0000000..922ce6a --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/util/pm_constant_pool.c @@ -0,0 +1,342 @@ +#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) { + if (capacity) { + list->ids = xcalloc(capacity, sizeof(pm_constant_id_t)); + if (list->ids == NULL) abort(); + } else { + list->ids = NULL; + } + + 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; +} + +/** + * 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) (type & 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/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/util/pm_integer.c b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/util/pm_integer.c new file mode 100644 index 0000000..4170ecc --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/util/pm_integer.c @@ -0,0 +1,670 @@ +#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) { length, values, 0, false }; +} + +/** + * Internal use for karatsuba_multiply. Calculates `a - b - c` with the given + * base. Assume a, b, c, a - b - c all to be positive. + * 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) { a_length, values, 0, 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) { length, values, 0, 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 = { + .length = left_length, + .values = left_values, + .value = 0, + .negative = false + }; + + pm_integer_t sliced_right = { + .length = end_offset - start_offset, + .values = right_values + start_offset, + .value = 0, + .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) { left_length + right_length, values, 0, false }; + return; + } + + size_t half = left_length / 2; + pm_integer_t x0 = { half, left_values, 0, false }; + pm_integer_t x1 = { left_length - half, left_values + half, 0, false }; + pm_integer_t y0 = { half, right_values, 0, false }; + pm_integer_t y1 = { right_length - half, right_values + half, 0, 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) { length, values, 0, 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) { .values = NULL, .value = value, .length = 0, .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) { .length = length, .values = values, .value = 0, .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) { .length = length, .values = values, .value = 0, .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. + */ +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_DEFAULT: + while (*start == '0') start++; // 01 -> 1 + break; + 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; +} + +/** + * 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; +} + +/** + * Reduce a ratio of integers to its simplest form. + */ +void pm_integers_reduce(pm_integer_t *numerator, pm_integer_t *denominator) { + // If either the numerator or denominator do not fit into a 32-bit integer, + // then this function is a no-op. In the future, we may consider reducing + // even the larger numbers, but for now we're going to keep it simple. + if ( + // If the numerator doesn't fit into a 32-bit integer, return early. + numerator->length != 0 || + // If the denominator doesn't fit into a 32-bit integer, return early. + denominator->length != 0 || + // If the numerator is 0, then return early. + numerator->value == 0 || + // If the denominator is 1, then return early. + denominator->value == 1 + ) return; + + // Find the greatest common divisor of the numerator and denominator. + uint32_t divisor = numerator->value; + uint32_t remainder = denominator->value; + + while (remainder != 0) { + uint32_t temporary = remainder; + remainder = divisor % remainder; + divisor = temporary; + } + + // Divide the numerator and denominator by the greatest common divisor. + numerator->value /= divisor; + denominator->value /= divisor; +} + +/** + * 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/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/util/pm_list.c b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/util/pm_list.c new file mode 100644 index 0000000..ad2294c --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/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/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/util/pm_memchr.c b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/util/pm_memchr.c new file mode 100644 index 0000000..7ea20ac --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/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/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/util/pm_newline_list.c b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/util/pm_newline_list.c new file mode 100644 index 0000000..8331618 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/util/pm_newline_list.c @@ -0,0 +1,125 @@ +#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 of the given offset. If the offset is not in the list, the + * line of the closest offset less than the given offset is returned. + */ +int32_t +pm_newline_list_line(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 ((int32_t) mid) + start_line; + } + + if (list->offsets[mid] < offset) { + left = mid + 1; + } else { + right = mid - 1; + } + } + + return ((int32_t) left) + start_line - 1; +} + +/** + * 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/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/util/pm_string.c b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/util/pm_string.c new file mode 100644 index 0000000..a7493c4 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/util/pm_string.c @@ -0,0 +1,381 @@ +#include "prism/util/pm_string.h" + +static const uint8_t empty_source[] = ""; + +/** + * 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 + }; +} + +#ifdef _WIN32 +/** + * Represents a file handle on Windows, where the path will need to be freed + * when the file is closed. + */ +typedef struct { + /** The path to the file, which will become allocated memory. */ + WCHAR *path; + + /** The handle to the file, which will start as uninitialized memory. */ + HANDLE file; +} pm_string_file_handle_t; + +/** + * Open the file indicated by the filepath parameter for reading on Windows. + * Perform any kind of normalization that needs to happen on the filepath. + */ +static pm_string_init_result_t +pm_string_file_handle_open(pm_string_file_handle_t *handle, const char *filepath) { + int length = MultiByteToWideChar(CP_UTF8, 0, filepath, -1, NULL, 0); + if (length == 0) return PM_STRING_INIT_ERROR_GENERIC; + + handle->path = xmalloc(sizeof(WCHAR) * ((size_t) length)); + if ((handle->path == NULL) || (MultiByteToWideChar(CP_UTF8, 0, filepath, -1, handle->path, length) == 0)) { + xfree(handle->path); + return PM_STRING_INIT_ERROR_GENERIC; + } + + handle->file = CreateFileW(handle->path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); + if (handle->file == INVALID_HANDLE_VALUE) { + pm_string_init_result_t result = PM_STRING_INIT_ERROR_GENERIC; + + if (GetLastError() == ERROR_ACCESS_DENIED) { + DWORD attributes = GetFileAttributesW(handle->path); + if ((attributes != INVALID_FILE_ATTRIBUTES) && (attributes & FILE_ATTRIBUTE_DIRECTORY)) { + result = PM_STRING_INIT_ERROR_DIRECTORY; + } + } + + xfree(handle->path); + return result; + } + + return PM_STRING_INIT_SUCCESS; +} + +/** + * Close the file handle and free the path. + */ +static void +pm_string_file_handle_close(pm_string_file_handle_t *handle) { + xfree(handle->path); + CloseHandle(handle->file); +} +#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. + * + * 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 pm_string_init_result_t +pm_string_mapped_init(pm_string_t *string, const char *filepath) { +#ifdef _WIN32 + // Open the file for reading. + pm_string_file_handle_t handle; + pm_string_init_result_t result = pm_string_file_handle_open(&handle, filepath); + if (result != PM_STRING_INIT_SUCCESS) return result; + + // Get the file size. + DWORD file_size = GetFileSize(handle.file, NULL); + if (file_size == INVALID_FILE_SIZE) { + pm_string_file_handle_close(&handle); + return PM_STRING_INIT_ERROR_GENERIC; + } + + // 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) { + pm_string_file_handle_close(&handle); + *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = empty_source, .length = 0 }; + return PM_STRING_INIT_SUCCESS; + } + + // Create a mapping of the file. + HANDLE mapping = CreateFileMapping(handle.file, NULL, PAGE_READONLY, 0, 0, NULL); + if (mapping == NULL) { + pm_string_file_handle_close(&handle); + return PM_STRING_INIT_ERROR_GENERIC; + } + + // Map the file into memory. + uint8_t *source = (uint8_t *) MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0); + CloseHandle(mapping); + pm_string_file_handle_close(&handle); + + if (source == NULL) { + return PM_STRING_INIT_ERROR_GENERIC; + } + + *string = (pm_string_t) { .type = PM_STRING_MAPPED, .source = source, .length = (size_t) file_size }; + return PM_STRING_INIT_SUCCESS; +#elif defined(_POSIX_MAPPED_FILES) + // Open the file for reading + int fd = open(filepath, O_RDONLY); + if (fd == -1) { + return PM_STRING_INIT_ERROR_GENERIC; + } + + // Stat the file to get the file size + struct stat sb; + if (fstat(fd, &sb) == -1) { + close(fd); + return PM_STRING_INIT_ERROR_GENERIC; + } + + // Ensure it is a file and not a directory + if (S_ISDIR(sb.st_mode)) { + close(fd); + return PM_STRING_INIT_ERROR_DIRECTORY; + } + + // 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); + *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = empty_source, .length = 0 }; + return PM_STRING_INIT_SUCCESS; + } + + source = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); + if (source == MAP_FAILED) { + close(fd); + return PM_STRING_INIT_ERROR_GENERIC; + } + + close(fd); + *string = (pm_string_t) { .type = PM_STRING_MAPPED, .source = source, .length = size }; + return PM_STRING_INIT_SUCCESS; +#else + return pm_string_file_init(string, filepath); +#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 pm_string_init_result_t +pm_string_file_init(pm_string_t *string, const char *filepath) { +#ifdef _WIN32 + // Open the file for reading. + pm_string_file_handle_t handle; + pm_string_init_result_t result = pm_string_file_handle_open(&handle, filepath); + if (result != PM_STRING_INIT_SUCCESS) return result; + + // Get the file size. + DWORD file_size = GetFileSize(handle.file, NULL); + if (file_size == INVALID_FILE_SIZE) { + pm_string_file_handle_close(&handle); + return PM_STRING_INIT_ERROR_GENERIC; + } + + // 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) { + pm_string_file_handle_close(&handle); + *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = empty_source, .length = 0 }; + return PM_STRING_INIT_SUCCESS; + } + + // Create a buffer to read the file into. + uint8_t *source = xmalloc(file_size); + if (source == NULL) { + pm_string_file_handle_close(&handle); + return PM_STRING_INIT_ERROR_GENERIC; + } + + // Read the contents of the file + DWORD bytes_read; + if (!ReadFile(handle.file, source, file_size, &bytes_read, NULL)) { + pm_string_file_handle_close(&handle); + return PM_STRING_INIT_ERROR_GENERIC; + } + + // Check the number of bytes read + if (bytes_read != file_size) { + xfree(source); + pm_string_file_handle_close(&handle); + return PM_STRING_INIT_ERROR_GENERIC; + } + + pm_string_file_handle_close(&handle); + *string = (pm_string_t) { .type = PM_STRING_OWNED, .source = source, .length = (size_t) file_size }; + return PM_STRING_INIT_SUCCESS; +#elif defined(PRISM_HAS_FILESYSTEM) + // Open the file for reading + int fd = open(filepath, O_RDONLY); + if (fd == -1) { + return PM_STRING_INIT_ERROR_GENERIC; + } + + // Stat the file to get the file size + struct stat sb; + if (fstat(fd, &sb) == -1) { + close(fd); + return PM_STRING_INIT_ERROR_GENERIC; + } + + // Ensure it is a file and not a directory + if (S_ISDIR(sb.st_mode)) { + close(fd); + return PM_STRING_INIT_ERROR_DIRECTORY; + } + + // Check the size to see if it's empty + size_t size = (size_t) sb.st_size; + if (size == 0) { + close(fd); + *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = empty_source, .length = 0 }; + return PM_STRING_INIT_SUCCESS; + } + + size_t length = (size_t) size; + uint8_t *source = xmalloc(length); + if (source == NULL) { + close(fd); + return PM_STRING_INIT_ERROR_GENERIC; + } + + long bytes_read = (long) read(fd, source, length); + close(fd); + + if (bytes_read == -1) { + xfree(source); + return PM_STRING_INIT_ERROR_GENERIC; + } + + *string = (pm_string_t) { .type = PM_STRING_OWNED, .source = source, .length = length }; + return PM_STRING_INIT_SUCCESS; +#else + (void) string; + (void) filepath; + perror("pm_string_file_init is not implemented for this platform"); + return PM_STRING_INIT_ERROR_GENERIC; +#endif +} + +/** + * 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/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/util/pm_strncasecmp.c b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/util/pm_strncasecmp.c new file mode 100644 index 0000000..3f58421 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/util/pm_strncasecmp.c @@ -0,0 +1,36 @@ +#include "prism/util/pm_strncasecmp.h" + +/** + * A locale-insensitive version of `tolower(3)` + */ +static inline int +pm_tolower(int c) +{ + if ('A' <= c && c <= 'Z') { + return c | 0x20; + } + return c; +} + +/** + * 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 = pm_tolower(string1[offset]) - pm_tolower(string2[offset])) != 0) return difference; + offset++; + } + + return difference; +} diff --git a/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/util/pm_strpbrk.c b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/util/pm_strpbrk.c new file mode 100644 index 0000000..916a4cc --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/prism-1.9.0/src/util/pm_strpbrk.c @@ -0,0 +1,206 @@ +#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); +} + +/** + * Set the explicit encoding for the parser to the current encoding. + */ +static inline void +pm_strpbrk_explicit_encoding_set(pm_parser_t *parser, const uint8_t *source, size_t width) { + if (parser->explicit_encoding != NULL) { + if (parser->explicit_encoding == parser->encoding) { + // Okay, we already locked to this encoding. + } else if (parser->explicit_encoding == PM_ENCODING_UTF_8_ENTRY) { + // Not okay, we already found a Unicode escape sequence and this + // conflicts. + pm_diagnostic_list_append_format(&parser->error_list, source, source + width, PM_ERR_MIXED_ENCODING, parser->encoding->name); + } else { + // Should not be anything else. + assert(false && "unreachable"); + } + } + + parser->explicit_encoding = parser->encoding; +} + +/** + * 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(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 (validate && source[index] >= 0x80) pm_strpbrk_explicit_encoding_set(parser, source, 1); + 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; + const pm_encoding_t *encoding = parser->encoding; + + while (index < maximum) { + if (strchr((const char *) charset, source[index]) != NULL) { + return source + index; + } + + if (source[index] < 0x80) { + index++; + } else { + size_t width = encoding->char_width(source + index, (ptrdiff_t) (maximum - index)); + if (validate) pm_strpbrk_explicit_encoding_set(parser, source, width); + + 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 && 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; + const pm_encoding_t *encoding = parser->encoding; + + while (index < maximum) { + if (strchr((const char *) charset, source[index]) != NULL) { + return source + index; + } + + if (source[index] < 0x80 || !validate) { + index++; + } else { + size_t width = encoding->char_width(source + index, (ptrdiff_t) (maximum - index)); + pm_strpbrk_explicit_encoding_set(parser, source, width); + + 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 && 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(parser, source, charset, (size_t) length, validate); + } 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/vendor/bundle/ruby/3.3.0/gems/public_suffix-7.0.2/.yardopts b/vendor/bundle/ruby/3.3.0/gems/public_suffix-7.0.2/.yardopts new file mode 100644 index 0000000..0a782de --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/public_suffix-7.0.2/.yardopts @@ -0,0 +1 @@ +--title 'Ruby Public Suffix API Documentation' diff --git a/vendor/bundle/ruby/3.3.0/gems/public_suffix-7.0.2/CHANGELOG.md b/vendor/bundle/ruby/3.3.0/gems/public_suffix-7.0.2/CHANGELOG.md new file mode 100644 index 0000000..63a2a86 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/public_suffix-7.0.2/CHANGELOG.md @@ -0,0 +1,631 @@ +# Changelog + + +## Unreleased + + +## 7.0.2 - 2026-01-04 + +### Changed + +- Excluded symlinks and unnecessary files from gem packaging. On Windows symlinks cannot be created without Administrator privileges or with developer mode enabled #496. + + +## 7.0.1 - 2026-01-03 + +### Changed + +- Updated definitions. + + +## 7.0.0 - 2024-11-17 + +### Changed + +- Updated definitions. +- Minimum Ruby version is 3.2 + + +## 6.0.2 - 2024-04-30 + +### Changed + +- Updated definitions. + + +## 6.0.1 - 2024-07-23 + +### Changed + +- Updated definitions. + + +## 6.0.0 - 2024-06-17 + +Same as 5.1.0. Re-releasing as a major version change due to a major ruby version requirement change. + +### Changed + +- Updated definitions. +- Minimum Ruby version is 3.0 + + +## 5.1.1 - 2024-06-17 + +No significant changes. Releasing a mini version to address 5.1.0 release with major ruby requirement change #315. + + +## 5.1.0 - 2024-06-15 + +### Changed + +- Updated definitions. +- Minimum Ruby version is 3.0 + + +## 5.0.5 - 2024-04-02 + +### Changed + +- Updated definitions. + + +## 5.0.4 - 2023-11-17 + +### Changed + +- Reduced .gem file size #258. (Thanks @ybiquitous) +- Updated definitions. + + +## 5.0.3 - 2023-07-11 + +### Fixed + +- Fixed automated release workflow. + + +## 5.0.2 - 2023-07-11 + +### Changed + +- Updated definitions. + + +## 5.0.1 - 2022-12-07 + +### Changed + +- Updated definitions. + + +## 5.0.0 - 2022-07-24 + +### Changed + +- Minimum Ruby version is 2.6 +- Updated definitions. + + +## 4.0.7 - 2022-04-12 + +### Fixed + +- Fixed YARD rake task #179 + +### Changed + +- Updated definitions. + + +## 4.0.6 - 2020-09-02 + +### Changed + +- Updated definitions. + + +## 4.0.5 - 2020-05-09 + +### Changed + +- Updated definitions. + + +## 4.0.4 - 2020-04-05 + +### Changed + +- Updated definitions. + + +## 4.0.3 - 2020-01-05 + +### Fixed + +- Fixed 2.7 deprecations and warnings #167. (Thanks @BrianHawley) + + +## 4.0.2 - 2019-12-27 + +### Changed + +- Updated definitions. + + +## 4.0.1 - 2019-08-09 + +### Changed + +- Updated definitions. + + +## 4.0.0 - 2019-06-25 + +### Changed + +- Minimum Ruby version is 2.3 + + +## 3.1.1 - 2019-06-25 + +IMPORTANT: 3.x is the latest version compatible with Ruby 2.1 and Ruby 2.2. + +### Changed + +- Updated definitions. +- Rolled back support for Ruby 2.3 #161, #162 + + +## 3.1.0 - 2019-05-27 + +### Changed + +- Updated definitions. +- Minimum Ruby version is 2.3 +- Upgraded to Bundler 2.x + + +## 3.0.3 - 2018-08-15 + +### Changed + +- Updated definitions. + + +## 3.0.2 - 2018-02-12 + +### Changed + +- Updated definitions. + + +## 3.0.1 - 2017-11-08 + +### Changed + +- Updated definitions. +- Improve performance and avoid allocation #146. (Thanks @robholland) + + +## 3.0.0 - 2017-08-04 + +This new version includes a major redesign of the library internals, with the goal to drastically improve the lookup time while reducing storage space. + +For this reason, several public methods that are no longer applicable have been deprecated and/or removed. You can find more information at #133. + +### Changed + +- Updated definitions. +- Dropped support for Ruby < 2.1 +- `PublicSuffix::List#rules` is now protected. You should not rely on it as the internal rule representation is subject to change to optimize performances. +- Removed `PublicSuffix::List.clear`, it was an unnecessary accessor method. Use `PublicSuffix::List.default = nil` if you **really** need to reset the default list. You shouldn't. +- `PublicSuffix::List#select` is now private. You should not use it, instead use `PublicSuffix::List#find`. +- `PublicSuffix::List` no longer implements Enumerable. Instead, use `#each` to loop over, or get an Enumerator. +- Redesigned internal list storage and lookup algorithm to achieve O(1) lookup time (see #133). + + +## 2.0.5 - 2017-01-02 + +### Changed + +- Updated definitions. +- Initialization performance improvements #128. (Thanks @casperisfine) + + +## 2.0.4 - 2016-11-07 + +### Fixed + +- Fixed a bug that caused the GEM to be published with the wrong version number in the gemspec #121. + +### Changed + +- Updated definitions. + + +## 2.0.3 - 2016-09-30 + +### Changed + +- Updated definitions. + + +## 2.0.2 - 2016-06-10 + +### Changed + +- Updated definitions. + + +## 2.0.1 - 2016-05-22 + +### Fixed + +- Fix bug that prevented .valid? to reset the default rule + + +## 2.0.0 - 2016-05-20 + +### Added + +- Added PublicSuffix.domain # => sld.tld +- Added the ability to disable the use of private domains either at runtime, in addition to the ability to not load the private domains section when reading the list (`private_domains: false`). This feature also superseded the `private_domains` class-level attribute, that is no longer available. + +### Changed + +- Considerable performance improvements #92 +- Updated definitions. +- Removed deprecated PublicSuffix::InvalidDomain exception +- If the suffix is now listed, then the prevaling rule is "*" as defined by the PSL algorithm #91 +- Input validation is performed only if you call `PublicSuffix.parse` or `PublicSuffix.list` +- Input with leading dot is invalid per PSL acceptance tests +- Removed `private_domains` class-level attribute. It is replaced by the `private_domains: false` option in the list parse method. +- The default list now assumes you use UTF-8 for reading the input #94, + +### Removed + +- Removed futile utility helpers such as `Domain#rule`, `Domain#is_a_domain?`, `Domain#is_a_subdomain?`, `Domain#valid?`. You can easily obtain the same result by having a custom method that reconstructs the logic, and/or calling `PublicSuffix.{domain|parse}(domain.to_s)`. + + +## 1.5.3 - 2015-12-14 + +### Fixed + +- Don't duplicate rule indices when creating index #77. (Thanks @ags) + +### Changed + +- Updated definitions. + + +## 1.5.2 - 2015-10-27 + +### Changed + +- Updated definitions. + + +## 1.5.1 - 2015-04-10 + +### Fixed + +- Ignore case for parsing and validating #62 + +### Changed + +- Updated definitions. + + +## 1.5.0 - 2015-03-24 + +### Changed + +- Dropped support for Ruby < 2.0 +- Updated definitions. + + +## 1.4.6 - 2014-09-10 + +### Changed + +- Updated definitions. + + +## 1.4.5 - 2014-08-18 + +### Changed + +- Updated definitions. + + +## 1.4.4 - 2014-06-17 + +### Changed + +- Updated definitions. + + +## 1.4.3 - 2014-06-12 + +### Changed + +- Updated definitions. + + +## 1.4.2 - 2014-03-10 + +### Changed + +- Updated definitions. + + +## 1.4.1 - 2014-03-07 + +### Changed + +- Updated definitions. + + +## 1.4.0 - 2014-02-01 + +### Changed + +- Moved the definitions in the lib folder. +- Updated definitions. + + +## 1.3.3 - 2013-12-01 + +### Changed + +- Updated definitions. + + +## 1.3.2 - 2013-11-11 + +### Changed + +- Updated definitions. + + +## 1.3.1 - 2013-08-09 + +### Changed + +- Updated definitions. + + +## 1.3.0 - 2013-04-03 + +### Added + +- Ability to skip Private Domains #28. (Thanks @rb2k) + +### Changed + +- Updated definitions. + + +## 1.2.1 - 2013-03-26 + +### Changed + +- Updated definitions. + + +## 1.2.0 - 2012-12-24 + +### Added + +- Allow a custom List on `PublicSuffix.parse` #26. (Thanks @itspriddle) + +### Fixed + +- PublicSuffix.parse and PublicSuffix.valid? crashes when input is nil #20. + +### Changed + +- Updated definitions. + + +## 1.1.3 - 2012-09-17 + +### Changed + +- Updated definitions. + + +## 1.1.2 - 2012-09-03 + +### Changed + +- Updated definitions. + + +## 1.1.1 - 2012-06-26 + +### Changed + +- Updated definitions. + + +## 1.1.0 - 2012-03-16 + +### Fixed + +- #valid? and #parse consider URIs as valid domains #15 + +### Changed + +- Updated definitions. +- Removed deprecatd PublicSuffixService::RuleList. + + +## 1.0.0 - 2011-12-24 + +### Changed + +- Updated definitions. + + +## 1.0.0.rc1 - 2011-12-24 + +The library is now known as PublicSuffix. + + +## 0.9.1 - 2011-12-24 + +### Changed + +- Renamed PublicSuffixService::RuleList to PublicSuffixService::List. +- Renamed PublicSuffixService::List#list to PublicSuffixService::List#rules. +- Renamed PublicSuffixService to PublicSuffix. +- Updated definitions. + + +## 0.9.0 - 2011-06-17 + +### Changed + +- Minimum Ruby version increased to Ruby 1.8.7. +- rake/gempackagetask is deprecated. Use rubygems/package_task instead. + + +## 0.8.4 - 2011-06-17 + +### Fixed + +- Reverted bugfix for issue #12 for Ruby 1.8.6. This is the latest version compatible with Ruby 1.8.6. + + +## 0.8.3 - 2011-05-27 + +### Fixed + +- Fixed ArgumentError: invalid byte sequence in US-ASCII with Ruby 1.9.2 (#12). + +### Changed + +- Updated definitions (#11). +- Renamed definitions.txt to definitions.dat. + + +## 0.8.2 - 2011-03-11 + +### Added + +- Added support for rubygems-test. + +### Changed + +- Integrated Bundler. +- Updated definitions. + + +## 0.8.1 - 2010-12-07 + +### Fixed + +- The files in the release 0.8.0 have wrong permission 600 and can't be loaded #10. + + +## 0.8.0 - 2010-12-05 + +### Added + +- Add support for Fully Qualified Domain Names #7 + +### Changed + +- Update public suffix list to d1a5599b49fa 2010-10-25 15:10 +0100 #9 + + +## 0.7.0 - 2010-10-09 + +### Fixed + +- RuleList cache is not recreated when a new rule is appended to the list #6 +- PublicSuffixService.valid? should return false if the domain is not defined or not allowed #4, #5 + +### Changed + +- Using YARD to document the code instead of RDoc. + + +## 0.6.0 - 2010-09-18 + +### Added + +- PublicSuffixService.parse raises DomainNotAllowed when trying to parse a domain name which exists, but is not allowed by the current definition list #3 + + PublicSuffixService.parse("nic.do") + # => PublicSuffixService::DomainNotAllowed + +### Changed + +- Renamed PublicSuffixService::InvalidDomain to PublicSuffixService::DomainInvalid + + +## 0.5.2 - 2010-09-17 + +### Changed + +- Update public suffix list to 248ea690d671 2010-09-16 18:02 +0100 + + +## 0.5.1 - 2010-09-15 + +### Changed + +- Update public suffix list to 14dc66dd53c1 2010-09-15 17:09 +0100 + + +## 0.5.0 - 2010-09-13 + +### Changed + +- Improve documentation for Domain#domain and Domain#subdomain #1. +- Performance improvements #2. + + +## 0.4.0 - 2010-05-31 + +### Changed + +- Rename library from DomainName to PublicSuffixService to reduce the probability of name conflicts. + + +## 0.3.1 - 2010-05-31 + +### Changed + +- Deprecated DomainName library. + + +## 0.3.0 - 2010-05-31 + +### Changed + +- DomainName#domain and DomainName#subdomain are no longer alias of Domain#sld and Domain#tld. +- Removed DomainName#labels and decoupled Rule from DomainName. +- DomainName#valid? no longer instantiates new DomainName objects. This means less overhead. +- Refactoring the entire DomainName API. Removed the internal on-the-fly parsing. Added a bunch of new methods to check and validate the DomainName. + + +## 0.2.0 - 2010-05-31 + +### Added + +- DomainName#valid? +- DomainName#parse and DomainName#parse! +- DomainName#valid_domain? and DomainName#valid_subdomain? + +### Changed + +- Make sure RuleList lookup is only performed once. + + +## 0.1.0 - 2010-05-31 + +- Initial version diff --git a/vendor/bundle/ruby/3.3.0/gems/public_suffix-7.0.2/LICENSE.txt b/vendor/bundle/ruby/3.3.0/gems/public_suffix-7.0.2/LICENSE.txt new file mode 100644 index 0000000..2d60588 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/public_suffix-7.0.2/LICENSE.txt @@ -0,0 +1,22 @@ +Copyright (c) 2009-2026 Simone Carletti + +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/vendor/bundle/ruby/3.3.0/gems/public_suffix-7.0.2/README.md b/vendor/bundle/ruby/3.3.0/gems/public_suffix-7.0.2/README.md new file mode 100644 index 0000000..05ae1d1 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/public_suffix-7.0.2/README.md @@ -0,0 +1,231 @@ +# PublicSuffix for Ruby + +`PublicSuffix` is a Ruby domain name parser based on the [Public Suffix List](https://publicsuffix.org/). + +[![Build Status](https://github.com/weppos/publicsuffix-ruby/actions/workflows/tests.yml/badge.svg)](https://github.com/weppos/publicsuffix-ruby/actions/workflows/tests.yml) +[![Tidelift dependencies](https://tidelift.com/badges/package/rubygems/public_suffix)](https://tidelift.com/subscription/pkg/rubygems-public-suffix?utm_source=rubygems-public-suffix&utm_medium=referral&utm_campaign=enterprise) + + +## Links + +- [Homepage](https://simonecarletti.com/code/publicsuffix-ruby) +- [Repository](https://github.com/weppos/publicsuffix-ruby) +- [API Documentation](https://rubydoc.info/gems/public_suffix) +- [Introducing the Public Suffix List library for Ruby](https://simonecarletti.com/blog/2010/06/public-suffix-list-library-for-ruby/) + + +## Requirements + +`PublicSuffix` requires **Ruby >= 3.2**. For older versions of Ruby, use a previous release. + + +## Installation + +You can install the gem manually: + +```shell +gem install public_suffix +``` + +Or use Bundler and define it as a dependency in your `Gemfile`: + +```ruby +gem 'public_suffix' +``` + +## Usage + +Extract the domain out from a name: + +```ruby +PublicSuffix.domain("google.com") +# => "google.com" +PublicSuffix.domain("www.google.com") +# => "google.com" +PublicSuffix.domain("www.google.co.uk") +# => "google.co.uk" +``` + +Parse a domain without subdomains: + +```ruby +domain = PublicSuffix.parse("google.com") +# => # +domain.tld +# => "com" +domain.sld +# => "google" +domain.trd +# => nil +domain.domain +# => "google.com" +domain.subdomain +# => nil +``` + +Parse a domain with subdomains: + +```ruby +domain = PublicSuffix.parse("www.google.com") +# => # +domain.tld +# => "com" +domain.sld +# => "google" +domain.trd +# => "www" +domain.domain +# => "google.com" +domain.subdomain +# => "www.google.com" +``` + +Simple validation example: + +```ruby +PublicSuffix.valid?("google.com") +# => true + +PublicSuffix.valid?("www.google.com") +# => true + +# Explicitly forbidden, it is listed as a private domain +PublicSuffix.valid?("blogspot.com") +# => false + +# Unknown/not-listed TLD domains are valid by default +PublicSuffix.valid?("example.tldnotlisted") +# => true +``` + +Strict validation (without applying the default * rule): + +```ruby +PublicSuffix.valid?("example.tldnotlisted", default_rule: nil) +# => false +``` + + +## Fully qualified domain names + +This library automatically recognizes Fully Qualified Domain Names. A FQDN is a domain name that ends with a trailing dot. + +```ruby +# Parse a standard domain name +PublicSuffix.domain("www.google.com") +# => "google.com" + +# Parse a fully qualified domain name +PublicSuffix.domain("www.google.com.") +# => "google.com" +``` + +## Private domains + +This library supports toggling private (non-ICANN) domain handling. + +```ruby +# Extract a domain including private domains (by default) +PublicSuffix.domain("something.blogspot.com") +# => "something.blogspot.com" + +# Extract a domain excluding private domains +PublicSuffix.domain("something.blogspot.com", ignore_private: true) +# => "blogspot.com" + +# It also works for #parse and #valid? +PublicSuffix.parse("something.blogspot.com", ignore_private: true) +PublicSuffix.valid?("something.blogspot.com", ignore_private: true) +``` + +If you don't care about private domains at all, it's more efficient to exclude them when the list is parsed: + +```ruby +# Disable support for private TLDs +PublicSuffix::List.default = PublicSuffix::List.parse(File.read(PublicSuffix::List::DEFAULT_LIST_PATH), private_domains: false) +# => "blogspot.com" +PublicSuffix.domain("something.blogspot.com") +# => "blogspot.com" +``` + +## Adding custom domains + +To manually add a domain to the list: + +```ruby +PublicSuffix::List.default << PublicSuffix::Rule.factory('onmicrosoft.com') +``` + +## What is the public suffix list? + +The [Public Suffix List](https://publicsuffix.org) is a cross-vendor initiative to provide an accurate list of domain name suffixes. + +The Public Suffix List is an initiative of the Mozilla Project, but is maintained as a community resource. It is available for use in any software, but was originally created to meet the needs of browser manufacturers. + +A "public suffix" is one under which Internet users can directly register names. Some examples of public suffixes are ".com", ".co.uk" and "pvt.k12.wy.us". The Public Suffix List is a list of all known public suffixes. + + +## Why use the public suffix list instead of regular expressions? + +Previously, browsers used an algorithm which basically only denied setting wide-ranging cookies for top-level domains with no dots (e.g. com or org). However, this did not work for top-level domains where only third-level registrations are allowed (e.g. co.uk). In these cases, websites could set a cookie for co.uk which will be passed onto every website registered under co.uk. + +Clearly, this was a security risk as it allowed websites other than the one setting the cookie to read it, and therefore potentially extract sensitive information. + +Since there is no algorithmic method of finding the highest level at which a domain may be registered for a particular top-level domain (the policies differ with each registry), the only method is to create a list of all top-level domains and the level at which domains can be registered. This is the aim of the effective TLD list. + +As well as being used to prevent cookies from being set where they shouldn't be, the list can also potentially be used for other applications where the registry controlled and privately controlled parts of a domain name need to be known, for example when grouping by top-level domains. + +Source: https://wiki.mozilla.org/Public_Suffix_List + +Not convinced yet? Check out [this real world example](https://stackoverflow.com/q/288810/123527). + + +## Does PublicSuffix make network requests? + +No. `PublicSuffix` comes with a bundled list. It does not make any HTTP requests to parse or validate a domain. + + +## Terminology + +- **TLD** (Top-Level Domain): The last segment of a domain name. For example, in `mozilla.org`, the `.org` portion is the TLD. + +- **SLD** (Second-Level Domain): A domain directly below a top-level domain. For example, in `https://www.mozilla.org/en-US/`, `mozilla` is the second-level domain of the `.org` TLD. + +- **TRD** (Third-Level Domain): Also known as a subdomain, this is the part of the domain before the SLD or root domain. For example, in `https://www.mozilla.org/en-US/`, `www` is the TRD. + +- **FQDN** (Fully Qualified Domain Name): A complete domain name that includes the hostname, domain, and top-level domain, ending with a trailing dot. The format is `[hostname].[domain].[tld].` (e.g., `www.mozilla.org.`). + + +## Documentation and support + +### Documentation + +Library documentation is auto-generated from the [README](https://github.com/weppos/publicsuffix-ruby/blob/master/README.md) and source code, and is available at https://rubydoc.info/gems/public_suffix. + +### Bug reports and contributions + +- **Bug Tracker**: https://github.com/weppos/publicsuffix-ruby/issues +- **Code Repository**: https://github.com/weppos/publicsuffix-ruby + +Contributions are welcome! Please include tests and/or feature coverage for every patch, and create a topic branch for every separate change you make. + +### Enterprise support + +[Consider subscribing to Tidelift](https://tidelift.com/subscription/pkg/rubygems-public-suffix?utm_source=rubygems-public-suffix&utm_medium=referral&utm_campaign=readme), which provides enterprise support for this project as part of the Tidelift Subscription. Tidelift subscriptions help fund the project, allowing us to ship releases, bugfixes, and security updates more frequently. + + +## Security and vulnerability reporting + +For full information and details about our security policy, please visit [`SECURITY.md`](SECURITY.md). + + +## Changelog + +See [CHANGELOG.md](CHANGELOG.md) for details. + + +## License + +Copyright (c) 2009-2026 Simone Carletti. [MIT License](LICENSE.txt). + +The [Public Suffix List source](https://publicsuffix.org/list/) is subject to the terms of the Mozilla Public License, v. 2.0. diff --git a/vendor/bundle/ruby/3.3.0/gems/public_suffix-7.0.2/SECURITY.md b/vendor/bundle/ruby/3.3.0/gems/public_suffix-7.0.2/SECURITY.md new file mode 100644 index 0000000..da6e619 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/public_suffix-7.0.2/SECURITY.md @@ -0,0 +1,24 @@ +# Security Policy + +## Supported Versions + +Security updates are provided only for the current minor version. + +If you are using a previous minor version, we recommend to upgrade to the current minor version. This project uses [semantic versioning](https://semver.org/), therefore you can upgrade to a more recent minor version without incurring into breaking changes. + +Exceptionally, we may support previous minor versions upon request if there are significant reasons preventing to immediately switch the latest minor version. + +Older major versions are no longer supported. + + +## Reporting a Vulnerability + +To make a report, please email weppos@weppos.net. + +> [!IMPORTANT] +> Please consider encrypting your report with GPG using the key [0x420da82a989398df](https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x420da82a989398df). + + +## Tracking Security Updates + +Information about security vulnerabilities are published in the [Security Advisories](https://github.com/weppos/publicsuffix-ruby/security/advisories) page. diff --git a/vendor/bundle/ruby/3.3.0/gems/public_suffix-7.0.2/data/list.txt b/vendor/bundle/ruby/3.3.0/gems/public_suffix-7.0.2/data/list.txt new file mode 100644 index 0000000..bdfb349 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/public_suffix-7.0.2/data/list.txt @@ -0,0 +1,16226 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +// Please pull this list from, and only from https://publicsuffix.org/list/public_suffix_list.dat, +// rather than any other VCS sites. Pulling from any other URL is not guaranteed to be supported. + +// Instructions on pulling and using this list can be found at https://publicsuffix.org/list/. + +// ===BEGIN ICANN DOMAINS=== + +// ac : http://nic.ac/rules.htm +ac +com.ac +edu.ac +gov.ac +mil.ac +net.ac +org.ac + +// ad : https://www.iana.org/domains/root/db/ad.html +// Confirmed by Amadeu Abril i Abril (CORE) 2024-11-17 +ad + +// ae : https://www.iana.org/domains/root/db/ae.html +ae +ac.ae +co.ae +gov.ae +mil.ae +net.ae +org.ae +sch.ae + +// aero : https://information.aero/registration/policies/dmp +aero +// 2LDs +airline.aero +airport.aero +// 2LDs (currently not accepting registration, seemingly never have) +// As of 2024-07, these are marked as reserved for potential 3LD +// registrations (clause 11 "allocated subdomains" in the 2006 TLD +// policy), but the relevant industry partners have not opened them up +// for registration. Current status can be determined from the TLD's +// policy document: 2LDs that are open for registration must list +// their policy in the TLD's policy. Any 2LD without such a policy is +// not open for registrations. +accident-investigation.aero +accident-prevention.aero +aerobatic.aero +aeroclub.aero +aerodrome.aero +agents.aero +air-surveillance.aero +air-traffic-control.aero +aircraft.aero +airtraffic.aero +ambulance.aero +association.aero +author.aero +ballooning.aero +broker.aero +caa.aero +cargo.aero +catering.aero +certification.aero +championship.aero +charter.aero +civilaviation.aero +club.aero +conference.aero +consultant.aero +consulting.aero +control.aero +council.aero +crew.aero +design.aero +dgca.aero +educator.aero +emergency.aero +engine.aero +engineer.aero +entertainment.aero +equipment.aero +exchange.aero +express.aero +federation.aero +flight.aero +freight.aero +fuel.aero +gliding.aero +government.aero +groundhandling.aero +group.aero +hanggliding.aero +homebuilt.aero +insurance.aero +journal.aero +journalist.aero +leasing.aero +logistics.aero +magazine.aero +maintenance.aero +marketplace.aero +media.aero +microlight.aero +modelling.aero +navigation.aero +parachuting.aero +paragliding.aero +passenger-association.aero +pilot.aero +press.aero +production.aero +recreation.aero +repbody.aero +res.aero +research.aero +rotorcraft.aero +safety.aero +scientist.aero +services.aero +show.aero +skydiving.aero +software.aero +student.aero +taxi.aero +trader.aero +trading.aero +trainer.aero +union.aero +workinggroup.aero +works.aero + +// af : https://www.nic.af/domain-price +af +com.af +edu.af +gov.af +net.af +org.af + +// ag : http://www.nic.ag/prices.htm +ag +co.ag +com.ag +net.ag +nom.ag +org.ag + +// ai : http://nic.com.ai/ +ai +com.ai +net.ai +off.ai +org.ai + +// al : http://www.ert.gov.al/ert_alb/faq_det.html?Id=31 +al +com.al +edu.al +gov.al +mil.al +net.al +org.al + +// am : https://www.amnic.net/policy/en/Policy_EN.pdf +// Confirmed by ISOC AM 2024-11-18 +am +co.am +com.am +commune.am +net.am +org.am + +// ao : https://www.iana.org/domains/root/db/ao.html +// https://www.dns.ao/ao/ +ao +co.ao +ed.ao +edu.ao +gov.ao +gv.ao +it.ao +og.ao +org.ao +pb.ao + +// aq : https://www.iana.org/domains/root/db/aq.html +aq + +// ar : https://nic.ar/es/nic-argentina/normativa +ar +bet.ar +com.ar +coop.ar +edu.ar +gob.ar +gov.ar +int.ar +mil.ar +musica.ar +mutual.ar +net.ar +org.ar +seg.ar +senasa.ar +tur.ar + +// arpa : https://www.iana.org/domains/root/db/arpa.html +// Confirmed by registry 2008-06-18 +arpa +e164.arpa +home.arpa +in-addr.arpa +ip6.arpa +iris.arpa +uri.arpa +urn.arpa + +// as : https://www.iana.org/domains/root/db/as.html +as +gov.as + +// asia : https://www.iana.org/domains/root/db/asia.html +asia + +// at : https://www.iana.org/domains/root/db/at.html +// Confirmed by registry 2008-06-17 +at +ac.at +sth.ac.at +co.at +gv.at +or.at + +// au : https://www.iana.org/domains/root/db/au.html +// https://www.auda.org.au/ +// Confirmed by registry 2025-07-16 +au +// 2LDs +asn.au +com.au +edu.au +gov.au +id.au +net.au +org.au +// Historic 2LDs (closed to new registration, but sites still exist) +conf.au +oz.au +// CGDNs : https://www.auda.org.au/au-domain-names/the-different-au-domain-names/state-and-territory-domain-names/ +act.au +nsw.au +nt.au +qld.au +sa.au +tas.au +vic.au +wa.au +// 3LDs +act.edu.au +catholic.edu.au +// eq.edu.au - Removed at the request of the Queensland Department of Education +nsw.edu.au +nt.edu.au +qld.edu.au +sa.edu.au +tas.edu.au +vic.edu.au +wa.edu.au +// act.gov.au - Bug 984824 - Removed at request of Greg Tankard +// nsw.gov.au - Bug 547985 - Removed at request of +// nt.gov.au - Bug 940478 - Removed at request of Greg Connors +qld.gov.au +sa.gov.au +tas.gov.au +vic.gov.au +wa.gov.au +// 4LDs +// education.tas.edu.au - Removed at the request of the Department of Education Tasmania +// schools.nsw.edu.au - Removed at the request of the New South Wales Department of Education. + +// aw : https://www.iana.org/domains/root/db/aw.html +aw +com.aw + +// ax : https://www.iana.org/domains/root/db/ax.html +ax + +// az : https://www.iana.org/domains/root/db/az.html +// Confirmed via https://whois.az/?page_id=10 2024-12-11 +az +biz.az +co.az +com.az +edu.az +gov.az +info.az +int.az +mil.az +name.az +net.az +org.az +pp.az +// No longer available for registration, however domains exist as of 2024-12-11 +// see https://whois.az/?page_id=783 +pro.az + +// ba : https://www.iana.org/domains/root/db/ba.html +ba +com.ba +edu.ba +gov.ba +mil.ba +net.ba +org.ba + +// bb : https://www.iana.org/domains/root/db/bb.html +bb +biz.bb +co.bb +com.bb +edu.bb +gov.bb +info.bb +net.bb +org.bb +store.bb +tv.bb + +// bd : https://www.iana.org/domains/root/db/bd.html +// Confirmed by registry +bd +ac.bd +ai.bd +co.bd +com.bd +edu.bd +gov.bd +id.bd +info.bd +it.bd +mil.bd +net.bd +org.bd +sch.bd +tv.bd + +// be : https://www.iana.org/domains/root/db/be.html +// Confirmed by registry 2008-06-08 +be +ac.be + +// bf : https://www.iana.org/domains/root/db/bf.html +bf +gov.bf + +// bg : https://www.iana.org/domains/root/db/bg.html +// https://www.register.bg/user/static/rules/en/index.html +bg +0.bg +1.bg +2.bg +3.bg +4.bg +5.bg +6.bg +7.bg +8.bg +9.bg +a.bg +b.bg +c.bg +d.bg +e.bg +f.bg +g.bg +h.bg +i.bg +j.bg +k.bg +l.bg +m.bg +n.bg +o.bg +p.bg +q.bg +r.bg +s.bg +t.bg +u.bg +v.bg +w.bg +x.bg +y.bg +z.bg + +// bh : https://www.iana.org/domains/root/db/bh.html +bh +com.bh +edu.bh +gov.bh +net.bh +org.bh + +// bi : https://www.iana.org/domains/root/db/bi.html +// http://whois.nic.bi/ +bi +co.bi +com.bi +edu.bi +or.bi +org.bi + +// biz : https://www.iana.org/domains/root/db/biz.html +biz + +// bj : https://nic.bj/bj-suffixes.txt +// Submitted by registry +bj +africa.bj +agro.bj +architectes.bj +assur.bj +avocats.bj +co.bj +com.bj +eco.bj +econo.bj +edu.bj +info.bj +loisirs.bj +money.bj +net.bj +org.bj +ote.bj +restaurant.bj +resto.bj +tourism.bj +univ.bj + +// bm : https://www.bermudanic.bm/domain-registration/index.php +bm +com.bm +edu.bm +gov.bm +net.bm +org.bm + +// bn : http://www.bnnic.bn/faqs +bn +com.bn +edu.bn +gov.bn +net.bn +org.bn + +// bo : https://nic.bo +// Confirmed by registry 2024-11-19 +bo +com.bo +edu.bo +gob.bo +int.bo +mil.bo +net.bo +org.bo +tv.bo +web.bo +// Social Domains +academia.bo +agro.bo +arte.bo +blog.bo +bolivia.bo +ciencia.bo +cooperativa.bo +democracia.bo +deporte.bo +ecologia.bo +economia.bo +empresa.bo +indigena.bo +industria.bo +info.bo +medicina.bo +movimiento.bo +musica.bo +natural.bo +nombre.bo +noticias.bo +patria.bo +plurinacional.bo +politica.bo +profesional.bo +pueblo.bo +revista.bo +salud.bo +tecnologia.bo +tksat.bo +transporte.bo +wiki.bo + +// br : http://registro.br/dominio/categoria.html +// Submitted by registry +br +9guacu.br +abc.br +adm.br +adv.br +agr.br +aju.br +am.br +anani.br +aparecida.br +api.br +app.br +arq.br +art.br +ato.br +b.br +barueri.br +belem.br +bet.br +bhz.br +bib.br +bio.br +blog.br +bmd.br +boavista.br +bsb.br +campinagrande.br +campinas.br +caxias.br +cim.br +cng.br +cnt.br +com.br +contagem.br +coop.br +coz.br +cri.br +cuiaba.br +curitiba.br +def.br +des.br +det.br +dev.br +ecn.br +eco.br +edu.br +emp.br +enf.br +eng.br +esp.br +etc.br +eti.br +far.br +feira.br +flog.br +floripa.br +fm.br +fnd.br +fortal.br +fot.br +foz.br +fst.br +g12.br +geo.br +ggf.br +goiania.br +gov.br +// gov.br 26 states + df https://en.wikipedia.org/wiki/States_of_Brazil +ac.gov.br +al.gov.br +am.gov.br +ap.gov.br +ba.gov.br +ce.gov.br +df.gov.br +es.gov.br +go.gov.br +ma.gov.br +mg.gov.br +ms.gov.br +mt.gov.br +pa.gov.br +pb.gov.br +pe.gov.br +pi.gov.br +pr.gov.br +rj.gov.br +rn.gov.br +ro.gov.br +rr.gov.br +rs.gov.br +sc.gov.br +se.gov.br +sp.gov.br +to.gov.br +gru.br +ia.br +imb.br +ind.br +inf.br +jab.br +jampa.br +jdf.br +joinville.br +jor.br +jus.br +leg.br +leilao.br +lel.br +log.br +londrina.br +macapa.br +maceio.br +manaus.br +maringa.br +mat.br +med.br +mil.br +morena.br +mp.br +mus.br +natal.br +net.br +niteroi.br +*.nom.br +not.br +ntr.br +odo.br +ong.br +org.br +osasco.br +palmas.br +poa.br +ppg.br +pro.br +psc.br +psi.br +pvh.br +qsl.br +radio.br +rec.br +recife.br +rep.br +ribeirao.br +rio.br +riobranco.br +riopreto.br +salvador.br +sampa.br +santamaria.br +santoandre.br +saobernardo.br +saogonca.br +seg.br +sjc.br +slg.br +slz.br +social.br +sorocaba.br +srv.br +taxi.br +tc.br +tec.br +teo.br +the.br +tmp.br +trd.br +tur.br +tv.br +udi.br +vet.br +vix.br +vlog.br +wiki.br +xyz.br +zlg.br + +// bs : http://www.nic.bs/rules.html +bs +com.bs +edu.bs +gov.bs +net.bs +org.bs + +// bt : https://www.iana.org/domains/root/db/bt.html +bt +com.bt +edu.bt +gov.bt +net.bt +org.bt + +// bv : No registrations at this time. +// Submitted by registry +bv + +// bw : https://www.iana.org/domains/root/db/bw.html +// https://nic.net.bw/bw-name-structure +bw +ac.bw +co.bw +gov.bw +net.bw +org.bw + +// by : https://www.iana.org/domains/root/db/by.html +// http://tld.by/rules_2006_en.html +// list of other 2nd level tlds ? +by +gov.by +mil.by +// Official information does not indicate that com.by is a reserved +// second-level domain, but it's being used as one (see www.google.com.by and +// www.yahoo.com.by, for example), so we list it here for safety's sake. +com.by +// http://hoster.by/ +of.by + +// bz : https://www.iana.org/domains/root/db/bz.html +// http://www.belizenic.bz/ +bz +co.bz +com.bz +edu.bz +gov.bz +net.bz +org.bz + +// ca : https://www.iana.org/domains/root/db/ca.html +ca +// ca geographical names +ab.ca +bc.ca +mb.ca +nb.ca +nf.ca +nl.ca +ns.ca +nt.ca +nu.ca +on.ca +pe.ca +qc.ca +sk.ca +yk.ca +// gc.ca: https://en.wikipedia.org/wiki/.gc.ca +// see also: http://registry.gc.ca/en/SubdomainFAQ +gc.ca + +// cat : https://www.iana.org/domains/root/db/cat.html +cat + +// cc : https://www.iana.org/domains/root/db/cc.html +cc + +// cd : https://www.iana.org/domains/root/db/cd.html +// https://www.nic.cd +cd +gov.cd + +// cf : https://www.iana.org/domains/root/db/cf.html +cf + +// cg : https://www.iana.org/domains/root/db/cg.html +cg + +// ch : https://www.iana.org/domains/root/db/ch.html +ch + +// ci : https://www.iana.org/domains/root/db/ci.html +ci +ac.ci +aéroport.ci +asso.ci +co.ci +com.ci +ed.ci +edu.ci +go.ci +gouv.ci +int.ci +net.ci +or.ci +org.ci + +// ck : https://www.iana.org/domains/root/db/ck.html +*.ck +!www.ck + +// cl : https://www.nic.cl +// Confirmed by .CL registry +cl +co.cl +gob.cl +gov.cl +mil.cl + +// cm : https://www.iana.org/domains/root/db/cm.html plus bug 981927 +cm +co.cm +com.cm +gov.cm +net.cm + +// cn : https://www.iana.org/domains/root/db/cn.html +// Submitted by registry +cn +ac.cn +com.cn +edu.cn +gov.cn +mil.cn +net.cn +org.cn +公司.cn +網絡.cn +网络.cn +// cn geographic names +ah.cn +bj.cn +cq.cn +fj.cn +gd.cn +gs.cn +gx.cn +gz.cn +ha.cn +hb.cn +he.cn +hi.cn +hk.cn +hl.cn +hn.cn +jl.cn +js.cn +jx.cn +ln.cn +mo.cn +nm.cn +nx.cn +qh.cn +sc.cn +sd.cn +sh.cn +sn.cn +sx.cn +tj.cn +tw.cn +xj.cn +xz.cn +yn.cn +zj.cn + +// co : https://www.iana.org/domains/root/db/co.html +// https://www.cointernet.com.co/como-funciona-un-dominio-restringido +// Confirmed by registry 2024-11-18 +co +com.co +edu.co +gov.co +mil.co +net.co +nom.co +org.co + +// com : https://www.iana.org/domains/root/db/com.html +com + +// coop : https://www.iana.org/domains/root/db/coop.html +coop + +// cr : https://nic.cr/capitulo-1-registro-de-un-nombre-de-dominio/ +cr +ac.cr +co.cr +ed.cr +fi.cr +go.cr +or.cr +sa.cr + +// cu : https://www.iana.org/domains/root/db/cu.html +cu +com.cu +edu.cu +gob.cu +inf.cu +nat.cu +net.cu +org.cu + +// cv : https://www.iana.org/domains/root/db/cv.html +// https://ola.cv/domain-extensions-under-cv/ +// Confirmed by registry 2024-11-26 +cv +com.cv +edu.cv +id.cv +int.cv +net.cv +nome.cv +org.cv +publ.cv + +// cw : https://www.uoc.cw/cw-registry +// Confirmed by registry 2024-11-19 +cw +com.cw +edu.cw +net.cw +org.cw + +// cx : https://www.iana.org/domains/root/db/cx.html +// list of other 2nd level tlds ? +cx +gov.cx + +// cy : http://www.nic.cy/ +// Submitted by Panayiotou Fotia +// https://nic.cy/wp-content/uploads/2024/01/Create-Request-for-domain-name-registration-1.pdf +cy +ac.cy +biz.cy +com.cy +ekloges.cy +gov.cy +ltd.cy +mil.cy +net.cy +org.cy +press.cy +pro.cy +tm.cy + +// cz : https://www.iana.org/domains/root/db/cz.html +// Confirmed by registry 2025-08-06 +cz +gov.cz + +// de : https://www.iana.org/domains/root/db/de.html +// Confirmed by registry (with technical +// reservations) 2008-07-01 +de + +// dj : https://www.iana.org/domains/root/db/dj.html +dj + +// dk : https://www.iana.org/domains/root/db/dk.html +// Confirmed by registry 2008-06-17 +dk + +// dm : https://www.iana.org/domains/root/db/dm.html +// https://nic.dm/policies/pdf/DMRulesandGuidelines2024v1.pdf +// Confirmed by registry 2024-11-19 +dm +co.dm +com.dm +edu.dm +gov.dm +net.dm +org.dm + +// do : https://www.iana.org/domains/root/db/do.html +do +art.do +com.do +edu.do +gob.do +gov.do +mil.do +net.do +org.do +sld.do +web.do + +// dz : http://www.nic.dz/images/pdf_nic/charte.pdf +dz +art.dz +asso.dz +com.dz +edu.dz +gov.dz +net.dz +org.dz +pol.dz +soc.dz +tm.dz + +// ec : https://www.nic.ec/ +// Submitted by registry +ec +abg.ec +adm.ec +agron.ec +arqt.ec +art.ec +bar.ec +chef.ec +com.ec +cont.ec +cpa.ec +cue.ec +dent.ec +dgn.ec +disco.ec +doc.ec +edu.ec +eng.ec +esm.ec +fin.ec +fot.ec +gal.ec +gob.ec +gov.ec +gye.ec +ibr.ec +info.ec +k12.ec +lat.ec +loj.ec +med.ec +mil.ec +mktg.ec +mon.ec +net.ec +ntr.ec +odont.ec +org.ec +pro.ec +prof.ec +psic.ec +psiq.ec +pub.ec +rio.ec +rrpp.ec +sal.ec +tech.ec +tul.ec +tur.ec +uio.ec +vet.ec +xxx.ec + +// edu : https://www.iana.org/domains/root/db/edu.html +edu + +// ee : https://www.internet.ee/domains/general-domains-and-procedure-for-registration-of-sub-domains-under-general-domains +ee +aip.ee +com.ee +edu.ee +fie.ee +gov.ee +lib.ee +med.ee +org.ee +pri.ee +riik.ee + +// eg : https://www.iana.org/domains/root/db/eg.html +// https://domain.eg/en/domain-rules/subdomain-names-types/ +eg +ac.eg +com.eg +edu.eg +eun.eg +gov.eg +info.eg +me.eg +mil.eg +name.eg +net.eg +org.eg +sci.eg +sport.eg +tv.eg + +// er : https://www.iana.org/domains/root/db/er.html +*.er + +// es : https://www.dominios.es/en +es +com.es +edu.es +gob.es +nom.es +org.es + +// et : https://www.iana.org/domains/root/db/et.html +et +biz.et +com.et +edu.et +gov.et +info.et +name.et +net.et +org.et + +// eu : https://www.iana.org/domains/root/db/eu.html +eu + +// fi : https://www.iana.org/domains/root/db/fi.html +fi +// aland.fi : https://www.iana.org/domains/root/db/ax.html +// This domain is being phased out in favor of .ax. As there are still many +// domains under aland.fi, we still keep it on the list until aland.fi is +// completely removed. +aland.fi + +// fj : https://www.iana.org/domains/root/db/fj.html +fj +ac.fj +biz.fj +com.fj +edu.fj +gov.fj +id.fj +info.fj +mil.fj +name.fj +net.fj +org.fj +pro.fj + +// fk : https://www.iana.org/domains/root/db/fk.html +*.fk + +// fm : https://www.iana.org/domains/root/db/fm.html +fm +com.fm +edu.fm +net.fm +org.fm + +// fo : https://www.iana.org/domains/root/db/fo.html +fo + +// fr : https://www.afnic.fr/ https://www.afnic.fr/wp-media/uploads/2022/12/afnic-naming-policy-2023-01-01.pdf +fr +asso.fr +com.fr +gouv.fr +nom.fr +prd.fr +tm.fr +// Other SLDs now selfmanaged out of AFNIC range. Former "domaines sectoriels", still registration suffixes +avoues.fr +cci.fr +greta.fr +huissier-justice.fr + +// ga : https://www.iana.org/domains/root/db/ga.html +ga + +// gb : This registry is effectively dormant +// Submitted by registry +gb + +// gd : https://www.iana.org/domains/root/db/gd.html +gd +edu.gd +gov.gd + +// ge : https://nic.ge/en/administrator/the-ge-domain-regulations +// Confirmed by registry 2024-11-20 +ge +com.ge +edu.ge +gov.ge +net.ge +org.ge +pvt.ge +school.ge + +// gf : https://www.iana.org/domains/root/db/gf.html +gf + +// gg : https://www.channelisles.net/register-1/register-direct +// Confirmed by registry 2013-11-28 +gg +co.gg +net.gg +org.gg + +// gh : https://www.iana.org/domains/root/db/gh.html +// https://www.nic.gh/ +// Although domains directly at second level are not possible at the moment, +// they have been possible for some time and may come back. +gh +biz.gh +com.gh +edu.gh +gov.gh +mil.gh +net.gh +org.gh + +// gi : http://www.nic.gi/rules.html +gi +com.gi +edu.gi +gov.gi +ltd.gi +mod.gi +org.gi + +// gl : https://www.iana.org/domains/root/db/gl.html +// http://nic.gl +gl +co.gl +com.gl +edu.gl +net.gl +org.gl + +// gm : http://www.nic.gm/htmlpages%5Cgm-policy.htm +gm + +// gn : http://psg.com/dns/gn/gn.txt +// Submitted by registry +gn +ac.gn +com.gn +edu.gn +gov.gn +net.gn +org.gn + +// gov : https://www.iana.org/domains/root/db/gov.html +gov + +// gp : http://www.nic.gp/index.php?lang=en +gp +asso.gp +com.gp +edu.gp +mobi.gp +net.gp +org.gp + +// gq : https://www.iana.org/domains/root/db/gq.html +gq + +// gr : https://www.iana.org/domains/root/db/gr.html +// Submitted by registry +gr +com.gr +edu.gr +gov.gr +net.gr +org.gr + +// gs : https://www.iana.org/domains/root/db/gs.html +gs + +// gt : https://www.gt/sitio/registration_policy.php?lang=en +gt +com.gt +edu.gt +gob.gt +ind.gt +mil.gt +net.gt +org.gt + +// gu : http://gadao.gov.gu/register.html +// University of Guam : https://www.uog.edu +// Submitted by uognoc@triton.uog.edu +gu +com.gu +edu.gu +gov.gu +guam.gu +info.gu +net.gu +org.gu +web.gu + +// gw : https://www.iana.org/domains/root/db/gw.html +// gw : https://nic.gw/regras/ +gw + +// gy : https://www.iana.org/domains/root/db/gy.html +// http://registry.gy/ +gy +co.gy +com.gy +edu.gy +gov.gy +net.gy +org.gy + +// Hercules : https://hercules.app +// Submitted by Brendan Falk +onhercules.app +hercules-app.com +hercules-dev.com + +// hk : https://www.hkirc.hk +// Submitted by registry +hk +com.hk +edu.hk +gov.hk +idv.hk +net.hk +org.hk +个人.hk +個人.hk +公司.hk +政府.hk +敎育.hk +教育.hk +箇人.hk +組織.hk +組织.hk +網絡.hk +網络.hk +组織.hk +组织.hk +网絡.hk +网络.hk + +// hm : https://www.iana.org/domains/root/db/hm.html +hm + +// hn : https://www.iana.org/domains/root/db/hn.html +hn +com.hn +edu.hn +gob.hn +mil.hn +net.hn +org.hn + +// hr : http://www.dns.hr/documents/pdf/HRTLD-regulations.pdf +hr +com.hr +from.hr +iz.hr +name.hr + +// ht : http://www.nic.ht/info/charte.cfm +ht +adult.ht +art.ht +asso.ht +com.ht +coop.ht +edu.ht +firm.ht +gouv.ht +info.ht +med.ht +net.ht +org.ht +perso.ht +pol.ht +pro.ht +rel.ht +shop.ht + +// hu : https://www.iana.org/domains/root/db/hu.html +// Confirmed by registry 2008-06-12 +hu +2000.hu +agrar.hu +bolt.hu +casino.hu +city.hu +co.hu +erotica.hu +erotika.hu +film.hu +forum.hu +games.hu +hotel.hu +info.hu +ingatlan.hu +jogasz.hu +konyvelo.hu +lakas.hu +media.hu +news.hu +org.hu +priv.hu +reklam.hu +sex.hu +shop.hu +sport.hu +suli.hu +szex.hu +tm.hu +tozsde.hu +utazas.hu +video.hu + +// id : https://www.iana.org/domains/root/db/id.html +id +ac.id +biz.id +co.id +desa.id +go.id +kop.id +mil.id +my.id +net.id +or.id +ponpes.id +sch.id +web.id +// xn--9tfky.id (.id, Und-Bali) +ᬩᬮᬶ.id + +// ie : https://www.iana.org/domains/root/db/ie.html +ie +gov.ie + +// il : http://www.isoc.org.il/domains/ +// see also: https://en.isoc.org.il/il-cctld/registration-rules +// ISOC-IL (operated by .il Registry) +il +ac.il +co.il +gov.il +idf.il +k12.il +muni.il +net.il +org.il +// xn--4dbrk0ce ("Israel", Hebrew) : IL +ישראל +// xn--4dbgdty6c.xn--4dbrk0ce. +אקדמיה.ישראל +// xn--5dbhl8d.xn--4dbrk0ce. +ישוב.ישראל +// xn--8dbq2a.xn--4dbrk0ce. +צהל.ישראל +// xn--hebda8b.xn--4dbrk0ce. +ממשל.ישראל + +// im : https://www.nic.im/ +// Submitted by registry +im +ac.im +co.im +ltd.co.im +plc.co.im +com.im +net.im +org.im +tt.im +tv.im + +// in : https://www.iana.org/domains/root/db/in.html +// see also: https://registry.in/policies +// Please note, that nic.in is not an official eTLD, but used by most +// government institutions. +// Confirmed by Gaurav Kansal 2025-11-06 +in +5g.in +6g.in +ac.in +ai.in +am.in +bank.in +bihar.in +biz.in +business.in +ca.in +cn.in +co.in +com.in +coop.in +cs.in +delhi.in +dr.in +edu.in +er.in +fin.in +firm.in +gen.in +gov.in +gujarat.in +ind.in +info.in +int.in +internet.in +io.in +me.in +mil.in +net.in +nic.in +org.in +pg.in +post.in +pro.in +res.in +travel.in +tv.in +uk.in +up.in +us.in + +// info : https://www.iana.org/domains/root/db/info.html +info + +// int : https://www.iana.org/domains/root/db/int.html +// Confirmed by registry 2008-06-18 +int +eu.int + +// io : http://www.nic.io/rules.htm +io +co.io +com.io +edu.io +gov.io +mil.io +net.io +nom.io +org.io + +// iq : http://www.cmc.iq/english/iq/iqregister1.htm +iq +com.iq +edu.iq +gov.iq +mil.iq +net.iq +org.iq + +// ir : http://www.nic.ir/Terms_and_Conditions_ir,_Appendix_1_Domain_Rules +// Also see http://www.nic.ir/Internationalized_Domain_Names +// Two .ir entries added at request of , 2010-04-16 +ir +ac.ir +co.ir +gov.ir +id.ir +net.ir +org.ir +sch.ir +// xn--mgba3a4f16a.ir (.ir, Persian YEH) +ایران.ir +// xn--mgba3a4fra.ir (.ir, Arabic YEH) +ايران.ir + +// is : http://www.isnic.is/domain/rules.php +// Confirmed by registry 2024-11-17 +is + +// it : https://www.iana.org/domains/root/db/it.html +// https://www.nic.it/ +it +edu.it +gov.it +// Regions (3.3.1) +// https://www.nic.it/en/manage-your-it/forms-and-docs -> "Assignment and Management of domain names" +abr.it +abruzzo.it +aosta-valley.it +aostavalley.it +bas.it +basilicata.it +cal.it +calabria.it +cam.it +campania.it +emilia-romagna.it +emiliaromagna.it +emr.it +friuli-v-giulia.it +friuli-ve-giulia.it +friuli-vegiulia.it +friuli-venezia-giulia.it +friuli-veneziagiulia.it +friuli-vgiulia.it +friuliv-giulia.it +friulive-giulia.it +friulivegiulia.it +friulivenezia-giulia.it +friuliveneziagiulia.it +friulivgiulia.it +fvg.it +laz.it +lazio.it +lig.it +liguria.it +lom.it +lombardia.it +lombardy.it +lucania.it +mar.it +marche.it +mol.it +molise.it +piedmont.it +piemonte.it +pmn.it +pug.it +puglia.it +sar.it +sardegna.it +sardinia.it +sic.it +sicilia.it +sicily.it +taa.it +tos.it +toscana.it +trentin-sud-tirol.it +trentin-süd-tirol.it +trentin-sudtirol.it +trentin-südtirol.it +trentin-sued-tirol.it +trentin-suedtirol.it +trentino.it +trentino-a-adige.it +trentino-aadige.it +trentino-alto-adige.it +trentino-altoadige.it +trentino-s-tirol.it +trentino-stirol.it +trentino-sud-tirol.it +trentino-süd-tirol.it +trentino-sudtirol.it +trentino-südtirol.it +trentino-sued-tirol.it +trentino-suedtirol.it +trentinoa-adige.it +trentinoaadige.it +trentinoalto-adige.it +trentinoaltoadige.it +trentinos-tirol.it +trentinostirol.it +trentinosud-tirol.it +trentinosüd-tirol.it +trentinosudtirol.it +trentinosüdtirol.it +trentinosued-tirol.it +trentinosuedtirol.it +trentinsud-tirol.it +trentinsüd-tirol.it +trentinsudtirol.it +trentinsüdtirol.it +trentinsued-tirol.it +trentinsuedtirol.it +tuscany.it +umb.it +umbria.it +val-d-aosta.it +val-daosta.it +vald-aosta.it +valdaosta.it +valle-aosta.it +valle-d-aosta.it +valle-daosta.it +valleaosta.it +valled-aosta.it +valledaosta.it +vallee-aoste.it +vallée-aoste.it +vallee-d-aoste.it +vallée-d-aoste.it +valleeaoste.it +valléeaoste.it +valleedaoste.it +valléedaoste.it +vao.it +vda.it +ven.it +veneto.it +// Provinces (3.3.2) +ag.it +agrigento.it +al.it +alessandria.it +alto-adige.it +altoadige.it +an.it +ancona.it +andria-barletta-trani.it +andria-trani-barletta.it +andriabarlettatrani.it +andriatranibarletta.it +ao.it +aosta.it +aoste.it +ap.it +aq.it +aquila.it +ar.it +arezzo.it +ascoli-piceno.it +ascolipiceno.it +asti.it +at.it +av.it +avellino.it +ba.it +balsan.it +balsan-sudtirol.it +balsan-südtirol.it +balsan-suedtirol.it +bari.it +barletta-trani-andria.it +barlettatraniandria.it +belluno.it +benevento.it +bergamo.it +bg.it +bi.it +biella.it +bl.it +bn.it +bo.it +bologna.it +bolzano.it +bolzano-altoadige.it +bozen.it +bozen-sudtirol.it +bozen-südtirol.it +bozen-suedtirol.it +br.it +brescia.it +brindisi.it +bs.it +bt.it +bulsan.it +bulsan-sudtirol.it +bulsan-südtirol.it +bulsan-suedtirol.it +bz.it +ca.it +cagliari.it +caltanissetta.it +campidano-medio.it +campidanomedio.it +campobasso.it +carbonia-iglesias.it +carboniaiglesias.it +carrara-massa.it +carraramassa.it +caserta.it +catania.it +catanzaro.it +cb.it +ce.it +cesena-forli.it +cesena-forlì.it +cesenaforli.it +cesenaforlì.it +ch.it +chieti.it +ci.it +cl.it +cn.it +co.it +como.it +cosenza.it +cr.it +cremona.it +crotone.it +cs.it +ct.it +cuneo.it +cz.it +dell-ogliastra.it +dellogliastra.it +en.it +enna.it +fc.it +fe.it +fermo.it +ferrara.it +fg.it +fi.it +firenze.it +florence.it +fm.it +foggia.it +forli-cesena.it +forlì-cesena.it +forlicesena.it +forlìcesena.it +fr.it +frosinone.it +ge.it +genoa.it +genova.it +go.it +gorizia.it +gr.it +grosseto.it +iglesias-carbonia.it +iglesiascarbonia.it +im.it +imperia.it +is.it +isernia.it +kr.it +la-spezia.it +laquila.it +laspezia.it +latina.it +lc.it +le.it +lecce.it +lecco.it +li.it +livorno.it +lo.it +lodi.it +lt.it +lu.it +lucca.it +macerata.it +mantova.it +massa-carrara.it +massacarrara.it +matera.it +mb.it +mc.it +me.it +medio-campidano.it +mediocampidano.it +messina.it +mi.it +milan.it +milano.it +mn.it +mo.it +modena.it +monza.it +monza-brianza.it +monza-e-della-brianza.it +monzabrianza.it +monzaebrianza.it +monzaedellabrianza.it +ms.it +mt.it +na.it +naples.it +napoli.it +no.it +novara.it +nu.it +nuoro.it +og.it +ogliastra.it +olbia-tempio.it +olbiatempio.it +or.it +oristano.it +ot.it +pa.it +padova.it +padua.it +palermo.it +parma.it +pavia.it +pc.it +pd.it +pe.it +perugia.it +pesaro-urbino.it +pesarourbino.it +pescara.it +pg.it +pi.it +piacenza.it +pisa.it +pistoia.it +pn.it +po.it +pordenone.it +potenza.it +pr.it +prato.it +pt.it +pu.it +pv.it +pz.it +ra.it +ragusa.it +ravenna.it +rc.it +re.it +reggio-calabria.it +reggio-emilia.it +reggiocalabria.it +reggioemilia.it +rg.it +ri.it +rieti.it +rimini.it +rm.it +rn.it +ro.it +roma.it +rome.it +rovigo.it +sa.it +salerno.it +sassari.it +savona.it +si.it +siena.it +siracusa.it +so.it +sondrio.it +sp.it +sr.it +ss.it +südtirol.it +suedtirol.it +sv.it +ta.it +taranto.it +te.it +tempio-olbia.it +tempioolbia.it +teramo.it +terni.it +tn.it +to.it +torino.it +tp.it +tr.it +trani-andria-barletta.it +trani-barletta-andria.it +traniandriabarletta.it +tranibarlettaandria.it +trapani.it +trento.it +treviso.it +trieste.it +ts.it +turin.it +tv.it +ud.it +udine.it +urbino-pesaro.it +urbinopesaro.it +va.it +varese.it +vb.it +vc.it +ve.it +venezia.it +venice.it +verbania.it +vercelli.it +verona.it +vi.it +vibo-valentia.it +vibovalentia.it +vicenza.it +viterbo.it +vr.it +vs.it +vt.it +vv.it + +// je : https://www.iana.org/domains/root/db/je.html +// Confirmed by registry 2013-11-28 +je +co.je +net.je +org.je + +// jm : http://www.com.jm/register.html +*.jm + +// jo : https://www.dns.jo/JoFamily.aspx +// Confirmed by registry 2024-11-17 +jo +agri.jo +ai.jo +com.jo +edu.jo +eng.jo +fm.jo +gov.jo +mil.jo +net.jo +org.jo +per.jo +phd.jo +sch.jo +tv.jo + +// jobs : https://www.iana.org/domains/root/db/jobs.html +jobs + +// jp : https://www.iana.org/domains/root/db/jp.html +// http://jprs.co.jp/en/jpdomain.html +// Confirmed by registry 2024-11-22 +jp +// jp organizational type names +ac.jp +ad.jp +co.jp +ed.jp +go.jp +gr.jp +lg.jp +ne.jp +or.jp +// jp prefecture type names +aichi.jp +akita.jp +aomori.jp +chiba.jp +ehime.jp +fukui.jp +fukuoka.jp +fukushima.jp +gifu.jp +gunma.jp +hiroshima.jp +hokkaido.jp +hyogo.jp +ibaraki.jp +ishikawa.jp +iwate.jp +kagawa.jp +kagoshima.jp +kanagawa.jp +kochi.jp +kumamoto.jp +kyoto.jp +mie.jp +miyagi.jp +miyazaki.jp +nagano.jp +nagasaki.jp +nara.jp +niigata.jp +oita.jp +okayama.jp +okinawa.jp +osaka.jp +saga.jp +saitama.jp +shiga.jp +shimane.jp +shizuoka.jp +tochigi.jp +tokushima.jp +tokyo.jp +tottori.jp +toyama.jp +wakayama.jp +yamagata.jp +yamaguchi.jp +yamanashi.jp +三重.jp +京都.jp +佐賀.jp +兵庫.jp +北海道.jp +千葉.jp +和歌山.jp +埼玉.jp +大分.jp +大阪.jp +奈良.jp +宮城.jp +宮崎.jp +富山.jp +山口.jp +山形.jp +山梨.jp +岐阜.jp +岡山.jp +岩手.jp +島根.jp +広島.jp +徳島.jp +愛媛.jp +愛知.jp +新潟.jp +東京.jp +栃木.jp +沖縄.jp +滋賀.jp +熊本.jp +石川.jp +神奈川.jp +福井.jp +福岡.jp +福島.jp +秋田.jp +群馬.jp +茨城.jp +長崎.jp +長野.jp +青森.jp +静岡.jp +香川.jp +高知.jp +鳥取.jp +鹿児島.jp +// jp geographic type names +// http://jprs.jp/doc/rule/saisoku-1.html +// 2024-11-22: JPRS confirmed that jp geographic type names no longer accept new registrations. +// Once all existing registrations expire (marking full discontinuation), these suffixes +// will be removed from the PSL. +*.kawasaki.jp +!city.kawasaki.jp +*.kitakyushu.jp +!city.kitakyushu.jp +*.kobe.jp +!city.kobe.jp +*.nagoya.jp +!city.nagoya.jp +*.sapporo.jp +!city.sapporo.jp +*.sendai.jp +!city.sendai.jp +*.yokohama.jp +!city.yokohama.jp +// 4th level registration +aisai.aichi.jp +ama.aichi.jp +anjo.aichi.jp +asuke.aichi.jp +chiryu.aichi.jp +chita.aichi.jp +fuso.aichi.jp +gamagori.aichi.jp +handa.aichi.jp +hazu.aichi.jp +hekinan.aichi.jp +higashiura.aichi.jp +ichinomiya.aichi.jp +inazawa.aichi.jp +inuyama.aichi.jp +isshiki.aichi.jp +iwakura.aichi.jp +kanie.aichi.jp +kariya.aichi.jp +kasugai.aichi.jp +kira.aichi.jp +kiyosu.aichi.jp +komaki.aichi.jp +konan.aichi.jp +kota.aichi.jp +mihama.aichi.jp +miyoshi.aichi.jp +nishio.aichi.jp +nisshin.aichi.jp +obu.aichi.jp +oguchi.aichi.jp +oharu.aichi.jp +okazaki.aichi.jp +owariasahi.aichi.jp +seto.aichi.jp +shikatsu.aichi.jp +shinshiro.aichi.jp +shitara.aichi.jp +tahara.aichi.jp +takahama.aichi.jp +tobishima.aichi.jp +toei.aichi.jp +togo.aichi.jp +tokai.aichi.jp +tokoname.aichi.jp +toyoake.aichi.jp +toyohashi.aichi.jp +toyokawa.aichi.jp +toyone.aichi.jp +toyota.aichi.jp +tsushima.aichi.jp +yatomi.aichi.jp +akita.akita.jp +daisen.akita.jp +fujisato.akita.jp +gojome.akita.jp +hachirogata.akita.jp +happou.akita.jp +higashinaruse.akita.jp +honjo.akita.jp +honjyo.akita.jp +ikawa.akita.jp +kamikoani.akita.jp +kamioka.akita.jp +katagami.akita.jp +kazuno.akita.jp +kitaakita.akita.jp +kosaka.akita.jp +kyowa.akita.jp +misato.akita.jp +mitane.akita.jp +moriyoshi.akita.jp +nikaho.akita.jp +noshiro.akita.jp +odate.akita.jp +oga.akita.jp +ogata.akita.jp +semboku.akita.jp +yokote.akita.jp +yurihonjo.akita.jp +aomori.aomori.jp +gonohe.aomori.jp +hachinohe.aomori.jp +hashikami.aomori.jp +hiranai.aomori.jp +hirosaki.aomori.jp +itayanagi.aomori.jp +kuroishi.aomori.jp +misawa.aomori.jp +mutsu.aomori.jp +nakadomari.aomori.jp +noheji.aomori.jp +oirase.aomori.jp +owani.aomori.jp +rokunohe.aomori.jp +sannohe.aomori.jp +shichinohe.aomori.jp +shingo.aomori.jp +takko.aomori.jp +towada.aomori.jp +tsugaru.aomori.jp +tsuruta.aomori.jp +abiko.chiba.jp +asahi.chiba.jp +chonan.chiba.jp +chosei.chiba.jp +choshi.chiba.jp +chuo.chiba.jp +funabashi.chiba.jp +futtsu.chiba.jp +hanamigawa.chiba.jp +ichihara.chiba.jp +ichikawa.chiba.jp +ichinomiya.chiba.jp +inzai.chiba.jp +isumi.chiba.jp +kamagaya.chiba.jp +kamogawa.chiba.jp +kashiwa.chiba.jp +katori.chiba.jp +katsuura.chiba.jp +kimitsu.chiba.jp +kisarazu.chiba.jp +kozaki.chiba.jp +kujukuri.chiba.jp +kyonan.chiba.jp +matsudo.chiba.jp +midori.chiba.jp +mihama.chiba.jp +minamiboso.chiba.jp +mobara.chiba.jp +mutsuzawa.chiba.jp +nagara.chiba.jp +nagareyama.chiba.jp +narashino.chiba.jp +narita.chiba.jp +noda.chiba.jp +oamishirasato.chiba.jp +omigawa.chiba.jp +onjuku.chiba.jp +otaki.chiba.jp +sakae.chiba.jp +sakura.chiba.jp +shimofusa.chiba.jp +shirako.chiba.jp +shiroi.chiba.jp +shisui.chiba.jp +sodegaura.chiba.jp +sosa.chiba.jp +tako.chiba.jp +tateyama.chiba.jp +togane.chiba.jp +tohnosho.chiba.jp +tomisato.chiba.jp +urayasu.chiba.jp +yachimata.chiba.jp +yachiyo.chiba.jp +yokaichiba.chiba.jp +yokoshibahikari.chiba.jp +yotsukaido.chiba.jp +ainan.ehime.jp +honai.ehime.jp +ikata.ehime.jp +imabari.ehime.jp +iyo.ehime.jp +kamijima.ehime.jp +kihoku.ehime.jp +kumakogen.ehime.jp +masaki.ehime.jp +matsuno.ehime.jp +matsuyama.ehime.jp +namikata.ehime.jp +niihama.ehime.jp +ozu.ehime.jp +saijo.ehime.jp +seiyo.ehime.jp +shikokuchuo.ehime.jp +tobe.ehime.jp +toon.ehime.jp +uchiko.ehime.jp +uwajima.ehime.jp +yawatahama.ehime.jp +echizen.fukui.jp +eiheiji.fukui.jp +fukui.fukui.jp +ikeda.fukui.jp +katsuyama.fukui.jp +mihama.fukui.jp +minamiechizen.fukui.jp +obama.fukui.jp +ohi.fukui.jp +ono.fukui.jp +sabae.fukui.jp +sakai.fukui.jp +takahama.fukui.jp +tsuruga.fukui.jp +wakasa.fukui.jp +ashiya.fukuoka.jp +buzen.fukuoka.jp +chikugo.fukuoka.jp +chikuho.fukuoka.jp +chikujo.fukuoka.jp +chikushino.fukuoka.jp +chikuzen.fukuoka.jp +chuo.fukuoka.jp +dazaifu.fukuoka.jp +fukuchi.fukuoka.jp +hakata.fukuoka.jp +higashi.fukuoka.jp +hirokawa.fukuoka.jp +hisayama.fukuoka.jp +iizuka.fukuoka.jp +inatsuki.fukuoka.jp +kaho.fukuoka.jp +kasuga.fukuoka.jp +kasuya.fukuoka.jp +kawara.fukuoka.jp +keisen.fukuoka.jp +koga.fukuoka.jp +kurate.fukuoka.jp +kurogi.fukuoka.jp +kurume.fukuoka.jp +minami.fukuoka.jp +miyako.fukuoka.jp +miyama.fukuoka.jp +miyawaka.fukuoka.jp +mizumaki.fukuoka.jp +munakata.fukuoka.jp +nakagawa.fukuoka.jp +nakama.fukuoka.jp +nishi.fukuoka.jp +nogata.fukuoka.jp +ogori.fukuoka.jp +okagaki.fukuoka.jp +okawa.fukuoka.jp +oki.fukuoka.jp +omuta.fukuoka.jp +onga.fukuoka.jp +onojo.fukuoka.jp +oto.fukuoka.jp +saigawa.fukuoka.jp +sasaguri.fukuoka.jp +shingu.fukuoka.jp +shinyoshitomi.fukuoka.jp +shonai.fukuoka.jp +soeda.fukuoka.jp +sue.fukuoka.jp +tachiarai.fukuoka.jp +tagawa.fukuoka.jp +takata.fukuoka.jp +toho.fukuoka.jp +toyotsu.fukuoka.jp +tsuiki.fukuoka.jp +ukiha.fukuoka.jp +umi.fukuoka.jp +usui.fukuoka.jp +yamada.fukuoka.jp +yame.fukuoka.jp +yanagawa.fukuoka.jp +yukuhashi.fukuoka.jp +aizubange.fukushima.jp +aizumisato.fukushima.jp +aizuwakamatsu.fukushima.jp +asakawa.fukushima.jp +bandai.fukushima.jp +date.fukushima.jp +fukushima.fukushima.jp +furudono.fukushima.jp +futaba.fukushima.jp +hanawa.fukushima.jp +higashi.fukushima.jp +hirata.fukushima.jp +hirono.fukushima.jp +iitate.fukushima.jp +inawashiro.fukushima.jp +ishikawa.fukushima.jp +iwaki.fukushima.jp +izumizaki.fukushima.jp +kagamiishi.fukushima.jp +kaneyama.fukushima.jp +kawamata.fukushima.jp +kitakata.fukushima.jp +kitashiobara.fukushima.jp +koori.fukushima.jp +koriyama.fukushima.jp +kunimi.fukushima.jp +miharu.fukushima.jp +mishima.fukushima.jp +namie.fukushima.jp +nango.fukushima.jp +nishiaizu.fukushima.jp +nishigo.fukushima.jp +okuma.fukushima.jp +omotego.fukushima.jp +ono.fukushima.jp +otama.fukushima.jp +samegawa.fukushima.jp +shimogo.fukushima.jp +shirakawa.fukushima.jp +showa.fukushima.jp +soma.fukushima.jp +sukagawa.fukushima.jp +taishin.fukushima.jp +tamakawa.fukushima.jp +tanagura.fukushima.jp +tenei.fukushima.jp +yabuki.fukushima.jp +yamato.fukushima.jp +yamatsuri.fukushima.jp +yanaizu.fukushima.jp +yugawa.fukushima.jp +anpachi.gifu.jp +ena.gifu.jp +gifu.gifu.jp +ginan.gifu.jp +godo.gifu.jp +gujo.gifu.jp +hashima.gifu.jp +hichiso.gifu.jp +hida.gifu.jp +higashishirakawa.gifu.jp +ibigawa.gifu.jp +ikeda.gifu.jp +kakamigahara.gifu.jp +kani.gifu.jp +kasahara.gifu.jp +kasamatsu.gifu.jp +kawaue.gifu.jp +kitagata.gifu.jp +mino.gifu.jp +minokamo.gifu.jp +mitake.gifu.jp +mizunami.gifu.jp +motosu.gifu.jp +nakatsugawa.gifu.jp +ogaki.gifu.jp +sakahogi.gifu.jp +seki.gifu.jp +sekigahara.gifu.jp +shirakawa.gifu.jp +tajimi.gifu.jp +takayama.gifu.jp +tarui.gifu.jp +toki.gifu.jp +tomika.gifu.jp +wanouchi.gifu.jp +yamagata.gifu.jp +yaotsu.gifu.jp +yoro.gifu.jp +annaka.gunma.jp +chiyoda.gunma.jp +fujioka.gunma.jp +higashiagatsuma.gunma.jp +isesaki.gunma.jp +itakura.gunma.jp +kanna.gunma.jp +kanra.gunma.jp +katashina.gunma.jp +kawaba.gunma.jp +kiryu.gunma.jp +kusatsu.gunma.jp +maebashi.gunma.jp +meiwa.gunma.jp +midori.gunma.jp +minakami.gunma.jp +naganohara.gunma.jp +nakanojo.gunma.jp +nanmoku.gunma.jp +numata.gunma.jp +oizumi.gunma.jp +ora.gunma.jp +ota.gunma.jp +shibukawa.gunma.jp +shimonita.gunma.jp +shinto.gunma.jp +showa.gunma.jp +takasaki.gunma.jp +takayama.gunma.jp +tamamura.gunma.jp +tatebayashi.gunma.jp +tomioka.gunma.jp +tsukiyono.gunma.jp +tsumagoi.gunma.jp +ueno.gunma.jp +yoshioka.gunma.jp +asaminami.hiroshima.jp +daiwa.hiroshima.jp +etajima.hiroshima.jp +fuchu.hiroshima.jp +fukuyama.hiroshima.jp +hatsukaichi.hiroshima.jp +higashihiroshima.hiroshima.jp +hongo.hiroshima.jp +jinsekikogen.hiroshima.jp +kaita.hiroshima.jp +kui.hiroshima.jp +kumano.hiroshima.jp +kure.hiroshima.jp +mihara.hiroshima.jp +miyoshi.hiroshima.jp +naka.hiroshima.jp +onomichi.hiroshima.jp +osakikamijima.hiroshima.jp +otake.hiroshima.jp +saka.hiroshima.jp +sera.hiroshima.jp +seranishi.hiroshima.jp +shinichi.hiroshima.jp +shobara.hiroshima.jp +takehara.hiroshima.jp +abashiri.hokkaido.jp +abira.hokkaido.jp +aibetsu.hokkaido.jp +akabira.hokkaido.jp +akkeshi.hokkaido.jp +asahikawa.hokkaido.jp +ashibetsu.hokkaido.jp +ashoro.hokkaido.jp +assabu.hokkaido.jp +atsuma.hokkaido.jp +bibai.hokkaido.jp +biei.hokkaido.jp +bifuka.hokkaido.jp +bihoro.hokkaido.jp +biratori.hokkaido.jp +chippubetsu.hokkaido.jp +chitose.hokkaido.jp +date.hokkaido.jp +ebetsu.hokkaido.jp +embetsu.hokkaido.jp +eniwa.hokkaido.jp +erimo.hokkaido.jp +esan.hokkaido.jp +esashi.hokkaido.jp +fukagawa.hokkaido.jp +fukushima.hokkaido.jp +furano.hokkaido.jp +furubira.hokkaido.jp +haboro.hokkaido.jp +hakodate.hokkaido.jp +hamatonbetsu.hokkaido.jp +hidaka.hokkaido.jp +higashikagura.hokkaido.jp +higashikawa.hokkaido.jp +hiroo.hokkaido.jp +hokuryu.hokkaido.jp +hokuto.hokkaido.jp +honbetsu.hokkaido.jp +horokanai.hokkaido.jp +horonobe.hokkaido.jp +ikeda.hokkaido.jp +imakane.hokkaido.jp +ishikari.hokkaido.jp +iwamizawa.hokkaido.jp +iwanai.hokkaido.jp +kamifurano.hokkaido.jp +kamikawa.hokkaido.jp +kamishihoro.hokkaido.jp +kamisunagawa.hokkaido.jp +kamoenai.hokkaido.jp +kayabe.hokkaido.jp +kembuchi.hokkaido.jp +kikonai.hokkaido.jp +kimobetsu.hokkaido.jp +kitahiroshima.hokkaido.jp +kitami.hokkaido.jp +kiyosato.hokkaido.jp +koshimizu.hokkaido.jp +kunneppu.hokkaido.jp +kuriyama.hokkaido.jp +kuromatsunai.hokkaido.jp +kushiro.hokkaido.jp +kutchan.hokkaido.jp +kyowa.hokkaido.jp +mashike.hokkaido.jp +matsumae.hokkaido.jp +mikasa.hokkaido.jp +minamifurano.hokkaido.jp +mombetsu.hokkaido.jp +moseushi.hokkaido.jp +mukawa.hokkaido.jp +muroran.hokkaido.jp +naie.hokkaido.jp +nakagawa.hokkaido.jp +nakasatsunai.hokkaido.jp +nakatombetsu.hokkaido.jp +nanae.hokkaido.jp +nanporo.hokkaido.jp +nayoro.hokkaido.jp +nemuro.hokkaido.jp +niikappu.hokkaido.jp +niki.hokkaido.jp +nishiokoppe.hokkaido.jp +noboribetsu.hokkaido.jp +numata.hokkaido.jp +obihiro.hokkaido.jp +obira.hokkaido.jp +oketo.hokkaido.jp +okoppe.hokkaido.jp +otaru.hokkaido.jp +otobe.hokkaido.jp +otofuke.hokkaido.jp +otoineppu.hokkaido.jp +oumu.hokkaido.jp +ozora.hokkaido.jp +pippu.hokkaido.jp +rankoshi.hokkaido.jp +rebun.hokkaido.jp +rikubetsu.hokkaido.jp +rishiri.hokkaido.jp +rishirifuji.hokkaido.jp +saroma.hokkaido.jp +sarufutsu.hokkaido.jp +shakotan.hokkaido.jp +shari.hokkaido.jp +shibecha.hokkaido.jp +shibetsu.hokkaido.jp +shikabe.hokkaido.jp +shikaoi.hokkaido.jp +shimamaki.hokkaido.jp +shimizu.hokkaido.jp +shimokawa.hokkaido.jp +shinshinotsu.hokkaido.jp +shintoku.hokkaido.jp +shiranuka.hokkaido.jp +shiraoi.hokkaido.jp +shiriuchi.hokkaido.jp +sobetsu.hokkaido.jp +sunagawa.hokkaido.jp +taiki.hokkaido.jp +takasu.hokkaido.jp +takikawa.hokkaido.jp +takinoue.hokkaido.jp +teshikaga.hokkaido.jp +tobetsu.hokkaido.jp +tohma.hokkaido.jp +tomakomai.hokkaido.jp +tomari.hokkaido.jp +toya.hokkaido.jp +toyako.hokkaido.jp +toyotomi.hokkaido.jp +toyoura.hokkaido.jp +tsubetsu.hokkaido.jp +tsukigata.hokkaido.jp +urakawa.hokkaido.jp +urausu.hokkaido.jp +uryu.hokkaido.jp +utashinai.hokkaido.jp +wakkanai.hokkaido.jp +wassamu.hokkaido.jp +yakumo.hokkaido.jp +yoichi.hokkaido.jp +aioi.hyogo.jp +akashi.hyogo.jp +ako.hyogo.jp +amagasaki.hyogo.jp +aogaki.hyogo.jp +asago.hyogo.jp +ashiya.hyogo.jp +awaji.hyogo.jp +fukusaki.hyogo.jp +goshiki.hyogo.jp +harima.hyogo.jp +himeji.hyogo.jp +ichikawa.hyogo.jp +inagawa.hyogo.jp +itami.hyogo.jp +kakogawa.hyogo.jp +kamigori.hyogo.jp +kamikawa.hyogo.jp +kasai.hyogo.jp +kasuga.hyogo.jp +kawanishi.hyogo.jp +miki.hyogo.jp +minamiawaji.hyogo.jp +nishinomiya.hyogo.jp +nishiwaki.hyogo.jp +ono.hyogo.jp +sanda.hyogo.jp +sannan.hyogo.jp +sasayama.hyogo.jp +sayo.hyogo.jp +shingu.hyogo.jp +shinonsen.hyogo.jp +shiso.hyogo.jp +sumoto.hyogo.jp +taishi.hyogo.jp +taka.hyogo.jp +takarazuka.hyogo.jp +takasago.hyogo.jp +takino.hyogo.jp +tamba.hyogo.jp +tatsuno.hyogo.jp +toyooka.hyogo.jp +yabu.hyogo.jp +yashiro.hyogo.jp +yoka.hyogo.jp +yokawa.hyogo.jp +ami.ibaraki.jp +asahi.ibaraki.jp +bando.ibaraki.jp +chikusei.ibaraki.jp +daigo.ibaraki.jp +fujishiro.ibaraki.jp +hitachi.ibaraki.jp +hitachinaka.ibaraki.jp +hitachiomiya.ibaraki.jp +hitachiota.ibaraki.jp +ibaraki.ibaraki.jp +ina.ibaraki.jp +inashiki.ibaraki.jp +itako.ibaraki.jp +iwama.ibaraki.jp +joso.ibaraki.jp +kamisu.ibaraki.jp +kasama.ibaraki.jp +kashima.ibaraki.jp +kasumigaura.ibaraki.jp +koga.ibaraki.jp +miho.ibaraki.jp +mito.ibaraki.jp +moriya.ibaraki.jp +naka.ibaraki.jp +namegata.ibaraki.jp +oarai.ibaraki.jp +ogawa.ibaraki.jp +omitama.ibaraki.jp +ryugasaki.ibaraki.jp +sakai.ibaraki.jp +sakuragawa.ibaraki.jp +shimodate.ibaraki.jp +shimotsuma.ibaraki.jp +shirosato.ibaraki.jp +sowa.ibaraki.jp +suifu.ibaraki.jp +takahagi.ibaraki.jp +tamatsukuri.ibaraki.jp +tokai.ibaraki.jp +tomobe.ibaraki.jp +tone.ibaraki.jp +toride.ibaraki.jp +tsuchiura.ibaraki.jp +tsukuba.ibaraki.jp +uchihara.ibaraki.jp +ushiku.ibaraki.jp +yachiyo.ibaraki.jp +yamagata.ibaraki.jp +yawara.ibaraki.jp +yuki.ibaraki.jp +anamizu.ishikawa.jp +hakui.ishikawa.jp +hakusan.ishikawa.jp +kaga.ishikawa.jp +kahoku.ishikawa.jp +kanazawa.ishikawa.jp +kawakita.ishikawa.jp +komatsu.ishikawa.jp +nakanoto.ishikawa.jp +nanao.ishikawa.jp +nomi.ishikawa.jp +nonoichi.ishikawa.jp +noto.ishikawa.jp +shika.ishikawa.jp +suzu.ishikawa.jp +tsubata.ishikawa.jp +tsurugi.ishikawa.jp +uchinada.ishikawa.jp +wajima.ishikawa.jp +fudai.iwate.jp +fujisawa.iwate.jp +hanamaki.iwate.jp +hiraizumi.iwate.jp +hirono.iwate.jp +ichinohe.iwate.jp +ichinoseki.iwate.jp +iwaizumi.iwate.jp +iwate.iwate.jp +joboji.iwate.jp +kamaishi.iwate.jp +kanegasaki.iwate.jp +karumai.iwate.jp +kawai.iwate.jp +kitakami.iwate.jp +kuji.iwate.jp +kunohe.iwate.jp +kuzumaki.iwate.jp +miyako.iwate.jp +mizusawa.iwate.jp +morioka.iwate.jp +ninohe.iwate.jp +noda.iwate.jp +ofunato.iwate.jp +oshu.iwate.jp +otsuchi.iwate.jp +rikuzentakata.iwate.jp +shiwa.iwate.jp +shizukuishi.iwate.jp +sumita.iwate.jp +tanohata.iwate.jp +tono.iwate.jp +yahaba.iwate.jp +yamada.iwate.jp +ayagawa.kagawa.jp +higashikagawa.kagawa.jp +kanonji.kagawa.jp +kotohira.kagawa.jp +manno.kagawa.jp +marugame.kagawa.jp +mitoyo.kagawa.jp +naoshima.kagawa.jp +sanuki.kagawa.jp +tadotsu.kagawa.jp +takamatsu.kagawa.jp +tonosho.kagawa.jp +uchinomi.kagawa.jp +utazu.kagawa.jp +zentsuji.kagawa.jp +akune.kagoshima.jp +amami.kagoshima.jp +hioki.kagoshima.jp +isa.kagoshima.jp +isen.kagoshima.jp +izumi.kagoshima.jp +kagoshima.kagoshima.jp +kanoya.kagoshima.jp +kawanabe.kagoshima.jp +kinko.kagoshima.jp +kouyama.kagoshima.jp +makurazaki.kagoshima.jp +matsumoto.kagoshima.jp +minamitane.kagoshima.jp +nakatane.kagoshima.jp +nishinoomote.kagoshima.jp +satsumasendai.kagoshima.jp +soo.kagoshima.jp +tarumizu.kagoshima.jp +yusui.kagoshima.jp +aikawa.kanagawa.jp +atsugi.kanagawa.jp +ayase.kanagawa.jp +chigasaki.kanagawa.jp +ebina.kanagawa.jp +fujisawa.kanagawa.jp +hadano.kanagawa.jp +hakone.kanagawa.jp +hiratsuka.kanagawa.jp +isehara.kanagawa.jp +kaisei.kanagawa.jp +kamakura.kanagawa.jp +kiyokawa.kanagawa.jp +matsuda.kanagawa.jp +minamiashigara.kanagawa.jp +miura.kanagawa.jp +nakai.kanagawa.jp +ninomiya.kanagawa.jp +odawara.kanagawa.jp +oi.kanagawa.jp +oiso.kanagawa.jp +sagamihara.kanagawa.jp +samukawa.kanagawa.jp +tsukui.kanagawa.jp +yamakita.kanagawa.jp +yamato.kanagawa.jp +yokosuka.kanagawa.jp +yugawara.kanagawa.jp +zama.kanagawa.jp +zushi.kanagawa.jp +aki.kochi.jp +geisei.kochi.jp +hidaka.kochi.jp +higashitsuno.kochi.jp +ino.kochi.jp +kagami.kochi.jp +kami.kochi.jp +kitagawa.kochi.jp +kochi.kochi.jp +mihara.kochi.jp +motoyama.kochi.jp +muroto.kochi.jp +nahari.kochi.jp +nakamura.kochi.jp +nankoku.kochi.jp +nishitosa.kochi.jp +niyodogawa.kochi.jp +ochi.kochi.jp +okawa.kochi.jp +otoyo.kochi.jp +otsuki.kochi.jp +sakawa.kochi.jp +sukumo.kochi.jp +susaki.kochi.jp +tosa.kochi.jp +tosashimizu.kochi.jp +toyo.kochi.jp +tsuno.kochi.jp +umaji.kochi.jp +yasuda.kochi.jp +yusuhara.kochi.jp +amakusa.kumamoto.jp +arao.kumamoto.jp +aso.kumamoto.jp +choyo.kumamoto.jp +gyokuto.kumamoto.jp +kamiamakusa.kumamoto.jp +kikuchi.kumamoto.jp +kumamoto.kumamoto.jp +mashiki.kumamoto.jp +mifune.kumamoto.jp +minamata.kumamoto.jp +minamioguni.kumamoto.jp +nagasu.kumamoto.jp +nishihara.kumamoto.jp +oguni.kumamoto.jp +ozu.kumamoto.jp +sumoto.kumamoto.jp +takamori.kumamoto.jp +uki.kumamoto.jp +uto.kumamoto.jp +yamaga.kumamoto.jp +yamato.kumamoto.jp +yatsushiro.kumamoto.jp +ayabe.kyoto.jp +fukuchiyama.kyoto.jp +higashiyama.kyoto.jp +ide.kyoto.jp +ine.kyoto.jp +joyo.kyoto.jp +kameoka.kyoto.jp +kamo.kyoto.jp +kita.kyoto.jp +kizu.kyoto.jp +kumiyama.kyoto.jp +kyotamba.kyoto.jp +kyotanabe.kyoto.jp +kyotango.kyoto.jp +maizuru.kyoto.jp +minami.kyoto.jp +minamiyamashiro.kyoto.jp +miyazu.kyoto.jp +muko.kyoto.jp +nagaokakyo.kyoto.jp +nakagyo.kyoto.jp +nantan.kyoto.jp +oyamazaki.kyoto.jp +sakyo.kyoto.jp +seika.kyoto.jp +tanabe.kyoto.jp +uji.kyoto.jp +ujitawara.kyoto.jp +wazuka.kyoto.jp +yamashina.kyoto.jp +yawata.kyoto.jp +asahi.mie.jp +inabe.mie.jp +ise.mie.jp +kameyama.mie.jp +kawagoe.mie.jp +kiho.mie.jp +kisosaki.mie.jp +kiwa.mie.jp +komono.mie.jp +kumano.mie.jp +kuwana.mie.jp +matsusaka.mie.jp +meiwa.mie.jp +mihama.mie.jp +minamiise.mie.jp +misugi.mie.jp +miyama.mie.jp +nabari.mie.jp +shima.mie.jp +suzuka.mie.jp +tado.mie.jp +taiki.mie.jp +taki.mie.jp +tamaki.mie.jp +toba.mie.jp +tsu.mie.jp +udono.mie.jp +ureshino.mie.jp +watarai.mie.jp +yokkaichi.mie.jp +furukawa.miyagi.jp +higashimatsushima.miyagi.jp +ishinomaki.miyagi.jp +iwanuma.miyagi.jp +kakuda.miyagi.jp +kami.miyagi.jp +kawasaki.miyagi.jp +marumori.miyagi.jp +matsushima.miyagi.jp +minamisanriku.miyagi.jp +misato.miyagi.jp +murata.miyagi.jp +natori.miyagi.jp +ogawara.miyagi.jp +ohira.miyagi.jp +onagawa.miyagi.jp +osaki.miyagi.jp +rifu.miyagi.jp +semine.miyagi.jp +shibata.miyagi.jp +shichikashuku.miyagi.jp +shikama.miyagi.jp +shiogama.miyagi.jp +shiroishi.miyagi.jp +tagajo.miyagi.jp +taiwa.miyagi.jp +tome.miyagi.jp +tomiya.miyagi.jp +wakuya.miyagi.jp +watari.miyagi.jp +yamamoto.miyagi.jp +zao.miyagi.jp +aya.miyazaki.jp +ebino.miyazaki.jp +gokase.miyazaki.jp +hyuga.miyazaki.jp +kadogawa.miyazaki.jp +kawaminami.miyazaki.jp +kijo.miyazaki.jp +kitagawa.miyazaki.jp +kitakata.miyazaki.jp +kitaura.miyazaki.jp +kobayashi.miyazaki.jp +kunitomi.miyazaki.jp +kushima.miyazaki.jp +mimata.miyazaki.jp +miyakonojo.miyazaki.jp +miyazaki.miyazaki.jp +morotsuka.miyazaki.jp +nichinan.miyazaki.jp +nishimera.miyazaki.jp +nobeoka.miyazaki.jp +saito.miyazaki.jp +shiiba.miyazaki.jp +shintomi.miyazaki.jp +takaharu.miyazaki.jp +takanabe.miyazaki.jp +takazaki.miyazaki.jp +tsuno.miyazaki.jp +achi.nagano.jp +agematsu.nagano.jp +anan.nagano.jp +aoki.nagano.jp +asahi.nagano.jp +azumino.nagano.jp +chikuhoku.nagano.jp +chikuma.nagano.jp +chino.nagano.jp +fujimi.nagano.jp +hakuba.nagano.jp +hara.nagano.jp +hiraya.nagano.jp +iida.nagano.jp +iijima.nagano.jp +iiyama.nagano.jp +iizuna.nagano.jp +ikeda.nagano.jp +ikusaka.nagano.jp +ina.nagano.jp +karuizawa.nagano.jp +kawakami.nagano.jp +kiso.nagano.jp +kisofukushima.nagano.jp +kitaaiki.nagano.jp +komagane.nagano.jp +komoro.nagano.jp +matsukawa.nagano.jp +matsumoto.nagano.jp +miasa.nagano.jp +minamiaiki.nagano.jp +minamimaki.nagano.jp +minamiminowa.nagano.jp +minowa.nagano.jp +miyada.nagano.jp +miyota.nagano.jp +mochizuki.nagano.jp +nagano.nagano.jp +nagawa.nagano.jp +nagiso.nagano.jp +nakagawa.nagano.jp +nakano.nagano.jp +nozawaonsen.nagano.jp +obuse.nagano.jp +ogawa.nagano.jp +okaya.nagano.jp +omachi.nagano.jp +omi.nagano.jp +ookuwa.nagano.jp +ooshika.nagano.jp +otaki.nagano.jp +otari.nagano.jp +sakae.nagano.jp +sakaki.nagano.jp +saku.nagano.jp +sakuho.nagano.jp +shimosuwa.nagano.jp +shinanomachi.nagano.jp +shiojiri.nagano.jp +suwa.nagano.jp +suzaka.nagano.jp +takagi.nagano.jp +takamori.nagano.jp +takayama.nagano.jp +tateshina.nagano.jp +tatsuno.nagano.jp +togakushi.nagano.jp +togura.nagano.jp +tomi.nagano.jp +ueda.nagano.jp +wada.nagano.jp +yamagata.nagano.jp +yamanouchi.nagano.jp +yasaka.nagano.jp +yasuoka.nagano.jp +chijiwa.nagasaki.jp +futsu.nagasaki.jp +goto.nagasaki.jp +hasami.nagasaki.jp +hirado.nagasaki.jp +iki.nagasaki.jp +isahaya.nagasaki.jp +kawatana.nagasaki.jp +kuchinotsu.nagasaki.jp +matsuura.nagasaki.jp +nagasaki.nagasaki.jp +obama.nagasaki.jp +omura.nagasaki.jp +oseto.nagasaki.jp +saikai.nagasaki.jp +sasebo.nagasaki.jp +seihi.nagasaki.jp +shimabara.nagasaki.jp +shinkamigoto.nagasaki.jp +togitsu.nagasaki.jp +tsushima.nagasaki.jp +unzen.nagasaki.jp +ando.nara.jp +gose.nara.jp +heguri.nara.jp +higashiyoshino.nara.jp +ikaruga.nara.jp +ikoma.nara.jp +kamikitayama.nara.jp +kanmaki.nara.jp +kashiba.nara.jp +kashihara.nara.jp +katsuragi.nara.jp +kawai.nara.jp +kawakami.nara.jp +kawanishi.nara.jp +koryo.nara.jp +kurotaki.nara.jp +mitsue.nara.jp +miyake.nara.jp +nara.nara.jp +nosegawa.nara.jp +oji.nara.jp +ouda.nara.jp +oyodo.nara.jp +sakurai.nara.jp +sango.nara.jp +shimoichi.nara.jp +shimokitayama.nara.jp +shinjo.nara.jp +soni.nara.jp +takatori.nara.jp +tawaramoto.nara.jp +tenkawa.nara.jp +tenri.nara.jp +uda.nara.jp +yamatokoriyama.nara.jp +yamatotakada.nara.jp +yamazoe.nara.jp +yoshino.nara.jp +aga.niigata.jp +agano.niigata.jp +gosen.niigata.jp +itoigawa.niigata.jp +izumozaki.niigata.jp +joetsu.niigata.jp +kamo.niigata.jp +kariwa.niigata.jp +kashiwazaki.niigata.jp +minamiuonuma.niigata.jp +mitsuke.niigata.jp +muika.niigata.jp +murakami.niigata.jp +myoko.niigata.jp +nagaoka.niigata.jp +niigata.niigata.jp +ojiya.niigata.jp +omi.niigata.jp +sado.niigata.jp +sanjo.niigata.jp +seiro.niigata.jp +seirou.niigata.jp +sekikawa.niigata.jp +shibata.niigata.jp +tagami.niigata.jp +tainai.niigata.jp +tochio.niigata.jp +tokamachi.niigata.jp +tsubame.niigata.jp +tsunan.niigata.jp +uonuma.niigata.jp +yahiko.niigata.jp +yoita.niigata.jp +yuzawa.niigata.jp +beppu.oita.jp +bungoono.oita.jp +bungotakada.oita.jp +hasama.oita.jp +hiji.oita.jp +himeshima.oita.jp +hita.oita.jp +kamitsue.oita.jp +kokonoe.oita.jp +kuju.oita.jp +kunisaki.oita.jp +kusu.oita.jp +oita.oita.jp +saiki.oita.jp +taketa.oita.jp +tsukumi.oita.jp +usa.oita.jp +usuki.oita.jp +yufu.oita.jp +akaiwa.okayama.jp +asakuchi.okayama.jp +bizen.okayama.jp +hayashima.okayama.jp +ibara.okayama.jp +kagamino.okayama.jp +kasaoka.okayama.jp +kibichuo.okayama.jp +kumenan.okayama.jp +kurashiki.okayama.jp +maniwa.okayama.jp +misaki.okayama.jp +nagi.okayama.jp +niimi.okayama.jp +nishiawakura.okayama.jp +okayama.okayama.jp +satosho.okayama.jp +setouchi.okayama.jp +shinjo.okayama.jp +shoo.okayama.jp +soja.okayama.jp +takahashi.okayama.jp +tamano.okayama.jp +tsuyama.okayama.jp +wake.okayama.jp +yakage.okayama.jp +aguni.okinawa.jp +ginowan.okinawa.jp +ginoza.okinawa.jp +gushikami.okinawa.jp +haebaru.okinawa.jp +higashi.okinawa.jp +hirara.okinawa.jp +iheya.okinawa.jp +ishigaki.okinawa.jp +ishikawa.okinawa.jp +itoman.okinawa.jp +izena.okinawa.jp +kadena.okinawa.jp +kin.okinawa.jp +kitadaito.okinawa.jp +kitanakagusuku.okinawa.jp +kumejima.okinawa.jp +kunigami.okinawa.jp +minamidaito.okinawa.jp +motobu.okinawa.jp +nago.okinawa.jp +naha.okinawa.jp +nakagusuku.okinawa.jp +nakijin.okinawa.jp +nanjo.okinawa.jp +nishihara.okinawa.jp +ogimi.okinawa.jp +okinawa.okinawa.jp +onna.okinawa.jp +shimoji.okinawa.jp +taketomi.okinawa.jp +tarama.okinawa.jp +tokashiki.okinawa.jp +tomigusuku.okinawa.jp +tonaki.okinawa.jp +urasoe.okinawa.jp +uruma.okinawa.jp +yaese.okinawa.jp +yomitan.okinawa.jp +yonabaru.okinawa.jp +yonaguni.okinawa.jp +zamami.okinawa.jp +abeno.osaka.jp +chihayaakasaka.osaka.jp +chuo.osaka.jp +daito.osaka.jp +fujiidera.osaka.jp +habikino.osaka.jp +hannan.osaka.jp +higashiosaka.osaka.jp +higashisumiyoshi.osaka.jp +higashiyodogawa.osaka.jp +hirakata.osaka.jp +ibaraki.osaka.jp +ikeda.osaka.jp +izumi.osaka.jp +izumiotsu.osaka.jp +izumisano.osaka.jp +kadoma.osaka.jp +kaizuka.osaka.jp +kanan.osaka.jp +kashiwara.osaka.jp +katano.osaka.jp +kawachinagano.osaka.jp +kishiwada.osaka.jp +kita.osaka.jp +kumatori.osaka.jp +matsubara.osaka.jp +minato.osaka.jp +minoh.osaka.jp +misaki.osaka.jp +moriguchi.osaka.jp +neyagawa.osaka.jp +nishi.osaka.jp +nose.osaka.jp +osakasayama.osaka.jp +sakai.osaka.jp +sayama.osaka.jp +sennan.osaka.jp +settsu.osaka.jp +shijonawate.osaka.jp +shimamoto.osaka.jp +suita.osaka.jp +tadaoka.osaka.jp +taishi.osaka.jp +tajiri.osaka.jp +takaishi.osaka.jp +takatsuki.osaka.jp +tondabayashi.osaka.jp +toyonaka.osaka.jp +toyono.osaka.jp +yao.osaka.jp +ariake.saga.jp +arita.saga.jp +fukudomi.saga.jp +genkai.saga.jp +hamatama.saga.jp +hizen.saga.jp +imari.saga.jp +kamimine.saga.jp +kanzaki.saga.jp +karatsu.saga.jp +kashima.saga.jp +kitagata.saga.jp +kitahata.saga.jp +kiyama.saga.jp +kouhoku.saga.jp +kyuragi.saga.jp +nishiarita.saga.jp +ogi.saga.jp +omachi.saga.jp +ouchi.saga.jp +saga.saga.jp +shiroishi.saga.jp +taku.saga.jp +tara.saga.jp +tosu.saga.jp +yoshinogari.saga.jp +arakawa.saitama.jp +asaka.saitama.jp +chichibu.saitama.jp +fujimi.saitama.jp +fujimino.saitama.jp +fukaya.saitama.jp +hanno.saitama.jp +hanyu.saitama.jp +hasuda.saitama.jp +hatogaya.saitama.jp +hatoyama.saitama.jp +hidaka.saitama.jp +higashichichibu.saitama.jp +higashimatsuyama.saitama.jp +honjo.saitama.jp +ina.saitama.jp +iruma.saitama.jp +iwatsuki.saitama.jp +kamiizumi.saitama.jp +kamikawa.saitama.jp +kamisato.saitama.jp +kasukabe.saitama.jp +kawagoe.saitama.jp +kawaguchi.saitama.jp +kawajima.saitama.jp +kazo.saitama.jp +kitamoto.saitama.jp +koshigaya.saitama.jp +kounosu.saitama.jp +kuki.saitama.jp +kumagaya.saitama.jp +matsubushi.saitama.jp +minano.saitama.jp +misato.saitama.jp +miyashiro.saitama.jp +miyoshi.saitama.jp +moroyama.saitama.jp +nagatoro.saitama.jp +namegawa.saitama.jp +niiza.saitama.jp +ogano.saitama.jp +ogawa.saitama.jp +ogose.saitama.jp +okegawa.saitama.jp +omiya.saitama.jp +otaki.saitama.jp +ranzan.saitama.jp +ryokami.saitama.jp +saitama.saitama.jp +sakado.saitama.jp +satte.saitama.jp +sayama.saitama.jp +shiki.saitama.jp +shiraoka.saitama.jp +soka.saitama.jp +sugito.saitama.jp +toda.saitama.jp +tokigawa.saitama.jp +tokorozawa.saitama.jp +tsurugashima.saitama.jp +urawa.saitama.jp +warabi.saitama.jp +yashio.saitama.jp +yokoze.saitama.jp +yono.saitama.jp +yorii.saitama.jp +yoshida.saitama.jp +yoshikawa.saitama.jp +yoshimi.saitama.jp +aisho.shiga.jp +gamo.shiga.jp +higashiomi.shiga.jp +hikone.shiga.jp +koka.shiga.jp +konan.shiga.jp +kosei.shiga.jp +koto.shiga.jp +kusatsu.shiga.jp +maibara.shiga.jp +moriyama.shiga.jp +nagahama.shiga.jp +nishiazai.shiga.jp +notogawa.shiga.jp +omihachiman.shiga.jp +otsu.shiga.jp +ritto.shiga.jp +ryuoh.shiga.jp +takashima.shiga.jp +takatsuki.shiga.jp +torahime.shiga.jp +toyosato.shiga.jp +yasu.shiga.jp +akagi.shimane.jp +ama.shimane.jp +gotsu.shimane.jp +hamada.shimane.jp +higashiizumo.shimane.jp +hikawa.shimane.jp +hikimi.shimane.jp +izumo.shimane.jp +kakinoki.shimane.jp +masuda.shimane.jp +matsue.shimane.jp +misato.shimane.jp +nishinoshima.shimane.jp +ohda.shimane.jp +okinoshima.shimane.jp +okuizumo.shimane.jp +shimane.shimane.jp +tamayu.shimane.jp +tsuwano.shimane.jp +unnan.shimane.jp +yakumo.shimane.jp +yasugi.shimane.jp +yatsuka.shimane.jp +arai.shizuoka.jp +atami.shizuoka.jp +fuji.shizuoka.jp +fujieda.shizuoka.jp +fujikawa.shizuoka.jp +fujinomiya.shizuoka.jp +fukuroi.shizuoka.jp +gotemba.shizuoka.jp +haibara.shizuoka.jp +hamamatsu.shizuoka.jp +higashiizu.shizuoka.jp +ito.shizuoka.jp +iwata.shizuoka.jp +izu.shizuoka.jp +izunokuni.shizuoka.jp +kakegawa.shizuoka.jp +kannami.shizuoka.jp +kawanehon.shizuoka.jp +kawazu.shizuoka.jp +kikugawa.shizuoka.jp +kosai.shizuoka.jp +makinohara.shizuoka.jp +matsuzaki.shizuoka.jp +minamiizu.shizuoka.jp +mishima.shizuoka.jp +morimachi.shizuoka.jp +nishiizu.shizuoka.jp +numazu.shizuoka.jp +omaezaki.shizuoka.jp +shimada.shizuoka.jp +shimizu.shizuoka.jp +shimoda.shizuoka.jp +shizuoka.shizuoka.jp +susono.shizuoka.jp +yaizu.shizuoka.jp +yoshida.shizuoka.jp +ashikaga.tochigi.jp +bato.tochigi.jp +haga.tochigi.jp +ichikai.tochigi.jp +iwafune.tochigi.jp +kaminokawa.tochigi.jp +kanuma.tochigi.jp +karasuyama.tochigi.jp +kuroiso.tochigi.jp +mashiko.tochigi.jp +mibu.tochigi.jp +moka.tochigi.jp +motegi.tochigi.jp +nasu.tochigi.jp +nasushiobara.tochigi.jp +nikko.tochigi.jp +nishikata.tochigi.jp +nogi.tochigi.jp +ohira.tochigi.jp +ohtawara.tochigi.jp +oyama.tochigi.jp +sakura.tochigi.jp +sano.tochigi.jp +shimotsuke.tochigi.jp +shioya.tochigi.jp +takanezawa.tochigi.jp +tochigi.tochigi.jp +tsuga.tochigi.jp +ujiie.tochigi.jp +utsunomiya.tochigi.jp +yaita.tochigi.jp +aizumi.tokushima.jp +anan.tokushima.jp +ichiba.tokushima.jp +itano.tokushima.jp +kainan.tokushima.jp +komatsushima.tokushima.jp +matsushige.tokushima.jp +mima.tokushima.jp +minami.tokushima.jp +miyoshi.tokushima.jp +mugi.tokushima.jp +nakagawa.tokushima.jp +naruto.tokushima.jp +sanagochi.tokushima.jp +shishikui.tokushima.jp +tokushima.tokushima.jp +wajiki.tokushima.jp +adachi.tokyo.jp +akiruno.tokyo.jp +akishima.tokyo.jp +aogashima.tokyo.jp +arakawa.tokyo.jp +bunkyo.tokyo.jp +chiyoda.tokyo.jp +chofu.tokyo.jp +chuo.tokyo.jp +edogawa.tokyo.jp +fuchu.tokyo.jp +fussa.tokyo.jp +hachijo.tokyo.jp +hachioji.tokyo.jp +hamura.tokyo.jp +higashikurume.tokyo.jp +higashimurayama.tokyo.jp +higashiyamato.tokyo.jp +hino.tokyo.jp +hinode.tokyo.jp +hinohara.tokyo.jp +inagi.tokyo.jp +itabashi.tokyo.jp +katsushika.tokyo.jp +kita.tokyo.jp +kiyose.tokyo.jp +kodaira.tokyo.jp +koganei.tokyo.jp +kokubunji.tokyo.jp +komae.tokyo.jp +koto.tokyo.jp +kouzushima.tokyo.jp +kunitachi.tokyo.jp +machida.tokyo.jp +meguro.tokyo.jp +minato.tokyo.jp +mitaka.tokyo.jp +mizuho.tokyo.jp +musashimurayama.tokyo.jp +musashino.tokyo.jp +nakano.tokyo.jp +nerima.tokyo.jp +ogasawara.tokyo.jp +okutama.tokyo.jp +ome.tokyo.jp +oshima.tokyo.jp +ota.tokyo.jp +setagaya.tokyo.jp +shibuya.tokyo.jp +shinagawa.tokyo.jp +shinjuku.tokyo.jp +suginami.tokyo.jp +sumida.tokyo.jp +tachikawa.tokyo.jp +taito.tokyo.jp +tama.tokyo.jp +toshima.tokyo.jp +chizu.tottori.jp +hino.tottori.jp +kawahara.tottori.jp +koge.tottori.jp +kotoura.tottori.jp +misasa.tottori.jp +nanbu.tottori.jp +nichinan.tottori.jp +sakaiminato.tottori.jp +tottori.tottori.jp +wakasa.tottori.jp +yazu.tottori.jp +yonago.tottori.jp +asahi.toyama.jp +fuchu.toyama.jp +fukumitsu.toyama.jp +funahashi.toyama.jp +himi.toyama.jp +imizu.toyama.jp +inami.toyama.jp +johana.toyama.jp +kamiichi.toyama.jp +kurobe.toyama.jp +nakaniikawa.toyama.jp +namerikawa.toyama.jp +nanto.toyama.jp +nyuzen.toyama.jp +oyabe.toyama.jp +taira.toyama.jp +takaoka.toyama.jp +tateyama.toyama.jp +toga.toyama.jp +tonami.toyama.jp +toyama.toyama.jp +unazuki.toyama.jp +uozu.toyama.jp +yamada.toyama.jp +arida.wakayama.jp +aridagawa.wakayama.jp +gobo.wakayama.jp +hashimoto.wakayama.jp +hidaka.wakayama.jp +hirogawa.wakayama.jp +inami.wakayama.jp +iwade.wakayama.jp +kainan.wakayama.jp +kamitonda.wakayama.jp +katsuragi.wakayama.jp +kimino.wakayama.jp +kinokawa.wakayama.jp +kitayama.wakayama.jp +koya.wakayama.jp +koza.wakayama.jp +kozagawa.wakayama.jp +kudoyama.wakayama.jp +kushimoto.wakayama.jp +mihama.wakayama.jp +misato.wakayama.jp +nachikatsuura.wakayama.jp +shingu.wakayama.jp +shirahama.wakayama.jp +taiji.wakayama.jp +tanabe.wakayama.jp +wakayama.wakayama.jp +yuasa.wakayama.jp +yura.wakayama.jp +asahi.yamagata.jp +funagata.yamagata.jp +higashine.yamagata.jp +iide.yamagata.jp +kahoku.yamagata.jp +kaminoyama.yamagata.jp +kaneyama.yamagata.jp +kawanishi.yamagata.jp +mamurogawa.yamagata.jp +mikawa.yamagata.jp +murayama.yamagata.jp +nagai.yamagata.jp +nakayama.yamagata.jp +nanyo.yamagata.jp +nishikawa.yamagata.jp +obanazawa.yamagata.jp +oe.yamagata.jp +oguni.yamagata.jp +ohkura.yamagata.jp +oishida.yamagata.jp +sagae.yamagata.jp +sakata.yamagata.jp +sakegawa.yamagata.jp +shinjo.yamagata.jp +shirataka.yamagata.jp +shonai.yamagata.jp +takahata.yamagata.jp +tendo.yamagata.jp +tozawa.yamagata.jp +tsuruoka.yamagata.jp +yamagata.yamagata.jp +yamanobe.yamagata.jp +yonezawa.yamagata.jp +yuza.yamagata.jp +abu.yamaguchi.jp +hagi.yamaguchi.jp +hikari.yamaguchi.jp +hofu.yamaguchi.jp +iwakuni.yamaguchi.jp +kudamatsu.yamaguchi.jp +mitou.yamaguchi.jp +nagato.yamaguchi.jp +oshima.yamaguchi.jp +shimonoseki.yamaguchi.jp +shunan.yamaguchi.jp +tabuse.yamaguchi.jp +tokuyama.yamaguchi.jp +toyota.yamaguchi.jp +ube.yamaguchi.jp +yuu.yamaguchi.jp +chuo.yamanashi.jp +doshi.yamanashi.jp +fuefuki.yamanashi.jp +fujikawa.yamanashi.jp +fujikawaguchiko.yamanashi.jp +fujiyoshida.yamanashi.jp +hayakawa.yamanashi.jp +hokuto.yamanashi.jp +ichikawamisato.yamanashi.jp +kai.yamanashi.jp +kofu.yamanashi.jp +koshu.yamanashi.jp +kosuge.yamanashi.jp +minami-alps.yamanashi.jp +minobu.yamanashi.jp +nakamichi.yamanashi.jp +nanbu.yamanashi.jp +narusawa.yamanashi.jp +nirasaki.yamanashi.jp +nishikatsura.yamanashi.jp +oshino.yamanashi.jp +otsuki.yamanashi.jp +showa.yamanashi.jp +tabayama.yamanashi.jp +tsuru.yamanashi.jp +uenohara.yamanashi.jp +yamanakako.yamanashi.jp +yamanashi.yamanashi.jp + +// ke : http://www.kenic.or.ke/index.php/en/ke-domains/ke-domains +ke +ac.ke +co.ke +go.ke +info.ke +me.ke +mobi.ke +ne.ke +or.ke +sc.ke + +// kg : http://www.domain.kg/dmn_n.html +kg +com.kg +edu.kg +gov.kg +mil.kg +net.kg +org.kg + +// kh : http://www.mptc.gov.kh/dns_registration.htm +*.kh + +// ki : https://www.iana.org/domains/root/db/ki.html +ki +biz.ki +com.ki +edu.ki +gov.ki +info.ki +net.ki +org.ki + +// km : https://www.iana.org/domains/root/db/km.html +// http://www.domaine.km/documents/charte.doc +km +ass.km +com.km +edu.km +gov.km +mil.km +nom.km +org.km +prd.km +tm.km +// These are only mentioned as proposed suggestions at domaine.km, but +// https://www.iana.org/domains/root/db/km.html says they're available for registration: +asso.km +coop.km +gouv.km +medecin.km +notaires.km +pharmaciens.km +presse.km +veterinaire.km + +// kn : https://www.iana.org/domains/root/db/kn.html +// http://www.dot.kn/domainRules.html +kn +edu.kn +gov.kn +net.kn +org.kn + +// kp : http://www.kcce.kp/en_index.php +kp +com.kp +edu.kp +gov.kp +org.kp +rep.kp +tra.kp + +// kr : https://www.iana.org/domains/root/db/kr.html +// see also: https://krnic.kisa.or.kr/jsp/infoboard/law/domBylawsReg.jsp +kr +ac.kr +ai.kr +co.kr +es.kr +go.kr +hs.kr +io.kr +it.kr +kg.kr +me.kr +mil.kr +ms.kr +ne.kr +or.kr +pe.kr +re.kr +sc.kr +// kr geographical names +busan.kr +chungbuk.kr +chungnam.kr +daegu.kr +daejeon.kr +gangwon.kr +gwangju.kr +gyeongbuk.kr +gyeonggi.kr +gyeongnam.kr +incheon.kr +jeju.kr +jeonbuk.kr +jeonnam.kr +seoul.kr +ulsan.kr + +// kw : https://www.nic.kw/policies/ +// Confirmed by registry +kw +com.kw +edu.kw +emb.kw +gov.kw +ind.kw +net.kw +org.kw + +// ky : http://www.icta.ky/da_ky_reg_dom.php +// Confirmed by registry 2008-06-17 +ky +com.ky +edu.ky +net.ky +org.ky + +// kz : https://www.iana.org/domains/root/db/kz.html +// see also: http://www.nic.kz/rules/index.jsp +kz +com.kz +edu.kz +gov.kz +mil.kz +net.kz +org.kz + +// la : https://www.iana.org/domains/root/db/la.html +// Submitted by registry +la +com.la +edu.la +gov.la +info.la +int.la +net.la +org.la +per.la + +// lb : https://www.iana.org/domains/root/db/lb.html +// Submitted by registry +lb +com.lb +edu.lb +gov.lb +net.lb +org.lb + +// lc : https://www.iana.org/domains/root/db/lc.html +// see also: http://www.nic.lc/rules.htm +lc +co.lc +com.lc +edu.lc +gov.lc +net.lc +org.lc + +// li : https://www.iana.org/domains/root/db/li.html +li + +// lk : https://www.iana.org/domains/root/db/lk.html +lk +ac.lk +assn.lk +com.lk +edu.lk +gov.lk +grp.lk +hotel.lk +int.lk +ltd.lk +net.lk +ngo.lk +org.lk +sch.lk +soc.lk +web.lk + +// lr : http://psg.com/dns/lr/lr.txt +// Submitted by registry +lr +com.lr +edu.lr +gov.lr +net.lr +org.lr + +// ls : http://www.nic.ls/ +// Confirmed by registry +ls +ac.ls +biz.ls +co.ls +edu.ls +gov.ls +info.ls +net.ls +org.ls +sc.ls + +// lt : https://www.iana.org/domains/root/db/lt.html +lt +// gov.lt : http://www.gov.lt/index_en.php +gov.lt + +// lu : http://www.dns.lu/en/ +lu + +// lv : https://www.iana.org/domains/root/db/lv.html +lv +asn.lv +com.lv +conf.lv +edu.lv +gov.lv +id.lv +mil.lv +net.lv +org.lv + +// ly : http://www.nic.ly/regulations.php +ly +com.ly +edu.ly +gov.ly +id.ly +med.ly +net.ly +org.ly +plc.ly +sch.ly + +// ma : https://www.iana.org/domains/root/db/ma.html +// http://www.anrt.ma/fr/admin/download/upload/file_fr782.pdf +ma +ac.ma +co.ma +gov.ma +net.ma +org.ma +press.ma + +// mc : http://www.nic.mc/ +mc +asso.mc +tm.mc + +// md : https://www.iana.org/domains/root/db/md.html +md + +// me : https://www.iana.org/domains/root/db/me.html +me +ac.me +co.me +edu.me +gov.me +its.me +net.me +org.me +priv.me + +// mg : https://nic.mg +mg +co.mg +com.mg +edu.mg +gov.mg +mil.mg +nom.mg +org.mg +prd.mg + +// mh : https://www.iana.org/domains/root/db/mh.html +mh + +// mil : https://www.iana.org/domains/root/db/mil.html +mil + +// mk : https://www.iana.org/domains/root/db/mk.html +// see also: http://dns.marnet.net.mk/postapka.php +mk +com.mk +edu.mk +gov.mk +inf.mk +name.mk +net.mk +org.mk + +// ml : https://www.iana.org/domains/root/db/ml.html +// Confirmed by Boubacar NDIAYE 2024-12-31 +ml +ac.ml +art.ml +asso.ml +com.ml +edu.ml +gouv.ml +gov.ml +info.ml +inst.ml +net.ml +org.ml +pr.ml +presse.ml + +// mm : https://www.iana.org/domains/root/db/mm.html +*.mm + +// mn : https://www.iana.org/domains/root/db/mn.html +mn +edu.mn +gov.mn +org.mn + +// mo : http://www.monic.net.mo/ +mo +com.mo +edu.mo +gov.mo +net.mo +org.mo + +// mobi : https://www.iana.org/domains/root/db/mobi.html +mobi + +// mp : http://www.dot.mp/ +// Confirmed by registry 2008-06-17 +mp + +// mq : https://www.iana.org/domains/root/db/mq.html +mq + +// mr : https://www.iana.org/domains/root/db/mr.html +mr +gov.mr + +// ms : https://www.iana.org/domains/root/db/ms.html +ms +com.ms +edu.ms +gov.ms +net.ms +org.ms + +// mt : https://www.nic.org.mt/go/policy +// Submitted by registry +mt +com.mt +edu.mt +net.mt +org.mt + +// mu : https://www.iana.org/domains/root/db/mu.html +mu +ac.mu +co.mu +com.mu +gov.mu +net.mu +or.mu +org.mu + +// museum : https://welcome.museum/wp-content/uploads/2018/05/20180525-Registration-Policy-MUSEUM-EN_VF-2.pdf https://welcome.museum/buy-your-dot-museum-2/ +museum + +// mv : https://www.iana.org/domains/root/db/mv.html +// "mv" included because, contra Wikipedia, google.mv exists. +mv +aero.mv +biz.mv +com.mv +coop.mv +edu.mv +gov.mv +info.mv +int.mv +mil.mv +museum.mv +name.mv +net.mv +org.mv +pro.mv + +// mw : http://www.registrar.mw/ +mw +ac.mw +biz.mw +co.mw +com.mw +coop.mw +edu.mw +gov.mw +int.mw +net.mw +org.mw + +// mx : http://www.nic.mx/ +// Submitted by registry +mx +com.mx +edu.mx +gob.mx +net.mx +org.mx + +// my : http://www.mynic.my/ +// Available strings: https://mynic.my/resources/domains/buying-a-domain/ +my +biz.my +com.my +edu.my +gov.my +mil.my +name.my +net.my +org.my + +// mz : http://www.uem.mz/ +// Submitted by registry +mz +ac.mz +adv.mz +co.mz +edu.mz +gov.mz +mil.mz +net.mz +org.mz + +// na : http://www.na-nic.com.na/ +na +alt.na +co.na +com.na +gov.na +net.na +org.na + +// name : http://www.nic.name/ +// Regarding 2LDs: https://github.com/publicsuffix/list/issues/2306 +name + +// nc : http://www.cctld.nc/ +nc +asso.nc +nom.nc + +// ne : https://www.iana.org/domains/root/db/ne.html +ne + +// net : https://www.iana.org/domains/root/db/net.html +net + +// nf : https://www.iana.org/domains/root/db/nf.html +nf +arts.nf +com.nf +firm.nf +info.nf +net.nf +other.nf +per.nf +rec.nf +store.nf +web.nf + +// ng : http://www.nira.org.ng/index.php/join-us/register-ng-domain/189-nira-slds +ng +com.ng +edu.ng +gov.ng +i.ng +mil.ng +mobi.ng +name.ng +net.ng +org.ng +sch.ng + +// ni : http://www.nic.ni/ +ni +ac.ni +biz.ni +co.ni +com.ni +edu.ni +gob.ni +in.ni +info.ni +int.ni +mil.ni +net.ni +nom.ni +org.ni +web.ni + +// nl : https://www.iana.org/domains/root/db/nl.html +// https://www.sidn.nl/ +nl + +// no : https://www.norid.no/en/om-domenenavn/regelverk-for-no/ +// Norid geographical second level domains : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-b/ +// Norid category second level domains : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-c/ +// Norid category second-level domains managed by parties other than Norid : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-d/ +// RSS feed: https://teknisk.norid.no/en/feed/ +no +// Norid category second level domains : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-c/ +fhs.no +folkebibl.no +fylkesbibl.no +idrett.no +museum.no +priv.no +vgs.no +// Norid category second-level domains managed by parties other than Norid : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-d/ +dep.no +herad.no +kommune.no +mil.no +stat.no +// Norid geographical second level domains : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-b/ +// counties +aa.no +ah.no +bu.no +fm.no +hl.no +hm.no +jan-mayen.no +mr.no +nl.no +nt.no +of.no +ol.no +oslo.no +rl.no +sf.no +st.no +svalbard.no +tm.no +tr.no +va.no +vf.no +// primary and lower secondary schools per county +gs.aa.no +gs.ah.no +gs.bu.no +gs.fm.no +gs.hl.no +gs.hm.no +gs.jan-mayen.no +gs.mr.no +gs.nl.no +gs.nt.no +gs.of.no +gs.ol.no +gs.oslo.no +gs.rl.no +gs.sf.no +gs.st.no +gs.svalbard.no +gs.tm.no +gs.tr.no +gs.va.no +gs.vf.no +// cities +akrehamn.no +åkrehamn.no +algard.no +ålgård.no +arna.no +bronnoysund.no +brønnøysund.no +brumunddal.no +bryne.no +drobak.no +drøbak.no +egersund.no +fetsund.no +floro.no +florø.no +fredrikstad.no +hokksund.no +honefoss.no +hønefoss.no +jessheim.no +jorpeland.no +jørpeland.no +kirkenes.no +kopervik.no +krokstadelva.no +langevag.no +langevåg.no +leirvik.no +mjondalen.no +mjøndalen.no +mo-i-rana.no +mosjoen.no +mosjøen.no +nesoddtangen.no +orkanger.no +osoyro.no +osøyro.no +raholt.no +råholt.no +sandnessjoen.no +sandnessjøen.no +skedsmokorset.no +slattum.no +spjelkavik.no +stathelle.no +stavern.no +stjordalshalsen.no +stjørdalshalsen.no +tananger.no +tranby.no +vossevangen.no +// communities +aarborte.no +aejrie.no +afjord.no +åfjord.no +agdenes.no +nes.akershus.no +aknoluokta.no +ákŋoluokta.no +al.no +ål.no +alaheadju.no +álaheadju.no +alesund.no +ålesund.no +alstahaug.no +alta.no +áltá.no +alvdal.no +amli.no +åmli.no +amot.no +åmot.no +andasuolo.no +andebu.no +andoy.no +andøy.no +ardal.no +årdal.no +aremark.no +arendal.no +ås.no +aseral.no +åseral.no +asker.no +askim.no +askoy.no +askøy.no +askvoll.no +asnes.no +åsnes.no +audnedaln.no +aukra.no +aure.no +aurland.no +aurskog-holand.no +aurskog-høland.no +austevoll.no +austrheim.no +averoy.no +averøy.no +badaddja.no +bådåddjå.no +bærum.no +bahcavuotna.no +báhcavuotna.no +bahccavuotna.no +báhccavuotna.no +baidar.no +báidár.no +bajddar.no +bájddar.no +balat.no +bálát.no +balestrand.no +ballangen.no +balsfjord.no +bamble.no +bardu.no +barum.no +batsfjord.no +båtsfjord.no +bearalvahki.no +bearalváhki.no +beardu.no +beiarn.no +berg.no +bergen.no +berlevag.no +berlevåg.no +bievat.no +bievát.no +bindal.no +birkenes.no +bjerkreim.no +bjugn.no +bodo.no +bodø.no +bokn.no +bomlo.no +bømlo.no +bremanger.no +bronnoy.no +brønnøy.no +budejju.no +nes.buskerud.no +bygland.no +bykle.no +cahcesuolo.no +čáhcesuolo.no +davvenjarga.no +davvenjárga.no +davvesiida.no +deatnu.no +dielddanuorri.no +divtasvuodna.no +divttasvuotna.no +donna.no +dønna.no +dovre.no +drammen.no +drangedal.no +dyroy.no +dyrøy.no +eid.no +eidfjord.no +eidsberg.no +eidskog.no +eidsvoll.no +eigersund.no +elverum.no +enebakk.no +engerdal.no +etne.no +etnedal.no +evenassi.no +evenášši.no +evenes.no +evje-og-hornnes.no +farsund.no +fauske.no +fedje.no +fet.no +finnoy.no +finnøy.no +fitjar.no +fjaler.no +fjell.no +fla.no +flå.no +flakstad.no +flatanger.no +flekkefjord.no +flesberg.no +flora.no +folldal.no +forde.no +førde.no +forsand.no +fosnes.no +fræna.no +frana.no +frei.no +frogn.no +froland.no +frosta.no +froya.no +frøya.no +fuoisku.no +fuossko.no +fusa.no +fyresdal.no +gaivuotna.no +gáivuotna.no +galsa.no +gálsá.no +gamvik.no +gangaviika.no +gáŋgaviika.no +gaular.no +gausdal.no +giehtavuoatna.no +gildeskal.no +gildeskål.no +giske.no +gjemnes.no +gjerdrum.no +gjerstad.no +gjesdal.no +gjovik.no +gjøvik.no +gloppen.no +gol.no +gran.no +grane.no +granvin.no +gratangen.no +grimstad.no +grong.no +grue.no +gulen.no +guovdageaidnu.no +ha.no +hå.no +habmer.no +hábmer.no +hadsel.no +hægebostad.no +hagebostad.no +halden.no +halsa.no +hamar.no +hamaroy.no +hammarfeasta.no +hámmárfeasta.no +hammerfest.no +hapmir.no +hápmir.no +haram.no +hareid.no +harstad.no +hasvik.no +hattfjelldal.no +haugesund.no +os.hedmark.no +valer.hedmark.no +våler.hedmark.no +hemne.no +hemnes.no +hemsedal.no +hitra.no +hjartdal.no +hjelmeland.no +hobol.no +hobøl.no +hof.no +hol.no +hole.no +holmestrand.no +holtalen.no +holtålen.no +os.hordaland.no +hornindal.no +horten.no +hoyanger.no +høyanger.no +hoylandet.no +høylandet.no +hurdal.no +hurum.no +hvaler.no +hyllestad.no +ibestad.no +inderoy.no +inderøy.no +iveland.no +ivgu.no +jevnaker.no +jolster.no +jølster.no +jondal.no +kafjord.no +kåfjord.no +karasjohka.no +kárášjohka.no +karasjok.no +karlsoy.no +karmoy.no +karmøy.no +kautokeino.no +klabu.no +klæbu.no +klepp.no +kongsberg.no +kongsvinger.no +kraanghke.no +kråanghke.no +kragero.no +kragerø.no +kristiansand.no +kristiansund.no +krodsherad.no +krødsherad.no +kvæfjord.no +kvænangen.no +kvafjord.no +kvalsund.no +kvam.no +kvanangen.no +kvinesdal.no +kvinnherad.no +kviteseid.no +kvitsoy.no +kvitsøy.no +laakesvuemie.no +lærdal.no +lahppi.no +láhppi.no +lardal.no +larvik.no +lavagis.no +lavangen.no +leangaviika.no +leaŋgaviika.no +lebesby.no +leikanger.no +leirfjord.no +leka.no +leksvik.no +lenvik.no +lerdal.no +lesja.no +levanger.no +lier.no +lierne.no +lillehammer.no +lillesand.no +lindas.no +lindås.no +lindesnes.no +loabat.no +loabát.no +lodingen.no +lødingen.no +lom.no +loppa.no +lorenskog.no +lørenskog.no +loten.no +løten.no +lund.no +lunner.no +luroy.no +lurøy.no +luster.no +lyngdal.no +lyngen.no +malatvuopmi.no +málatvuopmi.no +malselv.no +målselv.no +malvik.no +mandal.no +marker.no +marnardal.no +masfjorden.no +masoy.no +måsøy.no +matta-varjjat.no +mátta-várjjat.no +meland.no +meldal.no +melhus.no +meloy.no +meløy.no +meraker.no +meråker.no +midsund.no +midtre-gauldal.no +moareke.no +moåreke.no +modalen.no +modum.no +molde.no +heroy.more-og-romsdal.no +sande.more-og-romsdal.no +herøy.møre-og-romsdal.no +sande.møre-og-romsdal.no +moskenes.no +moss.no +muosat.no +muosát.no +naamesjevuemie.no +nååmesjevuemie.no +nærøy.no +namdalseid.no +namsos.no +namsskogan.no +nannestad.no +naroy.no +narviika.no +narvik.no +naustdal.no +navuotna.no +návuotna.no +nedre-eiker.no +nesna.no +nesodden.no +nesseby.no +nesset.no +nissedal.no +nittedal.no +nord-aurdal.no +nord-fron.no +nord-odal.no +norddal.no +nordkapp.no +bo.nordland.no +bø.nordland.no +heroy.nordland.no +herøy.nordland.no +nordre-land.no +nordreisa.no +nore-og-uvdal.no +notodden.no +notteroy.no +nøtterøy.no +odda.no +oksnes.no +øksnes.no +omasvuotna.no +oppdal.no +oppegard.no +oppegård.no +orkdal.no +orland.no +ørland.no +orskog.no +ørskog.no +orsta.no +ørsta.no +osen.no +osteroy.no +osterøy.no +valer.ostfold.no +våler.østfold.no +ostre-toten.no +østre-toten.no +overhalla.no +ovre-eiker.no +øvre-eiker.no +oyer.no +øyer.no +oygarden.no +øygarden.no +oystre-slidre.no +øystre-slidre.no +porsanger.no +porsangu.no +porsáŋgu.no +porsgrunn.no +rade.no +råde.no +radoy.no +radøy.no +rælingen.no +rahkkeravju.no +ráhkkerávju.no +raisa.no +ráisa.no +rakkestad.no +ralingen.no +rana.no +randaberg.no +rauma.no +rendalen.no +rennebu.no +rennesoy.no +rennesøy.no +rindal.no +ringebu.no +ringerike.no +ringsaker.no +risor.no +risør.no +rissa.no +roan.no +rodoy.no +rødøy.no +rollag.no +romsa.no +romskog.no +rømskog.no +roros.no +røros.no +rost.no +røst.no +royken.no +røyken.no +royrvik.no +røyrvik.no +ruovat.no +rygge.no +salangen.no +salat.no +sálat.no +sálát.no +saltdal.no +samnanger.no +sandefjord.no +sandnes.no +sandoy.no +sandøy.no +sarpsborg.no +sauda.no +sauherad.no +sel.no +selbu.no +selje.no +seljord.no +siellak.no +sigdal.no +siljan.no +sirdal.no +skanit.no +skánit.no +skanland.no +skånland.no +skaun.no +skedsmo.no +ski.no +skien.no +skierva.no +skiervá.no +skiptvet.no +skjak.no +skjåk.no +skjervoy.no +skjervøy.no +skodje.no +smola.no +smøla.no +snaase.no +snåase.no +snasa.no +snåsa.no +snillfjord.no +snoasa.no +sogndal.no +sogne.no +søgne.no +sokndal.no +sola.no +solund.no +somna.no +sømna.no +sondre-land.no +søndre-land.no +songdalen.no +sor-aurdal.no +sør-aurdal.no +sor-fron.no +sør-fron.no +sor-odal.no +sør-odal.no +sor-varanger.no +sør-varanger.no +sorfold.no +sørfold.no +sorreisa.no +sørreisa.no +sortland.no +sorum.no +sørum.no +spydeberg.no +stange.no +stavanger.no +steigen.no +steinkjer.no +stjordal.no +stjørdal.no +stokke.no +stor-elvdal.no +stord.no +stordal.no +storfjord.no +strand.no +stranda.no +stryn.no +sula.no +suldal.no +sund.no +sunndal.no +surnadal.no +sveio.no +svelvik.no +sykkylven.no +tana.no +bo.telemark.no +bø.telemark.no +time.no +tingvoll.no +tinn.no +tjeldsund.no +tjome.no +tjøme.no +tokke.no +tolga.no +tonsberg.no +tønsberg.no +torsken.no +træna.no +trana.no +tranoy.no +tranøy.no +troandin.no +trogstad.no +trøgstad.no +tromsa.no +tromso.no +tromsø.no +trondheim.no +trysil.no +tvedestrand.no +tydal.no +tynset.no +tysfjord.no +tysnes.no +tysvær.no +tysvar.no +ullensaker.no +ullensvang.no +ulvik.no +unjarga.no +unjárga.no +utsira.no +vaapste.no +vadso.no +vadsø.no +værøy.no +vaga.no +vågå.no +vagan.no +vågan.no +vagsoy.no +vågsøy.no +vaksdal.no +valle.no +vang.no +vanylven.no +vardo.no +vardø.no +varggat.no +várggát.no +varoy.no +vefsn.no +vega.no +vegarshei.no +vegårshei.no +vennesla.no +verdal.no +verran.no +vestby.no +sande.vestfold.no +vestnes.no +vestre-slidre.no +vestre-toten.no +vestvagoy.no +vestvågøy.no +vevelstad.no +vik.no +vikna.no +vindafjord.no +voagat.no +volda.no +voss.no + +// np : http://www.mos.com.np/register.html +*.np + +// nr : http://cenpac.net.nr/dns/index.html +// Submitted by registry +nr +biz.nr +com.nr +edu.nr +gov.nr +info.nr +net.nr +org.nr + +// nu : https://www.iana.org/domains/root/db/nu.html +nu + +// nz : https://www.iana.org/domains/root/db/nz.html +// Submitted by registry +nz +ac.nz +co.nz +cri.nz +geek.nz +gen.nz +govt.nz +health.nz +iwi.nz +kiwi.nz +maori.nz +māori.nz +mil.nz +net.nz +org.nz +parliament.nz +school.nz + +// om : https://www.iana.org/domains/root/db/om.html +om +co.om +com.om +edu.om +gov.om +med.om +museum.om +net.om +org.om +pro.om + +// onion : https://tools.ietf.org/html/rfc7686 +onion + +// org : https://www.iana.org/domains/root/db/org.html +org + +// pa : http://www.nic.pa/ +// Some additional second level "domains" resolve directly as hostnames, such as +// pannet.pa, so we add a rule for "pa". +pa +abo.pa +ac.pa +com.pa +edu.pa +gob.pa +ing.pa +med.pa +net.pa +nom.pa +org.pa +sld.pa + +// pe : https://www.nic.pe/InformeFinalComision.pdf +pe +com.pe +edu.pe +gob.pe +mil.pe +net.pe +nom.pe +org.pe + +// pf : http://www.gobin.info/domainname/formulaire-pf.pdf +pf +com.pf +edu.pf +org.pf + +// pg : https://www.iana.org/domains/root/db/pg.html +*.pg + +// ph : https://www.iana.org/domains/root/db/ph.html +// Submitted by registry +ph +com.ph +edu.ph +gov.ph +i.ph +mil.ph +net.ph +ngo.ph +org.ph + +// pk : https://pk5.pknic.net.pk/pk5/msgNamepk.PK +// Contact Email: staff@pknic.net.pk +pk +ac.pk +biz.pk +com.pk +edu.pk +fam.pk +gkp.pk +gob.pk +gog.pk +gok.pk +gop.pk +gos.pk +gov.pk +net.pk +org.pk +web.pk + +// pl : https://www.dns.pl/en/ +// Confirmed by registry 2024-11-18 +pl +com.pl +net.pl +org.pl +// pl functional domains : https://www.dns.pl/en/list_of_functional_domain_names +agro.pl +aid.pl +atm.pl +auto.pl +biz.pl +edu.pl +gmina.pl +gsm.pl +info.pl +mail.pl +media.pl +miasta.pl +mil.pl +nieruchomosci.pl +nom.pl +pc.pl +powiat.pl +priv.pl +realestate.pl +rel.pl +sex.pl +shop.pl +sklep.pl +sos.pl +szkola.pl +targi.pl +tm.pl +tourism.pl +travel.pl +turystyka.pl +// Government domains : https://www.dns.pl/informacje_o_rejestracji_domen_gov_pl +// In accordance with the .gov.pl Domain Name Regulations : https://www.dns.pl/regulamin_gov_pl +gov.pl +ap.gov.pl +griw.gov.pl +ic.gov.pl +is.gov.pl +kmpsp.gov.pl +konsulat.gov.pl +kppsp.gov.pl +kwp.gov.pl +kwpsp.gov.pl +mup.gov.pl +mw.gov.pl +oia.gov.pl +oirm.gov.pl +oke.gov.pl +oow.gov.pl +oschr.gov.pl +oum.gov.pl +pa.gov.pl +pinb.gov.pl +piw.gov.pl +po.gov.pl +pr.gov.pl +psp.gov.pl +psse.gov.pl +pup.gov.pl +rzgw.gov.pl +sa.gov.pl +sdn.gov.pl +sko.gov.pl +so.gov.pl +sr.gov.pl +starostwo.gov.pl +ug.gov.pl +ugim.gov.pl +um.gov.pl +umig.gov.pl +upow.gov.pl +uppo.gov.pl +us.gov.pl +uw.gov.pl +uzs.gov.pl +wif.gov.pl +wiih.gov.pl +winb.gov.pl +wios.gov.pl +witd.gov.pl +wiw.gov.pl +wkz.gov.pl +wsa.gov.pl +wskr.gov.pl +wsse.gov.pl +wuoz.gov.pl +wzmiuw.gov.pl +zp.gov.pl +zpisdn.gov.pl +// pl regional domains : https://www.dns.pl/en/list_of_regional_domain_names +augustow.pl +babia-gora.pl +bedzin.pl +beskidy.pl +bialowieza.pl +bialystok.pl +bielawa.pl +bieszczady.pl +boleslawiec.pl +bydgoszcz.pl +bytom.pl +cieszyn.pl +czeladz.pl +czest.pl +dlugoleka.pl +elblag.pl +elk.pl +glogow.pl +gniezno.pl +gorlice.pl +grajewo.pl +ilawa.pl +jaworzno.pl +jelenia-gora.pl +jgora.pl +kalisz.pl +karpacz.pl +kartuzy.pl +kaszuby.pl +katowice.pl +kazimierz-dolny.pl +kepno.pl +ketrzyn.pl +klodzko.pl +kobierzyce.pl +kolobrzeg.pl +konin.pl +konskowola.pl +kutno.pl +lapy.pl +lebork.pl +legnica.pl +lezajsk.pl +limanowa.pl +lomza.pl +lowicz.pl +lubin.pl +lukow.pl +malbork.pl +malopolska.pl +mazowsze.pl +mazury.pl +mielec.pl +mielno.pl +mragowo.pl +naklo.pl +nowaruda.pl +nysa.pl +olawa.pl +olecko.pl +olkusz.pl +olsztyn.pl +opoczno.pl +opole.pl +ostroda.pl +ostroleka.pl +ostrowiec.pl +ostrowwlkp.pl +pila.pl +pisz.pl +podhale.pl +podlasie.pl +polkowice.pl +pomorskie.pl +pomorze.pl +prochowice.pl +pruszkow.pl +przeworsk.pl +pulawy.pl +radom.pl +rawa-maz.pl +rybnik.pl +rzeszow.pl +sanok.pl +sejny.pl +skoczow.pl +slask.pl +slupsk.pl +sosnowiec.pl +stalowa-wola.pl +starachowice.pl +stargard.pl +suwalki.pl +swidnica.pl +swiebodzin.pl +swinoujscie.pl +szczecin.pl +szczytno.pl +tarnobrzeg.pl +tgory.pl +turek.pl +tychy.pl +ustka.pl +walbrzych.pl +warmia.pl +warszawa.pl +waw.pl +wegrow.pl +wielun.pl +wlocl.pl +wloclawek.pl +wodzislaw.pl +wolomin.pl +wroclaw.pl +zachpomor.pl +zagan.pl +zarow.pl +zgora.pl +zgorzelec.pl + +// pm : https://www.afnic.fr/wp-media/uploads/2022/12/afnic-naming-policy-2023-01-01.pdf +pm + +// pn : https://www.iana.org/domains/root/db/pn.html +pn +co.pn +edu.pn +gov.pn +net.pn +org.pn + +// post : https://www.iana.org/domains/root/db/post.html +post + +// pr : http://www.nic.pr/index.asp?f=1 +pr +biz.pr +com.pr +edu.pr +gov.pr +info.pr +isla.pr +name.pr +net.pr +org.pr +pro.pr +// these aren't mentioned on nic.pr, but on https://www.iana.org/domains/root/db/pr.html +ac.pr +est.pr +prof.pr + +// pro : http://registry.pro/get-pro +pro +aaa.pro +aca.pro +acct.pro +avocat.pro +bar.pro +cpa.pro +eng.pro +jur.pro +law.pro +med.pro +recht.pro + +// ps : https://www.iana.org/domains/root/db/ps.html +// http://www.nic.ps/registration/policy.html#reg +ps +com.ps +edu.ps +gov.ps +net.ps +org.ps +plo.ps +sec.ps + +// pt : https://www.dns.pt/en/domain/pt-terms-and-conditions-registration-rules/ +pt +com.pt +edu.pt +gov.pt +int.pt +net.pt +nome.pt +org.pt +publ.pt + +// pw : https://www.iana.org/domains/root/db/pw.html +// Confirmed by registry in private correspondence with @dnsguru 2024-12-09 +pw +gov.pw + +// py : https://www.iana.org/domains/root/db/py.html +// Submitted by registry +py +com.py +coop.py +edu.py +gov.py +mil.py +net.py +org.py + +// qa : http://domains.qa/en/ +qa +com.qa +edu.qa +gov.qa +mil.qa +name.qa +net.qa +org.qa +sch.qa + +// re : https://www.afnic.fr/wp-media/uploads/2022/12/afnic-naming-policy-2023-01-01.pdf +// Confirmed by registry 2024-11-18 +re +// Closed for registration on 2013-03-15 but domains are still maintained +asso.re +com.re + +// ro : http://www.rotld.ro/ +ro +arts.ro +com.ro +firm.ro +info.ro +nom.ro +nt.ro +org.ro +rec.ro +store.ro +tm.ro +www.ro + +// rs : https://www.rnids.rs/en/domains/national-domains +rs +ac.rs +co.rs +edu.rs +gov.rs +in.rs +org.rs + +// ru : https://cctld.ru/files/pdf/docs/en/rules_ru-rf.pdf +// Submitted by George Georgievsky +ru + +// rw : https://www.iana.org/domains/root/db/rw.html +rw +ac.rw +co.rw +coop.rw +gov.rw +mil.rw +net.rw +org.rw + +// sa : http://www.nic.net.sa/ +sa +com.sa +edu.sa +gov.sa +med.sa +net.sa +org.sa +pub.sa +sch.sa + +// sb : http://www.sbnic.net.sb/ +// Submitted by registry +sb +com.sb +edu.sb +gov.sb +net.sb +org.sb + +// sc : http://www.nic.sc/ +sc +com.sc +edu.sc +gov.sc +net.sc +org.sc + +// sd : https://www.iana.org/domains/root/db/sd.html +// Submitted by registry +sd +com.sd +edu.sd +gov.sd +info.sd +med.sd +net.sd +org.sd +tv.sd + +// se : https://www.iana.org/domains/root/db/se.html +// https://data.internetstiftelsen.se/barred_domains_list.txt -> Second level domains & Sub-domains +// Confirmed by Registry Services 2024-11-20 +se +a.se +ac.se +b.se +bd.se +brand.se +c.se +d.se +e.se +f.se +fh.se +fhsk.se +fhv.se +g.se +h.se +i.se +k.se +komforb.se +kommunalforbund.se +komvux.se +l.se +lanbib.se +m.se +n.se +naturbruksgymn.se +o.se +org.se +p.se +parti.se +pp.se +press.se +r.se +s.se +t.se +tm.se +u.se +w.se +x.se +y.se +z.se + +// sg : https://www.sgnic.sg/domain-registration/sg-categories-rules +// Confirmed by registry 2024-11-19 +sg +com.sg +edu.sg +gov.sg +net.sg +org.sg + +// sh : http://nic.sh/rules.htm +sh +com.sh +gov.sh +mil.sh +net.sh +org.sh + +// si : https://www.iana.org/domains/root/db/si.html +si + +// sj : No registrations at this time. +// Submitted by registry +sj + +// sk : https://www.iana.org/domains/root/db/sk.html +// https://sk-nic.sk/ +sk +org.sk + +// sl : http://www.nic.sl +// Submitted by registry +sl +com.sl +edu.sl +gov.sl +net.sl +org.sl + +// sm : https://www.iana.org/domains/root/db/sm.html +sm + +// sn : https://www.iana.org/domains/root/db/sn.html +sn +art.sn +com.sn +edu.sn +gouv.sn +org.sn +perso.sn +univ.sn + +// so : http://sonic.so/policies/ +so +com.so +edu.so +gov.so +me.so +net.so +org.so + +// sr : https://www.iana.org/domains/root/db/sr.html +sr + +// ss : https://registry.nic.ss/ +// Submitted by registry +ss +biz.ss +co.ss +com.ss +edu.ss +gov.ss +me.ss +net.ss +org.ss +sch.ss + +// st : http://www.nic.st/html/policyrules/ +st +co.st +com.st +consulado.st +edu.st +embaixada.st +mil.st +net.st +org.st +principe.st +saotome.st +store.st + +// su : https://www.iana.org/domains/root/db/su.html +su + +// sv : https://www.iana.org/domains/root/db/sv.html +sv +com.sv +edu.sv +gob.sv +org.sv +red.sv + +// sx : https://www.iana.org/domains/root/db/sx.html +// Submitted by registry +sx +gov.sx + +// sy : https://www.iana.org/domains/root/db/sy.html +sy +com.sy +edu.sy +gov.sy +mil.sy +net.sy +org.sy + +// sz : https://www.iana.org/domains/root/db/sz.html +// http://www.sispa.org.sz/ +sz +ac.sz +co.sz +org.sz + +// tc : https://www.iana.org/domains/root/db/tc.html +tc + +// td : https://www.iana.org/domains/root/db/td.html +td + +// tel : https://www.iana.org/domains/root/db/tel.html +// http://www.telnic.org/ +tel + +// tf : https://www.afnic.fr/wp-media/uploads/2022/12/afnic-naming-policy-2023-01-01.pdf +tf + +// tg : https://www.iana.org/domains/root/db/tg.html +// http://www.nic.tg/ +tg + +// th : https://www.iana.org/domains/root/db/th.html +// Submitted by registry +th +ac.th +co.th +go.th +in.th +mi.th +net.th +or.th + +// tj : http://www.nic.tj/policy.html +tj +ac.tj +biz.tj +co.tj +com.tj +edu.tj +go.tj +gov.tj +int.tj +mil.tj +name.tj +net.tj +nic.tj +org.tj +test.tj +web.tj + +// tk : https://www.iana.org/domains/root/db/tk.html +tk + +// tl : https://www.iana.org/domains/root/db/tl.html +tl +gov.tl + +// tm : https://www.nic.tm/local.html +// Confirmed by registry 2024-11-19 +tm +co.tm +com.tm +edu.tm +gov.tm +mil.tm +net.tm +nom.tm +org.tm + +// tn : http://www.registre.tn/fr/ +// https://whois.ati.tn/ +tn +com.tn +ens.tn +fin.tn +gov.tn +ind.tn +info.tn +intl.tn +mincom.tn +nat.tn +net.tn +org.tn +perso.tn +tourism.tn + +// to : https://www.iana.org/domains/root/db/to.html +// Submitted by registry +to +com.to +edu.to +gov.to +mil.to +net.to +org.to + +// tr : https://nic.tr/ +// https://nic.tr/forms/eng/policies.pdf +// https://nic.tr/index.php?USRACTN=PRICELST +tr +av.tr +bbs.tr +bel.tr +biz.tr +com.tr +dr.tr +edu.tr +gen.tr +gov.tr +info.tr +k12.tr +kep.tr +mil.tr +name.tr +net.tr +org.tr +pol.tr +tel.tr +tsk.tr +tv.tr +web.tr +// Used by Northern Cyprus +nc.tr +// Used by government agencies of Northern Cyprus +gov.nc.tr + +// tt : https://www.nic.tt/ +// Confirmed by registry 2024-11-19 +tt +biz.tt +co.tt +com.tt +edu.tt +gov.tt +info.tt +mil.tt +name.tt +net.tt +org.tt +pro.tt + +// tv : https://www.iana.org/domains/root/db/tv.html +// Not listing any 2LDs as reserved since none seem to exist in practice, +// Wikipedia notwithstanding. +tv + +// tw : https://www.iana.org/domains/root/db/tw.html +// https://twnic.tw/dnservice_catag.php +// Confirmed by registry 2024-11-26 +tw +club.tw +com.tw +ebiz.tw +edu.tw +game.tw +gov.tw +idv.tw +mil.tw +net.tw +org.tw + +// tz : http://www.tznic.or.tz/index.php/domains +// Submitted by registry +tz +ac.tz +co.tz +go.tz +hotel.tz +info.tz +me.tz +mil.tz +mobi.tz +ne.tz +or.tz +sc.tz +tv.tz + +// ua : https://hostmaster.ua/policy/?ua +// Submitted by registry +ua +// ua 2LD +com.ua +edu.ua +gov.ua +in.ua +net.ua +org.ua +// ua geographic names +// https://hostmaster.ua/2ld/ +cherkassy.ua +cherkasy.ua +chernigov.ua +chernihiv.ua +chernivtsi.ua +chernovtsy.ua +ck.ua +cn.ua +cr.ua +crimea.ua +cv.ua +dn.ua +dnepropetrovsk.ua +dnipropetrovsk.ua +donetsk.ua +dp.ua +if.ua +ivano-frankivsk.ua +kh.ua +kharkiv.ua +kharkov.ua +kherson.ua +khmelnitskiy.ua +khmelnytskyi.ua +kiev.ua +kirovograd.ua +km.ua +kr.ua +kropyvnytskyi.ua +krym.ua +ks.ua +kv.ua +kyiv.ua +lg.ua +lt.ua +lugansk.ua +luhansk.ua +lutsk.ua +lv.ua +lviv.ua +mk.ua +mykolaiv.ua +nikolaev.ua +od.ua +odesa.ua +odessa.ua +pl.ua +poltava.ua +rivne.ua +rovno.ua +rv.ua +sb.ua +sebastopol.ua +sevastopol.ua +sm.ua +sumy.ua +te.ua +ternopil.ua +uz.ua +uzhgorod.ua +uzhhorod.ua +vinnica.ua +vinnytsia.ua +vn.ua +volyn.ua +yalta.ua +zakarpattia.ua +zaporizhzhe.ua +zaporizhzhia.ua +zhitomir.ua +zhytomyr.ua +zp.ua +zt.ua + +// ug : https://www.registry.co.ug/ +// https://www.registry.co.ug, https://whois.co.ug +// Confirmed by registry 2025-01-20 +ug +ac.ug +co.ug +com.ug +edu.ug +go.ug +gov.ug +mil.ug +ne.ug +or.ug +org.ug +sc.ug +us.ug + +// uk : https://www.iana.org/domains/root/db/uk.html +// Submitted by registry +uk +ac.uk +co.uk +gov.uk +ltd.uk +me.uk +net.uk +nhs.uk +org.uk +plc.uk +police.uk +*.sch.uk + +// us : https://www.iana.org/domains/root/db/us.html +// Confirmed via the .us zone file by William Harrison 2024-12-10 +us +dni.us +isa.us +nsn.us +// Geographic Names +ak.us +al.us +ar.us +as.us +az.us +ca.us +co.us +ct.us +dc.us +de.us +fl.us +ga.us +gu.us +hi.us +ia.us +id.us +il.us +in.us +ks.us +ky.us +la.us +ma.us +md.us +me.us +mi.us +mn.us +mo.us +ms.us +mt.us +nc.us +nd.us +ne.us +nh.us +nj.us +nm.us +nv.us +ny.us +oh.us +ok.us +or.us +pa.us +pr.us +ri.us +sc.us +sd.us +tn.us +tx.us +ut.us +va.us +vi.us +vt.us +wa.us +wi.us +wv.us +wy.us +// The registrar notes several more specific domains available in each state, +// such as state.*.us, dst.*.us, etc., but resolution of these is somewhat +// haphazard; in some states these domains resolve as addresses, while in others +// only subdomains are available, or even nothing at all. We include the +// most common ones where it's clear that different sites are different +// entities. +k12.ak.us +k12.al.us +k12.ar.us +k12.as.us +k12.az.us +k12.ca.us +k12.co.us +k12.ct.us +k12.dc.us +k12.fl.us +k12.ga.us +k12.gu.us +// k12.hi.us - Bug 614565 - Hawaii has a state-wide DOE login +k12.ia.us +k12.id.us +k12.il.us +k12.in.us +k12.ks.us +k12.ky.us +k12.la.us +k12.ma.us +k12.md.us +k12.me.us +k12.mi.us +k12.mn.us +k12.mo.us +k12.ms.us +k12.mt.us +k12.nc.us +// k12.nd.us - Bug 1028347 - Removed at request of Travis Rosso +k12.ne.us +k12.nh.us +k12.nj.us +k12.nm.us +k12.nv.us +k12.ny.us +k12.oh.us +k12.ok.us +k12.or.us +k12.pa.us +k12.pr.us +// k12.ri.us - Removed at request of Kim Cournoyer +k12.sc.us +// k12.sd.us - Bug 934131 - Removed at request of James Booze +k12.tn.us +k12.tx.us +k12.ut.us +k12.va.us +k12.vi.us +k12.vt.us +k12.wa.us +k12.wi.us +// k12.wv.us - Bug 947705 - Removed at request of Verne Britton +cc.ak.us +lib.ak.us +cc.al.us +lib.al.us +cc.ar.us +lib.ar.us +cc.as.us +lib.as.us +cc.az.us +lib.az.us +cc.ca.us +lib.ca.us +cc.co.us +lib.co.us +cc.ct.us +lib.ct.us +cc.dc.us +lib.dc.us +cc.de.us +cc.fl.us +lib.fl.us +cc.ga.us +lib.ga.us +cc.gu.us +lib.gu.us +cc.hi.us +lib.hi.us +cc.ia.us +lib.ia.us +cc.id.us +lib.id.us +cc.il.us +lib.il.us +cc.in.us +lib.in.us +cc.ks.us +lib.ks.us +cc.ky.us +lib.ky.us +cc.la.us +lib.la.us +cc.ma.us +lib.ma.us +cc.md.us +lib.md.us +cc.me.us +lib.me.us +cc.mi.us +lib.mi.us +cc.mn.us +lib.mn.us +cc.mo.us +lib.mo.us +cc.ms.us +cc.mt.us +lib.mt.us +cc.nc.us +lib.nc.us +cc.nd.us +lib.nd.us +cc.ne.us +lib.ne.us +cc.nh.us +lib.nh.us +cc.nj.us +lib.nj.us +cc.nm.us +lib.nm.us +cc.nv.us +lib.nv.us +cc.ny.us +lib.ny.us +cc.oh.us +lib.oh.us +cc.ok.us +lib.ok.us +cc.or.us +lib.or.us +cc.pa.us +lib.pa.us +cc.pr.us +lib.pr.us +cc.ri.us +lib.ri.us +cc.sc.us +lib.sc.us +cc.sd.us +lib.sd.us +cc.tn.us +lib.tn.us +cc.tx.us +lib.tx.us +cc.ut.us +lib.ut.us +cc.va.us +lib.va.us +cc.vi.us +lib.vi.us +cc.vt.us +lib.vt.us +cc.wa.us +lib.wa.us +cc.wi.us +lib.wi.us +cc.wv.us +cc.wy.us +k12.wy.us +// lib.wv.us - Bug 941670 - Removed at request of Larry W Arnold +lib.wy.us +// k12.ma.us contains school districts in Massachusetts. The 4LDs are +// managed independently except for private (PVT), charter (CHTR) and +// parochial (PAROCH) schools. Those are delegated directly to the +// 5LD operators. +chtr.k12.ma.us +paroch.k12.ma.us +pvt.k12.ma.us +// Merit Network, Inc. maintains the registry for =~ /(k12|cc|lib).mi.us/ and the following +// see also: https://domreg.merit.edu : domreg@merit.edu +// see also: whois -h whois.domreg.merit.edu help +ann-arbor.mi.us +cog.mi.us +dst.mi.us +eaton.mi.us +gen.mi.us +mus.mi.us +tec.mi.us +washtenaw.mi.us + +// uy : http://www.nic.org.uy/ +uy +com.uy +edu.uy +gub.uy +mil.uy +net.uy +org.uy + +// uz : http://www.reg.uz/ +uz +co.uz +com.uz +net.uz +org.uz + +// va : https://www.iana.org/domains/root/db/va.html +va + +// vc : https://www.iana.org/domains/root/db/vc.html +// Submitted by registry +vc +com.vc +edu.vc +gov.vc +mil.vc +net.vc +org.vc + +// ve : https://registro.nic.ve/ +// https://nic.ve/site/user-agreement -> under "III. Clasificación de Nombres de Dominio" +// Submitted by registry nic@nic.ve and nicve@conatel.gob.ve +ve +arts.ve +bib.ve +co.ve +com.ve +e12.ve +edu.ve +emprende.ve +firm.ve +gob.ve +gov.ve +ia.ve +info.ve +int.ve +mil.ve +net.ve +nom.ve +org.ve +rar.ve +rec.ve +store.ve +tec.ve +web.ve + +// vg : https://www.iana.org/domains/root/db/vg.html +// Confirmed by registry 2025-01-10 +vg +edu.vg + +// vi : https://www.iana.org/domains/root/db/vi.html +vi +co.vi +com.vi +k12.vi +net.vi +org.vi + +// vn : https://www.vnnic.vn/en/domain/cctld-vn +// https://vnnic.vn/sites/default/files/tailieu/vn.cctld.domains.txt +vn +ac.vn +ai.vn +biz.vn +com.vn +edu.vn +gov.vn +health.vn +id.vn +info.vn +int.vn +io.vn +name.vn +net.vn +org.vn +pro.vn + +// vn geographical names +angiang.vn +bacgiang.vn +backan.vn +baclieu.vn +bacninh.vn +baria-vungtau.vn +bentre.vn +binhdinh.vn +binhduong.vn +binhphuoc.vn +binhthuan.vn +camau.vn +cantho.vn +caobang.vn +daklak.vn +daknong.vn +danang.vn +dienbien.vn +dongnai.vn +dongthap.vn +gialai.vn +hagiang.vn +haiduong.vn +haiphong.vn +hanam.vn +hanoi.vn +hatinh.vn +haugiang.vn +hoabinh.vn +hungyen.vn +khanhhoa.vn +kiengiang.vn +kontum.vn +laichau.vn +lamdong.vn +langson.vn +laocai.vn +longan.vn +namdinh.vn +nghean.vn +ninhbinh.vn +ninhthuan.vn +phutho.vn +phuyen.vn +quangbinh.vn +quangnam.vn +quangngai.vn +quangninh.vn +quangtri.vn +soctrang.vn +sonla.vn +tayninh.vn +thaibinh.vn +thainguyen.vn +thanhhoa.vn +thanhphohochiminh.vn +thuathienhue.vn +tiengiang.vn +travinh.vn +tuyenquang.vn +vinhlong.vn +vinhphuc.vn +yenbai.vn + +// vu : https://www.iana.org/domains/root/db/vu.html +// http://www.vunic.vu/ +vu +com.vu +edu.vu +net.vu +org.vu + +// wf : https://www.afnic.fr/wp-media/uploads/2022/12/afnic-naming-policy-2023-01-01.pdf +wf + +// ws : https://www.iana.org/domains/root/db/ws.html +// http://samoanic.ws/index.dhtml +ws +com.ws +edu.ws +gov.ws +net.ws +org.ws + +// yt : https://www.afnic.fr/wp-media/uploads/2022/12/afnic-naming-policy-2023-01-01.pdf +yt + +// IDN ccTLDs +// When submitting patches, please maintain a sort by ISO 3166 ccTLD, then +// U-label, and follow this format: +// // A-Label ("", [, variant info]) : +// // [sponsoring org] +// U-Label + +// xn--mgbaam7a8h ("Emerat", Arabic) : AE +// http://nic.ae/english/arabicdomain/rules.jsp +امارات + +// xn--y9a3aq ("hye", Armenian) : AM +// ISOC AM (operated by .am Registry) +հայ + +// xn--54b7fta0cc ("Bangla", Bangla) : BD +বাংলা + +// xn--90ae ("bg", Bulgarian) : BG +бг + +// xn--mgbcpq6gpa1a ("albahrain", Arabic) : BH +البحرين + +// xn--90ais ("bel", Belarusian/Russian Cyrillic) : BY +// Operated by .by registry +бел + +// xn--fiqs8s ("Zhongguo/China", Chinese, Simplified) : CN +// CNNIC +// https://www.cnnic.cn/11/192/index.html +中国 + +// xn--fiqz9s ("Zhongguo/China", Chinese, Traditional) : CN +// CNNIC +// https://www.cnnic.com.cn/AU/MediaC/Announcement/201609/t20160905_54470.htm +中國 + +// xn--lgbbat1ad8j ("Algeria/Al Jazair", Arabic) : DZ +الجزائر + +// xn--wgbh1c ("Egypt/Masr", Arabic) : EG +// http://www.dotmasr.eg/ +مصر + +// xn--e1a4c ("eu", Cyrillic) : EU +// https://eurid.eu +ею + +// xn--qxa6a ("eu", Greek) : EU +// https://eurid.eu +ευ + +// xn--mgbah1a3hjkrd ("Mauritania", Arabic) : MR +موريتانيا + +// xn--node ("ge", Georgian Mkhedruli) : GE +გე + +// xn--qxam ("el", Greek) : GR +// Hellenic Ministry of Infrastructure, Transport, and Networks +ελ + +// xn--j6w193g ("Hong Kong", Chinese) : HK +// https://www.hkirc.hk +// Submitted by registry +// https://www.hkirc.hk/content.jsp?id=30#!/34 +香港 +個人.香港 +公司.香港 +政府.香港 +教育.香港 +組織.香港 +網絡.香港 + +// xn--2scrj9c ("Bharat", Kannada) : IN +// India +ಭಾರತ + +// xn--3hcrj9c ("Bharat", Oriya) : IN +// India +ଭାରତ + +// xn--45br5cyl ("Bharatam", Assamese) : IN +// India +ভাৰত + +// xn--h2breg3eve ("Bharatam", Sanskrit) : IN +// India +भारतम् + +// xn--h2brj9c8c ("Bharot", Santali) : IN +// India +भारोत + +// xn--mgbgu82a ("Bharat", Sindhi) : IN +// India +ڀارت + +// xn--rvc1e0am3e ("Bharatam", Malayalam) : IN +// India +ഭാരതം + +// xn--h2brj9c ("Bharat", Devanagari) : IN +// India +भारत + +// xn--mgbbh1a ("Bharat", Kashmiri) : IN +// India +بارت + +// xn--mgbbh1a71e ("Bharat", Arabic) : IN +// India +بھارت + +// xn--fpcrj9c3d ("Bharat", Telugu) : IN +// India +భారత్ + +// xn--gecrj9c ("Bharat", Gujarati) : IN +// India +ભારત + +// xn--s9brj9c ("Bharat", Gurmukhi) : IN +// India +ਭਾਰਤ + +// xn--45brj9c ("Bharat", Bengali) : IN +// India +ভারত + +// xn--xkc2dl3a5ee0h ("India", Tamil) : IN +// India +இந்தியா + +// xn--mgba3a4f16a ("Iran", Persian) : IR +ایران + +// xn--mgba3a4fra ("Iran", Arabic) : IR +ايران + +// xn--mgbtx2b ("Iraq", Arabic) : IQ +// Communications and Media Commission +عراق + +// xn--mgbayh7gpa ("al-Ordon", Arabic) : JO +// National Information Technology Center (NITC) +// Royal Scientific Society, Al-Jubeiha +الاردن + +// xn--3e0b707e ("Republic of Korea", Hangul) : KR +한국 + +// xn--80ao21a ("Kaz", Kazakh) : KZ +қаз + +// xn--q7ce6a ("Lao", Lao) : LA +ລາວ + +// xn--fzc2c9e2c ("Lanka", Sinhalese-Sinhala) : LK +// https://nic.lk +ලංකා + +// xn--xkc2al3hye2a ("Ilangai", Tamil) : LK +// https://nic.lk +இலங்கை + +// xn--mgbc0a9azcg ("Morocco/al-Maghrib", Arabic) : MA +المغرب + +// xn--d1alf ("mkd", Macedonian) : MK +// MARnet +мкд + +// xn--l1acc ("mon", Mongolian) : MN +мон + +// xn--mix891f ("Macao", Chinese, Traditional) : MO +// MONIC / HNET Asia (Registry Operator for .mo) +澳門 + +// xn--mix082f ("Macao", Chinese, Simplified) : MO +澳门 + +// xn--mgbx4cd0ab ("Malaysia", Malay) : MY +مليسيا + +// xn--mgb9awbf ("Oman", Arabic) : OM +عمان + +// xn--mgbai9azgqp6j ("Pakistan", Urdu/Arabic) : PK +پاکستان + +// xn--mgbai9a5eva00b ("Pakistan", Urdu/Arabic, variant) : PK +پاكستان + +// xn--ygbi2ammx ("Falasteen", Arabic) : PS +// The Palestinian National Internet Naming Authority (PNINA) +// http://www.pnina.ps +فلسطين + +// xn--90a3ac ("srb", Cyrillic) : RS +// https://www.rnids.rs/en/domains/national-domains +срб +ак.срб +обр.срб +од.срб +орг.срб +пр.срб +упр.срб + +// xn--p1ai ("rf", Russian-Cyrillic) : RU +// https://cctld.ru/files/pdf/docs/en/rules_ru-rf.pdf +// Submitted by George Georgievsky +рф + +// xn--wgbl6a ("Qatar", Arabic) : QA +// http://www.ict.gov.qa/ +قطر + +// xn--mgberp4a5d4ar ("AlSaudiah", Arabic) : SA +// http://www.nic.net.sa/ +السعودية + +// xn--mgberp4a5d4a87g ("AlSaudiah", Arabic, variant): SA +السعودیة + +// xn--mgbqly7c0a67fbc ("AlSaudiah", Arabic, variant) : SA +السعودیۃ + +// xn--mgbqly7cvafr ("AlSaudiah", Arabic, variant) : SA +السعوديه + +// xn--mgbpl2fh ("sudan", Arabic) : SD +// Operated by .sd registry +سودان + +// xn--yfro4i67o Singapore ("Singapore", Chinese) : SG +新加坡 + +// xn--clchc0ea0b2g2a9gcd ("Singapore", Tamil) : SG +சிங்கப்பூர் + +// xn--ogbpf8fl ("Syria", Arabic) : SY +سورية + +// xn--mgbtf8fl ("Syria", Arabic, variant) : SY +سوريا + +// xn--o3cw4h ("Thai", Thai) : TH +// http://www.thnic.co.th +ไทย +ทหาร.ไทย +ธุรกิจ.ไทย +เน็ต.ไทย +รัฐบาล.ไทย +ศึกษา.ไทย +องค์กร.ไทย + +// xn--pgbs0dh ("Tunisia", Arabic) : TN +// http://nic.tn +تونس + +// xn--kpry57d ("Taiwan", Chinese, Traditional) : TW +// https://twnic.tw/dnservice_catag.php +台灣 + +// xn--kprw13d ("Taiwan", Chinese, Simplified) : TW +// http://www.twnic.net/english/dn/dn_07a.htm +台湾 + +// xn--nnx388a ("Taiwan", Chinese, variant) : TW +臺灣 + +// xn--j1amh ("ukr", Cyrillic) : UA +укр + +// xn--mgb2ddes ("AlYemen", Arabic) : YE +اليمن + +// xxx : http://icmregistry.com +xxx + +// ye : http://www.y.net.ye/services/domain_name.htm +ye +com.ye +edu.ye +gov.ye +mil.ye +net.ye +org.ye + +// za : https://www.iana.org/domains/root/db/za.html +ac.za +agric.za +alt.za +co.za +edu.za +gov.za +grondar.za +law.za +mil.za +net.za +ngo.za +nic.za +nis.za +nom.za +org.za +school.za +tm.za +web.za + +// zm : https://zicta.zm/ +// Submitted by registry +zm +ac.zm +biz.zm +co.zm +com.zm +edu.zm +gov.zm +info.zm +mil.zm +net.zm +org.zm +sch.zm + +// zw : https://www.potraz.gov.zw/ +// Confirmed by registry 2017-01-25 +zw +ac.zw +co.zw +gov.zw +mil.zw +org.zw + +// newGTLDs + +// List of new gTLDs imported from https://www.icann.org/resources/registries/gtlds/v2/gtlds.json on 2025-12-06T15:17:27Z +// This list is auto-generated, don't edit it manually. +// aaa : American Automobile Association, Inc. +// https://www.iana.org/domains/root/db/aaa.html +aaa + +// aarp : AARP +// https://www.iana.org/domains/root/db/aarp.html +aarp + +// abb : ABB Ltd +// https://www.iana.org/domains/root/db/abb.html +abb + +// abbott : Abbott Laboratories, Inc. +// https://www.iana.org/domains/root/db/abbott.html +abbott + +// abbvie : AbbVie Inc. +// https://www.iana.org/domains/root/db/abbvie.html +abbvie + +// abc : Disney Enterprises, Inc. +// https://www.iana.org/domains/root/db/abc.html +abc + +// able : Able Inc. +// https://www.iana.org/domains/root/db/able.html +able + +// abogado : Registry Services, LLC +// https://www.iana.org/domains/root/db/abogado.html +abogado + +// abudhabi : Abu Dhabi Systems and Information Centre +// https://www.iana.org/domains/root/db/abudhabi.html +abudhabi + +// academy : Binky Moon, LLC +// https://www.iana.org/domains/root/db/academy.html +academy + +// accenture : Accenture plc +// https://www.iana.org/domains/root/db/accenture.html +accenture + +// accountant : dot Accountant Limited +// https://www.iana.org/domains/root/db/accountant.html +accountant + +// accountants : Binky Moon, LLC +// https://www.iana.org/domains/root/db/accountants.html +accountants + +// aco : ACO Severin Ahlmann GmbH & Co. KG +// https://www.iana.org/domains/root/db/aco.html +aco + +// actor : Dog Beach, LLC +// https://www.iana.org/domains/root/db/actor.html +actor + +// ads : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/ads.html +ads + +// adult : ICM Registry AD LLC +// https://www.iana.org/domains/root/db/adult.html +adult + +// aeg : Aktiebolaget Electrolux +// https://www.iana.org/domains/root/db/aeg.html +aeg + +// aetna : Aetna Life Insurance Company +// https://www.iana.org/domains/root/db/aetna.html +aetna + +// afl : Australian Football League +// https://www.iana.org/domains/root/db/afl.html +afl + +// africa : ZA Central Registry NPC trading as Registry.Africa +// https://www.iana.org/domains/root/db/africa.html +africa + +// agakhan : Fondation Aga Khan (Aga Khan Foundation) +// https://www.iana.org/domains/root/db/agakhan.html +agakhan + +// agency : Binky Moon, LLC +// https://www.iana.org/domains/root/db/agency.html +agency + +// aig : American International Group, Inc. +// https://www.iana.org/domains/root/db/aig.html +aig + +// airbus : Airbus S.A.S. +// https://www.iana.org/domains/root/db/airbus.html +airbus + +// airforce : Dog Beach, LLC +// https://www.iana.org/domains/root/db/airforce.html +airforce + +// airtel : Bharti Airtel Limited +// https://www.iana.org/domains/root/db/airtel.html +airtel + +// akdn : Fondation Aga Khan (Aga Khan Foundation) +// https://www.iana.org/domains/root/db/akdn.html +akdn + +// alibaba : Alibaba Group Holding Limited +// https://www.iana.org/domains/root/db/alibaba.html +alibaba + +// alipay : Alibaba Group Holding Limited +// https://www.iana.org/domains/root/db/alipay.html +alipay + +// allfinanz : Allfinanz Deutsche Vermögensberatung Aktiengesellschaft +// https://www.iana.org/domains/root/db/allfinanz.html +allfinanz + +// allstate : Allstate Fire and Casualty Insurance Company +// https://www.iana.org/domains/root/db/allstate.html +allstate + +// ally : Ally Financial Inc. +// https://www.iana.org/domains/root/db/ally.html +ally + +// alsace : Region Grand Est +// https://www.iana.org/domains/root/db/alsace.html +alsace + +// alstom : ALSTOM +// https://www.iana.org/domains/root/db/alstom.html +alstom + +// amazon : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/amazon.html +amazon + +// americanexpress : American Express Travel Related Services Company, Inc. +// https://www.iana.org/domains/root/db/americanexpress.html +americanexpress + +// americanfamily : AmFam, Inc. +// https://www.iana.org/domains/root/db/americanfamily.html +americanfamily + +// amex : American Express Travel Related Services Company, Inc. +// https://www.iana.org/domains/root/db/amex.html +amex + +// amfam : AmFam, Inc. +// https://www.iana.org/domains/root/db/amfam.html +amfam + +// amica : Amica Mutual Insurance Company +// https://www.iana.org/domains/root/db/amica.html +amica + +// amsterdam : Gemeente Amsterdam +// https://www.iana.org/domains/root/db/amsterdam.html +amsterdam + +// analytics : Campus IP LLC +// https://www.iana.org/domains/root/db/analytics.html +analytics + +// android : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/android.html +android + +// anquan : Beijing Qihu Keji Co., Ltd. +// https://www.iana.org/domains/root/db/anquan.html +anquan + +// anz : Australia and New Zealand Banking Group Limited +// https://www.iana.org/domains/root/db/anz.html +anz + +// aol : Yahoo Inc. +// https://www.iana.org/domains/root/db/aol.html +aol + +// apartments : Binky Moon, LLC +// https://www.iana.org/domains/root/db/apartments.html +apartments + +// app : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/app.html +app + +// apple : Apple Inc. +// https://www.iana.org/domains/root/db/apple.html +apple + +// aquarelle : Aquarelle.com +// https://www.iana.org/domains/root/db/aquarelle.html +aquarelle + +// arab : League of Arab States +// https://www.iana.org/domains/root/db/arab.html +arab + +// aramco : Aramco Services Company +// https://www.iana.org/domains/root/db/aramco.html +aramco + +// archi : Identity Digital Domains Limited +// https://www.iana.org/domains/root/db/archi.html +archi + +// army : Dog Beach, LLC +// https://www.iana.org/domains/root/db/army.html +army + +// art : UK Creative Ideas Limited +// https://www.iana.org/domains/root/db/art.html +art + +// arte : Association Relative à la Télévision Européenne G.E.I.E. +// https://www.iana.org/domains/root/db/arte.html +arte + +// asda : Asda Stores Limited +// https://www.iana.org/domains/root/db/asda.html +asda + +// associates : Binky Moon, LLC +// https://www.iana.org/domains/root/db/associates.html +associates + +// athleta : The Gap, Inc. +// https://www.iana.org/domains/root/db/athleta.html +athleta + +// attorney : Dog Beach, LLC +// https://www.iana.org/domains/root/db/attorney.html +attorney + +// auction : Dog Beach, LLC +// https://www.iana.org/domains/root/db/auction.html +auction + +// audi : AUDI Aktiengesellschaft +// https://www.iana.org/domains/root/db/audi.html +audi + +// audible : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/audible.html +audible + +// audio : XYZ.COM LLC +// https://www.iana.org/domains/root/db/audio.html +audio + +// auspost : Australian Postal Corporation +// https://www.iana.org/domains/root/db/auspost.html +auspost + +// author : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/author.html +author + +// auto : XYZ.COM LLC +// https://www.iana.org/domains/root/db/auto.html +auto + +// autos : XYZ.COM LLC +// https://www.iana.org/domains/root/db/autos.html +autos + +// aws : AWS Registry LLC +// https://www.iana.org/domains/root/db/aws.html +aws + +// axa : AXA Group Operations SAS +// https://www.iana.org/domains/root/db/axa.html +axa + +// azure : Microsoft Corporation +// https://www.iana.org/domains/root/db/azure.html +azure + +// baby : XYZ.COM LLC +// https://www.iana.org/domains/root/db/baby.html +baby + +// baidu : Baidu, Inc. +// https://www.iana.org/domains/root/db/baidu.html +baidu + +// banamex : Citigroup Inc. +// https://www.iana.org/domains/root/db/banamex.html +banamex + +// band : Dog Beach, LLC +// https://www.iana.org/domains/root/db/band.html +band + +// bank : fTLD Registry Services LLC +// https://www.iana.org/domains/root/db/bank.html +bank + +// bar : Punto 2012 Sociedad Anonima Promotora de Inversion de Capital Variable +// https://www.iana.org/domains/root/db/bar.html +bar + +// barcelona : Municipi de Barcelona +// https://www.iana.org/domains/root/db/barcelona.html +barcelona + +// barclaycard : Barclays Bank PLC +// https://www.iana.org/domains/root/db/barclaycard.html +barclaycard + +// barclays : Barclays Bank PLC +// https://www.iana.org/domains/root/db/barclays.html +barclays + +// barefoot : Gallo Vineyards, Inc. +// https://www.iana.org/domains/root/db/barefoot.html +barefoot + +// bargains : Binky Moon, LLC +// https://www.iana.org/domains/root/db/bargains.html +bargains + +// baseball : MLB Advanced Media DH, LLC +// https://www.iana.org/domains/root/db/baseball.html +baseball + +// basketball : Fédération Internationale de Basketball (FIBA) +// https://www.iana.org/domains/root/db/basketball.html +basketball + +// bauhaus : Werkhaus GmbH +// https://www.iana.org/domains/root/db/bauhaus.html +bauhaus + +// bayern : Bayern Connect GmbH +// https://www.iana.org/domains/root/db/bayern.html +bayern + +// bbc : British Broadcasting Corporation +// https://www.iana.org/domains/root/db/bbc.html +bbc + +// bbt : BB&T Corporation +// https://www.iana.org/domains/root/db/bbt.html +bbt + +// bbva : BANCO BILBAO VIZCAYA ARGENTARIA, S.A. +// https://www.iana.org/domains/root/db/bbva.html +bbva + +// bcg : The Boston Consulting Group, Inc. +// https://www.iana.org/domains/root/db/bcg.html +bcg + +// bcn : Municipi de Barcelona +// https://www.iana.org/domains/root/db/bcn.html +bcn + +// beats : Beats Electronics, LLC +// https://www.iana.org/domains/root/db/beats.html +beats + +// beauty : XYZ.COM LLC +// https://www.iana.org/domains/root/db/beauty.html +beauty + +// beer : Registry Services, LLC +// https://www.iana.org/domains/root/db/beer.html +beer + +// berlin : dotBERLIN GmbH & Co. KG +// https://www.iana.org/domains/root/db/berlin.html +berlin + +// best : BestTLD Pty Ltd +// https://www.iana.org/domains/root/db/best.html +best + +// bestbuy : BBY Solutions, Inc. +// https://www.iana.org/domains/root/db/bestbuy.html +bestbuy + +// bet : Identity Digital Domains Limited +// https://www.iana.org/domains/root/db/bet.html +bet + +// bharti : Bharti Enterprises (Holding) Private Limited +// https://www.iana.org/domains/root/db/bharti.html +bharti + +// bible : American Bible Society +// https://www.iana.org/domains/root/db/bible.html +bible + +// bid : dot Bid Limited +// https://www.iana.org/domains/root/db/bid.html +bid + +// bike : Binky Moon, LLC +// https://www.iana.org/domains/root/db/bike.html +bike + +// bing : Microsoft Corporation +// https://www.iana.org/domains/root/db/bing.html +bing + +// bingo : Binky Moon, LLC +// https://www.iana.org/domains/root/db/bingo.html +bingo + +// bio : Identity Digital Domains Limited +// https://www.iana.org/domains/root/db/bio.html +bio + +// black : Identity Digital Domains Limited +// https://www.iana.org/domains/root/db/black.html +black + +// blackfriday : Registry Services, LLC +// https://www.iana.org/domains/root/db/blackfriday.html +blackfriday + +// blockbuster : Dish DBS Corporation +// https://www.iana.org/domains/root/db/blockbuster.html +blockbuster + +// blog : Knock Knock WHOIS There, LLC +// https://www.iana.org/domains/root/db/blog.html +blog + +// bloomberg : Bloomberg IP Holdings LLC +// https://www.iana.org/domains/root/db/bloomberg.html +bloomberg + +// blue : Identity Digital Domains Limited +// https://www.iana.org/domains/root/db/blue.html +blue + +// bms : Bristol-Myers Squibb Company +// https://www.iana.org/domains/root/db/bms.html +bms + +// bmw : Bayerische Motoren Werke Aktiengesellschaft +// https://www.iana.org/domains/root/db/bmw.html +bmw + +// bnpparibas : BNP Paribas +// https://www.iana.org/domains/root/db/bnpparibas.html +bnpparibas + +// boats : XYZ.COM LLC +// https://www.iana.org/domains/root/db/boats.html +boats + +// boehringer : Boehringer Ingelheim International GmbH +// https://www.iana.org/domains/root/db/boehringer.html +boehringer + +// bofa : Bank of America Corporation +// https://www.iana.org/domains/root/db/bofa.html +bofa + +// bom : Núcleo de Informação e Coordenação do Ponto BR - NIC.br +// https://www.iana.org/domains/root/db/bom.html +bom + +// bond : ShortDot SA +// https://www.iana.org/domains/root/db/bond.html +bond + +// boo : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/boo.html +boo + +// book : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/book.html +book + +// booking : Booking.com B.V. +// https://www.iana.org/domains/root/db/booking.html +booking + +// bosch : Robert Bosch GMBH +// https://www.iana.org/domains/root/db/bosch.html +bosch + +// bostik : Bostik SA +// https://www.iana.org/domains/root/db/bostik.html +bostik + +// boston : Registry Services, LLC +// https://www.iana.org/domains/root/db/boston.html +boston + +// bot : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/bot.html +bot + +// boutique : Binky Moon, LLC +// https://www.iana.org/domains/root/db/boutique.html +boutique + +// box : Intercap Registry Inc. +// https://www.iana.org/domains/root/db/box.html +box + +// bradesco : Banco Bradesco S.A. +// https://www.iana.org/domains/root/db/bradesco.html +bradesco + +// bridgestone : Bridgestone Corporation +// https://www.iana.org/domains/root/db/bridgestone.html +bridgestone + +// broadway : Celebrate Broadway, Inc. +// https://www.iana.org/domains/root/db/broadway.html +broadway + +// broker : Dog Beach, LLC +// https://www.iana.org/domains/root/db/broker.html +broker + +// brother : Brother Industries, Ltd. +// https://www.iana.org/domains/root/db/brother.html +brother + +// brussels : DNS.be vzw +// https://www.iana.org/domains/root/db/brussels.html +brussels + +// build : Plan Bee LLC +// https://www.iana.org/domains/root/db/build.html +build + +// builders : Binky Moon, LLC +// https://www.iana.org/domains/root/db/builders.html +builders + +// business : Binky Moon, LLC +// https://www.iana.org/domains/root/db/business.html +business + +// buy : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/buy.html +buy + +// buzz : DOTSTRATEGY CO. +// https://www.iana.org/domains/root/db/buzz.html +buzz + +// bzh : Association www.bzh +// https://www.iana.org/domains/root/db/bzh.html +bzh + +// cab : Binky Moon, LLC +// https://www.iana.org/domains/root/db/cab.html +cab + +// cafe : Binky Moon, LLC +// https://www.iana.org/domains/root/db/cafe.html +cafe + +// cal : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/cal.html +cal + +// call : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/call.html +call + +// calvinklein : PVH gTLD Holdings LLC +// https://www.iana.org/domains/root/db/calvinklein.html +calvinklein + +// cam : Cam Connecting SARL +// https://www.iana.org/domains/root/db/cam.html +cam + +// camera : Binky Moon, LLC +// https://www.iana.org/domains/root/db/camera.html +camera + +// camp : Binky Moon, LLC +// https://www.iana.org/domains/root/db/camp.html +camp + +// canon : Canon Inc. +// https://www.iana.org/domains/root/db/canon.html +canon + +// capetown : ZA Central Registry NPC trading as ZA Central Registry +// https://www.iana.org/domains/root/db/capetown.html +capetown + +// capital : Binky Moon, LLC +// https://www.iana.org/domains/root/db/capital.html +capital + +// capitalone : Capital One Financial Corporation +// https://www.iana.org/domains/root/db/capitalone.html +capitalone + +// car : XYZ.COM LLC +// https://www.iana.org/domains/root/db/car.html +car + +// caravan : Caravan International, Inc. +// https://www.iana.org/domains/root/db/caravan.html +caravan + +// cards : Binky Moon, LLC +// https://www.iana.org/domains/root/db/cards.html +cards + +// care : Binky Moon, LLC +// https://www.iana.org/domains/root/db/care.html +care + +// career : dotCareer LLC +// https://www.iana.org/domains/root/db/career.html +career + +// careers : Binky Moon, LLC +// https://www.iana.org/domains/root/db/careers.html +careers + +// cars : XYZ.COM LLC +// https://www.iana.org/domains/root/db/cars.html +cars + +// casa : Registry Services, LLC +// https://www.iana.org/domains/root/db/casa.html +casa + +// case : Digity, LLC +// https://www.iana.org/domains/root/db/case.html +case + +// cash : Binky Moon, LLC +// https://www.iana.org/domains/root/db/cash.html +cash + +// casino : Binky Moon, LLC +// https://www.iana.org/domains/root/db/casino.html +casino + +// catering : Binky Moon, LLC +// https://www.iana.org/domains/root/db/catering.html +catering + +// catholic : Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication) +// https://www.iana.org/domains/root/db/catholic.html +catholic + +// cba : COMMONWEALTH BANK OF AUSTRALIA +// https://www.iana.org/domains/root/db/cba.html +cba + +// cbn : The Christian Broadcasting Network, Inc. +// https://www.iana.org/domains/root/db/cbn.html +cbn + +// cbre : CBRE, Inc. +// https://www.iana.org/domains/root/db/cbre.html +cbre + +// center : Binky Moon, LLC +// https://www.iana.org/domains/root/db/center.html +center + +// ceo : XYZ.COM LLC +// https://www.iana.org/domains/root/db/ceo.html +ceo + +// cern : European Organization for Nuclear Research ("CERN") +// https://www.iana.org/domains/root/db/cern.html +cern + +// cfa : CFA Institute +// https://www.iana.org/domains/root/db/cfa.html +cfa + +// cfd : ShortDot SA +// https://www.iana.org/domains/root/db/cfd.html +cfd + +// chanel : Chanel International B.V. +// https://www.iana.org/domains/root/db/chanel.html +chanel + +// channel : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/channel.html +channel + +// charity : Public Interest Registry +// https://www.iana.org/domains/root/db/charity.html +charity + +// chase : JPMorgan Chase Bank, National Association +// https://www.iana.org/domains/root/db/chase.html +chase + +// chat : Binky Moon, LLC +// https://www.iana.org/domains/root/db/chat.html +chat + +// cheap : Binky Moon, LLC +// https://www.iana.org/domains/root/db/cheap.html +cheap + +// chintai : CHINTAI Corporation +// https://www.iana.org/domains/root/db/chintai.html +chintai + +// christmas : XYZ.COM LLC +// https://www.iana.org/domains/root/db/christmas.html +christmas + +// chrome : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/chrome.html +chrome + +// church : Binky Moon, LLC +// https://www.iana.org/domains/root/db/church.html +church + +// cipriani : Hotel Cipriani Srl +// https://www.iana.org/domains/root/db/cipriani.html +cipriani + +// circle : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/circle.html +circle + +// cisco : Cisco Technology, Inc. +// https://www.iana.org/domains/root/db/cisco.html +cisco + +// citadel : Citadel Domain LLC +// https://www.iana.org/domains/root/db/citadel.html +citadel + +// citi : Citigroup Inc. +// https://www.iana.org/domains/root/db/citi.html +citi + +// citic : CITIC Group Corporation +// https://www.iana.org/domains/root/db/citic.html +citic + +// city : Binky Moon, LLC +// https://www.iana.org/domains/root/db/city.html +city + +// claims : Binky Moon, LLC +// https://www.iana.org/domains/root/db/claims.html +claims + +// cleaning : Binky Moon, LLC +// https://www.iana.org/domains/root/db/cleaning.html +cleaning + +// click : Waterford Limited +// https://www.iana.org/domains/root/db/click.html +click + +// clinic : Binky Moon, LLC +// https://www.iana.org/domains/root/db/clinic.html +clinic + +// clinique : The Estée Lauder Companies Inc. +// https://www.iana.org/domains/root/db/clinique.html +clinique + +// clothing : Binky Moon, LLC +// https://www.iana.org/domains/root/db/clothing.html +clothing + +// cloud : Aruba PEC S.p.A. +// https://www.iana.org/domains/root/db/cloud.html +cloud + +// club : Registry Services, LLC +// https://www.iana.org/domains/root/db/club.html +club + +// clubmed : Club Méditerranée S.A. +// https://www.iana.org/domains/root/db/clubmed.html +clubmed + +// coach : Binky Moon, LLC +// https://www.iana.org/domains/root/db/coach.html +coach + +// codes : Binky Moon, LLC +// https://www.iana.org/domains/root/db/codes.html +codes + +// coffee : Binky Moon, LLC +// https://www.iana.org/domains/root/db/coffee.html +coffee + +// college : XYZ.COM LLC +// https://www.iana.org/domains/root/db/college.html +college + +// cologne : dotKoeln GmbH +// https://www.iana.org/domains/root/db/cologne.html +cologne + +// commbank : COMMONWEALTH BANK OF AUSTRALIA +// https://www.iana.org/domains/root/db/commbank.html +commbank + +// community : Binky Moon, LLC +// https://www.iana.org/domains/root/db/community.html +community + +// company : Binky Moon, LLC +// https://www.iana.org/domains/root/db/company.html +company + +// compare : Registry Services, LLC +// https://www.iana.org/domains/root/db/compare.html +compare + +// computer : Binky Moon, LLC +// https://www.iana.org/domains/root/db/computer.html +computer + +// comsec : VeriSign, Inc. +// https://www.iana.org/domains/root/db/comsec.html +comsec + +// condos : Binky Moon, LLC +// https://www.iana.org/domains/root/db/condos.html +condos + +// construction : Binky Moon, LLC +// https://www.iana.org/domains/root/db/construction.html +construction + +// consulting : Dog Beach, LLC +// https://www.iana.org/domains/root/db/consulting.html +consulting + +// contact : Dog Beach, LLC +// https://www.iana.org/domains/root/db/contact.html +contact + +// contractors : Binky Moon, LLC +// https://www.iana.org/domains/root/db/contractors.html +contractors + +// cooking : Registry Services, LLC +// https://www.iana.org/domains/root/db/cooking.html +cooking + +// cool : Binky Moon, LLC +// https://www.iana.org/domains/root/db/cool.html +cool + +// corsica : Collectivité de Corse +// https://www.iana.org/domains/root/db/corsica.html +corsica + +// country : Internet Naming Company LLC +// https://www.iana.org/domains/root/db/country.html +country + +// coupon : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/coupon.html +coupon + +// coupons : Binky Moon, LLC +// https://www.iana.org/domains/root/db/coupons.html +coupons + +// courses : Registry Services, LLC +// https://www.iana.org/domains/root/db/courses.html +courses + +// cpa : American Institute of Certified Public Accountants +// https://www.iana.org/domains/root/db/cpa.html +cpa + +// credit : Binky Moon, LLC +// https://www.iana.org/domains/root/db/credit.html +credit + +// creditcard : Binky Moon, LLC +// https://www.iana.org/domains/root/db/creditcard.html +creditcard + +// creditunion : DotCooperation LLC +// https://www.iana.org/domains/root/db/creditunion.html +creditunion + +// cricket : dot Cricket Limited +// https://www.iana.org/domains/root/db/cricket.html +cricket + +// crown : Crown Equipment Corporation +// https://www.iana.org/domains/root/db/crown.html +crown + +// crs : Federated Co-operatives Limited +// https://www.iana.org/domains/root/db/crs.html +crs + +// cruise : Viking River Cruises (Bermuda) Ltd. +// https://www.iana.org/domains/root/db/cruise.html +cruise + +// cruises : Binky Moon, LLC +// https://www.iana.org/domains/root/db/cruises.html +cruises + +// cuisinella : SCHMIDT GROUPE S.A.S. +// https://www.iana.org/domains/root/db/cuisinella.html +cuisinella + +// cymru : Nominet UK +// https://www.iana.org/domains/root/db/cymru.html +cymru + +// cyou : ShortDot SA +// https://www.iana.org/domains/root/db/cyou.html +cyou + +// dad : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/dad.html +dad + +// dance : Dog Beach, LLC +// https://www.iana.org/domains/root/db/dance.html +dance + +// data : Dish DBS Corporation +// https://www.iana.org/domains/root/db/data.html +data + +// date : dot Date Limited +// https://www.iana.org/domains/root/db/date.html +date + +// dating : Binky Moon, LLC +// https://www.iana.org/domains/root/db/dating.html +dating + +// datsun : NISSAN MOTOR CO., LTD. +// https://www.iana.org/domains/root/db/datsun.html +datsun + +// day : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/day.html +day + +// dclk : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/dclk.html +dclk + +// dds : Registry Services, LLC +// https://www.iana.org/domains/root/db/dds.html +dds + +// deal : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/deal.html +deal + +// dealer : Intercap Registry Inc. +// https://www.iana.org/domains/root/db/dealer.html +dealer + +// deals : Binky Moon, LLC +// https://www.iana.org/domains/root/db/deals.html +deals + +// degree : Dog Beach, LLC +// https://www.iana.org/domains/root/db/degree.html +degree + +// delivery : Binky Moon, LLC +// https://www.iana.org/domains/root/db/delivery.html +delivery + +// dell : Dell Inc. +// https://www.iana.org/domains/root/db/dell.html +dell + +// deloitte : Deloitte Touche Tohmatsu +// https://www.iana.org/domains/root/db/deloitte.html +deloitte + +// delta : Delta Air Lines, Inc. +// https://www.iana.org/domains/root/db/delta.html +delta + +// democrat : Dog Beach, LLC +// https://www.iana.org/domains/root/db/democrat.html +democrat + +// dental : Binky Moon, LLC +// https://www.iana.org/domains/root/db/dental.html +dental + +// dentist : Dog Beach, LLC +// https://www.iana.org/domains/root/db/dentist.html +dentist + +// desi +// https://www.iana.org/domains/root/db/desi.html +desi + +// design : Registry Services, LLC +// https://www.iana.org/domains/root/db/design.html +design + +// dev : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/dev.html +dev + +// dhl : Deutsche Post AG +// https://www.iana.org/domains/root/db/dhl.html +dhl + +// diamonds : Binky Moon, LLC +// https://www.iana.org/domains/root/db/diamonds.html +diamonds + +// diet : XYZ.COM LLC +// https://www.iana.org/domains/root/db/diet.html +diet + +// digital : Binky Moon, LLC +// https://www.iana.org/domains/root/db/digital.html +digital + +// direct : Binky Moon, LLC +// https://www.iana.org/domains/root/db/direct.html +direct + +// directory : Binky Moon, LLC +// https://www.iana.org/domains/root/db/directory.html +directory + +// discount : Binky Moon, LLC +// https://www.iana.org/domains/root/db/discount.html +discount + +// discover : Discover Financial Services +// https://www.iana.org/domains/root/db/discover.html +discover + +// dish : Dish DBS Corporation +// https://www.iana.org/domains/root/db/dish.html +dish + +// diy : Internet Naming Company LLC +// https://www.iana.org/domains/root/db/diy.html +diy + +// dnp : Dai Nippon Printing Co., Ltd. +// https://www.iana.org/domains/root/db/dnp.html +dnp + +// docs : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/docs.html +docs + +// doctor : Binky Moon, LLC +// https://www.iana.org/domains/root/db/doctor.html +doctor + +// dog : Binky Moon, LLC +// https://www.iana.org/domains/root/db/dog.html +dog + +// domains : Binky Moon, LLC +// https://www.iana.org/domains/root/db/domains.html +domains + +// dot : Dish DBS Corporation +// https://www.iana.org/domains/root/db/dot.html +dot + +// download : dot Support Limited +// https://www.iana.org/domains/root/db/download.html +download + +// drive : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/drive.html +drive + +// dtv : Dish DBS Corporation +// https://www.iana.org/domains/root/db/dtv.html +dtv + +// dubai : Dubai Smart Government Department +// https://www.iana.org/domains/root/db/dubai.html +dubai + +// dupont : DuPont Specialty Products USA, LLC +// https://www.iana.org/domains/root/db/dupont.html +dupont + +// durban : ZA Central Registry NPC trading as ZA Central Registry +// https://www.iana.org/domains/root/db/durban.html +durban + +// dvag : Deutsche Vermögensberatung Aktiengesellschaft DVAG +// https://www.iana.org/domains/root/db/dvag.html +dvag + +// dvr : DISH Technologies L.L.C. +// https://www.iana.org/domains/root/db/dvr.html +dvr + +// earth : Interlink Systems Innovation Institute K.K. +// https://www.iana.org/domains/root/db/earth.html +earth + +// eat : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/eat.html +eat + +// eco : Big Room Inc. +// https://www.iana.org/domains/root/db/eco.html +eco + +// edeka : EDEKA Verband kaufmännischer Genossenschaften e.V. +// https://www.iana.org/domains/root/db/edeka.html +edeka + +// education : Binky Moon, LLC +// https://www.iana.org/domains/root/db/education.html +education + +// email : Binky Moon, LLC +// https://www.iana.org/domains/root/db/email.html +email + +// emerck : Merck KGaA +// https://www.iana.org/domains/root/db/emerck.html +emerck + +// energy : Binky Moon, LLC +// https://www.iana.org/domains/root/db/energy.html +energy + +// engineer : Dog Beach, LLC +// https://www.iana.org/domains/root/db/engineer.html +engineer + +// engineering : Binky Moon, LLC +// https://www.iana.org/domains/root/db/engineering.html +engineering + +// enterprises : Binky Moon, LLC +// https://www.iana.org/domains/root/db/enterprises.html +enterprises + +// epson : Seiko Epson Corporation +// https://www.iana.org/domains/root/db/epson.html +epson + +// equipment : Binky Moon, LLC +// https://www.iana.org/domains/root/db/equipment.html +equipment + +// ericsson : Telefonaktiebolaget L M Ericsson +// https://www.iana.org/domains/root/db/ericsson.html +ericsson + +// erni : ERNI Group Holding AG +// https://www.iana.org/domains/root/db/erni.html +erni + +// esq : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/esq.html +esq + +// estate : Binky Moon, LLC +// https://www.iana.org/domains/root/db/estate.html +estate + +// eurovision : European Broadcasting Union (EBU) +// https://www.iana.org/domains/root/db/eurovision.html +eurovision + +// eus : Puntueus Fundazioa +// https://www.iana.org/domains/root/db/eus.html +eus + +// events : Binky Moon, LLC +// https://www.iana.org/domains/root/db/events.html +events + +// exchange : Binky Moon, LLC +// https://www.iana.org/domains/root/db/exchange.html +exchange + +// expert : Binky Moon, LLC +// https://www.iana.org/domains/root/db/expert.html +expert + +// exposed : Binky Moon, LLC +// https://www.iana.org/domains/root/db/exposed.html +exposed + +// express : Binky Moon, LLC +// https://www.iana.org/domains/root/db/express.html +express + +// extraspace : Extra Space Storage LLC +// https://www.iana.org/domains/root/db/extraspace.html +extraspace + +// fage : Fage International S.A. +// https://www.iana.org/domains/root/db/fage.html +fage + +// fail : Binky Moon, LLC +// https://www.iana.org/domains/root/db/fail.html +fail + +// fairwinds : FairWinds Partners, LLC +// https://www.iana.org/domains/root/db/fairwinds.html +fairwinds + +// faith : dot Faith Limited +// https://www.iana.org/domains/root/db/faith.html +faith + +// family : Dog Beach, LLC +// https://www.iana.org/domains/root/db/family.html +family + +// fan : Dog Beach, LLC +// https://www.iana.org/domains/root/db/fan.html +fan + +// fans : ZDNS International Limited +// https://www.iana.org/domains/root/db/fans.html +fans + +// farm : Binky Moon, LLC +// https://www.iana.org/domains/root/db/farm.html +farm + +// farmers : Farmers Insurance Exchange +// https://www.iana.org/domains/root/db/farmers.html +farmers + +// fashion : Registry Services, LLC +// https://www.iana.org/domains/root/db/fashion.html +fashion + +// fast : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/fast.html +fast + +// fedex : Federal Express Corporation +// https://www.iana.org/domains/root/db/fedex.html +fedex + +// feedback : Top Level Spectrum, Inc. +// https://www.iana.org/domains/root/db/feedback.html +feedback + +// ferrari : Fiat Chrysler Automobiles N.V. +// https://www.iana.org/domains/root/db/ferrari.html +ferrari + +// ferrero : Ferrero Trading Lux S.A. +// https://www.iana.org/domains/root/db/ferrero.html +ferrero + +// fidelity : Fidelity Brokerage Services LLC +// https://www.iana.org/domains/root/db/fidelity.html +fidelity + +// fido : Rogers Communications Canada Inc. +// https://www.iana.org/domains/root/db/fido.html +fido + +// film : Motion Picture Domain Registry Pty Ltd +// https://www.iana.org/domains/root/db/film.html +film + +// final : Núcleo de Informação e Coordenação do Ponto BR - NIC.br +// https://www.iana.org/domains/root/db/final.html +final + +// finance : Binky Moon, LLC +// https://www.iana.org/domains/root/db/finance.html +finance + +// financial : Binky Moon, LLC +// https://www.iana.org/domains/root/db/financial.html +financial + +// fire : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/fire.html +fire + +// firestone : Bridgestone Licensing Services, Inc +// https://www.iana.org/domains/root/db/firestone.html +firestone + +// firmdale : Firmdale Holdings Limited +// https://www.iana.org/domains/root/db/firmdale.html +firmdale + +// fish : Binky Moon, LLC +// https://www.iana.org/domains/root/db/fish.html +fish + +// fishing : Registry Services, LLC +// https://www.iana.org/domains/root/db/fishing.html +fishing + +// fit : Registry Services, LLC +// https://www.iana.org/domains/root/db/fit.html +fit + +// fitness : Binky Moon, LLC +// https://www.iana.org/domains/root/db/fitness.html +fitness + +// flickr : Flickr, Inc. +// https://www.iana.org/domains/root/db/flickr.html +flickr + +// flights : Binky Moon, LLC +// https://www.iana.org/domains/root/db/flights.html +flights + +// flir : FLIR Systems, Inc. +// https://www.iana.org/domains/root/db/flir.html +flir + +// florist : Binky Moon, LLC +// https://www.iana.org/domains/root/db/florist.html +florist + +// flowers : XYZ.COM LLC +// https://www.iana.org/domains/root/db/flowers.html +flowers + +// fly : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/fly.html +fly + +// foo : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/foo.html +foo + +// food : Internet Naming Company LLC +// https://www.iana.org/domains/root/db/food.html +food + +// football : Binky Moon, LLC +// https://www.iana.org/domains/root/db/football.html +football + +// ford : Ford Motor Company +// https://www.iana.org/domains/root/db/ford.html +ford + +// forex : Dog Beach, LLC +// https://www.iana.org/domains/root/db/forex.html +forex + +// forsale : Dog Beach, LLC +// https://www.iana.org/domains/root/db/forsale.html +forsale + +// forum : Waterford Limited +// https://www.iana.org/domains/root/db/forum.html +forum + +// foundation : Public Interest Registry +// https://www.iana.org/domains/root/db/foundation.html +foundation + +// fox : FOX Registry, LLC +// https://www.iana.org/domains/root/db/fox.html +fox + +// free : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/free.html +free + +// fresenius : Fresenius Immobilien-Verwaltungs-GmbH +// https://www.iana.org/domains/root/db/fresenius.html +fresenius + +// frl : FRLregistry B.V. +// https://www.iana.org/domains/root/db/frl.html +frl + +// frogans : OP3FT +// https://www.iana.org/domains/root/db/frogans.html +frogans + +// frontier : Frontier Communications Corporation +// https://www.iana.org/domains/root/db/frontier.html +frontier + +// ftr : Frontier Communications Corporation +// https://www.iana.org/domains/root/db/ftr.html +ftr + +// fujitsu : Fujitsu Limited +// https://www.iana.org/domains/root/db/fujitsu.html +fujitsu + +// fun : Radix Technologies Inc SEZC +// https://www.iana.org/domains/root/db/fun.html +fun + +// fund : Binky Moon, LLC +// https://www.iana.org/domains/root/db/fund.html +fund + +// furniture : Binky Moon, LLC +// https://www.iana.org/domains/root/db/furniture.html +furniture + +// futbol : Dog Beach, LLC +// https://www.iana.org/domains/root/db/futbol.html +futbol + +// fyi : Binky Moon, LLC +// https://www.iana.org/domains/root/db/fyi.html +fyi + +// gal : Asociación puntoGAL +// https://www.iana.org/domains/root/db/gal.html +gal + +// gallery : Binky Moon, LLC +// https://www.iana.org/domains/root/db/gallery.html +gallery + +// gallo : Gallo Vineyards, Inc. +// https://www.iana.org/domains/root/db/gallo.html +gallo + +// gallup : Gallup, Inc. +// https://www.iana.org/domains/root/db/gallup.html +gallup + +// game : XYZ.COM LLC +// https://www.iana.org/domains/root/db/game.html +game + +// games : Dog Beach, LLC +// https://www.iana.org/domains/root/db/games.html +games + +// gap : The Gap, Inc. +// https://www.iana.org/domains/root/db/gap.html +gap + +// garden : Registry Services, LLC +// https://www.iana.org/domains/root/db/garden.html +garden + +// gay : Registry Services, LLC +// https://www.iana.org/domains/root/db/gay.html +gay + +// gbiz : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/gbiz.html +gbiz + +// gdn : Joint Stock Company "Navigation-information systems" +// https://www.iana.org/domains/root/db/gdn.html +gdn + +// gea : GEA Group Aktiengesellschaft +// https://www.iana.org/domains/root/db/gea.html +gea + +// gent : Easyhost BV +// https://www.iana.org/domains/root/db/gent.html +gent + +// genting : Resorts World Inc Pte. Ltd. +// https://www.iana.org/domains/root/db/genting.html +genting + +// george : Wal-Mart Stores, Inc. +// https://www.iana.org/domains/root/db/george.html +george + +// ggee : GMO Internet, Inc. +// https://www.iana.org/domains/root/db/ggee.html +ggee + +// gift : DotGift, LLC +// https://www.iana.org/domains/root/db/gift.html +gift + +// gifts : Binky Moon, LLC +// https://www.iana.org/domains/root/db/gifts.html +gifts + +// gives : Public Interest Registry +// https://www.iana.org/domains/root/db/gives.html +gives + +// giving : Public Interest Registry +// https://www.iana.org/domains/root/db/giving.html +giving + +// glass : Binky Moon, LLC +// https://www.iana.org/domains/root/db/glass.html +glass + +// gle : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/gle.html +gle + +// global : Identity Digital Domains Limited +// https://www.iana.org/domains/root/db/global.html +global + +// globo : Globo Comunicação e Participações S.A +// https://www.iana.org/domains/root/db/globo.html +globo + +// gmail : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/gmail.html +gmail + +// gmbh : Binky Moon, LLC +// https://www.iana.org/domains/root/db/gmbh.html +gmbh + +// gmo : GMO Internet, Inc. +// https://www.iana.org/domains/root/db/gmo.html +gmo + +// gmx : 1&1 Mail & Media GmbH +// https://www.iana.org/domains/root/db/gmx.html +gmx + +// godaddy : Go Daddy East, LLC +// https://www.iana.org/domains/root/db/godaddy.html +godaddy + +// gold : Binky Moon, LLC +// https://www.iana.org/domains/root/db/gold.html +gold + +// goldpoint : YODOBASHI CAMERA CO.,LTD. +// https://www.iana.org/domains/root/db/goldpoint.html +goldpoint + +// golf : Binky Moon, LLC +// https://www.iana.org/domains/root/db/golf.html +golf + +// goo : NTT DOCOMO, INC. +// https://www.iana.org/domains/root/db/goo.html +goo + +// goodyear : The Goodyear Tire & Rubber Company +// https://www.iana.org/domains/root/db/goodyear.html +goodyear + +// goog : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/goog.html +goog + +// google : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/google.html +google + +// gop : Republican State Leadership Committee, Inc. +// https://www.iana.org/domains/root/db/gop.html +gop + +// got : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/got.html +got + +// grainger : Grainger Registry Services, LLC +// https://www.iana.org/domains/root/db/grainger.html +grainger + +// graphics : Binky Moon, LLC +// https://www.iana.org/domains/root/db/graphics.html +graphics + +// gratis : Binky Moon, LLC +// https://www.iana.org/domains/root/db/gratis.html +gratis + +// green : Identity Digital Domains Limited +// https://www.iana.org/domains/root/db/green.html +green + +// gripe : Binky Moon, LLC +// https://www.iana.org/domains/root/db/gripe.html +gripe + +// grocery : Wal-Mart Stores, Inc. +// https://www.iana.org/domains/root/db/grocery.html +grocery + +// group : Binky Moon, LLC +// https://www.iana.org/domains/root/db/group.html +group + +// gucci : Guccio Gucci S.p.a. +// https://www.iana.org/domains/root/db/gucci.html +gucci + +// guge : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/guge.html +guge + +// guide : Binky Moon, LLC +// https://www.iana.org/domains/root/db/guide.html +guide + +// guitars : XYZ.COM LLC +// https://www.iana.org/domains/root/db/guitars.html +guitars + +// guru : Binky Moon, LLC +// https://www.iana.org/domains/root/db/guru.html +guru + +// hair : XYZ.COM LLC +// https://www.iana.org/domains/root/db/hair.html +hair + +// hamburg : Hamburg Top-Level-Domain GmbH +// https://www.iana.org/domains/root/db/hamburg.html +hamburg + +// hangout : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/hangout.html +hangout + +// haus : Dog Beach, LLC +// https://www.iana.org/domains/root/db/haus.html +haus + +// hbo : HBO Registry Services, Inc. +// https://www.iana.org/domains/root/db/hbo.html +hbo + +// hdfc : HDFC BANK LIMITED +// https://www.iana.org/domains/root/db/hdfc.html +hdfc + +// hdfcbank : HDFC BANK LIMITED +// https://www.iana.org/domains/root/db/hdfcbank.html +hdfcbank + +// health : Registry Services, LLC +// https://www.iana.org/domains/root/db/health.html +health + +// healthcare : Binky Moon, LLC +// https://www.iana.org/domains/root/db/healthcare.html +healthcare + +// help : Innovation service Limited +// https://www.iana.org/domains/root/db/help.html +help + +// helsinki : City of Helsinki +// https://www.iana.org/domains/root/db/helsinki.html +helsinki + +// here : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/here.html +here + +// hermes : HERMES INTERNATIONAL +// https://www.iana.org/domains/root/db/hermes.html +hermes + +// hiphop : Dot Hip Hop, LLC +// https://www.iana.org/domains/root/db/hiphop.html +hiphop + +// hisamitsu : Hisamitsu Pharmaceutical Co.,Inc. +// https://www.iana.org/domains/root/db/hisamitsu.html +hisamitsu + +// hitachi : Hitachi, Ltd. +// https://www.iana.org/domains/root/db/hitachi.html +hitachi + +// hiv : Internet Naming Company LLC +// https://www.iana.org/domains/root/db/hiv.html +hiv + +// hkt : PCCW-HKT DataCom Services Limited +// https://www.iana.org/domains/root/db/hkt.html +hkt + +// hockey : Binky Moon, LLC +// https://www.iana.org/domains/root/db/hockey.html +hockey + +// holdings : Binky Moon, LLC +// https://www.iana.org/domains/root/db/holdings.html +holdings + +// holiday : Binky Moon, LLC +// https://www.iana.org/domains/root/db/holiday.html +holiday + +// homedepot : Home Depot Product Authority, LLC +// https://www.iana.org/domains/root/db/homedepot.html +homedepot + +// homegoods : The TJX Companies, Inc. +// https://www.iana.org/domains/root/db/homegoods.html +homegoods + +// homes : XYZ.COM LLC +// https://www.iana.org/domains/root/db/homes.html +homes + +// homesense : The TJX Companies, Inc. +// https://www.iana.org/domains/root/db/homesense.html +homesense + +// honda : Honda Motor Co., Ltd. +// https://www.iana.org/domains/root/db/honda.html +honda + +// horse : Registry Services, LLC +// https://www.iana.org/domains/root/db/horse.html +horse + +// hospital : Binky Moon, LLC +// https://www.iana.org/domains/root/db/hospital.html +hospital + +// host : Radix Technologies Inc SEZC +// https://www.iana.org/domains/root/db/host.html +host + +// hosting : XYZ.COM LLC +// https://www.iana.org/domains/root/db/hosting.html +hosting + +// hot : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/hot.html +hot + +// hotel : HOTEL Top-Level-Domain S.a.r.l +// https://www.iana.org/domains/root/db/hotel.html +hotel + +// hotels : Booking.com B.V. +// https://www.iana.org/domains/root/db/hotels.html +hotels + +// hotmail : Microsoft Corporation +// https://www.iana.org/domains/root/db/hotmail.html +hotmail + +// house : Binky Moon, LLC +// https://www.iana.org/domains/root/db/house.html +house + +// how : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/how.html +how + +// hsbc : HSBC Global Services (UK) Limited +// https://www.iana.org/domains/root/db/hsbc.html +hsbc + +// hughes : Hughes Satellite Systems Corporation +// https://www.iana.org/domains/root/db/hughes.html +hughes + +// hyatt : Hyatt GTLD, L.L.C. +// https://www.iana.org/domains/root/db/hyatt.html +hyatt + +// hyundai : Hyundai Motor Company +// https://www.iana.org/domains/root/db/hyundai.html +hyundai + +// ibm : International Business Machines Corporation +// https://www.iana.org/domains/root/db/ibm.html +ibm + +// icbc : Industrial and Commercial Bank of China Limited +// https://www.iana.org/domains/root/db/icbc.html +icbc + +// ice : IntercontinentalExchange, Inc. +// https://www.iana.org/domains/root/db/ice.html +ice + +// icu : ShortDot SA +// https://www.iana.org/domains/root/db/icu.html +icu + +// ieee : IEEE Global LLC +// https://www.iana.org/domains/root/db/ieee.html +ieee + +// ifm : ifm electronic gmbh +// https://www.iana.org/domains/root/db/ifm.html +ifm + +// ikano : Ikano S.A. +// https://www.iana.org/domains/root/db/ikano.html +ikano + +// imamat : Fondation Aga Khan (Aga Khan Foundation) +// https://www.iana.org/domains/root/db/imamat.html +imamat + +// imdb : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/imdb.html +imdb + +// immo : Binky Moon, LLC +// https://www.iana.org/domains/root/db/immo.html +immo + +// immobilien : Dog Beach, LLC +// https://www.iana.org/domains/root/db/immobilien.html +immobilien + +// inc : Intercap Registry Inc. +// https://www.iana.org/domains/root/db/inc.html +inc + +// industries : Binky Moon, LLC +// https://www.iana.org/domains/root/db/industries.html +industries + +// infiniti : NISSAN MOTOR CO., LTD. +// https://www.iana.org/domains/root/db/infiniti.html +infiniti + +// ing : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/ing.html +ing + +// ink : Registry Services, LLC +// https://www.iana.org/domains/root/db/ink.html +ink + +// institute : Binky Moon, LLC +// https://www.iana.org/domains/root/db/institute.html +institute + +// insurance : fTLD Registry Services LLC +// https://www.iana.org/domains/root/db/insurance.html +insurance + +// insure : Binky Moon, LLC +// https://www.iana.org/domains/root/db/insure.html +insure + +// international : Binky Moon, LLC +// https://www.iana.org/domains/root/db/international.html +international + +// intuit : Intuit Administrative Services, Inc. +// https://www.iana.org/domains/root/db/intuit.html +intuit + +// investments : Binky Moon, LLC +// https://www.iana.org/domains/root/db/investments.html +investments + +// ipiranga : Ipiranga Produtos de Petroleo S.A. +// https://www.iana.org/domains/root/db/ipiranga.html +ipiranga + +// irish : Binky Moon, LLC +// https://www.iana.org/domains/root/db/irish.html +irish + +// ismaili : Fondation Aga Khan (Aga Khan Foundation) +// https://www.iana.org/domains/root/db/ismaili.html +ismaili + +// ist : Istanbul Metropolitan Municipality +// https://www.iana.org/domains/root/db/ist.html +ist + +// istanbul : Istanbul Metropolitan Municipality +// https://www.iana.org/domains/root/db/istanbul.html +istanbul + +// itau : Itau Unibanco Holding S.A. +// https://www.iana.org/domains/root/db/itau.html +itau + +// itv : ITV Services Limited +// https://www.iana.org/domains/root/db/itv.html +itv + +// jaguar : Jaguar Land Rover Ltd +// https://www.iana.org/domains/root/db/jaguar.html +jaguar + +// java : Oracle Corporation +// https://www.iana.org/domains/root/db/java.html +java + +// jcb : JCB Co., Ltd. +// https://www.iana.org/domains/root/db/jcb.html +jcb + +// jeep : FCA US LLC. +// https://www.iana.org/domains/root/db/jeep.html +jeep + +// jetzt : Binky Moon, LLC +// https://www.iana.org/domains/root/db/jetzt.html +jetzt + +// jewelry : Binky Moon, LLC +// https://www.iana.org/domains/root/db/jewelry.html +jewelry + +// jio : Reliance Industries Limited +// https://www.iana.org/domains/root/db/jio.html +jio + +// jll : Jones Lang LaSalle Incorporated +// https://www.iana.org/domains/root/db/jll.html +jll + +// jmp : Matrix IP LLC +// https://www.iana.org/domains/root/db/jmp.html +jmp + +// jnj : Johnson & Johnson Services, Inc. +// https://www.iana.org/domains/root/db/jnj.html +jnj + +// joburg : ZA Central Registry NPC trading as ZA Central Registry +// https://www.iana.org/domains/root/db/joburg.html +joburg + +// jot : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/jot.html +jot + +// joy : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/joy.html +joy + +// jpmorgan : JPMorgan Chase Bank, National Association +// https://www.iana.org/domains/root/db/jpmorgan.html +jpmorgan + +// jprs : Japan Registry Services Co., Ltd. +// https://www.iana.org/domains/root/db/jprs.html +jprs + +// juegos : Dog Beach, LLC +// https://www.iana.org/domains/root/db/juegos.html +juegos + +// juniper : JUNIPER NETWORKS, INC. +// https://www.iana.org/domains/root/db/juniper.html +juniper + +// kaufen : Dog Beach, LLC +// https://www.iana.org/domains/root/db/kaufen.html +kaufen + +// kddi : KDDI CORPORATION +// https://www.iana.org/domains/root/db/kddi.html +kddi + +// kerryhotels : Kerry Trading Co. Limited +// https://www.iana.org/domains/root/db/kerryhotels.html +kerryhotels + +// kerryproperties : Kerry Trading Co. Limited +// https://www.iana.org/domains/root/db/kerryproperties.html +kerryproperties + +// kfh : Kuwait Finance House +// https://www.iana.org/domains/root/db/kfh.html +kfh + +// kia : KIA MOTORS CORPORATION +// https://www.iana.org/domains/root/db/kia.html +kia + +// kids : DotKids Foundation Limited +// https://www.iana.org/domains/root/db/kids.html +kids + +// kim : Identity Digital Domains Limited +// https://www.iana.org/domains/root/db/kim.html +kim + +// kindle : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/kindle.html +kindle + +// kitchen : Binky Moon, LLC +// https://www.iana.org/domains/root/db/kitchen.html +kitchen + +// kiwi : DOT KIWI LIMITED +// https://www.iana.org/domains/root/db/kiwi.html +kiwi + +// koeln : dotKoeln GmbH +// https://www.iana.org/domains/root/db/koeln.html +koeln + +// komatsu : Komatsu Ltd. +// https://www.iana.org/domains/root/db/komatsu.html +komatsu + +// kosher : Kosher Marketing Assets LLC +// https://www.iana.org/domains/root/db/kosher.html +kosher + +// kpmg : KPMG International Cooperative (KPMG International Genossenschaft) +// https://www.iana.org/domains/root/db/kpmg.html +kpmg + +// kpn : Koninklijke KPN N.V. +// https://www.iana.org/domains/root/db/kpn.html +kpn + +// krd : KRG Department of Information Technology +// https://www.iana.org/domains/root/db/krd.html +krd + +// kred : KredTLD Pty Ltd +// https://www.iana.org/domains/root/db/kred.html +kred + +// kuokgroup : Kerry Trading Co. Limited +// https://www.iana.org/domains/root/db/kuokgroup.html +kuokgroup + +// kyoto : Academic Institution: Kyoto Jyoho Gakuen +// https://www.iana.org/domains/root/db/kyoto.html +kyoto + +// lacaixa : Fundación Bancaria Caixa d’Estalvis i Pensions de Barcelona, “la Caixa” +// https://www.iana.org/domains/root/db/lacaixa.html +lacaixa + +// lamborghini : Automobili Lamborghini S.p.A. +// https://www.iana.org/domains/root/db/lamborghini.html +lamborghini + +// lamer : The Estée Lauder Companies Inc. +// https://www.iana.org/domains/root/db/lamer.html +lamer + +// land : Binky Moon, LLC +// https://www.iana.org/domains/root/db/land.html +land + +// landrover : Jaguar Land Rover Ltd +// https://www.iana.org/domains/root/db/landrover.html +landrover + +// lanxess : LANXESS Corporation +// https://www.iana.org/domains/root/db/lanxess.html +lanxess + +// lasalle : Jones Lang LaSalle Incorporated +// https://www.iana.org/domains/root/db/lasalle.html +lasalle + +// lat : XYZ.COM LLC +// https://www.iana.org/domains/root/db/lat.html +lat + +// latino : Dish DBS Corporation +// https://www.iana.org/domains/root/db/latino.html +latino + +// latrobe : La Trobe University +// https://www.iana.org/domains/root/db/latrobe.html +latrobe + +// law : Registry Services, LLC +// https://www.iana.org/domains/root/db/law.html +law + +// lawyer : Dog Beach, LLC +// https://www.iana.org/domains/root/db/lawyer.html +lawyer + +// lds : IRI Domain Management, LLC +// https://www.iana.org/domains/root/db/lds.html +lds + +// lease : Binky Moon, LLC +// https://www.iana.org/domains/root/db/lease.html +lease + +// leclerc : A.C.D. LEC Association des Centres Distributeurs Edouard Leclerc +// https://www.iana.org/domains/root/db/leclerc.html +leclerc + +// lefrak : LeFrak Organization, Inc. +// https://www.iana.org/domains/root/db/lefrak.html +lefrak + +// legal : Binky Moon, LLC +// https://www.iana.org/domains/root/db/legal.html +legal + +// lego : LEGO Juris A/S +// https://www.iana.org/domains/root/db/lego.html +lego + +// lexus : TOYOTA MOTOR CORPORATION +// https://www.iana.org/domains/root/db/lexus.html +lexus + +// lgbt : Identity Digital Domains Limited +// https://www.iana.org/domains/root/db/lgbt.html +lgbt + +// lidl : Schwarz Domains und Services GmbH & Co. KG +// https://www.iana.org/domains/root/db/lidl.html +lidl + +// life : Binky Moon, LLC +// https://www.iana.org/domains/root/db/life.html +life + +// lifeinsurance : American Council of Life Insurers +// https://www.iana.org/domains/root/db/lifeinsurance.html +lifeinsurance + +// lifestyle : Internet Naming Company LLC +// https://www.iana.org/domains/root/db/lifestyle.html +lifestyle + +// lighting : Binky Moon, LLC +// https://www.iana.org/domains/root/db/lighting.html +lighting + +// like : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/like.html +like + +// lilly : Eli Lilly and Company +// https://www.iana.org/domains/root/db/lilly.html +lilly + +// limited : Binky Moon, LLC +// https://www.iana.org/domains/root/db/limited.html +limited + +// limo : Binky Moon, LLC +// https://www.iana.org/domains/root/db/limo.html +limo + +// lincoln : Ford Motor Company +// https://www.iana.org/domains/root/db/lincoln.html +lincoln + +// link : Nova Registry Ltd +// https://www.iana.org/domains/root/db/link.html +link + +// live : Dog Beach, LLC +// https://www.iana.org/domains/root/db/live.html +live + +// living : Internet Naming Company LLC +// https://www.iana.org/domains/root/db/living.html +living + +// llc : Identity Digital Domains Limited +// https://www.iana.org/domains/root/db/llc.html +llc + +// llp : Intercap Registry Inc. +// https://www.iana.org/domains/root/db/llp.html +llp + +// loan : dot Loan Limited +// https://www.iana.org/domains/root/db/loan.html +loan + +// loans : Binky Moon, LLC +// https://www.iana.org/domains/root/db/loans.html +loans + +// locker : Orange Domains LLC +// https://www.iana.org/domains/root/db/locker.html +locker + +// locus : Locus Analytics LLC +// https://www.iana.org/domains/root/db/locus.html +locus + +// lol : XYZ.COM LLC +// https://www.iana.org/domains/root/db/lol.html +lol + +// london : Dot London Domains Limited +// https://www.iana.org/domains/root/db/london.html +london + +// lotte : Lotte Holdings Co., Ltd. +// https://www.iana.org/domains/root/db/lotte.html +lotte + +// lotto : Identity Digital Domains Limited +// https://www.iana.org/domains/root/db/lotto.html +lotto + +// love : Waterford Limited +// https://www.iana.org/domains/root/db/love.html +love + +// lpl : LPL Holdings, Inc. +// https://www.iana.org/domains/root/db/lpl.html +lpl + +// lplfinancial : LPL Holdings, Inc. +// https://www.iana.org/domains/root/db/lplfinancial.html +lplfinancial + +// ltd : Binky Moon, LLC +// https://www.iana.org/domains/root/db/ltd.html +ltd + +// ltda : InterNetX, Corp +// https://www.iana.org/domains/root/db/ltda.html +ltda + +// lundbeck : H. Lundbeck A/S +// https://www.iana.org/domains/root/db/lundbeck.html +lundbeck + +// luxe : Registry Services, LLC +// https://www.iana.org/domains/root/db/luxe.html +luxe + +// luxury : Luxury Partners, LLC +// https://www.iana.org/domains/root/db/luxury.html +luxury + +// madrid : Comunidad de Madrid +// https://www.iana.org/domains/root/db/madrid.html +madrid + +// maif : Mutuelle Assurance Instituteur France (MAIF) +// https://www.iana.org/domains/root/db/maif.html +maif + +// maison : Binky Moon, LLC +// https://www.iana.org/domains/root/db/maison.html +maison + +// makeup : XYZ.COM LLC +// https://www.iana.org/domains/root/db/makeup.html +makeup + +// man : MAN Truck & Bus SE +// https://www.iana.org/domains/root/db/man.html +man + +// management : Binky Moon, LLC +// https://www.iana.org/domains/root/db/management.html +management + +// mango : PUNTO FA S.L. +// https://www.iana.org/domains/root/db/mango.html +mango + +// map : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/map.html +map + +// market : Dog Beach, LLC +// https://www.iana.org/domains/root/db/market.html +market + +// marketing : Binky Moon, LLC +// https://www.iana.org/domains/root/db/marketing.html +marketing + +// markets : Dog Beach, LLC +// https://www.iana.org/domains/root/db/markets.html +markets + +// marriott : Marriott Worldwide Corporation +// https://www.iana.org/domains/root/db/marriott.html +marriott + +// marshalls : The TJX Companies, Inc. +// https://www.iana.org/domains/root/db/marshalls.html +marshalls + +// mattel : Mattel IT Services, Inc. +// https://www.iana.org/domains/root/db/mattel.html +mattel + +// mba : Binky Moon, LLC +// https://www.iana.org/domains/root/db/mba.html +mba + +// mckinsey : McKinsey Holdings, Inc. +// https://www.iana.org/domains/root/db/mckinsey.html +mckinsey + +// med : Medistry LLC +// https://www.iana.org/domains/root/db/med.html +med + +// media : Binky Moon, LLC +// https://www.iana.org/domains/root/db/media.html +media + +// meet : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/meet.html +meet + +// melbourne : The Crown in right of the State of Victoria, represented by its Department of State Development, Business and Innovation +// https://www.iana.org/domains/root/db/melbourne.html +melbourne + +// meme : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/meme.html +meme + +// memorial : Dog Beach, LLC +// https://www.iana.org/domains/root/db/memorial.html +memorial + +// men : Exclusive Registry Limited +// https://www.iana.org/domains/root/db/men.html +men + +// menu : Dot Menu Registry, LLC +// https://www.iana.org/domains/root/db/menu.html +menu + +// merck : Merck Registry Holdings, Inc. +// https://www.iana.org/domains/root/db/merck.html +merck + +// merckmsd : MSD Registry Holdings, Inc. +// https://www.iana.org/domains/root/db/merckmsd.html +merckmsd + +// miami : Registry Services, LLC +// https://www.iana.org/domains/root/db/miami.html +miami + +// microsoft : Microsoft Corporation +// https://www.iana.org/domains/root/db/microsoft.html +microsoft + +// mini : Bayerische Motoren Werke Aktiengesellschaft +// https://www.iana.org/domains/root/db/mini.html +mini + +// mint : Intuit Administrative Services, Inc. +// https://www.iana.org/domains/root/db/mint.html +mint + +// mit : Massachusetts Institute of Technology +// https://www.iana.org/domains/root/db/mit.html +mit + +// mitsubishi : Mitsubishi Corporation +// https://www.iana.org/domains/root/db/mitsubishi.html +mitsubishi + +// mlb : MLB Advanced Media DH, LLC +// https://www.iana.org/domains/root/db/mlb.html +mlb + +// mls : The Canadian Real Estate Association +// https://www.iana.org/domains/root/db/mls.html +mls + +// mma : MMA IARD +// https://www.iana.org/domains/root/db/mma.html +mma + +// mobile : Dish DBS Corporation +// https://www.iana.org/domains/root/db/mobile.html +mobile + +// moda : Dog Beach, LLC +// https://www.iana.org/domains/root/db/moda.html +moda + +// moe : Interlink Systems Innovation Institute K.K. +// https://www.iana.org/domains/root/db/moe.html +moe + +// moi : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/moi.html +moi + +// mom : XYZ.COM LLC +// https://www.iana.org/domains/root/db/mom.html +mom + +// monash : Monash University +// https://www.iana.org/domains/root/db/monash.html +monash + +// money : Binky Moon, LLC +// https://www.iana.org/domains/root/db/money.html +money + +// monster : XYZ.COM LLC +// https://www.iana.org/domains/root/db/monster.html +monster + +// mormon : IRI Domain Management, LLC +// https://www.iana.org/domains/root/db/mormon.html +mormon + +// mortgage : Dog Beach, LLC +// https://www.iana.org/domains/root/db/mortgage.html +mortgage + +// moscow : Foundation for Assistance for Internet Technologies and Infrastructure Development (FAITID) +// https://www.iana.org/domains/root/db/moscow.html +moscow + +// moto : Motorola Trademark Holdings, LLC +// https://www.iana.org/domains/root/db/moto.html +moto + +// motorcycles : XYZ.COM LLC +// https://www.iana.org/domains/root/db/motorcycles.html +motorcycles + +// mov : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/mov.html +mov + +// movie : Binky Moon, LLC +// https://www.iana.org/domains/root/db/movie.html +movie + +// msd : MSD Registry Holdings, Inc. +// https://www.iana.org/domains/root/db/msd.html +msd + +// mtn : MTN Dubai Limited +// https://www.iana.org/domains/root/db/mtn.html +mtn + +// mtr : MTR Corporation Limited +// https://www.iana.org/domains/root/db/mtr.html +mtr + +// music : DotMusic Limited +// https://www.iana.org/domains/root/db/music.html +music + +// nab : National Australia Bank Limited +// https://www.iana.org/domains/root/db/nab.html +nab + +// nagoya : GMO Registry, Inc. +// https://www.iana.org/domains/root/db/nagoya.html +nagoya + +// navy : Dog Beach, LLC +// https://www.iana.org/domains/root/db/navy.html +navy + +// nba : NBA REGISTRY, LLC +// https://www.iana.org/domains/root/db/nba.html +nba + +// nec : NEC Corporation +// https://www.iana.org/domains/root/db/nec.html +nec + +// netbank : COMMONWEALTH BANK OF AUSTRALIA +// https://www.iana.org/domains/root/db/netbank.html +netbank + +// netflix : Netflix, Inc. +// https://www.iana.org/domains/root/db/netflix.html +netflix + +// network : Binky Moon, LLC +// https://www.iana.org/domains/root/db/network.html +network + +// neustar : NeuStar, Inc. +// https://www.iana.org/domains/root/db/neustar.html +neustar + +// new : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/new.html +new + +// news : Dog Beach, LLC +// https://www.iana.org/domains/root/db/news.html +news + +// next : Next plc +// https://www.iana.org/domains/root/db/next.html +next + +// nextdirect : Next plc +// https://www.iana.org/domains/root/db/nextdirect.html +nextdirect + +// nexus : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/nexus.html +nexus + +// nfl : NFL Reg Ops LLC +// https://www.iana.org/domains/root/db/nfl.html +nfl + +// ngo : Public Interest Registry +// https://www.iana.org/domains/root/db/ngo.html +ngo + +// nhk : Japan Broadcasting Corporation (NHK) +// https://www.iana.org/domains/root/db/nhk.html +nhk + +// nico : DWANGO Co., Ltd. +// https://www.iana.org/domains/root/db/nico.html +nico + +// nike : NIKE, Inc. +// https://www.iana.org/domains/root/db/nike.html +nike + +// nikon : NIKON CORPORATION +// https://www.iana.org/domains/root/db/nikon.html +nikon + +// ninja : Dog Beach, LLC +// https://www.iana.org/domains/root/db/ninja.html +ninja + +// nissan : NISSAN MOTOR CO., LTD. +// https://www.iana.org/domains/root/db/nissan.html +nissan + +// nissay : Nippon Life Insurance Company +// https://www.iana.org/domains/root/db/nissay.html +nissay + +// nokia : Nokia Corporation +// https://www.iana.org/domains/root/db/nokia.html +nokia + +// norton : Gen Digital Inc. +// https://www.iana.org/domains/root/db/norton.html +norton + +// now : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/now.html +now + +// nowruz +// https://www.iana.org/domains/root/db/nowruz.html +nowruz + +// nowtv : Starbucks (HK) Limited +// https://www.iana.org/domains/root/db/nowtv.html +nowtv + +// nra : National Rifle Association of America +// https://www.iana.org/domains/root/db/nra.html +nra + +// nrw : Minds + Machines GmbH +// https://www.iana.org/domains/root/db/nrw.html +nrw + +// ntt : NIPPON TELEGRAPH AND TELEPHONE CORPORATION +// https://www.iana.org/domains/root/db/ntt.html +ntt + +// nyc : The City of New York by and through the New York City Department of Information Technology & Telecommunications +// https://www.iana.org/domains/root/db/nyc.html +nyc + +// obi : OBI Group Holding SE & Co. KGaA +// https://www.iana.org/domains/root/db/obi.html +obi + +// observer : Fegistry, LLC +// https://www.iana.org/domains/root/db/observer.html +observer + +// office : Microsoft Corporation +// https://www.iana.org/domains/root/db/office.html +office + +// okinawa : BRregistry, Inc. +// https://www.iana.org/domains/root/db/okinawa.html +okinawa + +// olayan : Competrol (Luxembourg) Sarl +// https://www.iana.org/domains/root/db/olayan.html +olayan + +// olayangroup : Competrol (Luxembourg) Sarl +// https://www.iana.org/domains/root/db/olayangroup.html +olayangroup + +// ollo : Dish DBS Corporation +// https://www.iana.org/domains/root/db/ollo.html +ollo + +// omega : The Swatch Group Ltd +// https://www.iana.org/domains/root/db/omega.html +omega + +// one : One.com A/S +// https://www.iana.org/domains/root/db/one.html +one + +// ong : Public Interest Registry +// https://www.iana.org/domains/root/db/ong.html +ong + +// onl : iRegistry GmbH +// https://www.iana.org/domains/root/db/onl.html +onl + +// online : Radix Technologies Inc SEZC +// https://www.iana.org/domains/root/db/online.html +online + +// ooo : INFIBEAM AVENUES LIMITED +// https://www.iana.org/domains/root/db/ooo.html +ooo + +// open : American Express Travel Related Services Company, Inc. +// https://www.iana.org/domains/root/db/open.html +open + +// oracle : Oracle Corporation +// https://www.iana.org/domains/root/db/oracle.html +oracle + +// orange : Orange Brand Services Limited +// https://www.iana.org/domains/root/db/orange.html +orange + +// organic : Identity Digital Domains Limited +// https://www.iana.org/domains/root/db/organic.html +organic + +// origins : The Estée Lauder Companies Inc. +// https://www.iana.org/domains/root/db/origins.html +origins + +// osaka : Osaka Registry Co., Ltd. +// https://www.iana.org/domains/root/db/osaka.html +osaka + +// otsuka : Otsuka Holdings Co., Ltd. +// https://www.iana.org/domains/root/db/otsuka.html +otsuka + +// ott : Dish DBS Corporation +// https://www.iana.org/domains/root/db/ott.html +ott + +// ovh : MédiaBC +// https://www.iana.org/domains/root/db/ovh.html +ovh + +// page : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/page.html +page + +// panasonic : Panasonic Holdings Corporation +// https://www.iana.org/domains/root/db/panasonic.html +panasonic + +// paris : City of Paris +// https://www.iana.org/domains/root/db/paris.html +paris + +// pars +// https://www.iana.org/domains/root/db/pars.html +pars + +// partners : Binky Moon, LLC +// https://www.iana.org/domains/root/db/partners.html +partners + +// parts : Binky Moon, LLC +// https://www.iana.org/domains/root/db/parts.html +parts + +// party : Blue Sky Registry Limited +// https://www.iana.org/domains/root/db/party.html +party + +// pay : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/pay.html +pay + +// pccw : PCCW Enterprises Limited +// https://www.iana.org/domains/root/db/pccw.html +pccw + +// pet : Identity Digital Domains Limited +// https://www.iana.org/domains/root/db/pet.html +pet + +// pfizer : Pfizer Inc. +// https://www.iana.org/domains/root/db/pfizer.html +pfizer + +// pharmacy : National Association of Boards of Pharmacy +// https://www.iana.org/domains/root/db/pharmacy.html +pharmacy + +// phd : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/phd.html +phd + +// philips : Koninklijke Philips N.V. +// https://www.iana.org/domains/root/db/philips.html +philips + +// phone : Dish DBS Corporation +// https://www.iana.org/domains/root/db/phone.html +phone + +// photo : Registry Services, LLC +// https://www.iana.org/domains/root/db/photo.html +photo + +// photography : Binky Moon, LLC +// https://www.iana.org/domains/root/db/photography.html +photography + +// photos : Binky Moon, LLC +// https://www.iana.org/domains/root/db/photos.html +photos + +// physio : PhysBiz Pty Ltd +// https://www.iana.org/domains/root/db/physio.html +physio + +// pics : XYZ.COM LLC +// https://www.iana.org/domains/root/db/pics.html +pics + +// pictet : Banque Pictet & Cie SA +// https://www.iana.org/domains/root/db/pictet.html +pictet + +// pictures : Binky Moon, LLC +// https://www.iana.org/domains/root/db/pictures.html +pictures + +// pid : Top Level Spectrum, Inc. +// https://www.iana.org/domains/root/db/pid.html +pid + +// pin : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/pin.html +pin + +// ping : Ping Registry Provider, Inc. +// https://www.iana.org/domains/root/db/ping.html +ping + +// pink : Identity Digital Domains Limited +// https://www.iana.org/domains/root/db/pink.html +pink + +// pioneer : Pioneer Corporation +// https://www.iana.org/domains/root/db/pioneer.html +pioneer + +// pizza : Binky Moon, LLC +// https://www.iana.org/domains/root/db/pizza.html +pizza + +// place : Binky Moon, LLC +// https://www.iana.org/domains/root/db/place.html +place + +// play : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/play.html +play + +// playstation : Sony Interactive Entertainment Inc. +// https://www.iana.org/domains/root/db/playstation.html +playstation + +// plumbing : Binky Moon, LLC +// https://www.iana.org/domains/root/db/plumbing.html +plumbing + +// plus : Binky Moon, LLC +// https://www.iana.org/domains/root/db/plus.html +plus + +// pnc : PNC Domain Co., LLC +// https://www.iana.org/domains/root/db/pnc.html +pnc + +// pohl : Deutsche Vermögensberatung Aktiengesellschaft DVAG +// https://www.iana.org/domains/root/db/pohl.html +pohl + +// poker : Identity Digital Domains Limited +// https://www.iana.org/domains/root/db/poker.html +poker + +// politie : Politie Nederland +// https://www.iana.org/domains/root/db/politie.html +politie + +// porn : ICM Registry PN LLC +// https://www.iana.org/domains/root/db/porn.html +porn + +// praxi : Praxi S.p.A. +// https://www.iana.org/domains/root/db/praxi.html +praxi + +// press : Radix Technologies Inc SEZC +// https://www.iana.org/domains/root/db/press.html +press + +// prime : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/prime.html +prime + +// prod : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/prod.html +prod + +// productions : Binky Moon, LLC +// https://www.iana.org/domains/root/db/productions.html +productions + +// prof : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/prof.html +prof + +// progressive : Progressive Casualty Insurance Company +// https://www.iana.org/domains/root/db/progressive.html +progressive + +// promo : Identity Digital Domains Limited +// https://www.iana.org/domains/root/db/promo.html +promo + +// properties : Binky Moon, LLC +// https://www.iana.org/domains/root/db/properties.html +properties + +// property : Digital Property Infrastructure Limited +// https://www.iana.org/domains/root/db/property.html +property + +// protection : XYZ.COM LLC +// https://www.iana.org/domains/root/db/protection.html +protection + +// pru : Prudential Financial, Inc. +// https://www.iana.org/domains/root/db/pru.html +pru + +// prudential : Prudential Financial, Inc. +// https://www.iana.org/domains/root/db/prudential.html +prudential + +// pub : Dog Beach, LLC +// https://www.iana.org/domains/root/db/pub.html +pub + +// pwc : PricewaterhouseCoopers LLP +// https://www.iana.org/domains/root/db/pwc.html +pwc + +// qpon : dotQPON LLC +// https://www.iana.org/domains/root/db/qpon.html +qpon + +// quebec : PointQuébec Inc +// https://www.iana.org/domains/root/db/quebec.html +quebec + +// quest : XYZ.COM LLC +// https://www.iana.org/domains/root/db/quest.html +quest + +// racing : Premier Registry Limited +// https://www.iana.org/domains/root/db/racing.html +racing + +// radio : European Broadcasting Union (EBU) +// https://www.iana.org/domains/root/db/radio.html +radio + +// read : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/read.html +read + +// realestate : dotRealEstate LLC +// https://www.iana.org/domains/root/db/realestate.html +realestate + +// realtor : Real Estate Domains LLC +// https://www.iana.org/domains/root/db/realtor.html +realtor + +// realty : Waterford Limited +// https://www.iana.org/domains/root/db/realty.html +realty + +// recipes : Binky Moon, LLC +// https://www.iana.org/domains/root/db/recipes.html +recipes + +// red : Identity Digital Domains Limited +// https://www.iana.org/domains/root/db/red.html +red + +// redumbrella : Travelers TLD, LLC +// https://www.iana.org/domains/root/db/redumbrella.html +redumbrella + +// rehab : Dog Beach, LLC +// https://www.iana.org/domains/root/db/rehab.html +rehab + +// reise : Binky Moon, LLC +// https://www.iana.org/domains/root/db/reise.html +reise + +// reisen : Binky Moon, LLC +// https://www.iana.org/domains/root/db/reisen.html +reisen + +// reit : National Association of Real Estate Investment Trusts, Inc. +// https://www.iana.org/domains/root/db/reit.html +reit + +// reliance : Reliance Industries Limited +// https://www.iana.org/domains/root/db/reliance.html +reliance + +// ren : ZDNS International Limited +// https://www.iana.org/domains/root/db/ren.html +ren + +// rent : XYZ.COM LLC +// https://www.iana.org/domains/root/db/rent.html +rent + +// rentals : Binky Moon, LLC +// https://www.iana.org/domains/root/db/rentals.html +rentals + +// repair : Binky Moon, LLC +// https://www.iana.org/domains/root/db/repair.html +repair + +// report : Binky Moon, LLC +// https://www.iana.org/domains/root/db/report.html +report + +// republican : Dog Beach, LLC +// https://www.iana.org/domains/root/db/republican.html +republican + +// rest : Punto 2012 Sociedad Anonima Promotora de Inversion de Capital Variable +// https://www.iana.org/domains/root/db/rest.html +rest + +// restaurant : Binky Moon, LLC +// https://www.iana.org/domains/root/db/restaurant.html +restaurant + +// review : dot Review Limited +// https://www.iana.org/domains/root/db/review.html +review + +// reviews : Dog Beach, LLC +// https://www.iana.org/domains/root/db/reviews.html +reviews + +// rexroth : Robert Bosch GMBH +// https://www.iana.org/domains/root/db/rexroth.html +rexroth + +// rich : iRegistry GmbH +// https://www.iana.org/domains/root/db/rich.html +rich + +// richardli : Pacific Century Asset Management (HK) Limited +// https://www.iana.org/domains/root/db/richardli.html +richardli + +// ricoh : Ricoh Company, Ltd. +// https://www.iana.org/domains/root/db/ricoh.html +ricoh + +// ril : Reliance Industries Limited +// https://www.iana.org/domains/root/db/ril.html +ril + +// rio : Empresa Municipal de Informática SA - IPLANRIO +// https://www.iana.org/domains/root/db/rio.html +rio + +// rip : Dog Beach, LLC +// https://www.iana.org/domains/root/db/rip.html +rip + +// rocks : Dog Beach, LLC +// https://www.iana.org/domains/root/db/rocks.html +rocks + +// rodeo : Registry Services, LLC +// https://www.iana.org/domains/root/db/rodeo.html +rodeo + +// rogers : Rogers Communications Canada Inc. +// https://www.iana.org/domains/root/db/rogers.html +rogers + +// room : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/room.html +room + +// rsvp : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/rsvp.html +rsvp + +// rugby : World Rugby Strategic Developments Limited +// https://www.iana.org/domains/root/db/rugby.html +rugby + +// ruhr : dotSaarland GmbH +// https://www.iana.org/domains/root/db/ruhr.html +ruhr + +// run : Binky Moon, LLC +// https://www.iana.org/domains/root/db/run.html +run + +// rwe : RWE AG +// https://www.iana.org/domains/root/db/rwe.html +rwe + +// ryukyu : BRregistry, Inc. +// https://www.iana.org/domains/root/db/ryukyu.html +ryukyu + +// saarland : dotSaarland GmbH +// https://www.iana.org/domains/root/db/saarland.html +saarland + +// safe : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/safe.html +safe + +// safety : Safety Registry Services, LLC. +// https://www.iana.org/domains/root/db/safety.html +safety + +// sakura : SAKURA Internet Inc. +// https://www.iana.org/domains/root/db/sakura.html +sakura + +// sale : Dog Beach, LLC +// https://www.iana.org/domains/root/db/sale.html +sale + +// salon : Binky Moon, LLC +// https://www.iana.org/domains/root/db/salon.html +salon + +// samsclub : Wal-Mart Stores, Inc. +// https://www.iana.org/domains/root/db/samsclub.html +samsclub + +// samsung : SAMSUNG SDS CO., LTD +// https://www.iana.org/domains/root/db/samsung.html +samsung + +// sandvik : Sandvik AB +// https://www.iana.org/domains/root/db/sandvik.html +sandvik + +// sandvikcoromant : Sandvik AB +// https://www.iana.org/domains/root/db/sandvikcoromant.html +sandvikcoromant + +// sanofi : Sanofi +// https://www.iana.org/domains/root/db/sanofi.html +sanofi + +// sap : SAP AG +// https://www.iana.org/domains/root/db/sap.html +sap + +// sarl : Binky Moon, LLC +// https://www.iana.org/domains/root/db/sarl.html +sarl + +// sas : Research IP LLC +// https://www.iana.org/domains/root/db/sas.html +sas + +// save : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/save.html +save + +// saxo : Saxo Bank A/S +// https://www.iana.org/domains/root/db/saxo.html +saxo + +// sbi : STATE BANK OF INDIA +// https://www.iana.org/domains/root/db/sbi.html +sbi + +// sbs : ShortDot SA +// https://www.iana.org/domains/root/db/sbs.html +sbs + +// scb : The Siam Commercial Bank Public Company Limited ("SCB") +// https://www.iana.org/domains/root/db/scb.html +scb + +// schaeffler : Schaeffler Technologies AG & Co. KG +// https://www.iana.org/domains/root/db/schaeffler.html +schaeffler + +// schmidt : SCHMIDT GROUPE S.A.S. +// https://www.iana.org/domains/root/db/schmidt.html +schmidt + +// scholarships : Scholarships.com, LLC +// https://www.iana.org/domains/root/db/scholarships.html +scholarships + +// school : Binky Moon, LLC +// https://www.iana.org/domains/root/db/school.html +school + +// schule : Binky Moon, LLC +// https://www.iana.org/domains/root/db/schule.html +schule + +// schwarz : Schwarz Domains und Services GmbH & Co. KG +// https://www.iana.org/domains/root/db/schwarz.html +schwarz + +// science : dot Science Limited +// https://www.iana.org/domains/root/db/science.html +science + +// scot : Dot Scot Registry Limited +// https://www.iana.org/domains/root/db/scot.html +scot + +// search : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/search.html +search + +// seat : SEAT, S.A. (Sociedad Unipersonal) +// https://www.iana.org/domains/root/db/seat.html +seat + +// secure : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/secure.html +secure + +// security : XYZ.COM LLC +// https://www.iana.org/domains/root/db/security.html +security + +// seek : Seek Limited +// https://www.iana.org/domains/root/db/seek.html +seek + +// select : Registry Services, LLC +// https://www.iana.org/domains/root/db/select.html +select + +// sener : Sener Ingeniería y Sistemas, S.A. +// https://www.iana.org/domains/root/db/sener.html +sener + +// services : Binky Moon, LLC +// https://www.iana.org/domains/root/db/services.html +services + +// seven : Seven West Media Ltd +// https://www.iana.org/domains/root/db/seven.html +seven + +// sew : SEW-EURODRIVE GmbH & Co KG +// https://www.iana.org/domains/root/db/sew.html +sew + +// sex : ICM Registry SX LLC +// https://www.iana.org/domains/root/db/sex.html +sex + +// sexy : Internet Naming Company LLC +// https://www.iana.org/domains/root/db/sexy.html +sexy + +// sfr : Societe Francaise du Radiotelephone - SFR +// https://www.iana.org/domains/root/db/sfr.html +sfr + +// shangrila : Shangri‐La International Hotel Management Limited +// https://www.iana.org/domains/root/db/shangrila.html +shangrila + +// sharp : Sharp Corporation +// https://www.iana.org/domains/root/db/sharp.html +sharp + +// shell : Shell Information Technology International Inc +// https://www.iana.org/domains/root/db/shell.html +shell + +// shia +// https://www.iana.org/domains/root/db/shia.html +shia + +// shiksha : Identity Digital Domains Limited +// https://www.iana.org/domains/root/db/shiksha.html +shiksha + +// shoes : Binky Moon, LLC +// https://www.iana.org/domains/root/db/shoes.html +shoes + +// shop : GMO Registry, Inc. +// https://www.iana.org/domains/root/db/shop.html +shop + +// shopping : Binky Moon, LLC +// https://www.iana.org/domains/root/db/shopping.html +shopping + +// shouji : Beijing Qihu Keji Co., Ltd. +// https://www.iana.org/domains/root/db/shouji.html +shouji + +// show : Binky Moon, LLC +// https://www.iana.org/domains/root/db/show.html +show + +// silk : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/silk.html +silk + +// sina : Sina Corporation +// https://www.iana.org/domains/root/db/sina.html +sina + +// singles : Binky Moon, LLC +// https://www.iana.org/domains/root/db/singles.html +singles + +// site : Radix Technologies Inc SEZC +// https://www.iana.org/domains/root/db/site.html +site + +// ski : Identity Digital Domains Limited +// https://www.iana.org/domains/root/db/ski.html +ski + +// skin : XYZ.COM LLC +// https://www.iana.org/domains/root/db/skin.html +skin + +// sky : Sky UK Limited +// https://www.iana.org/domains/root/db/sky.html +sky + +// skype : Microsoft Corporation +// https://www.iana.org/domains/root/db/skype.html +skype + +// sling : DISH Technologies L.L.C. +// https://www.iana.org/domains/root/db/sling.html +sling + +// smart : Smart Communications, Inc. (SMART) +// https://www.iana.org/domains/root/db/smart.html +smart + +// smile : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/smile.html +smile + +// sncf : Société Nationale SNCF +// https://www.iana.org/domains/root/db/sncf.html +sncf + +// soccer : Binky Moon, LLC +// https://www.iana.org/domains/root/db/soccer.html +soccer + +// social : Dog Beach, LLC +// https://www.iana.org/domains/root/db/social.html +social + +// softbank : SoftBank Group Corp. +// https://www.iana.org/domains/root/db/softbank.html +softbank + +// software : Dog Beach, LLC +// https://www.iana.org/domains/root/db/software.html +software + +// sohu : Sohu.com Limited +// https://www.iana.org/domains/root/db/sohu.html +sohu + +// solar : Binky Moon, LLC +// https://www.iana.org/domains/root/db/solar.html +solar + +// solutions : Binky Moon, LLC +// https://www.iana.org/domains/root/db/solutions.html +solutions + +// song : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/song.html +song + +// sony : Sony Group Corporation +// https://www.iana.org/domains/root/db/sony.html +sony + +// soy : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/soy.html +soy + +// spa : Asia Spa and Wellness Promotion Council Limited +// https://www.iana.org/domains/root/db/spa.html +spa + +// space : Radix Technologies Inc SEZC +// https://www.iana.org/domains/root/db/space.html +space + +// sport : SportAccord +// https://www.iana.org/domains/root/db/sport.html +sport + +// spot : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/spot.html +spot + +// srl : InterNetX, Corp +// https://www.iana.org/domains/root/db/srl.html +srl + +// stada : STADA Arzneimittel AG +// https://www.iana.org/domains/root/db/stada.html +stada + +// staples : Staples, Inc. +// https://www.iana.org/domains/root/db/staples.html +staples + +// star : Star India Private Limited +// https://www.iana.org/domains/root/db/star.html +star + +// statebank : STATE BANK OF INDIA +// https://www.iana.org/domains/root/db/statebank.html +statebank + +// statefarm : State Farm Mutual Automobile Insurance Company +// https://www.iana.org/domains/root/db/statefarm.html +statefarm + +// stc : Saudi Telecom Company +// https://www.iana.org/domains/root/db/stc.html +stc + +// stcgroup : Saudi Telecom Company +// https://www.iana.org/domains/root/db/stcgroup.html +stcgroup + +// stockholm : Stockholms kommun +// https://www.iana.org/domains/root/db/stockholm.html +stockholm + +// storage : XYZ.COM LLC +// https://www.iana.org/domains/root/db/storage.html +storage + +// store : Radix Technologies Inc SEZC +// https://www.iana.org/domains/root/db/store.html +store + +// stream : dot Stream Limited +// https://www.iana.org/domains/root/db/stream.html +stream + +// studio : Dog Beach, LLC +// https://www.iana.org/domains/root/db/studio.html +studio + +// study : Registry Services, LLC +// https://www.iana.org/domains/root/db/study.html +study + +// style : Binky Moon, LLC +// https://www.iana.org/domains/root/db/style.html +style + +// sucks : Vox Populi Registry Ltd. +// https://www.iana.org/domains/root/db/sucks.html +sucks + +// supplies : Binky Moon, LLC +// https://www.iana.org/domains/root/db/supplies.html +supplies + +// supply : Binky Moon, LLC +// https://www.iana.org/domains/root/db/supply.html +supply + +// support : Binky Moon, LLC +// https://www.iana.org/domains/root/db/support.html +support + +// surf : Registry Services, LLC +// https://www.iana.org/domains/root/db/surf.html +surf + +// surgery : Binky Moon, LLC +// https://www.iana.org/domains/root/db/surgery.html +surgery + +// suzuki : SUZUKI MOTOR CORPORATION +// https://www.iana.org/domains/root/db/suzuki.html +suzuki + +// swatch : The Swatch Group Ltd +// https://www.iana.org/domains/root/db/swatch.html +swatch + +// swiss : Swiss Confederation +// https://www.iana.org/domains/root/db/swiss.html +swiss + +// sydney : State of New South Wales, Department of Premier and Cabinet +// https://www.iana.org/domains/root/db/sydney.html +sydney + +// systems : Binky Moon, LLC +// https://www.iana.org/domains/root/db/systems.html +systems + +// tab : Tabcorp Holdings Limited +// https://www.iana.org/domains/root/db/tab.html +tab + +// taipei : Taipei City Government +// https://www.iana.org/domains/root/db/taipei.html +taipei + +// talk : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/talk.html +talk + +// taobao : Alibaba Group Holding Limited +// https://www.iana.org/domains/root/db/taobao.html +taobao + +// target : Target Domain Holdings, LLC +// https://www.iana.org/domains/root/db/target.html +target + +// tatamotors : Tata Motors Ltd +// https://www.iana.org/domains/root/db/tatamotors.html +tatamotors + +// tatar : Limited Liability Company "Coordination Center of Regional Domain of Tatarstan Republic" +// https://www.iana.org/domains/root/db/tatar.html +tatar + +// tattoo : Registry Services, LLC +// https://www.iana.org/domains/root/db/tattoo.html +tattoo + +// tax : Binky Moon, LLC +// https://www.iana.org/domains/root/db/tax.html +tax + +// taxi : Binky Moon, LLC +// https://www.iana.org/domains/root/db/taxi.html +taxi + +// tci +// https://www.iana.org/domains/root/db/tci.html +tci + +// tdk : TDK Corporation +// https://www.iana.org/domains/root/db/tdk.html +tdk + +// team : Binky Moon, LLC +// https://www.iana.org/domains/root/db/team.html +team + +// tech : Radix Technologies Inc SEZC +// https://www.iana.org/domains/root/db/tech.html +tech + +// technology : Binky Moon, LLC +// https://www.iana.org/domains/root/db/technology.html +technology + +// temasek : Temasek Holdings (Private) Limited +// https://www.iana.org/domains/root/db/temasek.html +temasek + +// tennis : Binky Moon, LLC +// https://www.iana.org/domains/root/db/tennis.html +tennis + +// teva : Teva Pharmaceutical Industries Limited +// https://www.iana.org/domains/root/db/teva.html +teva + +// thd : Home Depot Product Authority, LLC +// https://www.iana.org/domains/root/db/thd.html +thd + +// theater : Binky Moon, LLC +// https://www.iana.org/domains/root/db/theater.html +theater + +// theatre : XYZ.COM LLC +// https://www.iana.org/domains/root/db/theatre.html +theatre + +// tiaa : Teachers Insurance and Annuity Association of America +// https://www.iana.org/domains/root/db/tiaa.html +tiaa + +// tickets : XYZ.COM LLC +// https://www.iana.org/domains/root/db/tickets.html +tickets + +// tienda : Binky Moon, LLC +// https://www.iana.org/domains/root/db/tienda.html +tienda + +// tips : Binky Moon, LLC +// https://www.iana.org/domains/root/db/tips.html +tips + +// tires : Binky Moon, LLC +// https://www.iana.org/domains/root/db/tires.html +tires + +// tirol : punkt Tirol GmbH +// https://www.iana.org/domains/root/db/tirol.html +tirol + +// tjmaxx : The TJX Companies, Inc. +// https://www.iana.org/domains/root/db/tjmaxx.html +tjmaxx + +// tjx : The TJX Companies, Inc. +// https://www.iana.org/domains/root/db/tjx.html +tjx + +// tkmaxx : The TJX Companies, Inc. +// https://www.iana.org/domains/root/db/tkmaxx.html +tkmaxx + +// tmall : Alibaba Group Holding Limited +// https://www.iana.org/domains/root/db/tmall.html +tmall + +// today : Binky Moon, LLC +// https://www.iana.org/domains/root/db/today.html +today + +// tokyo : GMO Registry, Inc. +// https://www.iana.org/domains/root/db/tokyo.html +tokyo + +// tools : Binky Moon, LLC +// https://www.iana.org/domains/root/db/tools.html +tools + +// top : .TOP Registry +// https://www.iana.org/domains/root/db/top.html +top + +// toray : Toray Industries, Inc. +// https://www.iana.org/domains/root/db/toray.html +toray + +// toshiba : TOSHIBA Corporation +// https://www.iana.org/domains/root/db/toshiba.html +toshiba + +// total : TotalEnergies SE +// https://www.iana.org/domains/root/db/total.html +total + +// tours : Binky Moon, LLC +// https://www.iana.org/domains/root/db/tours.html +tours + +// town : Binky Moon, LLC +// https://www.iana.org/domains/root/db/town.html +town + +// toyota : TOYOTA MOTOR CORPORATION +// https://www.iana.org/domains/root/db/toyota.html +toyota + +// toys : Binky Moon, LLC +// https://www.iana.org/domains/root/db/toys.html +toys + +// trade : Elite Registry Limited +// https://www.iana.org/domains/root/db/trade.html +trade + +// trading : Dog Beach, LLC +// https://www.iana.org/domains/root/db/trading.html +trading + +// training : Binky Moon, LLC +// https://www.iana.org/domains/root/db/training.html +training + +// travel : Dog Beach, LLC +// https://www.iana.org/domains/root/db/travel.html +travel + +// travelers : Travelers TLD, LLC +// https://www.iana.org/domains/root/db/travelers.html +travelers + +// travelersinsurance : Travelers TLD, LLC +// https://www.iana.org/domains/root/db/travelersinsurance.html +travelersinsurance + +// trust : Internet Naming Company LLC +// https://www.iana.org/domains/root/db/trust.html +trust + +// trv : Travelers TLD, LLC +// https://www.iana.org/domains/root/db/trv.html +trv + +// tube : Latin American Telecom LLC +// https://www.iana.org/domains/root/db/tube.html +tube + +// tui : TUI AG +// https://www.iana.org/domains/root/db/tui.html +tui + +// tunes : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/tunes.html +tunes + +// tushu : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/tushu.html +tushu + +// tvs : T V SUNDRAM IYENGAR & SONS LIMITED +// https://www.iana.org/domains/root/db/tvs.html +tvs + +// ubank : National Australia Bank Limited +// https://www.iana.org/domains/root/db/ubank.html +ubank + +// ubs : UBS AG +// https://www.iana.org/domains/root/db/ubs.html +ubs + +// unicom : China United Network Communications Corporation Limited +// https://www.iana.org/domains/root/db/unicom.html +unicom + +// university : Binky Moon, LLC +// https://www.iana.org/domains/root/db/university.html +university + +// uno : Radix Technologies Inc SEZC +// https://www.iana.org/domains/root/db/uno.html +uno + +// uol : UBN INTERNET LTDA. +// https://www.iana.org/domains/root/db/uol.html +uol + +// ups : UPS Market Driver, Inc. +// https://www.iana.org/domains/root/db/ups.html +ups + +// vacations : Binky Moon, LLC +// https://www.iana.org/domains/root/db/vacations.html +vacations + +// vana : D3 Registry LLC +// https://www.iana.org/domains/root/db/vana.html +vana + +// vanguard : The Vanguard Group, Inc. +// https://www.iana.org/domains/root/db/vanguard.html +vanguard + +// vegas : Dot Vegas, Inc. +// https://www.iana.org/domains/root/db/vegas.html +vegas + +// ventures : Binky Moon, LLC +// https://www.iana.org/domains/root/db/ventures.html +ventures + +// verisign : VeriSign, Inc. +// https://www.iana.org/domains/root/db/verisign.html +verisign + +// versicherung : tldbox GmbH +// https://www.iana.org/domains/root/db/versicherung.html +versicherung + +// vet : Dog Beach, LLC +// https://www.iana.org/domains/root/db/vet.html +vet + +// viajes : Binky Moon, LLC +// https://www.iana.org/domains/root/db/viajes.html +viajes + +// video : Dog Beach, LLC +// https://www.iana.org/domains/root/db/video.html +video + +// vig : VIENNA INSURANCE GROUP AG Wiener Versicherung Gruppe +// https://www.iana.org/domains/root/db/vig.html +vig + +// viking : Viking River Cruises (Bermuda) Ltd. +// https://www.iana.org/domains/root/db/viking.html +viking + +// villas : Binky Moon, LLC +// https://www.iana.org/domains/root/db/villas.html +villas + +// vin : Binky Moon, LLC +// https://www.iana.org/domains/root/db/vin.html +vin + +// vip : Registry Services, LLC +// https://www.iana.org/domains/root/db/vip.html +vip + +// virgin : Virgin Enterprises Limited +// https://www.iana.org/domains/root/db/virgin.html +virgin + +// visa : Visa Worldwide Pte. Limited +// https://www.iana.org/domains/root/db/visa.html +visa + +// vision : Binky Moon, LLC +// https://www.iana.org/domains/root/db/vision.html +vision + +// viva : Saudi Telecom Company +// https://www.iana.org/domains/root/db/viva.html +viva + +// vivo : Telefonica Brasil S.A. +// https://www.iana.org/domains/root/db/vivo.html +vivo + +// vlaanderen : DNS.be vzw +// https://www.iana.org/domains/root/db/vlaanderen.html +vlaanderen + +// vodka : Registry Services, LLC +// https://www.iana.org/domains/root/db/vodka.html +vodka + +// volvo : Volvo Holding Sverige Aktiebolag +// https://www.iana.org/domains/root/db/volvo.html +volvo + +// vote : Monolith Registry LLC +// https://www.iana.org/domains/root/db/vote.html +vote + +// voting : Valuetainment Corp. +// https://www.iana.org/domains/root/db/voting.html +voting + +// voto : Monolith Registry LLC +// https://www.iana.org/domains/root/db/voto.html +voto + +// voyage : Binky Moon, LLC +// https://www.iana.org/domains/root/db/voyage.html +voyage + +// wales : Nominet UK +// https://www.iana.org/domains/root/db/wales.html +wales + +// walmart : Wal-Mart Stores, Inc. +// https://www.iana.org/domains/root/db/walmart.html +walmart + +// walter : Sandvik AB +// https://www.iana.org/domains/root/db/walter.html +walter + +// wang : Zodiac Wang Limited +// https://www.iana.org/domains/root/db/wang.html +wang + +// wanggou : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/wanggou.html +wanggou + +// watch : Binky Moon, LLC +// https://www.iana.org/domains/root/db/watch.html +watch + +// watches : Identity Digital Domains Limited +// https://www.iana.org/domains/root/db/watches.html +watches + +// weather : International Business Machines Corporation +// https://www.iana.org/domains/root/db/weather.html +weather + +// weatherchannel : The Weather Company, LLC +// https://www.iana.org/domains/root/db/weatherchannel.html +weatherchannel + +// webcam : dot Webcam Limited +// https://www.iana.org/domains/root/db/webcam.html +webcam + +// weber : Saint-Gobain Weber SA +// https://www.iana.org/domains/root/db/weber.html +weber + +// website : Radix Technologies Inc SEZC +// https://www.iana.org/domains/root/db/website.html +website + +// wed +// https://www.iana.org/domains/root/db/wed.html +wed + +// wedding : Registry Services, LLC +// https://www.iana.org/domains/root/db/wedding.html +wedding + +// weibo : Sina Corporation +// https://www.iana.org/domains/root/db/weibo.html +weibo + +// weir : Weir Group IP Limited +// https://www.iana.org/domains/root/db/weir.html +weir + +// whoswho : Who's Who Registry +// https://www.iana.org/domains/root/db/whoswho.html +whoswho + +// wien : punkt.wien GmbH +// https://www.iana.org/domains/root/db/wien.html +wien + +// wiki : Registry Services, LLC +// https://www.iana.org/domains/root/db/wiki.html +wiki + +// williamhill : William Hill Organization Limited +// https://www.iana.org/domains/root/db/williamhill.html +williamhill + +// win : First Registry Limited +// https://www.iana.org/domains/root/db/win.html +win + +// windows : Microsoft Corporation +// https://www.iana.org/domains/root/db/windows.html +windows + +// wine : Binky Moon, LLC +// https://www.iana.org/domains/root/db/wine.html +wine + +// winners : The TJX Companies, Inc. +// https://www.iana.org/domains/root/db/winners.html +winners + +// wme : William Morris Endeavor Entertainment, LLC +// https://www.iana.org/domains/root/db/wme.html +wme + +// wolterskluwer : Wolters Kluwer N.V. +// https://www.iana.org/domains/root/db/wolterskluwer.html +wolterskluwer + +// woodside : Woodside Petroleum Limited +// https://www.iana.org/domains/root/db/woodside.html +woodside + +// work : Registry Services, LLC +// https://www.iana.org/domains/root/db/work.html +work + +// works : Binky Moon, LLC +// https://www.iana.org/domains/root/db/works.html +works + +// world : Binky Moon, LLC +// https://www.iana.org/domains/root/db/world.html +world + +// wow : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/wow.html +wow + +// wtc : World Trade Centers Association, Inc. +// https://www.iana.org/domains/root/db/wtc.html +wtc + +// wtf : Binky Moon, LLC +// https://www.iana.org/domains/root/db/wtf.html +wtf + +// xbox : Microsoft Corporation +// https://www.iana.org/domains/root/db/xbox.html +xbox + +// xerox : Xerox DNHC LLC +// https://www.iana.org/domains/root/db/xerox.html +xerox + +// xihuan : Beijing Qihu Keji Co., Ltd. +// https://www.iana.org/domains/root/db/xihuan.html +xihuan + +// xin : Elegant Leader Limited +// https://www.iana.org/domains/root/db/xin.html +xin + +// xn--11b4c3d : VeriSign Sarl +// https://www.iana.org/domains/root/db/xn--11b4c3d.html +कॉम + +// xn--1ck2e1b : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/xn--1ck2e1b.html +セール + +// xn--1qqw23a : Guangzhou YU Wei Information Technology Co., Ltd. +// https://www.iana.org/domains/root/db/xn--1qqw23a.html +佛山 + +// xn--30rr7y : Excellent First Limited +// https://www.iana.org/domains/root/db/xn--30rr7y.html +慈善 + +// xn--3bst00m : Eagle Horizon Limited +// https://www.iana.org/domains/root/db/xn--3bst00m.html +集团 + +// xn--3ds443g : Beijing TLD Registry Technology Limited +// https://www.iana.org/domains/root/db/xn--3ds443g.html +在线 + +// xn--3pxu8k : VeriSign Sarl +// https://www.iana.org/domains/root/db/xn--3pxu8k.html +点看 + +// xn--42c2d9a : VeriSign Sarl +// https://www.iana.org/domains/root/db/xn--42c2d9a.html +คอม + +// xn--45q11c : Zodiac Gemini Ltd +// https://www.iana.org/domains/root/db/xn--45q11c.html +八卦 + +// xn--4gbrim : Helium TLDs Ltd +// https://www.iana.org/domains/root/db/xn--4gbrim.html +موقع + +// xn--55qw42g : China Organizational Name Administration Center +// https://www.iana.org/domains/root/db/xn--55qw42g.html +公益 + +// xn--55qx5d : China Internet Network Information Center (CNNIC) +// https://www.iana.org/domains/root/db/xn--55qx5d.html +公司 + +// xn--5su34j936bgsg : Shangri‐La International Hotel Management Limited +// https://www.iana.org/domains/root/db/xn--5su34j936bgsg.html +香格里拉 + +// xn--5tzm5g : Global Website TLD Asia Limited +// https://www.iana.org/domains/root/db/xn--5tzm5g.html +网站 + +// xn--6frz82g : Identity Digital Domains Limited +// https://www.iana.org/domains/root/db/xn--6frz82g.html +移动 + +// xn--6qq986b3xl : Tycoon Treasure Limited +// https://www.iana.org/domains/root/db/xn--6qq986b3xl.html +我爱你 + +// xn--80adxhks : Foundation for Assistance for Internet Technologies and Infrastructure Development (FAITID) +// https://www.iana.org/domains/root/db/xn--80adxhks.html +москва + +// xn--80aqecdr1a : Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication) +// https://www.iana.org/domains/root/db/xn--80aqecdr1a.html +католик + +// xn--80asehdb : CORE Association +// https://www.iana.org/domains/root/db/xn--80asehdb.html +онлайн + +// xn--80aswg : CORE Association +// https://www.iana.org/domains/root/db/xn--80aswg.html +сайт + +// xn--8y0a063a : China United Network Communications Corporation Limited +// https://www.iana.org/domains/root/db/xn--8y0a063a.html +联通 + +// xn--9dbq2a : VeriSign Sarl +// https://www.iana.org/domains/root/db/xn--9dbq2a.html +קום + +// xn--9et52u : RISE VICTORY LIMITED +// https://www.iana.org/domains/root/db/xn--9et52u.html +时尚 + +// xn--9krt00a : Sina Corporation +// https://www.iana.org/domains/root/db/xn--9krt00a.html +微博 + +// xn--b4w605ferd : Temasek Holdings (Private) Limited +// https://www.iana.org/domains/root/db/xn--b4w605ferd.html +淡马锡 + +// xn--bck1b9a5dre4c : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/xn--bck1b9a5dre4c.html +ファッション + +// xn--c1avg : Public Interest Registry +// https://www.iana.org/domains/root/db/xn--c1avg.html +орг + +// xn--c2br7g : VeriSign Sarl +// https://www.iana.org/domains/root/db/xn--c2br7g.html +नेट + +// xn--cck2b3b : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/xn--cck2b3b.html +ストア + +// xn--cckwcxetd : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/xn--cckwcxetd.html +アマゾン + +// xn--cg4bki : SAMSUNG SDS CO., LTD +// https://www.iana.org/domains/root/db/xn--cg4bki.html +삼성 + +// xn--czr694b : Internet DotTrademark Organisation Limited +// https://www.iana.org/domains/root/db/xn--czr694b.html +商标 + +// xn--czrs0t : Binky Moon, LLC +// https://www.iana.org/domains/root/db/xn--czrs0t.html +商店 + +// xn--czru2d : Zodiac Aquarius Limited +// https://www.iana.org/domains/root/db/xn--czru2d.html +商城 + +// xn--d1acj3b : The Foundation for Network Initiatives “The Smart Internet” +// https://www.iana.org/domains/root/db/xn--d1acj3b.html +дети + +// xn--eckvdtc9d : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/xn--eckvdtc9d.html +ポイント + +// xn--efvy88h : Guangzhou YU Wei Information Technology Co., Ltd. +// https://www.iana.org/domains/root/db/xn--efvy88h.html +新闻 + +// xn--fct429k : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/xn--fct429k.html +家電 + +// xn--fhbei : VeriSign Sarl +// https://www.iana.org/domains/root/db/xn--fhbei.html +كوم + +// xn--fiq228c5hs : Beijing TLD Registry Technology Limited +// https://www.iana.org/domains/root/db/xn--fiq228c5hs.html +中文网 + +// xn--fiq64b : CITIC Group Corporation +// https://www.iana.org/domains/root/db/xn--fiq64b.html +中信 + +// xn--fjq720a : Binky Moon, LLC +// https://www.iana.org/domains/root/db/xn--fjq720a.html +娱乐 + +// xn--flw351e : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/xn--flw351e.html +谷歌 + +// xn--fzys8d69uvgm : PCCW Enterprises Limited +// https://www.iana.org/domains/root/db/xn--fzys8d69uvgm.html +電訊盈科 + +// xn--g2xx48c : Nawang Heli(Xiamen) Network Service Co., LTD. +// https://www.iana.org/domains/root/db/xn--g2xx48c.html +购物 + +// xn--gckr3f0f : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/xn--gckr3f0f.html +クラウド + +// xn--gk3at1e : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/xn--gk3at1e.html +通販 + +// xn--hxt814e : Zodiac Taurus Limited +// https://www.iana.org/domains/root/db/xn--hxt814e.html +网店 + +// xn--i1b6b1a6a2e : Public Interest Registry +// https://www.iana.org/domains/root/db/xn--i1b6b1a6a2e.html +संगठन + +// xn--imr513n : Internet DotTrademark Organisation Limited +// https://www.iana.org/domains/root/db/xn--imr513n.html +餐厅 + +// xn--io0a7i : China Internet Network Information Center (CNNIC) +// https://www.iana.org/domains/root/db/xn--io0a7i.html +网络 + +// xn--j1aef : VeriSign Sarl +// https://www.iana.org/domains/root/db/xn--j1aef.html +ком + +// xn--jlq480n2rg : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/xn--jlq480n2rg.html +亚马逊 + +// xn--jvr189m : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/xn--jvr189m.html +食品 + +// xn--kcrx77d1x4a : Koninklijke Philips N.V. +// https://www.iana.org/domains/root/db/xn--kcrx77d1x4a.html +飞利浦 + +// xn--kput3i : Beijing RITT-Net Technology Development Co., Ltd +// https://www.iana.org/domains/root/db/xn--kput3i.html +手机 + +// xn--mgba3a3ejt : Aramco Services Company +// https://www.iana.org/domains/root/db/xn--mgba3a3ejt.html +ارامكو + +// xn--mgba7c0bbn0a : Competrol (Luxembourg) Sarl +// https://www.iana.org/domains/root/db/xn--mgba7c0bbn0a.html +العليان + +// xn--mgbab2bd : CORE Association +// https://www.iana.org/domains/root/db/xn--mgbab2bd.html +بازار + +// xn--mgbca7dzdo : Abu Dhabi Systems and Information Centre +// https://www.iana.org/domains/root/db/xn--mgbca7dzdo.html +ابوظبي + +// xn--mgbi4ecexp : Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication) +// https://www.iana.org/domains/root/db/xn--mgbi4ecexp.html +كاثوليك + +// xn--mgbt3dhd +// https://www.iana.org/domains/root/db/xn--mgbt3dhd.html +همراه + +// xn--mk1bu44c : VeriSign Sarl +// https://www.iana.org/domains/root/db/xn--mk1bu44c.html +닷컴 + +// xn--mxtq1m : Net-Chinese Co., Ltd. +// https://www.iana.org/domains/root/db/xn--mxtq1m.html +政府 + +// xn--ngbc5azd : International Domain Registry Pty. Ltd. +// https://www.iana.org/domains/root/db/xn--ngbc5azd.html +شبكة + +// xn--ngbe9e0a : Kuwait Finance House +// https://www.iana.org/domains/root/db/xn--ngbe9e0a.html +بيتك + +// xn--ngbrx : League of Arab States +// https://www.iana.org/domains/root/db/xn--ngbrx.html +عرب + +// xn--nqv7f : Public Interest Registry +// https://www.iana.org/domains/root/db/xn--nqv7f.html +机构 + +// xn--nqv7fs00ema : Public Interest Registry +// https://www.iana.org/domains/root/db/xn--nqv7fs00ema.html +组织机构 + +// xn--nyqy26a : Stable Tone Limited +// https://www.iana.org/domains/root/db/xn--nyqy26a.html +健康 + +// xn--otu796d : Jiang Yu Liang Cai Technology Company Limited +// https://www.iana.org/domains/root/db/xn--otu796d.html +招聘 + +// xn--p1acf : Rusnames Limited +// https://www.iana.org/domains/root/db/xn--p1acf.html +рус + +// xn--pssy2u : VeriSign Sarl +// https://www.iana.org/domains/root/db/xn--pssy2u.html +大拿 + +// xn--q9jyb4c : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/xn--q9jyb4c.html +みんな + +// xn--qcka1pmc : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/xn--qcka1pmc.html +グーグル + +// xn--rhqv96g : Stable Tone Limited +// https://www.iana.org/domains/root/db/xn--rhqv96g.html +世界 + +// xn--rovu88b : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/xn--rovu88b.html +書籍 + +// xn--ses554g : KNET Co., Ltd. +// https://www.iana.org/domains/root/db/xn--ses554g.html +网址 + +// xn--t60b56a : VeriSign Sarl +// https://www.iana.org/domains/root/db/xn--t60b56a.html +닷넷 + +// xn--tckwe : VeriSign Sarl +// https://www.iana.org/domains/root/db/xn--tckwe.html +コム + +// xn--tiq49xqyj : Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication) +// https://www.iana.org/domains/root/db/xn--tiq49xqyj.html +天主教 + +// xn--unup4y : Binky Moon, LLC +// https://www.iana.org/domains/root/db/xn--unup4y.html +游戏 + +// xn--vermgensberater-ctb : Deutsche Vermögensberatung Aktiengesellschaft DVAG +// https://www.iana.org/domains/root/db/xn--vermgensberater-ctb.html +vermögensberater + +// xn--vermgensberatung-pwb : Deutsche Vermögensberatung Aktiengesellschaft DVAG +// https://www.iana.org/domains/root/db/xn--vermgensberatung-pwb.html +vermögensberatung + +// xn--vhquv : Binky Moon, LLC +// https://www.iana.org/domains/root/db/xn--vhquv.html +企业 + +// xn--vuq861b : Beijing Tele-info Technology Co., Ltd. +// https://www.iana.org/domains/root/db/xn--vuq861b.html +信息 + +// xn--w4r85el8fhu5dnra : Kerry Trading Co. Limited +// https://www.iana.org/domains/root/db/xn--w4r85el8fhu5dnra.html +嘉里大酒店 + +// xn--w4rs40l : Kerry Trading Co. Limited +// https://www.iana.org/domains/root/db/xn--w4rs40l.html +嘉里 + +// xn--xhq521b : Guangzhou YU Wei Information Technology Co., Ltd. +// https://www.iana.org/domains/root/db/xn--xhq521b.html +广东 + +// xn--zfr164b : China Organizational Name Administration Center +// https://www.iana.org/domains/root/db/xn--zfr164b.html +政务 + +// xyz : XYZ.COM LLC +// https://www.iana.org/domains/root/db/xyz.html +xyz + +// yachts : XYZ.COM LLC +// https://www.iana.org/domains/root/db/yachts.html +yachts + +// yahoo : Yahoo Inc. +// https://www.iana.org/domains/root/db/yahoo.html +yahoo + +// yamaxun : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/yamaxun.html +yamaxun + +// yandex : YANDEX, LLC +// https://www.iana.org/domains/root/db/yandex.html +yandex + +// yodobashi : YODOBASHI CAMERA CO.,LTD. +// https://www.iana.org/domains/root/db/yodobashi.html +yodobashi + +// yoga : Registry Services, LLC +// https://www.iana.org/domains/root/db/yoga.html +yoga + +// yokohama : GMO Registry, Inc. +// https://www.iana.org/domains/root/db/yokohama.html +yokohama + +// you : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/you.html +you + +// youtube : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/youtube.html +youtube + +// yun : Beijing Qihu Keji Co., Ltd. +// https://www.iana.org/domains/root/db/yun.html +yun + +// zappos : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/zappos.html +zappos + +// zara : Industria de Diseño Textil, S.A. (INDITEX, S.A.) +// https://www.iana.org/domains/root/db/zara.html +zara + +// zero : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/zero.html +zero + +// zip : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/zip.html +zip + +// zone : Binky Moon, LLC +// https://www.iana.org/domains/root/db/zone.html +zone + +// zuerich : Kanton Zürich (Canton of Zurich) +// https://www.iana.org/domains/root/db/zuerich.html +zuerich + +// ===END ICANN DOMAINS=== + +// ===BEGIN PRIVATE DOMAINS=== + +// (Note: these are in alphabetical order by company name) + +// .KRD : https://nic.krd +co.krd +edu.krd + +// .pl domains (grandfathered) +art.pl +gliwice.pl +krakow.pl +poznan.pl +wroc.pl +zakopane.pl + +// 12CHARS : https://12chars.com +// Submitted by Kenny Niehage +12chars.dev +12chars.it +12chars.pro + +// 1GB LLC : https://www.1gb.ua/ +// Submitted by 1GB LLC +cc.ua +inf.ua +ltd.ua + +// 611 blockchain domain name system : https://sixone.one/ +611.to + +// A2 Hosting +// Submitted by Tyler Hall +a2hosted.com +cpserver.com + +// Acorn Labs : https://acorn.io +// Submitted by Craig Jellick +*.on-acorn.io + +// ActiveTrail : https://www.activetrail.biz/ +// Submitted by Ofer Kalaora +activetrail.biz + +// Adaptable.io : https://adaptable.io +// Submitted by Mark Terrel +adaptable.app + +// addr.tools : https://addr.tools/ +// Submitted by Brian Shea +myaddr.dev +myaddr.io +dyn.addr.tools +myaddr.tools + +// Adobe : https://www.adobe.com/ +// Submitted by Ian Boston and Lars Trieloff +adobeaemcloud.com +*.dev.adobeaemcloud.com +aem.live +hlx.live +adobeaemcloud.net +aem.network +aem.page +hlx.page +aem.reviews + +// Adobe Developer Platform : https://developer.adobe.com +// Submitted by Jesse MacFadyen +adobeio-static.net +adobeioruntime.net + +// Africa.com Web Solutions Ltd : https://registry.africa.com +// Submitted by Gavin Brown +africa.com + +// AgentbaseAI Inc. : https://assistant-ui.com +// Submitted by Simon Farshid +*.auiusercontent.com + +// Agnat sp. z o.o. : https://domena.pl +// Submitted by Przemyslaw Plewa +beep.pl + +// Aiven : https://aiven.io/ +// Submitted by Aiven Security Team +aiven.app +aivencloud.com + +// Akamai : https://www.akamai.com/ +// Submitted by Akamai Team +akadns.net +akamai.net +akamai-staging.net +akamaiedge.net +akamaiedge-staging.net +akamaihd.net +akamaihd-staging.net +akamaiorigin.net +akamaiorigin-staging.net +akamaized.net +akamaized-staging.net +edgekey.net +edgekey-staging.net +edgesuite.net +edgesuite-staging.net + +// alboto.ca : http://alboto.ca +// Submitted by Anton Avramov +barsy.ca + +// Alces Software Ltd : http://alces-software.com +// Submitted by Mark J. Titorenko +*.compute.estate +*.alces.network + +// Alibaba Cloud API Gateway +// Submitted by Alibaba Cloud Security +alibabacloudcs.com + +// all-inkl.com : https://all-inkl.com +// Submitted by Werner Kaltofen +kasserver.com + +// Altervista : https://www.altervista.org +// Submitted by Carlo Cannas +altervista.org + +// alwaysdata : https://www.alwaysdata.com +// Submitted by Cyril +alwaysdata.net + +// Amaze Software : https://amaze.co +// Submitted by Domain Admin +myamaze.net + +// Amazon : https://www.amazon.com/ +// Submitted by AWS Security +// Subsections of Amazon/subsidiaries will appear until "concludes" tag + +// Amazon API Gateway +// Submitted by AWS Security +// Reference: 6a4f5a95-8c7d-4077-a7af-9cf1abec0a53 +execute-api.cn-north-1.amazonaws.com.cn +execute-api.cn-northwest-1.amazonaws.com.cn +execute-api.af-south-1.amazonaws.com +execute-api.ap-east-1.amazonaws.com +execute-api.ap-northeast-1.amazonaws.com +execute-api.ap-northeast-2.amazonaws.com +execute-api.ap-northeast-3.amazonaws.com +execute-api.ap-south-1.amazonaws.com +execute-api.ap-south-2.amazonaws.com +execute-api.ap-southeast-1.amazonaws.com +execute-api.ap-southeast-2.amazonaws.com +execute-api.ap-southeast-3.amazonaws.com +execute-api.ap-southeast-4.amazonaws.com +execute-api.ap-southeast-5.amazonaws.com +execute-api.ca-central-1.amazonaws.com +execute-api.ca-west-1.amazonaws.com +execute-api.eu-central-1.amazonaws.com +execute-api.eu-central-2.amazonaws.com +execute-api.eu-north-1.amazonaws.com +execute-api.eu-south-1.amazonaws.com +execute-api.eu-south-2.amazonaws.com +execute-api.eu-west-1.amazonaws.com +execute-api.eu-west-2.amazonaws.com +execute-api.eu-west-3.amazonaws.com +execute-api.il-central-1.amazonaws.com +execute-api.me-central-1.amazonaws.com +execute-api.me-south-1.amazonaws.com +execute-api.sa-east-1.amazonaws.com +execute-api.us-east-1.amazonaws.com +execute-api.us-east-2.amazonaws.com +execute-api.us-gov-east-1.amazonaws.com +execute-api.us-gov-west-1.amazonaws.com +execute-api.us-west-1.amazonaws.com +execute-api.us-west-2.amazonaws.com + +// Amazon CloudFront +// Submitted by Donavan Miller +// Reference: 54144616-fd49-4435-8535-19c6a601bdb3 +cloudfront.net + +// Amazon Cognito +// Submitted by AWS Security +// Reference: e7c02dc1-02f4-4a23-bde3-a8527c830127 +auth.af-south-1.amazoncognito.com +auth.ap-east-1.amazoncognito.com +auth.ap-northeast-1.amazoncognito.com +auth.ap-northeast-2.amazoncognito.com +auth.ap-northeast-3.amazoncognito.com +auth.ap-south-1.amazoncognito.com +auth.ap-south-2.amazoncognito.com +auth.ap-southeast-1.amazoncognito.com +auth.ap-southeast-2.amazoncognito.com +auth.ap-southeast-3.amazoncognito.com +auth.ap-southeast-4.amazoncognito.com +auth.ap-southeast-5.amazoncognito.com +auth.ap-southeast-7.amazoncognito.com +auth.ca-central-1.amazoncognito.com +auth.ca-west-1.amazoncognito.com +auth.eu-central-1.amazoncognito.com +auth.eu-central-2.amazoncognito.com +auth.eu-north-1.amazoncognito.com +auth.eu-south-1.amazoncognito.com +auth.eu-south-2.amazoncognito.com +auth.eu-west-1.amazoncognito.com +auth.eu-west-2.amazoncognito.com +auth.eu-west-3.amazoncognito.com +auth.il-central-1.amazoncognito.com +auth.me-central-1.amazoncognito.com +auth.me-south-1.amazoncognito.com +auth.mx-central-1.amazoncognito.com +auth.sa-east-1.amazoncognito.com +auth.us-east-1.amazoncognito.com +auth-fips.us-east-1.amazoncognito.com +auth.us-east-2.amazoncognito.com +auth-fips.us-east-2.amazoncognito.com +auth-fips.us-gov-east-1.amazoncognito.com +auth-fips.us-gov-west-1.amazoncognito.com +auth.us-west-1.amazoncognito.com +auth-fips.us-west-1.amazoncognito.com +auth.us-west-2.amazoncognito.com +auth-fips.us-west-2.amazoncognito.com + +// Amazon EC2 +// Submitted by Luke Wells +// Reference: 4c38fa71-58ac-4768-99e5-689c1767e537 +*.compute.amazonaws.com.cn +*.compute.amazonaws.com +*.compute-1.amazonaws.com +us-east-1.amazonaws.com + +// Amazon EMR +// Submitted by AWS Security +// Reference: 82f43f9f-bbb8-400e-8349-854f5a62f20d +emrappui-prod.cn-north-1.amazonaws.com.cn +emrnotebooks-prod.cn-north-1.amazonaws.com.cn +emrstudio-prod.cn-north-1.amazonaws.com.cn +emrappui-prod.cn-northwest-1.amazonaws.com.cn +emrnotebooks-prod.cn-northwest-1.amazonaws.com.cn +emrstudio-prod.cn-northwest-1.amazonaws.com.cn +emrappui-prod.af-south-1.amazonaws.com +emrnotebooks-prod.af-south-1.amazonaws.com +emrstudio-prod.af-south-1.amazonaws.com +emrappui-prod.ap-east-1.amazonaws.com +emrnotebooks-prod.ap-east-1.amazonaws.com +emrstudio-prod.ap-east-1.amazonaws.com +emrappui-prod.ap-northeast-1.amazonaws.com +emrnotebooks-prod.ap-northeast-1.amazonaws.com +emrstudio-prod.ap-northeast-1.amazonaws.com +emrappui-prod.ap-northeast-2.amazonaws.com +emrnotebooks-prod.ap-northeast-2.amazonaws.com +emrstudio-prod.ap-northeast-2.amazonaws.com +emrappui-prod.ap-northeast-3.amazonaws.com +emrnotebooks-prod.ap-northeast-3.amazonaws.com +emrstudio-prod.ap-northeast-3.amazonaws.com +emrappui-prod.ap-south-1.amazonaws.com +emrnotebooks-prod.ap-south-1.amazonaws.com +emrstudio-prod.ap-south-1.amazonaws.com +emrappui-prod.ap-south-2.amazonaws.com +emrnotebooks-prod.ap-south-2.amazonaws.com +emrstudio-prod.ap-south-2.amazonaws.com +emrappui-prod.ap-southeast-1.amazonaws.com +emrnotebooks-prod.ap-southeast-1.amazonaws.com +emrstudio-prod.ap-southeast-1.amazonaws.com +emrappui-prod.ap-southeast-2.amazonaws.com +emrnotebooks-prod.ap-southeast-2.amazonaws.com +emrstudio-prod.ap-southeast-2.amazonaws.com +emrappui-prod.ap-southeast-3.amazonaws.com +emrnotebooks-prod.ap-southeast-3.amazonaws.com +emrstudio-prod.ap-southeast-3.amazonaws.com +emrappui-prod.ap-southeast-4.amazonaws.com +emrnotebooks-prod.ap-southeast-4.amazonaws.com +emrstudio-prod.ap-southeast-4.amazonaws.com +emrappui-prod.ca-central-1.amazonaws.com +emrnotebooks-prod.ca-central-1.amazonaws.com +emrstudio-prod.ca-central-1.amazonaws.com +emrappui-prod.ca-west-1.amazonaws.com +emrnotebooks-prod.ca-west-1.amazonaws.com +emrstudio-prod.ca-west-1.amazonaws.com +emrappui-prod.eu-central-1.amazonaws.com +emrnotebooks-prod.eu-central-1.amazonaws.com +emrstudio-prod.eu-central-1.amazonaws.com +emrappui-prod.eu-central-2.amazonaws.com +emrnotebooks-prod.eu-central-2.amazonaws.com +emrstudio-prod.eu-central-2.amazonaws.com +emrappui-prod.eu-north-1.amazonaws.com +emrnotebooks-prod.eu-north-1.amazonaws.com +emrstudio-prod.eu-north-1.amazonaws.com +emrappui-prod.eu-south-1.amazonaws.com +emrnotebooks-prod.eu-south-1.amazonaws.com +emrstudio-prod.eu-south-1.amazonaws.com +emrappui-prod.eu-south-2.amazonaws.com +emrnotebooks-prod.eu-south-2.amazonaws.com +emrstudio-prod.eu-south-2.amazonaws.com +emrappui-prod.eu-west-1.amazonaws.com +emrnotebooks-prod.eu-west-1.amazonaws.com +emrstudio-prod.eu-west-1.amazonaws.com +emrappui-prod.eu-west-2.amazonaws.com +emrnotebooks-prod.eu-west-2.amazonaws.com +emrstudio-prod.eu-west-2.amazonaws.com +emrappui-prod.eu-west-3.amazonaws.com +emrnotebooks-prod.eu-west-3.amazonaws.com +emrstudio-prod.eu-west-3.amazonaws.com +emrappui-prod.il-central-1.amazonaws.com +emrnotebooks-prod.il-central-1.amazonaws.com +emrstudio-prod.il-central-1.amazonaws.com +emrappui-prod.me-central-1.amazonaws.com +emrnotebooks-prod.me-central-1.amazonaws.com +emrstudio-prod.me-central-1.amazonaws.com +emrappui-prod.me-south-1.amazonaws.com +emrnotebooks-prod.me-south-1.amazonaws.com +emrstudio-prod.me-south-1.amazonaws.com +emrappui-prod.sa-east-1.amazonaws.com +emrnotebooks-prod.sa-east-1.amazonaws.com +emrstudio-prod.sa-east-1.amazonaws.com +emrappui-prod.us-east-1.amazonaws.com +emrnotebooks-prod.us-east-1.amazonaws.com +emrstudio-prod.us-east-1.amazonaws.com +emrappui-prod.us-east-2.amazonaws.com +emrnotebooks-prod.us-east-2.amazonaws.com +emrstudio-prod.us-east-2.amazonaws.com +emrappui-prod.us-gov-east-1.amazonaws.com +emrnotebooks-prod.us-gov-east-1.amazonaws.com +emrstudio-prod.us-gov-east-1.amazonaws.com +emrappui-prod.us-gov-west-1.amazonaws.com +emrnotebooks-prod.us-gov-west-1.amazonaws.com +emrstudio-prod.us-gov-west-1.amazonaws.com +emrappui-prod.us-west-1.amazonaws.com +emrnotebooks-prod.us-west-1.amazonaws.com +emrstudio-prod.us-west-1.amazonaws.com +emrappui-prod.us-west-2.amazonaws.com +emrnotebooks-prod.us-west-2.amazonaws.com +emrstudio-prod.us-west-2.amazonaws.com + +// Amazon Managed Workflows for Apache Airflow +// Submitted by AWS Security +// Reference: bfd043cc-2816-451d-894e-612c6b61a438 +*.airflow.af-south-1.on.aws +*.airflow.ap-east-1.on.aws +*.airflow.ap-northeast-1.on.aws +*.airflow.ap-northeast-2.on.aws +*.airflow.ap-northeast-3.on.aws +*.airflow.ap-south-1.on.aws +*.airflow.ap-south-2.on.aws +*.airflow.ap-southeast-1.on.aws +*.airflow.ap-southeast-2.on.aws +*.airflow.ap-southeast-3.on.aws +*.airflow.ap-southeast-4.on.aws +*.airflow.ap-southeast-5.on.aws +*.airflow.ca-central-1.on.aws +*.airflow.ca-west-1.on.aws +*.airflow.eu-central-1.on.aws +*.airflow.eu-central-2.on.aws +*.airflow.eu-north-1.on.aws +*.airflow.eu-south-1.on.aws +*.airflow.eu-south-2.on.aws +*.airflow.eu-west-1.on.aws +*.airflow.eu-west-2.on.aws +*.airflow.eu-west-3.on.aws +*.airflow.il-central-1.on.aws +*.airflow.me-central-1.on.aws +*.airflow.me-south-1.on.aws +*.airflow.sa-east-1.on.aws +*.airflow.us-east-1.on.aws +*.airflow.us-east-2.on.aws +*.airflow.us-west-1.on.aws +*.airflow.us-west-2.on.aws +*.cn-north-1.airflow.amazonaws.com.cn +*.cn-northwest-1.airflow.amazonaws.com.cn +*.airflow.cn-north-1.on.amazonwebservices.com.cn +*.airflow.cn-northwest-1.on.amazonwebservices.com.cn +*.af-south-1.airflow.amazonaws.com +*.ap-east-1.airflow.amazonaws.com +*.ap-northeast-1.airflow.amazonaws.com +*.ap-northeast-2.airflow.amazonaws.com +*.ap-northeast-3.airflow.amazonaws.com +*.ap-south-1.airflow.amazonaws.com +*.ap-south-2.airflow.amazonaws.com +*.ap-southeast-1.airflow.amazonaws.com +*.ap-southeast-2.airflow.amazonaws.com +*.ap-southeast-3.airflow.amazonaws.com +*.ap-southeast-4.airflow.amazonaws.com +*.ap-southeast-5.airflow.amazonaws.com +*.ap-southeast-7.airflow.amazonaws.com +*.ca-central-1.airflow.amazonaws.com +*.ca-west-1.airflow.amazonaws.com +*.eu-central-1.airflow.amazonaws.com +*.eu-central-2.airflow.amazonaws.com +*.eu-north-1.airflow.amazonaws.com +*.eu-south-1.airflow.amazonaws.com +*.eu-south-2.airflow.amazonaws.com +*.eu-west-1.airflow.amazonaws.com +*.eu-west-2.airflow.amazonaws.com +*.eu-west-3.airflow.amazonaws.com +*.il-central-1.airflow.amazonaws.com +*.me-central-1.airflow.amazonaws.com +*.me-south-1.airflow.amazonaws.com +*.sa-east-1.airflow.amazonaws.com +*.us-east-1.airflow.amazonaws.com +*.us-east-2.airflow.amazonaws.com +*.us-west-1.airflow.amazonaws.com +*.us-west-2.airflow.amazonaws.com + +// Amazon Relational Database Service +// Submitted by: AWS Security +// Reference: 5aa87906-fd4f-4831-8727-4ffca6094159 +*.rds.cn-north-1.amazonaws.com.cn +*.rds.cn-northwest-1.amazonaws.com.cn +*.af-south-1.rds.amazonaws.com +*.ap-east-1.rds.amazonaws.com +*.ap-east-2.rds.amazonaws.com +*.ap-northeast-1.rds.amazonaws.com +*.ap-northeast-2.rds.amazonaws.com +*.ap-northeast-3.rds.amazonaws.com +*.ap-south-1.rds.amazonaws.com +*.ap-south-2.rds.amazonaws.com +*.ap-southeast-1.rds.amazonaws.com +*.ap-southeast-2.rds.amazonaws.com +*.ap-southeast-3.rds.amazonaws.com +*.ap-southeast-4.rds.amazonaws.com +*.ap-southeast-5.rds.amazonaws.com +*.ap-southeast-6.rds.amazonaws.com +*.ap-southeast-7.rds.amazonaws.com +*.ca-central-1.rds.amazonaws.com +*.ca-west-1.rds.amazonaws.com +*.eu-central-1.rds.amazonaws.com +*.eu-central-2.rds.amazonaws.com +*.eu-west-1.rds.amazonaws.com +*.eu-west-2.rds.amazonaws.com +*.eu-west-3.rds.amazonaws.com +*.il-central-1.rds.amazonaws.com +*.me-central-1.rds.amazonaws.com +*.me-south-1.rds.amazonaws.com +*.mx-central-1.rds.amazonaws.com +*.sa-east-1.rds.amazonaws.com +*.us-east-1.rds.amazonaws.com +*.us-east-2.rds.amazonaws.com +*.us-gov-east-1.rds.amazonaws.com +*.us-gov-west-1.rds.amazonaws.com +*.us-northeast-1.rds.amazonaws.com +*.us-west-1.rds.amazonaws.com +*.us-west-2.rds.amazonaws.com + +// Amazon S3 +// Submitted by AWS Security +// Reference: ada5c9df-55e1-4195-a1ce-732d6c81e357 +s3.dualstack.cn-north-1.amazonaws.com.cn +s3-accesspoint.dualstack.cn-north-1.amazonaws.com.cn +s3-website.dualstack.cn-north-1.amazonaws.com.cn +s3.cn-north-1.amazonaws.com.cn +s3-accesspoint.cn-north-1.amazonaws.com.cn +s3-deprecated.cn-north-1.amazonaws.com.cn +s3-object-lambda.cn-north-1.amazonaws.com.cn +s3-website.cn-north-1.amazonaws.com.cn +s3.dualstack.cn-northwest-1.amazonaws.com.cn +s3-accesspoint.dualstack.cn-northwest-1.amazonaws.com.cn +s3.cn-northwest-1.amazonaws.com.cn +s3-accesspoint.cn-northwest-1.amazonaws.com.cn +s3-object-lambda.cn-northwest-1.amazonaws.com.cn +s3-website.cn-northwest-1.amazonaws.com.cn +s3.dualstack.af-south-1.amazonaws.com +s3-accesspoint.dualstack.af-south-1.amazonaws.com +s3-website.dualstack.af-south-1.amazonaws.com +s3.af-south-1.amazonaws.com +s3-accesspoint.af-south-1.amazonaws.com +s3-object-lambda.af-south-1.amazonaws.com +s3-website.af-south-1.amazonaws.com +s3.dualstack.ap-east-1.amazonaws.com +s3-accesspoint.dualstack.ap-east-1.amazonaws.com +s3.ap-east-1.amazonaws.com +s3-accesspoint.ap-east-1.amazonaws.com +s3-object-lambda.ap-east-1.amazonaws.com +s3-website.ap-east-1.amazonaws.com +s3.dualstack.ap-northeast-1.amazonaws.com +s3-accesspoint.dualstack.ap-northeast-1.amazonaws.com +s3-website.dualstack.ap-northeast-1.amazonaws.com +s3.ap-northeast-1.amazonaws.com +s3-accesspoint.ap-northeast-1.amazonaws.com +s3-object-lambda.ap-northeast-1.amazonaws.com +s3-website.ap-northeast-1.amazonaws.com +s3.dualstack.ap-northeast-2.amazonaws.com +s3-accesspoint.dualstack.ap-northeast-2.amazonaws.com +s3-website.dualstack.ap-northeast-2.amazonaws.com +s3.ap-northeast-2.amazonaws.com +s3-accesspoint.ap-northeast-2.amazonaws.com +s3-object-lambda.ap-northeast-2.amazonaws.com +s3-website.ap-northeast-2.amazonaws.com +s3.dualstack.ap-northeast-3.amazonaws.com +s3-accesspoint.dualstack.ap-northeast-3.amazonaws.com +s3-website.dualstack.ap-northeast-3.amazonaws.com +s3.ap-northeast-3.amazonaws.com +s3-accesspoint.ap-northeast-3.amazonaws.com +s3-object-lambda.ap-northeast-3.amazonaws.com +s3-website.ap-northeast-3.amazonaws.com +s3.dualstack.ap-south-1.amazonaws.com +s3-accesspoint.dualstack.ap-south-1.amazonaws.com +s3-website.dualstack.ap-south-1.amazonaws.com +s3.ap-south-1.amazonaws.com +s3-accesspoint.ap-south-1.amazonaws.com +s3-object-lambda.ap-south-1.amazonaws.com +s3-website.ap-south-1.amazonaws.com +s3.dualstack.ap-south-2.amazonaws.com +s3-accesspoint.dualstack.ap-south-2.amazonaws.com +s3-website.dualstack.ap-south-2.amazonaws.com +s3.ap-south-2.amazonaws.com +s3-accesspoint.ap-south-2.amazonaws.com +s3-object-lambda.ap-south-2.amazonaws.com +s3-website.ap-south-2.amazonaws.com +s3.dualstack.ap-southeast-1.amazonaws.com +s3-accesspoint.dualstack.ap-southeast-1.amazonaws.com +s3-website.dualstack.ap-southeast-1.amazonaws.com +s3.ap-southeast-1.amazonaws.com +s3-accesspoint.ap-southeast-1.amazonaws.com +s3-object-lambda.ap-southeast-1.amazonaws.com +s3-website.ap-southeast-1.amazonaws.com +s3.dualstack.ap-southeast-2.amazonaws.com +s3-accesspoint.dualstack.ap-southeast-2.amazonaws.com +s3-website.dualstack.ap-southeast-2.amazonaws.com +s3.ap-southeast-2.amazonaws.com +s3-accesspoint.ap-southeast-2.amazonaws.com +s3-object-lambda.ap-southeast-2.amazonaws.com +s3-website.ap-southeast-2.amazonaws.com +s3.dualstack.ap-southeast-3.amazonaws.com +s3-accesspoint.dualstack.ap-southeast-3.amazonaws.com +s3-website.dualstack.ap-southeast-3.amazonaws.com +s3.ap-southeast-3.amazonaws.com +s3-accesspoint.ap-southeast-3.amazonaws.com +s3-object-lambda.ap-southeast-3.amazonaws.com +s3-website.ap-southeast-3.amazonaws.com +s3.dualstack.ap-southeast-4.amazonaws.com +s3-accesspoint.dualstack.ap-southeast-4.amazonaws.com +s3-website.dualstack.ap-southeast-4.amazonaws.com +s3.ap-southeast-4.amazonaws.com +s3-accesspoint.ap-southeast-4.amazonaws.com +s3-object-lambda.ap-southeast-4.amazonaws.com +s3-website.ap-southeast-4.amazonaws.com +s3.dualstack.ap-southeast-5.amazonaws.com +s3-accesspoint.dualstack.ap-southeast-5.amazonaws.com +s3-website.dualstack.ap-southeast-5.amazonaws.com +s3.ap-southeast-5.amazonaws.com +s3-accesspoint.ap-southeast-5.amazonaws.com +s3-deprecated.ap-southeast-5.amazonaws.com +s3-object-lambda.ap-southeast-5.amazonaws.com +s3-website.ap-southeast-5.amazonaws.com +s3.dualstack.ca-central-1.amazonaws.com +s3-accesspoint.dualstack.ca-central-1.amazonaws.com +s3-accesspoint-fips.dualstack.ca-central-1.amazonaws.com +s3-fips.dualstack.ca-central-1.amazonaws.com +s3-website.dualstack.ca-central-1.amazonaws.com +s3.ca-central-1.amazonaws.com +s3-accesspoint.ca-central-1.amazonaws.com +s3-accesspoint-fips.ca-central-1.amazonaws.com +s3-fips.ca-central-1.amazonaws.com +s3-object-lambda.ca-central-1.amazonaws.com +s3-website.ca-central-1.amazonaws.com +s3.dualstack.ca-west-1.amazonaws.com +s3-accesspoint.dualstack.ca-west-1.amazonaws.com +s3-accesspoint-fips.dualstack.ca-west-1.amazonaws.com +s3-fips.dualstack.ca-west-1.amazonaws.com +s3-website.dualstack.ca-west-1.amazonaws.com +s3.ca-west-1.amazonaws.com +s3-accesspoint.ca-west-1.amazonaws.com +s3-accesspoint-fips.ca-west-1.amazonaws.com +s3-fips.ca-west-1.amazonaws.com +s3-object-lambda.ca-west-1.amazonaws.com +s3-website.ca-west-1.amazonaws.com +s3.dualstack.eu-central-1.amazonaws.com +s3-accesspoint.dualstack.eu-central-1.amazonaws.com +s3-website.dualstack.eu-central-1.amazonaws.com +s3.eu-central-1.amazonaws.com +s3-accesspoint.eu-central-1.amazonaws.com +s3-object-lambda.eu-central-1.amazonaws.com +s3-website.eu-central-1.amazonaws.com +s3.dualstack.eu-central-2.amazonaws.com +s3-accesspoint.dualstack.eu-central-2.amazonaws.com +s3-website.dualstack.eu-central-2.amazonaws.com +s3.eu-central-2.amazonaws.com +s3-accesspoint.eu-central-2.amazonaws.com +s3-object-lambda.eu-central-2.amazonaws.com +s3-website.eu-central-2.amazonaws.com +s3.dualstack.eu-north-1.amazonaws.com +s3-accesspoint.dualstack.eu-north-1.amazonaws.com +s3.eu-north-1.amazonaws.com +s3-accesspoint.eu-north-1.amazonaws.com +s3-object-lambda.eu-north-1.amazonaws.com +s3-website.eu-north-1.amazonaws.com +s3.dualstack.eu-south-1.amazonaws.com +s3-accesspoint.dualstack.eu-south-1.amazonaws.com +s3-website.dualstack.eu-south-1.amazonaws.com +s3.eu-south-1.amazonaws.com +s3-accesspoint.eu-south-1.amazonaws.com +s3-object-lambda.eu-south-1.amazonaws.com +s3-website.eu-south-1.amazonaws.com +s3.dualstack.eu-south-2.amazonaws.com +s3-accesspoint.dualstack.eu-south-2.amazonaws.com +s3-website.dualstack.eu-south-2.amazonaws.com +s3.eu-south-2.amazonaws.com +s3-accesspoint.eu-south-2.amazonaws.com +s3-object-lambda.eu-south-2.amazonaws.com +s3-website.eu-south-2.amazonaws.com +s3.dualstack.eu-west-1.amazonaws.com +s3-accesspoint.dualstack.eu-west-1.amazonaws.com +s3-website.dualstack.eu-west-1.amazonaws.com +s3.eu-west-1.amazonaws.com +s3-accesspoint.eu-west-1.amazonaws.com +s3-deprecated.eu-west-1.amazonaws.com +s3-object-lambda.eu-west-1.amazonaws.com +s3-website.eu-west-1.amazonaws.com +s3.dualstack.eu-west-2.amazonaws.com +s3-accesspoint.dualstack.eu-west-2.amazonaws.com +s3.eu-west-2.amazonaws.com +s3-accesspoint.eu-west-2.amazonaws.com +s3-object-lambda.eu-west-2.amazonaws.com +s3-website.eu-west-2.amazonaws.com +s3.dualstack.eu-west-3.amazonaws.com +s3-accesspoint.dualstack.eu-west-3.amazonaws.com +s3-website.dualstack.eu-west-3.amazonaws.com +s3.eu-west-3.amazonaws.com +s3-accesspoint.eu-west-3.amazonaws.com +s3-object-lambda.eu-west-3.amazonaws.com +s3-website.eu-west-3.amazonaws.com +s3.dualstack.il-central-1.amazonaws.com +s3-accesspoint.dualstack.il-central-1.amazonaws.com +s3-website.dualstack.il-central-1.amazonaws.com +s3.il-central-1.amazonaws.com +s3-accesspoint.il-central-1.amazonaws.com +s3-object-lambda.il-central-1.amazonaws.com +s3-website.il-central-1.amazonaws.com +s3.dualstack.me-central-1.amazonaws.com +s3-accesspoint.dualstack.me-central-1.amazonaws.com +s3-website.dualstack.me-central-1.amazonaws.com +s3.me-central-1.amazonaws.com +s3-accesspoint.me-central-1.amazonaws.com +s3-object-lambda.me-central-1.amazonaws.com +s3-website.me-central-1.amazonaws.com +s3.dualstack.me-south-1.amazonaws.com +s3-accesspoint.dualstack.me-south-1.amazonaws.com +s3.me-south-1.amazonaws.com +s3-accesspoint.me-south-1.amazonaws.com +s3-object-lambda.me-south-1.amazonaws.com +s3-website.me-south-1.amazonaws.com +s3.amazonaws.com +s3-1.amazonaws.com +s3-ap-east-1.amazonaws.com +s3-ap-northeast-1.amazonaws.com +s3-ap-northeast-2.amazonaws.com +s3-ap-northeast-3.amazonaws.com +s3-ap-south-1.amazonaws.com +s3-ap-southeast-1.amazonaws.com +s3-ap-southeast-2.amazonaws.com +s3-ca-central-1.amazonaws.com +s3-eu-central-1.amazonaws.com +s3-eu-north-1.amazonaws.com +s3-eu-west-1.amazonaws.com +s3-eu-west-2.amazonaws.com +s3-eu-west-3.amazonaws.com +s3-external-1.amazonaws.com +s3-fips-us-gov-east-1.amazonaws.com +s3-fips-us-gov-west-1.amazonaws.com +mrap.accesspoint.s3-global.amazonaws.com +s3-me-south-1.amazonaws.com +s3-sa-east-1.amazonaws.com +s3-us-east-2.amazonaws.com +s3-us-gov-east-1.amazonaws.com +s3-us-gov-west-1.amazonaws.com +s3-us-west-1.amazonaws.com +s3-us-west-2.amazonaws.com +s3-website-ap-northeast-1.amazonaws.com +s3-website-ap-southeast-1.amazonaws.com +s3-website-ap-southeast-2.amazonaws.com +s3-website-eu-west-1.amazonaws.com +s3-website-sa-east-1.amazonaws.com +s3-website-us-east-1.amazonaws.com +s3-website-us-gov-west-1.amazonaws.com +s3-website-us-west-1.amazonaws.com +s3-website-us-west-2.amazonaws.com +s3.dualstack.sa-east-1.amazonaws.com +s3-accesspoint.dualstack.sa-east-1.amazonaws.com +s3-website.dualstack.sa-east-1.amazonaws.com +s3.sa-east-1.amazonaws.com +s3-accesspoint.sa-east-1.amazonaws.com +s3-object-lambda.sa-east-1.amazonaws.com +s3-website.sa-east-1.amazonaws.com +s3.dualstack.us-east-1.amazonaws.com +s3-accesspoint.dualstack.us-east-1.amazonaws.com +s3-accesspoint-fips.dualstack.us-east-1.amazonaws.com +s3-fips.dualstack.us-east-1.amazonaws.com +s3-website.dualstack.us-east-1.amazonaws.com +s3.us-east-1.amazonaws.com +s3-accesspoint.us-east-1.amazonaws.com +s3-accesspoint-fips.us-east-1.amazonaws.com +s3-deprecated.us-east-1.amazonaws.com +s3-fips.us-east-1.amazonaws.com +s3-object-lambda.us-east-1.amazonaws.com +s3-website.us-east-1.amazonaws.com +s3.dualstack.us-east-2.amazonaws.com +s3-accesspoint.dualstack.us-east-2.amazonaws.com +s3-accesspoint-fips.dualstack.us-east-2.amazonaws.com +s3-fips.dualstack.us-east-2.amazonaws.com +s3-website.dualstack.us-east-2.amazonaws.com +s3.us-east-2.amazonaws.com +s3-accesspoint.us-east-2.amazonaws.com +s3-accesspoint-fips.us-east-2.amazonaws.com +s3-deprecated.us-east-2.amazonaws.com +s3-fips.us-east-2.amazonaws.com +s3-object-lambda.us-east-2.amazonaws.com +s3-website.us-east-2.amazonaws.com +s3.dualstack.us-gov-east-1.amazonaws.com +s3-accesspoint.dualstack.us-gov-east-1.amazonaws.com +s3-accesspoint-fips.dualstack.us-gov-east-1.amazonaws.com +s3-fips.dualstack.us-gov-east-1.amazonaws.com +s3.us-gov-east-1.amazonaws.com +s3-accesspoint.us-gov-east-1.amazonaws.com +s3-accesspoint-fips.us-gov-east-1.amazonaws.com +s3-fips.us-gov-east-1.amazonaws.com +s3-object-lambda.us-gov-east-1.amazonaws.com +s3-website.us-gov-east-1.amazonaws.com +s3.dualstack.us-gov-west-1.amazonaws.com +s3-accesspoint.dualstack.us-gov-west-1.amazonaws.com +s3-accesspoint-fips.dualstack.us-gov-west-1.amazonaws.com +s3-fips.dualstack.us-gov-west-1.amazonaws.com +s3.us-gov-west-1.amazonaws.com +s3-accesspoint.us-gov-west-1.amazonaws.com +s3-accesspoint-fips.us-gov-west-1.amazonaws.com +s3-fips.us-gov-west-1.amazonaws.com +s3-object-lambda.us-gov-west-1.amazonaws.com +s3-website.us-gov-west-1.amazonaws.com +s3.dualstack.us-west-1.amazonaws.com +s3-accesspoint.dualstack.us-west-1.amazonaws.com +s3-accesspoint-fips.dualstack.us-west-1.amazonaws.com +s3-fips.dualstack.us-west-1.amazonaws.com +s3-website.dualstack.us-west-1.amazonaws.com +s3.us-west-1.amazonaws.com +s3-accesspoint.us-west-1.amazonaws.com +s3-accesspoint-fips.us-west-1.amazonaws.com +s3-fips.us-west-1.amazonaws.com +s3-object-lambda.us-west-1.amazonaws.com +s3-website.us-west-1.amazonaws.com +s3.dualstack.us-west-2.amazonaws.com +s3-accesspoint.dualstack.us-west-2.amazonaws.com +s3-accesspoint-fips.dualstack.us-west-2.amazonaws.com +s3-fips.dualstack.us-west-2.amazonaws.com +s3-website.dualstack.us-west-2.amazonaws.com +s3.us-west-2.amazonaws.com +s3-accesspoint.us-west-2.amazonaws.com +s3-accesspoint-fips.us-west-2.amazonaws.com +s3-deprecated.us-west-2.amazonaws.com +s3-fips.us-west-2.amazonaws.com +s3-object-lambda.us-west-2.amazonaws.com +s3-website.us-west-2.amazonaws.com + +// Amazon SageMaker Ground Truth +// Submitted by AWS Security +// Reference: 98dbfde4-7802-48c3-8751-b60f204e0d9c +labeling.ap-northeast-1.sagemaker.aws +labeling.ap-northeast-2.sagemaker.aws +labeling.ap-south-1.sagemaker.aws +labeling.ap-southeast-1.sagemaker.aws +labeling.ap-southeast-2.sagemaker.aws +labeling.ca-central-1.sagemaker.aws +labeling.eu-central-1.sagemaker.aws +labeling.eu-west-1.sagemaker.aws +labeling.eu-west-2.sagemaker.aws +labeling.us-east-1.sagemaker.aws +labeling.us-east-2.sagemaker.aws +labeling.us-west-2.sagemaker.aws + +// Amazon SageMaker Notebook Instances +// Submitted by AWS Security +// Reference: b5ea56df-669e-43cc-9537-14aa172f5dfc +notebook.af-south-1.sagemaker.aws +notebook.ap-east-1.sagemaker.aws +notebook.ap-northeast-1.sagemaker.aws +notebook.ap-northeast-2.sagemaker.aws +notebook.ap-northeast-3.sagemaker.aws +notebook.ap-south-1.sagemaker.aws +notebook.ap-south-2.sagemaker.aws +notebook.ap-southeast-1.sagemaker.aws +notebook.ap-southeast-2.sagemaker.aws +notebook.ap-southeast-3.sagemaker.aws +notebook.ap-southeast-4.sagemaker.aws +notebook.ca-central-1.sagemaker.aws +notebook-fips.ca-central-1.sagemaker.aws +notebook.ca-west-1.sagemaker.aws +notebook-fips.ca-west-1.sagemaker.aws +notebook.eu-central-1.sagemaker.aws +notebook.eu-central-2.sagemaker.aws +notebook.eu-north-1.sagemaker.aws +notebook.eu-south-1.sagemaker.aws +notebook.eu-south-2.sagemaker.aws +notebook.eu-west-1.sagemaker.aws +notebook.eu-west-2.sagemaker.aws +notebook.eu-west-3.sagemaker.aws +notebook.il-central-1.sagemaker.aws +notebook.me-central-1.sagemaker.aws +notebook.me-south-1.sagemaker.aws +notebook.sa-east-1.sagemaker.aws +notebook.us-east-1.sagemaker.aws +notebook-fips.us-east-1.sagemaker.aws +notebook.us-east-2.sagemaker.aws +notebook-fips.us-east-2.sagemaker.aws +notebook.us-gov-east-1.sagemaker.aws +notebook-fips.us-gov-east-1.sagemaker.aws +notebook.us-gov-west-1.sagemaker.aws +notebook-fips.us-gov-west-1.sagemaker.aws +notebook.us-west-1.sagemaker.aws +notebook-fips.us-west-1.sagemaker.aws +notebook.us-west-2.sagemaker.aws +notebook-fips.us-west-2.sagemaker.aws +notebook.cn-north-1.sagemaker.com.cn +notebook.cn-northwest-1.sagemaker.com.cn + +// Amazon SageMaker Studio +// Submitted by AWS Security +// Reference: 475f237e-ab88-4041-9f41-7cfccdf66aeb +studio.af-south-1.sagemaker.aws +studio.ap-east-1.sagemaker.aws +studio.ap-northeast-1.sagemaker.aws +studio.ap-northeast-2.sagemaker.aws +studio.ap-northeast-3.sagemaker.aws +studio.ap-south-1.sagemaker.aws +studio.ap-southeast-1.sagemaker.aws +studio.ap-southeast-2.sagemaker.aws +studio.ap-southeast-3.sagemaker.aws +studio.ca-central-1.sagemaker.aws +studio.eu-central-1.sagemaker.aws +studio.eu-central-2.sagemaker.aws +studio.eu-north-1.sagemaker.aws +studio.eu-south-1.sagemaker.aws +studio.eu-south-2.sagemaker.aws +studio.eu-west-1.sagemaker.aws +studio.eu-west-2.sagemaker.aws +studio.eu-west-3.sagemaker.aws +studio.il-central-1.sagemaker.aws +studio.me-central-1.sagemaker.aws +studio.me-south-1.sagemaker.aws +studio.sa-east-1.sagemaker.aws +studio.us-east-1.sagemaker.aws +studio.us-east-2.sagemaker.aws +studio.us-gov-east-1.sagemaker.aws +studio-fips.us-gov-east-1.sagemaker.aws +studio.us-gov-west-1.sagemaker.aws +studio-fips.us-gov-west-1.sagemaker.aws +studio.us-west-1.sagemaker.aws +studio.us-west-2.sagemaker.aws +studio.cn-north-1.sagemaker.com.cn +studio.cn-northwest-1.sagemaker.com.cn + +// Amazon SageMaker with MLflow +// Submited by: AWS Security +// Reference: c19f92b3-a82a-452d-8189-831b572eea7e +*.experiments.sagemaker.aws + +// Analytics on AWS +// Submitted by AWS Security +// Reference: 955f9f40-a495-4e73-ae85-67b77ac9cadd +analytics-gateway.ap-northeast-1.amazonaws.com +analytics-gateway.ap-northeast-2.amazonaws.com +analytics-gateway.ap-south-1.amazonaws.com +analytics-gateway.ap-southeast-1.amazonaws.com +analytics-gateway.ap-southeast-2.amazonaws.com +analytics-gateway.eu-central-1.amazonaws.com +analytics-gateway.eu-west-1.amazonaws.com +analytics-gateway.us-east-1.amazonaws.com +analytics-gateway.us-east-2.amazonaws.com +analytics-gateway.us-west-2.amazonaws.com + +// AWS Amplify +// Submitted by AWS Security +// Reference: c35bed18-6f4f-424f-9298-5756f2f7d72b +amplifyapp.com + +// AWS App Runner +// Submitted by AWS Security +// Reference: 6828c008-ba5d-442f-ade5-48da4e7c2316 +*.awsapprunner.com + +// AWS Cloud9 +// Submitted by: AWS Security +// Reference: 30717f72-4007-4f0f-8ed4-864c6f2efec9 +webview-assets.aws-cloud9.af-south-1.amazonaws.com +vfs.cloud9.af-south-1.amazonaws.com +webview-assets.cloud9.af-south-1.amazonaws.com +webview-assets.aws-cloud9.ap-east-1.amazonaws.com +vfs.cloud9.ap-east-1.amazonaws.com +webview-assets.cloud9.ap-east-1.amazonaws.com +webview-assets.aws-cloud9.ap-northeast-1.amazonaws.com +vfs.cloud9.ap-northeast-1.amazonaws.com +webview-assets.cloud9.ap-northeast-1.amazonaws.com +webview-assets.aws-cloud9.ap-northeast-2.amazonaws.com +vfs.cloud9.ap-northeast-2.amazonaws.com +webview-assets.cloud9.ap-northeast-2.amazonaws.com +webview-assets.aws-cloud9.ap-northeast-3.amazonaws.com +vfs.cloud9.ap-northeast-3.amazonaws.com +webview-assets.cloud9.ap-northeast-3.amazonaws.com +webview-assets.aws-cloud9.ap-south-1.amazonaws.com +vfs.cloud9.ap-south-1.amazonaws.com +webview-assets.cloud9.ap-south-1.amazonaws.com +webview-assets.aws-cloud9.ap-southeast-1.amazonaws.com +vfs.cloud9.ap-southeast-1.amazonaws.com +webview-assets.cloud9.ap-southeast-1.amazonaws.com +webview-assets.aws-cloud9.ap-southeast-2.amazonaws.com +vfs.cloud9.ap-southeast-2.amazonaws.com +webview-assets.cloud9.ap-southeast-2.amazonaws.com +webview-assets.aws-cloud9.ca-central-1.amazonaws.com +vfs.cloud9.ca-central-1.amazonaws.com +webview-assets.cloud9.ca-central-1.amazonaws.com +webview-assets.aws-cloud9.eu-central-1.amazonaws.com +vfs.cloud9.eu-central-1.amazonaws.com +webview-assets.cloud9.eu-central-1.amazonaws.com +webview-assets.aws-cloud9.eu-north-1.amazonaws.com +vfs.cloud9.eu-north-1.amazonaws.com +webview-assets.cloud9.eu-north-1.amazonaws.com +webview-assets.aws-cloud9.eu-south-1.amazonaws.com +vfs.cloud9.eu-south-1.amazonaws.com +webview-assets.cloud9.eu-south-1.amazonaws.com +webview-assets.aws-cloud9.eu-west-1.amazonaws.com +vfs.cloud9.eu-west-1.amazonaws.com +webview-assets.cloud9.eu-west-1.amazonaws.com +webview-assets.aws-cloud9.eu-west-2.amazonaws.com +vfs.cloud9.eu-west-2.amazonaws.com +webview-assets.cloud9.eu-west-2.amazonaws.com +webview-assets.aws-cloud9.eu-west-3.amazonaws.com +vfs.cloud9.eu-west-3.amazonaws.com +webview-assets.cloud9.eu-west-3.amazonaws.com +webview-assets.aws-cloud9.il-central-1.amazonaws.com +vfs.cloud9.il-central-1.amazonaws.com +webview-assets.aws-cloud9.me-south-1.amazonaws.com +vfs.cloud9.me-south-1.amazonaws.com +webview-assets.cloud9.me-south-1.amazonaws.com +webview-assets.aws-cloud9.sa-east-1.amazonaws.com +vfs.cloud9.sa-east-1.amazonaws.com +webview-assets.cloud9.sa-east-1.amazonaws.com +webview-assets.aws-cloud9.us-east-1.amazonaws.com +vfs.cloud9.us-east-1.amazonaws.com +webview-assets.cloud9.us-east-1.amazonaws.com +webview-assets.aws-cloud9.us-east-2.amazonaws.com +vfs.cloud9.us-east-2.amazonaws.com +webview-assets.cloud9.us-east-2.amazonaws.com +webview-assets.aws-cloud9.us-west-1.amazonaws.com +vfs.cloud9.us-west-1.amazonaws.com +webview-assets.cloud9.us-west-1.amazonaws.com +webview-assets.aws-cloud9.us-west-2.amazonaws.com +vfs.cloud9.us-west-2.amazonaws.com +webview-assets.cloud9.us-west-2.amazonaws.com + +// AWS Directory Service +// Submitted by AWS Security +// Reference: a13203e8-42dc-4045-a0d2-2ee67bed1068 +awsapps.com + +// AWS Elastic Beanstalk +// Submitted by AWS Security +// Reference: e4e02a54-eaf9-4fe7-b662-39ccbc011a04 +cn-north-1.eb.amazonaws.com.cn +cn-northwest-1.eb.amazonaws.com.cn +elasticbeanstalk.com +af-south-1.elasticbeanstalk.com +ap-east-1.elasticbeanstalk.com +ap-northeast-1.elasticbeanstalk.com +ap-northeast-2.elasticbeanstalk.com +ap-northeast-3.elasticbeanstalk.com +ap-south-1.elasticbeanstalk.com +ap-southeast-1.elasticbeanstalk.com +ap-southeast-2.elasticbeanstalk.com +ap-southeast-3.elasticbeanstalk.com +ap-southeast-5.elasticbeanstalk.com +ap-southeast-7.elasticbeanstalk.com +ca-central-1.elasticbeanstalk.com +eu-central-1.elasticbeanstalk.com +eu-north-1.elasticbeanstalk.com +eu-south-1.elasticbeanstalk.com +eu-south-2.elasticbeanstalk.com +eu-west-1.elasticbeanstalk.com +eu-west-2.elasticbeanstalk.com +eu-west-3.elasticbeanstalk.com +il-central-1.elasticbeanstalk.com +me-central-1.elasticbeanstalk.com +me-south-1.elasticbeanstalk.com +sa-east-1.elasticbeanstalk.com +us-east-1.elasticbeanstalk.com +us-east-2.elasticbeanstalk.com +us-gov-east-1.elasticbeanstalk.com +us-gov-west-1.elasticbeanstalk.com +us-west-1.elasticbeanstalk.com +us-west-2.elasticbeanstalk.com + +// (AWS) Elastic Load Balancing +// Submitted by Luke Wells +// Reference: 12a3d528-1bac-4433-a359-a395867ffed2 +*.elb.amazonaws.com.cn +*.elb.amazonaws.com + +// AWS Global Accelerator +// Submitted by Daniel Massaguer +// Reference: d916759d-a08b-4241-b536-4db887383a6a +awsglobalaccelerator.com + +// AWS Lambda Function URLs +// Submitted by AWS Security +// Reference: 57df74ca-0820-46a5-89ea-0f0d0c4714b7 +lambda-url.af-south-1.on.aws +lambda-url.ap-east-1.on.aws +lambda-url.ap-northeast-1.on.aws +lambda-url.ap-northeast-2.on.aws +lambda-url.ap-northeast-3.on.aws +lambda-url.ap-south-1.on.aws +lambda-url.ap-southeast-1.on.aws +lambda-url.ap-southeast-2.on.aws +lambda-url.ap-southeast-3.on.aws +lambda-url.ca-central-1.on.aws +lambda-url.eu-central-1.on.aws +lambda-url.eu-north-1.on.aws +lambda-url.eu-south-1.on.aws +lambda-url.eu-west-1.on.aws +lambda-url.eu-west-2.on.aws +lambda-url.eu-west-3.on.aws +lambda-url.me-south-1.on.aws +lambda-url.sa-east-1.on.aws +lambda-url.us-east-1.on.aws +lambda-url.us-east-2.on.aws +lambda-url.us-west-1.on.aws +lambda-url.us-west-2.on.aws + +// AWS re:Post Private +// Submitted by AWS Security +// Reference: 83385945-225f-416e-9aa0-ad0632bfdcee +*.private.repost.aws + +// AWS Transfer Family web apps +// Submitted by AWS Security +// Reference: 57a658c4-8899-410c-aa24-5b01e4a178d2 +transfer-webapp.af-south-1.on.aws +transfer-webapp.ap-east-1.on.aws +transfer-webapp.ap-northeast-1.on.aws +transfer-webapp.ap-northeast-2.on.aws +transfer-webapp.ap-northeast-3.on.aws +transfer-webapp.ap-south-1.on.aws +transfer-webapp.ap-south-2.on.aws +transfer-webapp.ap-southeast-1.on.aws +transfer-webapp.ap-southeast-2.on.aws +transfer-webapp.ap-southeast-3.on.aws +transfer-webapp.ap-southeast-4.on.aws +transfer-webapp.ap-southeast-5.on.aws +transfer-webapp.ca-central-1.on.aws +transfer-webapp.ca-west-1.on.aws +transfer-webapp.eu-central-1.on.aws +transfer-webapp.eu-central-2.on.aws +transfer-webapp.eu-north-1.on.aws +transfer-webapp.eu-south-1.on.aws +transfer-webapp.eu-south-2.on.aws +transfer-webapp.eu-west-1.on.aws +transfer-webapp.eu-west-2.on.aws +transfer-webapp.eu-west-3.on.aws +transfer-webapp.il-central-1.on.aws +transfer-webapp.me-central-1.on.aws +transfer-webapp.me-south-1.on.aws +transfer-webapp.sa-east-1.on.aws +transfer-webapp.us-east-1.on.aws +transfer-webapp.us-east-2.on.aws +transfer-webapp.us-gov-east-1.on.aws +transfer-webapp-fips.us-gov-east-1.on.aws +transfer-webapp.us-gov-west-1.on.aws +transfer-webapp-fips.us-gov-west-1.on.aws +transfer-webapp.us-west-1.on.aws +transfer-webapp.us-west-2.on.aws +transfer-webapp.cn-north-1.on.amazonwebservices.com.cn +transfer-webapp.cn-northwest-1.on.amazonwebservices.com.cn + +// eero +// Submitted by Yue Kang +// Reference: 264afe70-f62c-4c02-8ab9-b5281ed24461 +eero.online +eero-stage.online + +// concludes Amazon + +// Antagonist B.V. : https://www.antagonist.nl/ +// Submitted by Sander Hoentjen +antagonist.cloud + +// Apigee : https://apigee.com/ +// Submitted by Apigee Security Team +apigee.io + +// Apis Networks : https://apisnetworks.com +// Submitted by Matt Saladna +panel.dev + +// Apphud : https://apphud.com +// Submitted by Alexander Selivanov +siiites.com + +// Apple : https://www.apple.com +// Submitted by Apple DNS +int.apple +*.cloud.int.apple +*.r.cloud.int.apple +*.ap-north-1.r.cloud.int.apple +*.ap-south-1.r.cloud.int.apple +*.ap-south-2.r.cloud.int.apple +*.eu-central-1.r.cloud.int.apple +*.eu-north-1.r.cloud.int.apple +*.us-central-1.r.cloud.int.apple +*.us-central-2.r.cloud.int.apple +*.us-east-1.r.cloud.int.apple +*.us-east-2.r.cloud.int.apple +*.us-west-1.r.cloud.int.apple +*.us-west-2.r.cloud.int.apple +*.us-west-3.r.cloud.int.apple + +// Appspace : https://www.appspace.com +// Submitted by Appspace Security Team +appspacehosted.com +appspaceusercontent.com + +// Appudo UG (haftungsbeschränkt) : https://www.appudo.com +// Submitted by Alexander Hochbaum +appudo.net + +// Appwrite : https://appwrite.io +// Submitted by Steven Nguyen +appwrite.global +appwrite.network +*.appwrite.run + +// Aptible : https://www.aptible.com/ +// Submitted by Thomas Orozco +on-aptible.com + +// Aquapal : https://aquapal.net/ +// Submitted by Aki Ueno +f5.si + +// ArvanCloud EdgeCompute +// Submitted by ArvanCloud CDN +arvanedge.ir + +// ASEINet : https://www.aseinet.com/ +// Submitted by Asei SEKIGUCHI +user.aseinet.ne.jp +gv.vc +d.gv.vc + +// Asociación Amigos de la Informática "Euskalamiga" : http://encounter.eus/ +// Submitted by Hector Martin +user.party.eus + +// Association potager.org : https://potager.org/ +// Submitted by Lunar +pimienta.org +poivron.org +potager.org +sweetpepper.org + +// ASUSTOR Inc. : http://www.asustor.com +// Submitted by Vincent Tseng +myasustor.com + +// Atlassian : https://atlassian.com +// Submitted by Sam Smyth +cdn.prod.atlassian-dev.net + +// Authentick UG (haftungsbeschränkt) : https://authentick.net +// Submitted by Lukas Reschke +translated.page + +// AVM : https://avm.de +// Submitted by Andreas Weise +myfritz.link +myfritz.net + +// AVStack Pte. Ltd. : https://avstack.io +// Submitted by Jasper Hugo +onavstack.net + +// AW AdvisorWebsites.com Software Inc : https://advisorwebsites.com +// Submitted by James Kennedy +*.awdev.ca +*.advisor.ws + +// AZ.pl sp. z.o.o : https://az.pl +// Submitted by Krzysztof Wolski +ecommerce-shop.pl + +// b-data GmbH : https://www.b-data.io +// Submitted by Olivier Benz +b-data.io + +// Balena : https://www.balena.io +// Submitted by Petros Angelatos +balena-devices.com + +// BASE, Inc. : https://binc.jp +// Submitted by Yuya NAGASAWA +base.ec +official.ec +buyshop.jp +fashionstore.jp +handcrafted.jp +kawaiishop.jp +supersale.jp +theshop.jp +shopselect.net +base.shop + +// BeagleBoard.org Foundation : https://beagleboard.org +// Submitted by Jason Kridner +beagleboard.io + +// Bear Blog : https://bearblog.dev +// Submitted by Herman Martinus +bearblog.dev + +// Beget Ltd +// Submitted by Lev Nekrasov +*.beget.app + +// Besties : https://besties.house +// Submitted by Hazel Cora +pages.gay + +// BinaryLane : http://www.binarylane.com +// Submitted by Nathan O'Sullivan +bnr.la + +// Bitbucket : http://bitbucket.org +// Submitted by Andy Ortlieb +bitbucket.io + +// Blackbaud, Inc. : https://www.blackbaud.com +// Submitted by Paul Crowder +blackbaudcdn.net + +// Blatech : http://www.blatech.net +// Submitted by Luke Bratch +of.je + +// Block, Inc. : https://block.xyz +// Submitted by Jonathan Boice +square.site + +// Blue Bite, LLC : https://bluebite.com +// Submitted by Joshua Weiss +bluebite.io + +// Boomla : https://boomla.com +// Submitted by Tibor Halter +boomla.net + +// Boutir : https://www.boutir.com +// Submitted by Eric Ng Ka Ka +boutir.com + +// Boxfuse : https://boxfuse.com +// Submitted by Axel Fontaine +boxfuse.io + +// bplaced : https://www.bplaced.net/ +// Submitted by Miroslav Bozic +square7.ch +bplaced.com +bplaced.de +square7.de +bplaced.net +square7.net + +// Brave : https://brave.com +// Submitted by Andrea Brancaleoni +brave.app +*.s.brave.app +brave.dev +*.s.brave.dev +brave.io +*.s.brave.io + +// Brendly : https://brendly.rs +// Submitted by Dusan Radovanovic +shop.brendly.ba +shop.brendly.hr +shop.brendly.rs + +// BrowserSafetyMark +// Submitted by Dave Tharp +browsersafetymark.io + +// BRS Media : https://brsmedia.com/ +// Submitted by Gavin Brown +radio.am +radio.fm + +// Bubble : https://bubble.io/ +// Submitted by Merlin Zhao +cdn.bubble.io +bubbleapps.io + +// Bytemark Hosting : https://www.bytemark.co.uk +// Submitted by Paul Cammish +uk0.bigv.io +dh.bytemark.co.uk +vm.bytemark.co.uk + +// Caf.js Labs LLC : https://www.cafjs.com +// Submitted by Antonio Lain +cafjs.com + +// Canva Pty Ltd : https://canva.com/ +// Submitted by Joel Aquilina +canva-apps.cn +my.canvasite.cn +canva-apps.com +canva-hosted-embed.com +canvacode.com +rice-labs.com +canva.run +my.canva.site + +// Carrd : https://carrd.co +// Submitted by AJ +drr.ac +uwu.ai +carrd.co +crd.co +ju.mp + +// CDDO : https://www.gov.uk/guidance/get-an-api-domain-on-govuk +// Submitted by Jamie Tanna +api.gov.uk + +// CDN77.com : http://www.cdn77.com +// Submitted by Jan Krpes +cdn77-storage.com +rsc.contentproxy9.cz +r.cdn77.net +cdn77-ssl.net +c.cdn77.org +rsc.cdn77.org +ssl.origin.cdn77-secure.org + +// CentralNic : https://teaminternet.com/ +// Submitted by registry +za.bz +br.com +cn.com +de.com +eu.com +jpn.com +mex.com +ru.com +sa.com +uk.com +us.com +za.com +com.de +gb.net +hu.net +jp.net +se.net +uk.net +ae.org +com.se + +// Cityhost LLC : https://cityhost.ua +// Submitted by Maksym Rivtin +cx.ua + +// Civilized Discourse Construction Kit, Inc. : https://www.discourse.org/ +// Submitted by Rishabh Nambiar & Michael Brown +discourse.group +discourse.team + +// Clerk : https://www.clerk.dev +// Submitted by Colin Sidoti +clerk.app +clerkstage.app +*.lcl.dev +*.lclstage.dev +*.stg.dev +*.stgstage.dev + +// Clever Cloud : https://www.clever-cloud.com/ +// Submitted by Quentin Adam +cleverapps.cc +*.services.clever-cloud.com +cleverapps.io +cleverapps.tech + +// ClickRising : https://clickrising.com/ +// Submitted by Umut Gumeli +clickrising.net + +// Cloud DNS Ltd : http://www.cloudns.net +// Submitted by Aleksander Hristov & Boyan Peychev +cloudns.asia +cloudns.be +cloud-ip.biz +cloudns.biz +cloud-ip.cc +cloudns.cc +cloudns.ch +cloudns.cl +cloudns.club +abrdns.com +dnsabr.com +ip-ddns.com +cloudns.cx +cloudns.eu +cloudns.in +cloudns.info +ddns-ip.net +dns-cloud.net +dns-dynamic.net +cloudns.nz +cloudns.org +ip-dynamic.org +cloudns.ph +cloudns.pro +cloudns.pw +cloudns.us + +// Cloud66 : https://www.cloud66.com/ +// Submitted by Khash Sajadi +c66.me +cloud66.ws + +// CloudAccess.net : https://www.cloudaccess.net/ +// Submitted by Pawel Panek +jdevcloud.com +wpdevcloud.com +cloudaccess.host +freesite.host +cloudaccess.net + +// Cloudbees, Inc. : https://www.cloudbees.com/ +// Submitted by Mohideen Shajith +cloudbeesusercontent.io + +// Cloudera, Inc. : https://www.cloudera.com/ +// Submitted by Kedarnath Waikar +*.cloudera.site + +// Cloudflare, Inc. : https://www.cloudflare.com/ +// Submitted by Cloudflare Team +cloudflare.app +cf-ipfs.com +cloudflare-ipfs.com +trycloudflare.com +pages.dev +r2.dev +workers.dev +cloudflare.net +cdn.cloudflare.net +cdn.cloudflareanycast.net +cdn.cloudflarecn.net +cdn.cloudflareglobal.net + +// cloudscale.ch AG : https://www.cloudscale.ch/ +// Submitted by Gaudenz Steinlin +cust.cloudscale.ch +objects.lpg.cloudscale.ch +objects.rma.cloudscale.ch +lpg.objectstorage.ch +rma.objectstorage.ch + +// Clovyr : https://clovyr.io +// Submitted by Patrick Nielsen +wnext.app + +// CNPY : https://cnpy.gdn +// Submitted by Angelo Gladding +cnpy.gdn + +// Co & Co : https://co-co.nl/ +// Submitted by Govert Versluis +*.otap.co + +// co.ca : http://registry.co.ca/ +co.ca + +// co.com Registry, LLC : https://registry.co.com +// Submitted by Gavin Brown +co.com + +// Codeberg e. V. : https://codeberg.org +// Submitted by Moritz Marquardt +codeberg.page + +// CodeSandbox B.V. : https://codesandbox.io +// Submitted by Ives van Hoorne +csb.app +preview.csb.app + +// CoDNS B.V. +co.nl +co.no + +// Cognition AI, Inc. : https://cognition.ai +// Submitted by Philip Papurt +*.devinapps.com + +// Combell.com : https://www.combell.com +// Submitted by Combell Team +webhosting.be +prvw.eu +hosting-cluster.nl + +// Contentful GmbH : https://www.contentful.com +// Submitted by Contentful Developer Experience Team +ctfcloud.net + +// Convex : https://convex.dev/ +// Submitted by James Cowling +convex.app +convex.cloud +convex.site + +// Coordination Center for TLD RU and XN--P1AI : https://cctld.ru/en/domains/domens_ru/reserved/ +// Submitted by George Georgievsky +ac.ru +edu.ru +gov.ru +int.ru +mil.ru + +// COSIMO GmbH : http://www.cosimo.de +// Submitted by Rene Marticke +dyn.cosidns.de +dnsupdater.de +dynamisches-dns.de +internet-dns.de +l-o-g-i-n.de +dynamic-dns.info +feste-ip.net +knx-server.net +static-access.net + +// Craft Docs Ltd : https://www.craft.do/ +// Submitted by Zsombor Fuszenecker +craft.me + +// Craynic, s.r.o. : http://www.craynic.com/ +// Submitted by Ales Krajnik +realm.cz + +// Crisp IM SAS : https://crisp.chat/ +// Submitted by Baptiste Jamin +on.crisp.email + +// Cryptonomic : https://cryptonomic.net/ +// Submitted by Andrew Cady +*.cryptonomic.net + +// cyber_Folks S.A. : https://cyberfolks.pl +// Submitted by Bartlomiej Kida +cfolks.pl + +// cyon GmbH : https://www.cyon.ch/ +// Submitted by Dominic Luechinger +cyon.link +cyon.site + +// Dansk.net : http://www.dansk.net/ +// Submitted by Anani Voule +biz.dk +co.dk +firm.dk +reg.dk +store.dk + +// dappnode.io : https://dappnode.io/ +// Submitted by Abel Boldu / DAppNode Team +dyndns.dappnode.io + +// Dark, Inc. : https://darklang.com +// Submitted by Paul Biggar +builtwithdark.com +darklang.io + +// DataDetect, LLC. : https://datadetect.com +// Submitted by Andrew Banchich +demo.datadetect.com +instance.datadetect.com + +// Datawire, Inc : https://www.datawire.io +// Submitted by Richard Li +edgestack.me + +// Datto, Inc. : https://www.datto.com/ +// Submitted by Philipp Heckel +dattolocal.com +dattorelay.com +dattoweb.com +mydatto.com +dattolocal.net +mydatto.net + +// ddnss.de : https://www.ddnss.de/ +// Submitted by Robert Niedziela +ddnss.de +dyn.ddnss.de +dyndns.ddnss.de +dyn-ip24.de +dyndns1.de +home-webserver.de +dyn.home-webserver.de +myhome-server.de +ddnss.org + +// Debian : https://www.debian.org/ +// Submitted by Peter Palfrader / Debian Sysadmin Team +debian.net + +// Definima : http://www.definima.com/ +// Submitted by Maxence Bitterli +definima.io +definima.net + +// Deno Land Inc : https://deno.com/ +// Submitted by Luca Casonato +deno.dev +deno-staging.dev +deno.net + +// deSEC : https://desec.io/ +// Submitted by Peter Thomassen +dedyn.io + +// Deta : https://www.deta.sh/ +// Submitted by Aavash Shrestha +deta.app +deta.dev + +// Developed Methods LLC : https://methods.dev +// Submitted by Patrick Lorio +*.at.ply.gg +d6.ply.gg +joinmc.link +playit.plus +*.at.playit.plus +with.playit.plus + +// Dfinity Foundation: https://dfinity.org/ +// Submitted by Dfinity Team +icp0.io +*.raw.icp0.io +icp1.io +*.raw.icp1.io +*.icp.net +caffeine.site +caffeine.xyz + +// dhosting.pl Sp. z o.o. : https://dhosting.pl/ +// Submitted by Michal Kokoszkiewicz +dfirma.pl +dkonto.pl +you2.pl + +// DigitalOcean App Platform : https://www.digitalocean.com/products/app-platform/ +// Submitted by Braxton Huggins +ondigitalocean.app + +// DigitalOcean Spaces : https://www.digitalocean.com/products/spaces/ +// Submitted by Robin H. Johnson +*.digitaloceanspaces.com + +// DigitalPlat : https://www.digitalplat.org/ +// Submitted by Edward Hsing +qzz.io +us.kg +xx.kg +dpdns.org + +// Discord Inc : https://discord.com +// Submitted by Sahn Lam +discordsays.com +discordsez.com + +// DNS Africa Ltd : https://dns.business +// Submitted by Calvin Browne +jozi.biz + +// DNSHE : https://www.dnshe.com +// Submitted by DNSHE Team +ccwu.cc +cc.cd +us.ci +de5.net + +// DNShome : https://www.dnshome.de/ +// Submitted by Norbert Auler +dnshome.de + +// DotArai : https://www.dotarai.com/ +// Submitted by Atsadawat Netcharadsang +online.th +shop.th + +// dotScot Domains : https://domains.scot/ +// Submitted by DNS Team +co.scot +me.scot +org.scot + +// DrayTek Corp. : https://www.draytek.com/ +// Submitted by Paul Fang +drayddns.com + +// DreamCommerce : https://shoper.pl/ +// Submitted by Konrad Kotarba +shoparena.pl + +// DreamHost : http://www.dreamhost.com/ +// Submitted by Andrew Farmer +dreamhosters.com + +// Dreamyoungs, Inc. : https://durumis.com +// Submitted by Infra Team +durumis.com + +// DuckDNS : http://www.duckdns.org/ +// Submitted by Richard Harper +duckdns.org + +// dy.fi : http://dy.fi/ +// Submitted by Heikki Hannikainen +dy.fi +tunk.org + +// DynDNS.com : http://www.dyndns.com/services/dns/dyndns/ +dyndns.biz +for-better.biz +for-more.biz +for-some.biz +for-the.biz +selfip.biz +webhop.biz +ftpaccess.cc +game-server.cc +myphotos.cc +scrapping.cc +blogdns.com +cechire.com +dnsalias.com +dnsdojo.com +doesntexist.com +dontexist.com +doomdns.com +dyn-o-saur.com +dynalias.com +dyndns-at-home.com +dyndns-at-work.com +dyndns-blog.com +dyndns-free.com +dyndns-home.com +dyndns-ip.com +dyndns-mail.com +dyndns-office.com +dyndns-pics.com +dyndns-remote.com +dyndns-server.com +dyndns-web.com +dyndns-wiki.com +dyndns-work.com +est-a-la-maison.com +est-a-la-masion.com +est-le-patron.com +est-mon-blogueur.com +from-ak.com +from-al.com +from-ar.com +from-ca.com +from-ct.com +from-dc.com +from-de.com +from-fl.com +from-ga.com +from-hi.com +from-ia.com +from-id.com +from-il.com +from-in.com +from-ks.com +from-ky.com +from-ma.com +from-md.com +from-mi.com +from-mn.com +from-mo.com +from-ms.com +from-mt.com +from-nc.com +from-nd.com +from-ne.com +from-nh.com +from-nj.com +from-nm.com +from-nv.com +from-oh.com +from-ok.com +from-or.com +from-pa.com +from-pr.com +from-ri.com +from-sc.com +from-sd.com +from-tn.com +from-tx.com +from-ut.com +from-va.com +from-vt.com +from-wa.com +from-wi.com +from-wv.com +from-wy.com +getmyip.com +gotdns.com +hobby-site.com +homelinux.com +homeunix.com +iamallama.com +is-a-anarchist.com +is-a-blogger.com +is-a-bookkeeper.com +is-a-bulls-fan.com +is-a-caterer.com +is-a-chef.com +is-a-conservative.com +is-a-cpa.com +is-a-cubicle-slave.com +is-a-democrat.com +is-a-designer.com +is-a-doctor.com +is-a-financialadvisor.com +is-a-geek.com +is-a-green.com +is-a-guru.com +is-a-hard-worker.com +is-a-hunter.com +is-a-landscaper.com +is-a-lawyer.com +is-a-liberal.com +is-a-libertarian.com +is-a-llama.com +is-a-musician.com +is-a-nascarfan.com +is-a-nurse.com +is-a-painter.com +is-a-personaltrainer.com +is-a-photographer.com +is-a-player.com +is-a-republican.com +is-a-rockstar.com +is-a-socialist.com +is-a-student.com +is-a-teacher.com +is-a-techie.com +is-a-therapist.com +is-an-accountant.com +is-an-actor.com +is-an-actress.com +is-an-anarchist.com +is-an-artist.com +is-an-engineer.com +is-an-entertainer.com +is-certified.com +is-gone.com +is-into-anime.com +is-into-cars.com +is-into-cartoons.com +is-into-games.com +is-leet.com +is-not-certified.com +is-slick.com +is-uberleet.com +is-with-theband.com +isa-geek.com +isa-hockeynut.com +issmarterthanyou.com +likes-pie.com +likescandy.com +neat-url.com +saves-the-whales.com +selfip.com +sells-for-less.com +sells-for-u.com +servebbs.com +simple-url.com +space-to-rent.com +teaches-yoga.com +writesthisblog.com +ath.cx +fuettertdasnetz.de +isteingeek.de +istmein.de +lebtimnetz.de +leitungsen.de +traeumtgerade.de +barrel-of-knowledge.info +barrell-of-knowledge.info +dyndns.info +for-our.info +groks-the.info +groks-this.info +here-for-more.info +knowsitall.info +selfip.info +webhop.info +forgot.her.name +forgot.his.name +at-band-camp.net +blogdns.net +broke-it.net +buyshouses.net +dnsalias.net +dnsdojo.net +does-it.net +dontexist.net +dynalias.net +dynathome.net +endofinternet.net +from-az.net +from-co.net +from-la.net +from-ny.net +gets-it.net +ham-radio-op.net +homeftp.net +homeip.net +homelinux.net +homeunix.net +in-the-band.net +is-a-chef.net +is-a-geek.net +isa-geek.net +kicks-ass.net +office-on-the.net +podzone.net +scrapper-site.net +selfip.net +sells-it.net +servebbs.net +serveftp.net +thruhere.net +webhop.net +merseine.nu +mine.nu +shacknet.nu +blogdns.org +blogsite.org +boldlygoingnowhere.org +dnsalias.org +dnsdojo.org +doesntexist.org +dontexist.org +doomdns.org +dvrdns.org +dynalias.org +dyndns.org +go.dyndns.org +home.dyndns.org +endofinternet.org +endoftheinternet.org +from-me.org +game-host.org +gotdns.org +hobby-site.org +homedns.org +homeftp.org +homelinux.org +homeunix.org +is-a-bruinsfan.org +is-a-candidate.org +is-a-celticsfan.org +is-a-chef.org +is-a-geek.org +is-a-knight.org +is-a-linux-user.org +is-a-patsfan.org +is-a-soxfan.org +is-found.org +is-lost.org +is-saved.org +is-very-bad.org +is-very-evil.org +is-very-good.org +is-very-nice.org +is-very-sweet.org +isa-geek.org +kicks-ass.org +misconfused.org +podzone.org +readmyblog.org +selfip.org +sellsyourhome.org +servebbs.org +serveftp.org +servegame.org +stuff-4-sale.org +webhop.org +better-than.tv +dyndns.tv +on-the-web.tv +worse-than.tv +is-by.us +land-4-sale.us +stuff-4-sale.us +dyndns.ws +mypets.ws + +// Dynu.com : https://www.dynu.com/ +// Submitted by Sue Ye +ddnsfree.com +ddnsgeek.com +giize.com +gleeze.com +kozow.com +loseyourip.com +ooguy.com +theworkpc.com +casacam.net +dynu.net +accesscam.org +camdvr.org +freeddns.org +mywire.org +webredirect.org +myddns.rocks + +// dynv6 : https://dynv6.com +// Submitted by Dominik Menke +dynv6.net + +// E4YOU spol. s.r.o. : https://e4you.cz/ +// Submitted by Vladimir Dudr +e4.cz + +// Easypanel : https://easypanel.io +// Submitted by Andrei Canta +easypanel.app +easypanel.host + +// EasyWP : https://www.easywp.com +// Submitted by +*.ewp.live + +// eDirect Corp. : https://hosting.url.com.tw/ +// Submitted by C.S. chang +twmail.cc +twmail.net +twmail.org +mymailer.com.tw +url.tw + +// Electromagnetic Field : https://www.emfcamp.org +// Submitted by +at.emf.camp + +// Elefunc, Inc. : https://elefunc.com +// Submitted by Cetin Sert +rt.ht + +// Elementor : Elementor Ltd. +// Submitted by Anton Barkan +elementor.cloud +elementor.cool + +// Emergent : https://emergent.sh +// Submitted by Emergent Security Team +emergent.cloud +preview.emergentagent.com +emergent.host + +// Enalean SAS : https://www.enalean.com +// Submitted by Enalean Security Team +mytuleap.com +tuleap-partners.com + +// Encoretivity AB : https://encore.cloud +// Submitted by André Eriksson +encr.app +frontend.encr.app +encoreapi.com +lp.dev +api.lp.dev +objects.lp.dev + +// encoway GmbH : https://www.encoway.de +// Submitted by Marcel Daus +eu.encoway.cloud + +// EU.org : https://eu.org/ +// Submitted by Pierre Beyssac +eu.org +al.eu.org +asso.eu.org +at.eu.org +au.eu.org +be.eu.org +bg.eu.org +ca.eu.org +cd.eu.org +ch.eu.org +cn.eu.org +cy.eu.org +cz.eu.org +de.eu.org +dk.eu.org +edu.eu.org +ee.eu.org +es.eu.org +fi.eu.org +fr.eu.org +gr.eu.org +hr.eu.org +hu.eu.org +ie.eu.org +il.eu.org +in.eu.org +int.eu.org +is.eu.org +it.eu.org +jp.eu.org +kr.eu.org +lt.eu.org +lu.eu.org +lv.eu.org +me.eu.org +mk.eu.org +mt.eu.org +my.eu.org +net.eu.org +ng.eu.org +nl.eu.org +no.eu.org +nz.eu.org +pl.eu.org +pt.eu.org +ro.eu.org +ru.eu.org +se.eu.org +si.eu.org +sk.eu.org +tr.eu.org +uk.eu.org +us.eu.org + +// Eurobyte : https://eurobyte.ru +// Submitted by Evgeniy Subbotin +eurodir.ru + +// Evennode : http://www.evennode.com/ +// Submitted by Michal Kralik +eu-1.evennode.com +eu-2.evennode.com +eu-3.evennode.com +eu-4.evennode.com +us-1.evennode.com +us-2.evennode.com +us-3.evennode.com +us-4.evennode.com + +// Evervault : https://evervault.com +// Submitted by Hannah Neary +relay.evervault.app +relay.evervault.dev + +// Expo : https://expo.dev/ +// Submitted by James Ide +expo.app +staging.expo.app + +// Fabrica Technologies, Inc. : https://www.fabrica.dev/ +// Submitted by Eric Jiang +onfabrica.com + +// FAITID : https://faitid.org/ +// Submitted by Maxim Alzoba +// https://www.flexireg.net/stat_info +ru.net +adygeya.ru +bashkiria.ru +bir.ru +cbg.ru +com.ru +dagestan.ru +grozny.ru +kalmykia.ru +kustanai.ru +marine.ru +mordovia.ru +msk.ru +mytis.ru +nalchik.ru +nov.ru +pyatigorsk.ru +spb.ru +vladikavkaz.ru +vladimir.ru +abkhazia.su +adygeya.su +aktyubinsk.su +arkhangelsk.su +armenia.su +ashgabad.su +azerbaijan.su +balashov.su +bashkiria.su +bryansk.su +bukhara.su +chimkent.su +dagestan.su +east-kazakhstan.su +exnet.su +georgia.su +grozny.su +ivanovo.su +jambyl.su +kalmykia.su +kaluga.su +karacol.su +karaganda.su +karelia.su +khakassia.su +krasnodar.su +kurgan.su +kustanai.su +lenug.su +mangyshlak.su +mordovia.su +msk.su +murmansk.su +nalchik.su +navoi.su +north-kazakhstan.su +nov.su +obninsk.su +penza.su +pokrovsk.su +sochi.su +spb.su +tashkent.su +termez.su +togliatti.su +troitsk.su +tselinograd.su +tula.su +tuva.su +vladikavkaz.su +vladimir.su +vologda.su + +// Fancy Bits, LLC : http://getchannels.com +// Submitted by Aman Gupta +channelsdvr.net +u.channelsdvr.net + +// Fastly Inc. : http://www.fastly.com/ +// Submitted by Fastly Security +edgecompute.app +fastly-edge.com +fastly-terrarium.com +freetls.fastly.net +map.fastly.net +a.prod.fastly.net +global.prod.fastly.net +a.ssl.fastly.net +b.ssl.fastly.net +global.ssl.fastly.net +fastlylb.net +map.fastlylb.net + +// Fastmail : https://www.fastmail.com/ +// Submitted by Marc Bradshaw +*.user.fm + +// FASTVPS EESTI OU : https://fastvps.ru/ +// Submitted by Likhachev Vasiliy +fastvps-server.com +fastvps.host +myfast.host +fastvps.site +myfast.space + +// FearWorks Media Ltd. : https://fearworksmedia.co.uk +// Submitted by Keith Fairley +conn.uk +copro.uk +hosp.uk + +// Fedora : https://fedoraproject.org/ +// Submitted by Patrick Uiterwijk +fedorainfracloud.org +fedorapeople.org +cloud.fedoraproject.org +app.os.fedoraproject.org +app.os.stg.fedoraproject.org + +// Fermax : https://fermax.com/ +// Submitted by Koen Van Isterdael +mydobiss.com + +// FH Muenster : https://www.fh-muenster.de +// Submitted by Robin Naundorf +fh-muenster.io + +// Figma : https://www.figma.com +// Submitted by Nick Frost +figma.site +figma-gov.site +preview.site + +// Filegear Inc. : https://www.filegear.com +// Submitted by Jason Zhu +filegear.me + +// Firebase, Inc. +// Submitted by Chris Raynor +firebaseapp.com + +// FlashDrive : https://flashdrive.io +// Submitted by Eric Chan +fldrv.com + +// Fleek Labs Inc : https://fleek.xyz +// Submitted by Parsa Ghadimi +on-fleek.app + +// FlutterFlow : https://flutterflow.io +// Submitted by Anton Emelyanov +flutterflow.app + +// fly.io : https://fly.io +// Submitted by Kurt Mackey +sprites.app +fly.dev + +// FoundryLabs, Inc : https://e2b.dev/ +// Submitted by Jiri Sveceny +e2b.app + +// Framer : https://www.framer.com +// Submitted by Koen Rouwhorst +framer.ai +framer.app +framercanvas.com +framer.media +framer.photos +framer.website +framer.wiki + +// Frederik Braun : https://frederik-braun.com +// Submitted by Frederik Braun +*.0e.vc + +// Freebox : http://www.freebox.fr +// Submitted by Romain Fliedel +freebox-os.com +freeboxos.com +fbx-os.fr +fbxos.fr +freebox-os.fr +freeboxos.fr + +// freedesktop.org : https://www.freedesktop.org +// Submitted by Daniel Stone +freedesktop.org + +// freemyip.com : https://freemyip.com +// Submitted by Cadence +freemyip.com + +// Frusky MEDIA&PR : https://www.frusky.de +// Submitted by Victor Pupynin +*.frusky.de + +// FunkFeuer - Verein zur Förderung freier Netze : https://www.funkfeuer.at +// Submitted by Daniel A. Maierhofer +wien.funkfeuer.at + +// Future Versatile Group. : https://www.fvg-on.net/ +// T.Kabu +daemon.asia +dix.asia +mydns.bz +0am.jp +0g0.jp +0j0.jp +0t0.jp +mydns.jp +pgw.jp +wjg.jp +keyword-on.net +live-on.net +server-on.net +mydns.tw +mydns.vc + +// Futureweb GmbH : https://www.futureweb.at +// Submitted by Andreas Schnederle-Wagner +*.futurecms.at +*.ex.futurecms.at +*.in.futurecms.at +futurehosting.at +futuremailing.at +*.ex.ortsinfo.at +*.kunden.ortsinfo.at +*.statics.cloud + +// Gadget Software Inc. : https://gadget.dev +// Submitted by Harry Brundage +gadget.app +gadget.host + +// GCom Internet : https://www.gcom.net.au +// Submitted by Leo Julius +aliases121.com + +// GDS : https://www.gov.uk/service-manual/technology/managing-domain-names +// Submitted by Stephen Ford +campaign.gov.uk +service.gov.uk +independent-commission.uk +independent-inquest.uk +independent-inquiry.uk +independent-panel.uk +independent-review.uk +public-inquiry.uk +royal-commission.uk + +// Gehirn Inc. : https://www.gehirn.co.jp/ +// Submitted by Kohei YOSHIDA +gehirn.ne.jp +usercontent.jp + +// Gentlent, Inc. : https://www.gentlent.com +// Submitted by Tom Klein +gentapps.com +gentlentapis.com +cdn-edges.net + +// GignoSystemJapan : http://gsj.bz +// Submitted by GignoSystemJapan +gsj.bz + +// GitBook Inc. : https://www.gitbook.com/ +// Submitted by Samy Pesse +gitbook.io + +// GitHub, Inc. +// Submitted by Patrick Toomey +github.app +githubusercontent.com +githubpreview.dev +github.io + +// GitLab, Inc. : https://about.gitlab.com/ +// Submitted by Alex Hanselka +gitlab.io + +// Gitplac.si : https://gitplac.si +// Submitted by Aljaž Starc +gitapp.si +gitpage.si + +// Global NOG Alliance : https://nogalliance.org/ +// Submitted by Sander Steffann +nog.community + +// Globe Hosting SRL : https://www.globehosting.com/ +// Submitted by Gavin Brown +co.ro +shop.ro + +// GMO Pepabo, Inc. : https://pepabo.com/ +// Submitted by Hosting Div +lolipop.io +angry.jp +babyblue.jp +babymilk.jp +backdrop.jp +bambina.jp +bitter.jp +blush.jp +boo.jp +boy.jp +boyfriend.jp +but.jp +candypop.jp +capoo.jp +catfood.jp +cheap.jp +chicappa.jp +chillout.jp +chips.jp +chowder.jp +chu.jp +ciao.jp +cocotte.jp +coolblog.jp +cranky.jp +cutegirl.jp +daa.jp +deca.jp +deci.jp +digick.jp +egoism.jp +fakefur.jp +fem.jp +flier.jp +floppy.jp +fool.jp +frenchkiss.jp +girlfriend.jp +girly.jp +gloomy.jp +gonna.jp +greater.jp +hacca.jp +heavy.jp +her.jp +hiho.jp +hippy.jp +holy.jp +hungry.jp +icurus.jp +itigo.jp +jellybean.jp +kikirara.jp +kill.jp +kilo.jp +kuron.jp +littlestar.jp +lolipopmc.jp +lolitapunk.jp +lomo.jp +lovepop.jp +lovesick.jp +main.jp +mods.jp +mond.jp +mongolian.jp +moo.jp +namaste.jp +nikita.jp +nobushi.jp +noor.jp +oops.jp +parallel.jp +parasite.jp +pecori.jp +peewee.jp +penne.jp +pepper.jp +perma.jp +pigboat.jp +pinoko.jp +punyu.jp +pupu.jp +pussycat.jp +pya.jp +raindrop.jp +readymade.jp +sadist.jp +schoolbus.jp +secret.jp +staba.jp +stripper.jp +sub.jp +sunnyday.jp +thick.jp +tonkotsu.jp +under.jp +upper.jp +velvet.jp +verse.jp +versus.jp +vivian.jp +watson.jp +weblike.jp +whitesnow.jp +zombie.jp +heteml.net + +// GoDaddy Registry : https://registry.godaddy +// Submitted by Rohan Durrant +graphic.design + +// GoIP DNS Services : http://www.goip.de +// Submitted by Christian Poulter +goip.de + +// Google, Inc. +// Submitted by Shannon McCabe +*.hosted.app +*.run.app +*.mtls.run.app +web.app +*.0emm.com +appspot.com +*.r.appspot.com +blogspot.com +codespot.com +googleapis.com +googlecode.com +pagespeedmobilizer.com +withgoogle.com +withyoutube.com +*.gateway.dev +cloud.goog +translate.goog +*.usercontent.goog +cloudfunctions.net + +// Goupile : https://goupile.fr +// Submitted by Niels Martignene +goupile.fr + +// GOV.UK Pay : https://www.payments.service.gov.uk/ +// Submitted by Richard Baker +pymnt.uk + +// GOV.UK Platform as a Service : https://www.cloud.service.gov.uk/ +// Submitted by Tom Whitwell +cloudapps.digital +london.cloudapps.digital + +// Government of the Netherlands : https://www.government.nl +// Submitted by +gov.nl + +// Grafana Labs : https://grafana.com/ +// Submitted by Platform Engineering +grafana-dev.net + +// GrayJay Web Solutions Inc. : https://grayjaysports.ca +// Submitted by Matt Yamkowy +grayjayleagues.com + +// Grebedoc : https://grebedoc.dev +// Submitted by Catherine Zotova +grebedoc.dev + +// GünstigBestellen : https://günstigbestellen.de +// Submitted by Furkan Akkoc +günstigbestellen.de +günstigliefern.de + +// GV.UY : https://nic.gv.uy +// Submitted by cheng +gv.uy + +// Hackclub Nest : https://hackclub.app +// Submitted by Cyteon +hackclub.app + +// Häkkinen.fi : https://www.häkkinen.fi/ +// Submitted by Eero Häkkinen +häkkinen.fi + +// Hashbang : https://hashbang.sh +hashbang.sh + +// Hasura : https://hasura.io +// Submitted by Shahidh K Muhammed +hasura.app +hasura-app.io + +// Hatena Co., Ltd. : https://hatena.co.jp +// Submitted by Masato Nakamura +hatenablog.com +hatenadiary.com +hateblo.jp +hatenablog.jp +hatenadiary.jp +hatenadiary.org + +// Heilbronn University of Applied Sciences - Faculty Informatics (GitLab Pages) : https://www.hs-heilbronn.de +// Submitted by Richard Zowalla +pages.it.hs-heilbronn.de +pages-research.it.hs-heilbronn.de + +// HeiyuSpace : https://lazycat.cloud +// Submitted by Xia Bin +heiyu.space + +// Helio Networks : https://heliohost.org +// Submitted by Ben Frede +helioho.st +heliohost.us + +// Hepforge : https://www.hepforge.org +// Submitted by David Grellscheid +hepforge.org + +// Heroku : https://www.heroku.com/ +// Submitted by Shumon Huque +herokuapp.com + +// Heyflow : https://www.heyflow.com +// Submitted by Mirko Nitschke +heyflow.page +heyflow.site + +// Hibernating Rhinos +// Submitted by Oren Eini +ravendb.cloud +ravendb.community +development.run +ravendb.run + +// HiDNS : https://www.hidoha.net +// Submitted by ifeng +hidns.co +hidns.vip + +// home.pl S.A. : https://home.pl +// Submitted by Krzysztof Wolski +homesklep.pl + +// Homebase : https://homebase.id/ +// Submitted by Jason Babo +*.kin.one +*.id.pub +*.kin.pub + +// Hoplix : https://www.hoplix.com +// Submitted by Danilo De Franco +hoplix.shop + +// HOSTBIP REGISTRY : https://www.hostbip.com/ +// Submitted by Atanunu Igbunuroghene +orx.biz +biz.ng +co.biz.ng +dl.biz.ng +go.biz.ng +lg.biz.ng +on.biz.ng +col.ng +firm.ng +gen.ng +ltd.ng +ngo.ng +plc.ng + +// HostyHosting : https://hostyhosting.com +hostyhosting.io + +// Hugging Face : https://huggingface.co +// Submitted by Eliott Coyac +hf.space +static.hf.space + +// Hypernode B.V. : https://www.hypernode.com/ +// Submitted by Cipriano Groenendal +hypernode.io + +// I-O DATA DEVICE, INC. : http://www.iodata.com/ +// Submitted by Yuji Minagawa +iobb.net + +// i-registry s.r.o. : http://www.i-registry.cz/ +// Submitted by Martin Semrad +co.cz + +// Ici la Lune : http://www.icilalune.com/ +// Submitted by Simon Morvan +*.moonscale.io +moonscale.net + +// iDOT Services Limited : http://www.domain.gr.com +// Submitted by Gavin Brown +gr.com + +// iki.fi +// Submitted by Hannu Aronsson +iki.fi + +// iliad italia : https://www.iliad.it +// Submitted by Marios Makassikis +ibxos.it +iliadboxos.it + +// Imagine : https://imagine.dev +// Submitted by Steven Nguyen +imagine-proxy.work + +// Incsub, LLC : https://incsub.com/ +// Submitted by Aaron Edwards +smushcdn.com +wphostedmail.com +wpmucdn.com +tempurl.host +wpmudev.host + +// Indevs : https://indevs.in +// Submitted by Sudheer Bhuvana +indevs.in + +// Individual Network Berlin e.V. : https://www.in-berlin.de/ +// Submitted by Christian Seitz +dyn-berlin.de +in-berlin.de +in-brb.de +in-butter.de +in-dsl.de +in-vpn.de +in-dsl.net +in-vpn.net +in-dsl.org +in-vpn.org + +// Inferno Communications : https://inferno.co.uk +// Submitted by Connor McFarlane +oninferno.net + +// info.at : http://www.info.at/ +biz.at +info.at + +// info.cx : http://info.cx +// Submitted by June Slater +info.cx + +// Interlegis : http://www.interlegis.leg.br +// Submitted by Gabriel Ferreira +ac.leg.br +al.leg.br +am.leg.br +ap.leg.br +ba.leg.br +ce.leg.br +df.leg.br +es.leg.br +go.leg.br +ma.leg.br +mg.leg.br +ms.leg.br +mt.leg.br +pa.leg.br +pb.leg.br +pe.leg.br +pi.leg.br +pr.leg.br +rj.leg.br +rn.leg.br +ro.leg.br +rr.leg.br +rs.leg.br +sc.leg.br +se.leg.br +sp.leg.br +to.leg.br + +// intermetrics GmbH : https://pixolino.com/ +// Submitted by Wolfgang Schwarz +pixolino.com + +// Internet-Pro, LLP : https://netangels.ru/ +// Submitted by Vasiliy Sheredeko +na4u.ru + +// Inventor Services : https://inventor.gg/ +// Submitted by Inventor Team +botdash.app +botdash.dev +botdash.gg +botdash.net +botda.sh +botdash.xyz + +// IONOS SE : https://www.ionos.com/ +// IONOS Group SE : https://www.ionos-group.com/ +// Submitted by Henrik Willert +apps-1and1.com +live-website.com +webspace-host.com +apps-1and1.net +websitebuilder.online +app-ionos.space + +// iopsys software solutions AB : https://iopsys.eu/ +// Submitted by Roman Azarenko +iopsys.se + +// IPFS Project : https://ipfs.tech/ +// Submitted by Interplanetary Shipyard +*.inbrowser.dev +*.dweb.link +*.inbrowser.link + +// IPiFony Systems, Inc. : https://www.ipifony.com/ +// Submitted by Matthew Hardeman +ipifony.net + +// ir.md : https://nic.ir.md +// Submitted by Ali Soizi +ir.md + +// is-a-good.dev : https://is-a-good.dev +// Submitted by William Harrison +is-a-good.dev + +// IServ GmbH : https://iserv.de +// Submitted by Kim Brodowski +iservschule.de +mein-iserv.de +schuldock.de +schulplattform.de +schulserver.de +test-iserv.de +iserv.dev +iserv.host + +// Ispmanager : https://www.ispmanager.com/ +// Submitted by Ispmanager infrastructure team +ispmanager.name + +// Jelastic, Inc. : https://jelastic.com/ +// Submitted by Ihor Kolodyuk +mel.cloudlets.com.au +cloud.interhostsolutions.be +alp1.ae.flow.ch +appengine.flow.ch +es-1.axarnet.cloud +diadem.cloud +vip.jelastic.cloud +jele.cloud +it1.eur.aruba.jenv-aruba.cloud +it1.jenv-aruba.cloud +keliweb.cloud +cs.keliweb.cloud +oxa.cloud +tn.oxa.cloud +uk.oxa.cloud +primetel.cloud +uk.primetel.cloud +ca.reclaim.cloud +uk.reclaim.cloud +us.reclaim.cloud +ch.trendhosting.cloud +de.trendhosting.cloud +jele.club +dopaas.com +paas.hosted-by-previder.com +rag-cloud.hosteur.com +rag-cloud-ch.hosteur.com +jcloud.ik-server.com +jcloud-ver-jpc.ik-server.com +demo.jelastic.com +paas.massivegrid.com +jed.wafaicloud.com +ryd.wafaicloud.com +j.scaleforce.com.cy +jelastic.dogado.eu +fi.cloudplatform.fi +demo.datacenter.fi +paas.datacenter.fi +jele.host +mircloud.host +paas.beebyte.io +sekd1.beebyteapp.io +jele.io +jc.neen.it +jcloud.kz +cloudjiffy.net +fra1-de.cloudjiffy.net +west1-us.cloudjiffy.net +jls-sto1.elastx.net +jls-sto2.elastx.net +jls-sto3.elastx.net +fr-1.paas.massivegrid.net +lon-1.paas.massivegrid.net +lon-2.paas.massivegrid.net +ny-1.paas.massivegrid.net +ny-2.paas.massivegrid.net +sg-1.paas.massivegrid.net +jelastic.saveincloud.net +nordeste-idc.saveincloud.net +j.scaleforce.net +sdscloud.pl +unicloud.pl +mircloud.ru +enscaled.sg +jele.site +jelastic.team +orangecloud.tn +j.layershift.co.uk +phx.enscaled.us +mircloud.us + +// Jino : https://www.jino.ru +// Submitted by Sergey Ulyashin +myjino.ru +*.hosting.myjino.ru +*.landing.myjino.ru +*.spectrum.myjino.ru +*.vps.myjino.ru + +// Jotelulu S.L. : https://jotelulu.com +// Submitted by Daniel Fariña +jote.cloud +jotelulu.cloud +eu1-plenit.com +la1-plenit.com +us1-plenit.com + +// JouwWeb B.V. : https://www.jouwweb.nl +// Submitted by Camilo Sperberg +webadorsite.com +jouwweb.site + +// Joyent : https://www.joyent.com/ +// Submitted by Brian Bennett +*.cns.joyent.com +*.triton.zone + +// JS.ORG : http://dns.js.org +// Submitted by Stefan Keim +js.org + +// KaasHosting : http://www.kaashosting.nl/ +// Submitted by Wouter Bakker +kaas.gg +khplay.nl + +// Kapsi : https://kapsi.fi +// Submitted by Tomi Juntunen +kapsi.fi + +// Katholieke Universiteit Leuven : https://www.kuleuven.be +// Submitted by Abuse KU Leuven +ezproxy.kuleuven.be +kuleuven.cloud + +// Kevin Service : https://kevsrv.me +// Submitted by Kevin Service Team +ae.kg + +// Keyweb AG : https://www.keyweb.de +// Submitted by Martin Dannehl +keymachine.de + +// KingHost : https://king.host +// Submitted by Felipe Keller Braz +kinghost.net +uni5.net + +// KnightPoint Systems, LLC : http://www.knightpoint.com/ +// Submitted by Roy Keene +knightpoint.systems + +// KoobinEvent, SL : https://www.koobin.com +// Submitted by Iván Oliva +koobin.events + +// Krellian Ltd. : https://krellian.com +// Submitted by Ben Francis +webthings.io +krellian.net + +// KUROKU LTD : https://kuroku.ltd/ +// Submitted by DisposaBoy +oya.to + +// KV GmbH : https://www.nic.co.de +// Submitted by KV GmbH +// Abuse reports to +co.de + +// Laravel Holdings, Inc. : https://laravel.com +// Submitted by André Valentin & James Brooks +laravel.cloud +on-forge.com +on-vapor.com + +// LCube - Professional hosting e.K. : https://www.lcube-webhosting.de +// Submitted by Lars Laehn +git-repos.de +lcube-server.de +svn-repos.de + +// Leadpages : https://www.leadpages.net +// Submitted by Greg Dallavalle +leadpages.co +lpages.co +lpusercontent.com + +// Leapcell : https://leapcell.io/ +// Submitted by Leapcell Team +leapcell.app +leapcell.dev +leapcell.online + +// Liara : https://liara.ir +// Submitted by Amirhossein Badinloo +liara.run +iran.liara.run + +// libp2p project : https://libp2p.io +// Submitted by Interplanetary Shipyard +libp2p.direct + +// Libre IT Ltd : https://libre.nz +// Submitted by Tomas Maggio +runcontainers.dev + +// Lifetime Hosting : https://Lifetime.Hosting/ +// Submitted by Mike Fillator +co.business +co.education +co.events +co.financial +co.network +co.place +co.technology + +// linkyard ldt : https://www.linkyard.ch/ +// Submitted by Mario Siegenthaler +linkyard-cloud.ch +linkyard.cloud + +// Linode : https://linode.com +// Submitted by +members.linode.com +*.nodebalancer.linode.com +*.linodeobjects.com +ip.linodeusercontent.com + +// LiquidNet Ltd : http://www.liquidnetlimited.com/ +// Submitted by Victor Velchev +we.bs + +// Listen53 : https://www.l53.net +// Submitted by Gerry Keh +filegear-sg.me +ggff.net + +// Localcert : https://localcert.dev +// Submitted by Lann Martin +*.user.localcert.dev + +// Localtonet : https://localtonet.com/ +// Submitted by Burak Isleyici +localtonet.com +*.localto.net + +// Lodz University of Technology LODMAN regional domains : https://www.man.lodz.pl/dns +// Submitted by Piotr Wilk +lodz.pl +pabianice.pl +plock.pl +sieradz.pl +skierniewice.pl +zgierz.pl + +// Log'in Line : https://www.loginline.com/ +// Submitted by Rémi Mach +loginline.app +loginline.dev +loginline.io +loginline.services +loginline.site + +// Lõhmus Family, The : https://lohmus.me/ +// Submitted by Heiki Lõhmus +lohmus.me + +// Lovable : https://lovable.dev +// Submitted by Fabian Hedin +lovable.app +lovableproject.com +lovable.run +lovable.sh + +// LubMAN UMCS Sp. z o.o : https://lubman.pl/ +// Submitted by Ireneusz Maliszewski +krasnik.pl +leczna.pl +lubartow.pl +lublin.pl +poniatowa.pl +swidnik.pl + +// Lug.org.uk : https://lug.org.uk +// Submitted by Jon Spriggs +glug.org.uk +lug.org.uk +lugs.org.uk + +// Lukanet Ltd : https://lukanet.com +// Submitted by Anton Avramov +barsy.bg +barsy.club +barsycenter.com +barsyonline.com +barsy.de +barsy.dev +barsy.eu +barsy.gr +barsy.in +barsy.info +barsy.io +barsy.me +barsy.menu +barsyonline.menu +barsy.mobi +barsy.net +barsy.online +barsy.org +barsy.pro +barsy.pub +barsy.ro +barsy.rs +barsy.shop +barsyonline.shop +barsy.site +barsy.store +barsy.support +barsy.uk +barsy.co.uk +barsyonline.co.uk + +// Lutra : https://lutra.ai +// Submitted by Joshua Newman +*.lutrausercontent.com + +// Luyani Inc. : https://luyani.com/ +// Submitted by Umut Gumeli +luyani.app +luyani.net + +// Magento Commerce +// Submitted by Damien Tournoud +*.magentosite.cloud + +// Magic Patterns : https://www.magicpatterns.com +// Submitted by Teddy Ni +magicpatterns.app +magicpatternsapp.com + +// Mail.Ru Group : https://hb.cldmail.ru +// Submitted by Ilya Zaretskiy +hb.cldmail.ru + +// MathWorks : https://www.mathworks.com/ +// Submitted by Emily Reed +matlab.cloud +modelscape.com +mwcloudnonprod.com +polyspace.com + +// May First - People Link : https://mayfirst.org/ +// Submitted by Jamie McClelland +mayfirst.info +mayfirst.org + +// Maze Play : https://www.mazeplay.com +// Submitted by Adam Humpherys +mazeplay.com + +// McHost : https://mchost.ru +// Submitted by Evgeniy Subbotin +mcdir.me +mcdir.ru +vps.mcdir.ru +mcpre.ru + +// Mediatech : https://mediatech.by +// Submitted by Evgeniy Kozhuhovskiy +mediatech.by +mediatech.dev + +// Medicom Health : https://medicomhealth.com +// Submitted by Michael Olson +hra.health + +// MedusaJS, Inc : https://medusajs.com/ +// Submitted by Stevche Radevski +medusajs.app + +// Memset hosting : https://www.memset.com +// Submitted by Tom Whitwell +miniserver.com +memset.net + +// Messerli Informatik AG : https://www.messerli.ch/ +// Submitted by Ruben Schmidmeister +messerli.app + +// Meta Platforms, Inc. : https://meta.com/ +// Submitted by Jacob Cordero +atmeta.com +apps.fbsbx.com + +// MetaCentrum, CESNET z.s.p.o. : https://www.metacentrum.cz/en/ +// Submitted by Zdeněk Šustr and Radim Janča +*.cloud.metacentrum.cz +custom.metacentrum.cz +flt.cloud.muni.cz +usr.cloud.muni.cz + +// Meteor Development Group : https://www.meteor.com/hosting +// Submitted by Pierre Carrier +meteorapp.com +eu.meteorapp.com + +// Michau Enterprises Limited : http://www.co.pl/ +co.pl + +// Microsoft Corporation : http://microsoft.com +// Submitted by Public Suffix List Admin +// Managed by Corporate Domains +// Microsoft Azure : https://home.azure +*.azurecontainer.io +azure-api.net +azure-mobile.net +azureedge.net +azurefd.net +azurestaticapps.net +1.azurestaticapps.net +2.azurestaticapps.net +3.azurestaticapps.net +4.azurestaticapps.net +5.azurestaticapps.net +6.azurestaticapps.net +7.azurestaticapps.net +centralus.azurestaticapps.net +eastasia.azurestaticapps.net +eastus2.azurestaticapps.net +westeurope.azurestaticapps.net +westus2.azurestaticapps.net +azurewebsites.net +cloudapp.net +trafficmanager.net +servicebus.usgovcloudapi.net +usgovcloudapp.net +blob.core.windows.net +servicebus.windows.net +azure-api.us +azurewebsites.us + +// MikroTik : https://mikrotik.com +// Submitted by MikroTik SysAdmin Team +routingthecloud.com +sn.mynetname.net +routingthecloud.net +routingthecloud.org + +// Million Software, Inc : https://million.dev/ +// Submitted by Rayhan Noufal Arayilakath +same-app.com +same-preview.com + +// minion.systems : http://minion.systems +// Submitted by Robert Böttinger +csx.cc + +// Mittwald CM Service GmbH & Co. KG : https://mittwald.de +// Submitted by Marco Rieger +mydbserver.com +webspaceconfig.de +mittwald.info +mittwaldserver.info +typo3server.info +project.space + +// Mocha : https://getmocha.com +// Submitted by Ben Reinhart +mocha.app +mochausercontent.com +mocha-sandbox.dev + +// MODX Systems LLC : https://modx.com +// Submitted by Elizabeth Southwell +modx.dev + +// Mozilla Foundation : https://mozilla.org/ +// Submitted by glob +bmoattachments.org + +// MSK-IX : https://www.msk-ix.ru/ +// Submitted by Khannanov Roman +net.ru +org.ru +pp.ru + +// Mythic Beasts : https://www.mythic-beasts.com +// Submitted by Paul Cammish +hostedpi.com +caracal.mythic-beasts.com +customer.mythic-beasts.com +fentiger.mythic-beasts.com +lynx.mythic-beasts.com +ocelot.mythic-beasts.com +oncilla.mythic-beasts.com +onza.mythic-beasts.com +sphinx.mythic-beasts.com +vs.mythic-beasts.com +x.mythic-beasts.com +yali.mythic-beasts.com +cust.retrosnub.co.uk + +// Nabu Casa : https://www.nabucasa.com +// Submitted by Paulus Schoutsen +ui.nabu.casa + +// Needle Tools GmbH : https://needle.tools +// Submitted by Felix Herbst +needle.run + +// Neo : https://www.neo.space +// Submitted by Ankit Kulkarni +co.site + +// Net at Work Gmbh : https://www.netatwork.de +// Submitted by Jan Jaeschke +cloud.nospamproxy.com +o365.cloud.nospamproxy.com + +// Net libre : https://www.netlib.re +// Submitted by Philippe PITTOLI +netlib.re + +// Netlify : https://www.netlify.com +// Submitted by Jessica Parsons +netlify.app + +// Neustar Inc. +// Submitted by Trung Tran +4u.com + +// NFSN, Inc. : https://www.NearlyFreeSpeech.NET/ +// Submitted by Jeff Wheelhouse +nfshost.com + +// NFT.Storage : https://nft.storage/ +// Submitted by Vasco Santos or +ipfs.nftstorage.link + +// NGO.US Registry : https://nic.ngo.us +// Submitted by Alstra Solutions Ltd. Networking Team +ngo.us + +// ngrok : https://ngrok.com/ +// Submitted by Alan Shreve +ngrok.app +ngrok-free.app +ngrok.dev +ngrok-free.dev +ngrok.io +ap.ngrok.io +au.ngrok.io +eu.ngrok.io +in.ngrok.io +jp.ngrok.io +sa.ngrok.io +us.ngrok.io +ngrok.pizza +ngrok.pro + +// Nicolaus Copernicus University in Torun - MSK TORMAN : https://www.man.torun.pl +torun.pl + +// Nimbus Hosting Ltd. : https://www.nimbushosting.co.uk/ +// Submitted by Nicholas Ford +nh-serv.co.uk +nimsite.uk + +// No-IP.com : https://noip.com/ +// Submitted by Deven Reza +mmafan.biz +myftp.biz +no-ip.biz +no-ip.ca +fantasyleague.cc +gotdns.ch +3utilities.com +blogsyte.com +ciscofreak.com +damnserver.com +ddnsking.com +ditchyourip.com +dnsiskinky.com +dynns.com +geekgalaxy.com +health-carereform.com +homesecuritymac.com +homesecuritypc.com +myactivedirectory.com +mysecuritycamera.com +myvnc.com +net-freaks.com +onthewifi.com +point2this.com +quicksytes.com +securitytactics.com +servebeer.com +servecounterstrike.com +serveexchange.com +serveftp.com +servegame.com +servehalflife.com +servehttp.com +servehumour.com +serveirc.com +servemp3.com +servep2p.com +servepics.com +servequake.com +servesarcasm.com +stufftoread.com +unusualperson.com +workisboring.com +dvrcam.info +ilovecollege.info +no-ip.info +brasilia.me +ddns.me +dnsfor.me +hopto.me +loginto.me +noip.me +webhop.me +bounceme.net +ddns.net +eating-organic.net +mydissent.net +myeffect.net +mymediapc.net +mypsx.net +mysecuritycamera.net +nhlfan.net +no-ip.net +pgafan.net +privatizehealthinsurance.net +redirectme.net +serveblog.net +serveminecraft.net +sytes.net +cable-modem.org +collegefan.org +couchpotatofries.org +hopto.org +mlbfan.org +myftp.org +mysecuritycamera.org +nflfan.org +no-ip.org +read-books.org +ufcfan.org +zapto.org +no-ip.co.uk +golffan.us +noip.us +pointto.us + +// NodeArt : https://nodeart.io +// Submitted by Konstantin Nosov +stage.nodeart.io + +// Noop : https://noop.app +// Submitted by Nathaniel Schweinberg +*.developer.app +noop.app + +// Northflank Ltd. : https://northflank.com/ +// Submitted by Marco Suter +*.northflank.app +*.build.run +*.code.run +*.database.run +*.migration.run + +// Noticeable : https://noticeable.io +// Submitted by Laurent Pellegrino +noticeable.news + +// Notion Labs, Inc : https://www.notion.so/ +// Submitted by Jess Yao +notion.site + +// Now-DNS : https://now-dns.com +// Submitted by Steve Russell +dnsking.ch +mypi.co +myiphost.com +forumz.info +soundcast.me +tcp4.me +dnsup.net +hicam.net +now-dns.net +ownip.net +vpndns.net +dynserv.org +now-dns.org +x443.pw +ntdll.top +freeddns.us + +// nsupdate.info : https://www.nsupdate.info/ +// Submitted by Thomas Waldmann +nsupdate.info +nerdpol.ovh + +// O3O.Foundation : https://o3o.foundation/ +// Submitted by the prvcy.page Registry Team +prvcy.page + +// Observable, Inc. : https://observablehq.com +// Submitted by Mike Bostock +observablehq.cloud +static.observableusercontent.com + +// OMG.LOL : https://omg.lol +// Submitted by Adam Newbold +omg.lol + +// Omnibond Systems, LLC. : https://www.omnibond.com +// Submitted by Cole Estep +cloudycluster.net + +// OmniWe Limited : https://omniwe.com +// Submitted by Vicary Archangel +omniwe.site + +// One.com : https://www.one.com/ +// Submitted by Jacob Bunk Nielsen +123webseite.at +123website.be +simplesite.com.br +123website.ch +simplesite.com +123webseite.de +123hjemmeside.dk +123miweb.es +123kotisivu.fi +123siteweb.fr +simplesite.gr +123homepage.it +123website.lu +123website.nl +123hjemmeside.no +service.one +website.one +simplesite.pl +123paginaweb.pt +123minsida.se + +// ONID : https://get.onid.ca +// Submitted by ONID Engineering Team +onid.ca + +// Open Domains : https://open-domains.net +// Submitted by William Harrison +is-a-fullstack.dev +is-cool.dev +is-not-a.dev +localplayer.dev +is-local.org + +// Open Social : https://www.getopensocial.com/ +// Submitted by Alexander Varwijk +opensocial.site + +// OpenAI : https://openai.com +// Submitted by Thomas Shadwell +*.oaiusercontent.com + +// OpenCraft GmbH : http://opencraft.com/ +// Submitted by Sven Marnach +opencraft.hosting + +// OpenHost : https://registry.openhost.uk +// Submitted by OpenHost Registry Team +16-b.it +32-b.it +64-b.it + +// OpenResearch GmbH : https://openresearch.com/ +// Submitted by Philipp Schmid +orsites.com + +// Opera Software, A.S.A. +// Submitted by Yngve Pettersen +operaunite.com + +// Oracle Dyn : https://cloud.oracle.com/home https://dyn.com/dns/ +// Submitted by Gregory Drake +// Note: This is intended to also include customer-oci.com due to wildcards implicitly including the current label +*.customer-oci.com +*.oci.customer-oci.com +*.ocp.customer-oci.com +*.ocs.customer-oci.com +*.oraclecloudapps.com +*.oraclegovcloudapps.com +*.oraclegovcloudapps.uk + +// Orange : https://www.orange.com +// Submitted by Alexandre Linte +tech.orange + +// OsSav Technology Ltd. : https://ossav.com/ +// Submitted by OsSav Technology Ltd. +// https://nic.can.re +can.re + +// Oursky Limited : https://authgear.com/ +// Submitted by Authgear Team & Skygear Developer +authgear-staging.com +authgearapps.com +skygearapp.com + +// OutSystems +// Submitted by Duarte Santos +outsystemscloud.com + +// OVHcloud : https://ovhcloud.com +// Submitted by Vincent Cassé +*.hosting.ovh.net +*.webpaas.ovh.net + +// OwnProvider GmbH : http://www.ownprovider.com +// Submitted by Jan Moennich +ownprovider.com +own.pm + +// OwO : https://whats-th.is/ +// Submitted by Dean Sheather +*.owo.codes + +// OX : http://www.ox.rs +// Submitted by Adam Grand +ox.rs + +// oy.lc +// Submitted by Charly Coste +oy.lc + +// Pagefog : https://pagefog.com/ +// Submitted by Derek Myers +pgfog.com + +// PageXL : https://pagexl.com +// Submitted by Yann Guichard +pagexl.com + +// Pantheon Systems, Inc. : https://pantheon.io/ +// Submitted by Gary Dylina +gotpantheon.com +pantheonsite.io + +// Paywhirl, Inc : https://paywhirl.com/ +// Submitted by Daniel Netzer +*.paywhirl.com + +// pcarrier.ca Software Inc : https://pcarrier.ca/ +// Submitted by Pierre Carrier +*.xmit.co +xmit.dev +madethis.site +srv.us +gh.srv.us +gl.srv.us + +// Peplink | Pepwave : http://peplink.com/ +// Submitted by Steve Leung +mypep.link + +// Perspecta : https://perspecta.com/ +// Submitted by Kenneth Van Alstyne +perspecta.cloud + +// Ping Identity : https://www.pingidentity.com +// Submitted by Ping Identity +forgeblocks.com +id.forgerock.io + +// Plain : https://www.plain.com/ +// Submitted by Jesús Hernández +support.site + +// Planet-Work : https://www.planet-work.com/ +// Submitted by Frédéric VANNIÈRE +on-web.fr + +// Platform.sh : https://platform.sh +// Submitted by Nikola Kotur +*.upsun.app +upsunapp.com +ent.platform.sh +eu.platform.sh +us.platform.sh +*.platformsh.site +*.tst.site + +// Pley AB : https://www.pley.com/ +// Submitted by Henning Pohl +pley.games + +// Porter : https://porter.run/ +// Submitted by Rudraksh MK +onporter.run + +// Positive Codes Technology Company : http://co.bn/faq.html +// Submitted by Zulfais +co.bn + +// Postman, Inc : https://postman.com +// Submitted by Rahul Dhawan +postman-echo.com +pstmn.io +mock.pstmn.io +httpbin.org + +// prequalifyme.today : https://prequalifyme.today +// Submitted by DeepakTiwari deepak@ivylead.io +prequalifyme.today + +// prgmr.com : https://prgmr.com/ +// Submitted by Sarah Newman +xen.prgmr.com + +// priv.at : http://www.nic.priv.at/ +// Submitted by registry +priv.at + +// PROJECT ELIV : https://eliv.kr/ +// Submitted by PROJECT ELIV Domain Team +c01.kr +eliv-cdn.kr +eliv-dns.kr +mmv.kr +vki.kr + +// project-study : https://project-study.com +// Submitted by yumenewa +dev.project-study.com + +// Protonet GmbH : http://protonet.io +// Submitted by Martin Meier +protonet.io + +// PSL Sandbox : https://github.com/groundcat/PSL-Sandbox +// Submitted by groundcat +platter-app.dev + +// PT Ekossistim Indo Digital : https://e.id +// Submitted by Eid Team +e.id + +// Publication Presse Communication SARL : https://ppcom.fr +// Submitted by Yaacov Akiba Slama +chirurgiens-dentistes-en-france.fr +byen.site + +// PublicZone : https://publiczone.org/ +// Submitted by PublicZone NOC Team +nyc.mn +*.cn.st + +// pubtls.org : https://www.pubtls.org +// Submitted by Kor Nielsen +pubtls.org + +// PythonAnywhere LLP : https://www.pythonanywhere.com +// Submitted by Giles Thomas +pythonanywhere.com +eu.pythonanywhere.com + +// QA2 +// Submitted by Daniel Dent : https://www.danieldent.com/ +qa2.com + +// QCX +// Submitted by Cassandra Beelen +qcx.io +*.sys.qcx.io + +// QNAP System Inc : https://www.qnap.com +// Submitted by Nick Chang +myqnapcloud.cn +alpha-myqnapcloud.com +dev-myqnapcloud.com +mycloudnas.com +mynascloud.com +myqnapcloud.com + +// QOTO, Org. +// Submitted by Jeffrey Phillips Freeman +qoto.io + +// Qualifio : https://qualifio.com/ +// Submitted by Xavier De Cock +qualifioapp.com + +// Quality Unit : https://qualityunit.com +// Submitted by Vasyl Tsalko +ladesk.com + +// Qualy : https://qualyhq.com +// Submitted by Raphael Arias +*.qualyhqpartner.com +*.qualyhqportal.com + +// QuickBackend : https://www.quickbackend.com +// Submitted by Dani Biro +qbuser.com + +// Quip : https://quip.com +// Submitted by Patrick Linehan +*.quipelements.com + +// Qutheory LLC : http://qutheory.io +// Submitted by Jonas Schwartz +vapor.cloud +vaporcloud.io + +// Rackmaze LLC : https://www.rackmaze.com +// Submitted by Kirill Pertsev +rackmaze.com +rackmaze.net + +// Rad Web Hosting : https://radwebhosting.com +// Submitted by Scott Claeys +cloudsite.builders +myradweb.net +servername.us + +// Radix FZC : http://domains.in.net +// Submitted by Gavin Brown +web.in +in.net + +// Raidboxes GmbH : https://raidboxes.de +// Submitted by Auke Tembrink +myrdbx.io +site.rb-hosting.io + +// Railway Corporation : https://railway.com +// Submitted by Phineas Walton +up.railway.app + +// Rancher Labs, Inc : https://rancher.com +// Submitted by Vincent Fiduccia +*.on-rancher.cloud +*.on-k3s.io +*.on-rio.io + +// RavPage : https://www.ravpage.co.il +// Submitted by Roni Horowitz +ravpage.co.il + +// Read The Docs, Inc : https://www.readthedocs.org +// Submitted by David Fischer +readthedocs-hosted.com +readthedocs.io + +// Red Hat, Inc. OpenShift : https://openshift.redhat.com/ +// Submitted by Tim Kramer +rhcloud.com + +// Redgate Software : https://red-gate.com +// Submitted by Andrew Farries +instances.spawn.cc + +// Redpanda Data : https://redpanda.com +// Submitted by Infrastructure Team +*.clusters.rdpa.co +*.srvrless.rdpa.co + +// Render : https://render.com +// Submitted by Anurag Goel +onrender.com +app.render.com + +// Repl.it : https://repl.it +// Submitted by Lincoln Bergeson +replit.app +id.replit.app +firewalledreplit.co +id.firewalledreplit.co +repl.co +id.repl.co +replit.dev +archer.replit.dev +bones.replit.dev +canary.replit.dev +global.replit.dev +hacker.replit.dev +id.replit.dev +janeway.replit.dev +kim.replit.dev +kira.replit.dev +kirk.replit.dev +odo.replit.dev +paris.replit.dev +picard.replit.dev +pike.replit.dev +prerelease.replit.dev +reed.replit.dev +riker.replit.dev +sisko.replit.dev +spock.replit.dev +staging.replit.dev +sulu.replit.dev +tarpit.replit.dev +teams.replit.dev +tucker.replit.dev +wesley.replit.dev +worf.replit.dev +repl.run + +// Resin.io : https://resin.io +// Submitted by Tim Perry +resindevice.io +devices.resinstaging.io + +// RethinkDB : https://www.rethinkdb.com/ +// Submitted by Chris Kastorff +hzc.io + +// Rico Developments Limited : https://adimo.co +// Submitted by Colin Brown +adimo.co.uk + +// Riseup Networks : https://riseup.net +// Submitted by Micah Anderson +itcouldbewor.se + +// Roar Domains LLC : https://roar.basketball/ +// Submitted by Gavin Brown +aus.basketball +nz.basketball + +// ROBOT PAYMENT INC. : https://www.robotpayment.co.jp/ +// Submitted by Kentaro Takamori +subsc-pay.com +subsc-pay.net + +// Rochester Institute of Technology : http://www.rit.edu/ +// Submitted by Jennifer Herting +git-pages.rit.edu + +// Rocky Enterprise Software Foundation : https://resf.org +// Submitted by Neil Hanlon +rocky.page + +// Ruhr University Bochum : https://www.ruhr-uni-bochum.de/ +// Submitted by Andreas Jobs +rub.de +ruhr-uni-bochum.de +io.noc.ruhr-uni-bochum.de + +// Rusnames Limited : http://rusnames.ru/ +// Submitted by Sergey Zotov +биз.рус +ком.рус +крым.рус +мир.рус +мск.рус +орг.рус +самара.рус +сочи.рус +спб.рус +я.рус + +// Russian Academy of Sciences +// Submitted by Tech Support +ras.ru + +// Sakura Frp : https://www.natfrp.com +// Submitted by Bobo Liu +nyat.app + +// SAKURA Internet Inc. : https://www.sakura.ad.jp/ +// Submitted by Internet Service Department +180r.com +dojin.com +sakuratan.com +sakuraweb.com +x0.com +2-d.jp +bona.jp +crap.jp +daynight.jp +eek.jp +flop.jp +halfmoon.jp +jeez.jp +matrix.jp +mimoza.jp +ivory.ne.jp +mail-box.ne.jp +mints.ne.jp +mokuren.ne.jp +opal.ne.jp +sakura.ne.jp +sumomo.ne.jp +topaz.ne.jp +netgamers.jp +nyanta.jp +o0o0.jp +rdy.jp +rgr.jp +rulez.jp +s3.isk01.sakurastorage.jp +s3.isk02.sakurastorage.jp +saloon.jp +sblo.jp +skr.jp +tank.jp +uh-oh.jp +undo.jp +rs.webaccel.jp +user.webaccel.jp +websozai.jp +xii.jp +squares.net +jpn.org +kirara.st +x0.to +from.tv +sakura.tv + +// Salesforce.com, Inc. : https://salesforce.com/ +// Submitted by Salesforce Public Suffix List Team +*.builder.code.com +*.dev-builder.code.com +*.stg-builder.code.com +*.001.test.code-builder-stg.platform.salesforce.com +*.d.crm.dev +*.w.crm.dev +*.wa.crm.dev +*.wb.crm.dev +*.wc.crm.dev +*.wd.crm.dev +*.we.crm.dev +*.wf.crm.dev + +// Sandstorm Development Group, Inc. : https://sandcats.io/ +// Submitted by Asheesh Laroia +sandcats.io + +// Sav.com, LLC : https://marketing.sav.com/ +// Submitted by Mukul Kudegave +sav.case + +// SBE network solutions GmbH : https://www.sbe.de/ +// Submitted by Norman Meilick +logoip.com +logoip.de + +// Scaleway : https://www.scaleway.com/ +// Submitted by Scaleway PSL Maintainer +fr-par-1.baremetal.scw.cloud +fr-par-2.baremetal.scw.cloud +nl-ams-1.baremetal.scw.cloud +cockpit.fr-par.scw.cloud +ddl.fr-par.scw.cloud +dtwh.fr-par.scw.cloud +fnc.fr-par.scw.cloud +functions.fnc.fr-par.scw.cloud +ifr.fr-par.scw.cloud +k8s.fr-par.scw.cloud +nodes.k8s.fr-par.scw.cloud +kafk.fr-par.scw.cloud +mgdb.fr-par.scw.cloud +rdb.fr-par.scw.cloud +s3.fr-par.scw.cloud +s3-website.fr-par.scw.cloud +scbl.fr-par.scw.cloud +whm.fr-par.scw.cloud +priv.instances.scw.cloud +pub.instances.scw.cloud +k8s.scw.cloud +cockpit.nl-ams.scw.cloud +ddl.nl-ams.scw.cloud +dtwh.nl-ams.scw.cloud +ifr.nl-ams.scw.cloud +k8s.nl-ams.scw.cloud +nodes.k8s.nl-ams.scw.cloud +kafk.nl-ams.scw.cloud +mgdb.nl-ams.scw.cloud +rdb.nl-ams.scw.cloud +s3.nl-ams.scw.cloud +s3-website.nl-ams.scw.cloud +scbl.nl-ams.scw.cloud +whm.nl-ams.scw.cloud +cockpit.pl-waw.scw.cloud +ddl.pl-waw.scw.cloud +dtwh.pl-waw.scw.cloud +ifr.pl-waw.scw.cloud +k8s.pl-waw.scw.cloud +nodes.k8s.pl-waw.scw.cloud +kafk.pl-waw.scw.cloud +mgdb.pl-waw.scw.cloud +rdb.pl-waw.scw.cloud +s3.pl-waw.scw.cloud +s3-website.pl-waw.scw.cloud +scbl.pl-waw.scw.cloud +scalebook.scw.cloud +smartlabeling.scw.cloud +dedibox.fr + +// schokokeks.org GbR : https://schokokeks.org/ +// Submitted by Hanno Böck +schokokeks.net + +// Scottish Government : https://www.gov.scot +// Submitted by Martin Ellis +gov.scot +service.gov.scot + +// Scry Security : http://www.scrysec.com +// Submitted by Shante Adam +scrysec.com + +// Scrypted : https://scrypted.app +// Submitted by Koushik Dutta +client.scrypted.io + +// Securepoint GmbH : https://www.securepoint.de +// Submitted by Erik Anders +firewall-gateway.com +firewall-gateway.de +my-gateway.de +my-router.de +spdns.de +spdns.eu +firewall-gateway.net +my-firewall.org +myfirewall.org +spdns.org + +// Seidat : https://www.seidat.com +// Submitted by Artem Kondratev +seidat.net + +// Sellfy : https://sellfy.com +// Submitted by Yuriy Romadin +sellfy.store + +// Sendmsg : https://www.sendmsg.co.il +// Submitted by Assaf Stern +minisite.ms + +// Senseering GmbH : https://www.senseering.de +// Submitted by Felix Mönckemeyer +senseering.net + +// Servebolt AS : https://servebolt.com +// Submitted by Daniel Kjeserud +servebolt.cloud + +// Service Online LLC : http://drs.ua/ +// Submitted by Serhii Bulakh +biz.ua +co.ua +pp.ua + +// Shanghai Accounting Society : https://www.sasf.org.cn +// Submitted by Information Administration +as.sh.cn + +// Sheezy.Art : https://sheezy.art +// Submitted by Nyoom +sheezy.games + +// Shopblocks : http://www.shopblocks.com/ +// Submitted by Alex Bowers +myshopblocks.com + +// Shopify : https://www.shopify.com +// Submitted by Alex Richter +myshopify.com + +// Shopit : https://www.shopitcommerce.com/ +// Submitted by Craig McMahon +shopitsite.com + +// shopware AG : https://shopware.com +// Submitted by Jens Küper +shopware.shop +shopware.store + +// Siemens Mobility GmbH +// Submitted by Oliver Graebner +mo-siemens.io + +// SinaAppEngine : http://sae.sina.com.cn/ +// Submitted by SinaAppEngine +1kapp.com +appchizi.com +applinzi.com +sinaapp.com +vipsinaapp.com + +// Siteleaf : https://www.siteleaf.com/ +// Submitted by Skylar Challand +siteleaf.net + +// Small Technology Foundation : https://small-tech.org +// Submitted by Aral Balkan +small-web.org + +// Smallregistry by Promopixel SARL : https://www.smallregistry.net +// Former AFNIC's SLDs +// Submitted by Jérôme Lipowicz +aeroport.fr +avocat.fr +chambagri.fr +chirurgiens-dentistes.fr +experts-comptables.fr +medecin.fr +notaires.fr +pharmacien.fr +port.fr +veterinaire.fr + +// Smoove.io : https://www.smoove.io/ +// Submitted by Dan Kozak +vp4.me + +// Snowflake Inc : https://www.snowflake.com/ +// Submitted by Sam Haar +*.snowflake.app +*.privatelink.snowflake.app +streamlit.app +streamlitapp.com + +// Snowplow Analytics : https://snowplowanalytics.com/ +// Submitted by Ian Streeter +try-snowplow.com + +// Software Consulting Michal Zalewski : https://www.mafelo.com +// Submitted by Michal Zalewski +mafelo.net + +// Sony Interactive Entertainment LLC : https://sie.com/ +// Submitted by David Coles +playstation-cloud.com + +// SourceHut : https://sourcehut.org +// Submitted by Drew DeVault +srht.site + +// SourceLair PC : https://www.sourcelair.com +// Submitted by Antonis Kalipetis +apps.lair.io +*.stolos.io + +// sourceWAY GmbH : https://sourceway.de +// Submitted by Richard Reiber +4.at +my.at +my.de +*.nxa.eu +nx.gw + +// SpeedPartner GmbH : https://www.speedpartner.de/ +// Submitted by Stefan Neufeind +customer.speedpartner.de + +// Spreadshop (sprd.net AG) : https://www.spreadshop.com/ +// Submitted by Martin Breest +myspreadshop.at +myspreadshop.com.au +myspreadshop.be +myspreadshop.ca +myspreadshop.ch +myspreadshop.com +myspreadshop.de +myspreadshop.dk +myspreadshop.es +myspreadshop.fi +myspreadshop.fr +myspreadshop.ie +myspreadshop.it +myspreadshop.net +myspreadshop.nl +myspreadshop.no +myspreadshop.pl +myspreadshop.se +myspreadshop.co.uk + +// StackBlitz : https://stackblitz.com +// Submitted by Dominic Elm & Albert Pai +w-corp-staticblitz.com +w-credentialless-staticblitz.com +w-staticblitz.com +bolt.host + +// Stackhero : https://www.stackhero.io +// Submitted by Adrien Gillon +stackhero-network.com + +// STACKIT GmbH & Co. KG : https://www.stackit.de/en/ +// Submitted by STACKIT-DNS Team (Simon Stier) +runs.onstackit.cloud +stackit.gg +stackit.rocks +stackit.run +stackit.zone + +// Staclar : https://staclar.com +// Submitted by Q Misell +// Submitted by Matthias Merkel +musician.io +novecore.site + +// Standard Library : https://stdlib.com +// Submitted by Jacob Lee +api.stdlib.com + +// statichost.eu : https://www.statichost.eu +// Submitted by Eric Selin +statichost.page + +// stereosense GmbH : https://www.involve.me +// Submitted by Florian Burmann +feedback.ac +forms.ac +assessments.cx +calculators.cx +funnels.cx +paynow.cx +quizzes.cx +researched.cx +tests.cx +surveys.so + +// Storacha Network : https://storacha.network +// Submitted by Alan Shaw +ipfs.storacha.link +ipfs.w3s.link + +// Storebase : https://www.storebase.io +// Submitted by Tony Schirmer +storebase.store + +// Storj Labs Inc. : https://storj.io/ +// Submitted by Philip Hutchins +storj.farm + +// Strapi : https://strapi.io/ +// Submitted by Florent Baldino +strapiapp.com +media.strapiapp.com + +// Strategic System Consulting (eApps Hosting) : https://www.eapps.com/ +// Submitted by Alex Oancea +vps-host.net +atl.jelastic.vps-host.net +njs.jelastic.vps-host.net +ric.jelastic.vps-host.net + +// Streak : https://streak.com +// Submitted by Blake Kadatz +streak-link.com +streaklinks.com +streakusercontent.com + +// Student-Run Computing Facility : https://www.srcf.net/ +// Submitted by Edwin Balani +soc.srcf.net +user.srcf.net + +// Studenten Net Twente : http://www.snt.utwente.nl/ +// Submitted by Silke Hofstra +utwente.io + +// Sub 6 Limited : http://www.sub6.com +// Submitted by Dan Miller +temp-dns.com + +// Supabase : https://supabase.io +// Submitted by Supabase Security +supabase.co +realtime.supabase.co +storage.supabase.co +supabase.in +supabase.net + +// Syncloud : https://syncloud.org +// Submitted by Boris Rybalkin +syncloud.it + +// Synology, Inc. : https://www.synology.com/ +// Submitted by Rony Weng +dscloud.biz +direct.quickconnect.cn +dsmynas.com +familyds.com +diskstation.me +dscloud.me +i234.me +myds.me +synology.me +dscloud.mobi +dsmynas.net +familyds.net +dsmynas.org +familyds.org +direct.quickconnect.to +vpnplus.to + +// Tabit Technologies Ltd. : https://tabit.cloud/ +// Submitted by Oren Agiv +mytabit.com +mytabit.co.il +tabitorder.co.il + +// TAIFUN Software AG : http://taifun-software.de +// Submitted by Bjoern Henke +taifun-dns.de + +// Tailor Inc. : https://www.tailor.tech +// Submitted by Ryuzo Yamamoto +erp.dev +web.erp.dev + +// Tailscale Inc. : https://www.tailscale.com +// Submitted by David Anderson +ts.net +*.c.ts.net + +// TASK geographical domains : https://task.gda.pl/en/services/for-entrepreneurs/ +gda.pl +gdansk.pl +gdynia.pl +med.pl +sopot.pl + +// Tave Creative Corp : https://tave.com/ +// Submitted by Adrian Ziemkowski +taveusercontent.com + +// tawk.to, Inc : https://www.tawk.to +// Submitted by tawk.to developer team +p.tawk.email +p.tawkto.email + +// Tche.br : https://tche.br +// Submitted by Bruno Lorensi +tche.br + +// team.blue : https://team.blue +// Submitted by Cedric Dubois +site.tb-hosting.com +directwp.eu + +// TechEdge Limited: https://www.nic.uk.cc/ +// Submitted by TechEdge Developer +ec.cc +eu.cc +gu.cc +uk.cc +us.cc + +// Teckids e.V. : https://www.teckids.org +// Submitted by Dominik George +edugit.io +s3.teckids.org + +// Telebit : https://telebit.cloud +// Submitted by AJ ONeal +telebit.app +telebit.io +*.telebit.xyz + +// Teleport : https://goteleport.com +// Submitted by Rob Picard +teleport.sh + +// Thingdust AG : https://thingdust.com/ +// Submitted by Adrian Imboden +*.firenet.ch +*.svc.firenet.ch +reservd.com +thingdustdata.com +cust.dev.thingdust.io +reservd.dev.thingdust.io +cust.disrec.thingdust.io +reservd.disrec.thingdust.io +cust.prod.thingdust.io +cust.testing.thingdust.io +reservd.testing.thingdust.io + +// ticket i/O GmbH : https://ticket.io +// Submitted by Christian Franke +tickets.io + +// Tlon.io : https://tlon.io +// Submitted by Mark Staarink +arvo.network +azimuth.network +tlon.network + +// Tor Project, Inc. : https://torproject.org +// Submitted by Antoine Beaupré +torproject.net +pages.torproject.net + +// TownNews.com : http://www.townnews.com +// Submitted by Dustin Ward +townnews-staging.com + +// TrafficPlex GmbH : https://www.trafficplex.de/ +// Submitted by Phillipp Röll +12hp.at +2ix.at +4lima.at +lima-city.at +12hp.ch +2ix.ch +4lima.ch +lima-city.ch +trafficplex.cloud +de.cool +12hp.de +2ix.de +4lima.de +lima-city.de +1337.pictures +clan.rip +lima-city.rocks +webspace.rocks +lima.zone + +// TransIP : https://www.transip.nl +// Submitted by Rory Breuk and Cedric Dubois +*.transurl.be +*.transurl.eu +site.transip.me +*.transurl.nl + +// Tunnelmole: https://tunnelmole.com +// Submitted by Robbie Cahill +tunnelmole.net + +// TuxFamily : http://tuxfamily.org +// Submitted by TuxFamily administrators +tuxfamily.org + +// TwoDNS : https://www.twodns.de/ +// Submitted by TwoDNS-Support +dd-dns.de +dray-dns.de +draydns.de +dyn-vpn.de +dynvpn.de +mein-vigor.de +my-vigor.de +my-wan.de +syno-ds.de +synology-diskstation.de +synology-ds.de +diskstation.eu +diskstation.org + +// Typedream : https://typedream.com +// Submitted by Putri Karunia +typedream.app + +// Typeform : https://www.typeform.com +// Submitted by Typeform +pro.typeform.com + +// Uberspace : https://uberspace.de +// Submitted by Moritz Werner +uber.space + +// UDR Limited : http://www.udr.hk.com +// Submitted by registry +hk.com +inc.hk +ltd.hk +hk.org + +// UK Intis Telecom LTD : https://it.com +// Submitted by ITComdomains +it.com + +// Umso Software Inc. : https://www.umso.com +// Submitted by Alexis Taylor +umso.co + +// Unison Computing, PBC : https://unison.cloud +// Submitted by Simon Højberg +unison-services.cloud + +// United Gameserver GmbH : https://united-gameserver.de +// Submitted by Stefan Schwarz +virtual-user.de +virtualuser.de + +// United States Writing Corporation : https://uswriting.co +// Submitted by Andrew Sampson +obj.ag + +// UNIVERSAL DOMAIN REGISTRY : https://www.udr.org.yt/ +// see also: whois -h whois.udr.org.yt help +// Submitted by Atanunu Igbunuroghene +name.pm +sch.tf +biz.wf +sch.wf +org.yt + +// University of Banja Luka : https://unibl.org +// Domains for Republic of Srpska administrative entity. +// Submitted by Marko Ivanovic +rs.ba + +// University of Bielsko-Biala regional domain : http://dns.bielsko.pl/ +// Submitted by Marcin +bielsko.pl + +// urown.net : https://urown.net +// Submitted by Hostmaster +urown.cloud +dnsupdate.info + +// US REGISTRY LLC : http://us.org +// Submitted by Gavin Brown +us.org + +// V.UA Domain Registry: https://www.v.ua/ +// Submitted by Serhii Rostilo +v.ua + +// Val Town, Inc : https://val.town/ +// Submitted by Tom MacWright +val.run +web.val.run + +// Vercel, Inc : https://vercel.com/ +// Submitted by Laurens Duijvesteijn +vercel.app +v0.build +vercel.dev +vusercontent.net +vercel.run +now.sh + +// VeryPositive SIA : http://very.lv +// Submitted by Danko Aleksejevs +2038.io + +// Virtual-Info : https://www.virtual-info.info/ +// Submitted by Adnan RIHAN +v-info.info + +// VistaBlog : https://vistablog.ir/ +// Submitted by Hossein Piri +vistablog.ir + +// Viva Republica, Inc. : https://toss.im/ +// Submitted by Deus Team +deus-canvas.com + +// Voorloper.com : https://voorloper.com +// Submitted by Nathan van Bakel +voorloper.cloud + +// Vultr Objects : https://www.vultr.com/products/object-storage/ +// Submitted by Niels Maumenee +*.vultrobjects.com + +// Waffle Computer Inc., Ltd. : https://docs.waffleinfo.com +// Submitted by Masayuki Note +wafflecell.com + +// Walrus : https://walrus.xyz +// Submitted by Max Spector +wal.app + +// Wasmer: https://wasmer.io +// Submitted by Lorentz Kinde +wasmer.app + +// Webflow, Inc. : https://www.webflow.com +// Submitted by Webflow Security Team +webflow.io +webflowtest.io + +// WebHare bv : https://www.webhare.com/ +// Submitted by Arnold Hendriks +*.webhare.dev + +// WebHotelier Technologies Ltd : https://www.webhotelier.net/ +// Submitted by Apostolos Tsakpinis +bookonline.app +hotelwithflight.com +reserve-online.com +reserve-online.net + +// WebPros International, LLC : https://webpros.com/ +// Submitted by Nicolas Rochelemagne +cprapid.com +pleskns.com +wp2.host +pdns.page +plesk.page +cpanel.site +wpsquared.site + +// WebWaddle Ltd : https://webwaddle.com/ +// Submitted by Merlin Glander +*.wadl.top + +// Western Digital Technologies, Inc : https://www.wdc.com +// Submitted by Jung Jin +remotewd.com + +// Whatbox Inc. : https://whatbox.ca/ +// Submitted by Anthony Ryan +box.ca + +// WIARD Enterprises : https://wiardweb.com +// Submitted by Kidd Hustle +pages.wiardweb.com + +// Wikimedia Foundation : https://wikitech.wikimedia.org +// Submitted by Timo Tijhof +toolforge.org +wmcloud.org +beta.wmcloud.org +wmflabs.org + +// William Harrison : https://wharrison.com.au +// Submitted by William Harrison +vps.hrsn.au +hrsn.dev +is-a.dev +localcert.net + +// Windsurf : https://windsurf.com +// Submitted by Douglas Chen +windsurf.app +windsurf.build + +// WISP : https://wisp.gg +// Submitted by Stepan Fedotov +panel.gg +daemon.panel.gg + +// Wix.com, Inc. : https://www.wix.com +// Submitted by Shahar Talmi / Alon Kochba +wixsite.com +wixstudio.com +editorx.io +wixstudio.io +wix.run + +// Wizard Zines : https://wizardzines.com +// Submitted by Julia Evans +messwithdns.com + +// WoltLab GmbH : https://www.woltlab.com +// Submitted by Tim Düsterhus +woltlab-demo.com +myforum.community +community-pro.de +diskussionsbereich.de +community-pro.net +meinforum.net + +// Woods Valldata : https://www.woodsvalldata.co.uk/ +// Submitted by Chris Whittle +affinitylottery.org.uk +raffleentry.org.uk +weeklylottery.org.uk + +// WP Engine : https://wpengine.com/ +// Submitted by Michael Smith +// Submitted by Brandon DuRette +wpenginepowered.com +js.wpenginepowered.com + +// XenonCloud GbR : https://xenoncloud.net +// Submitted by Julian Uphoff +*.xenonconnect.de +half.host + +// XnBay Technology : http://www.xnbay.com/ +// Submitted by XnBay Developer +xnbay.com +u2.xnbay.com +u2-local.xnbay.com + +// XS4ALL Internet bv : https://www.xs4all.nl/ +// Submitted by Daniel Mostertman +cistron.nl +demon.nl +xs4all.space + +// xTool : https://xtool.com +// Submitted by Echo +xtooldevice.com + +// Yandex.Cloud LLC : https://cloud.yandex.com +// Submitted by Alexander Lodin +yandexcloud.net +storage.yandexcloud.net +website.yandexcloud.net +sourcecraft.site + +// YesCourse Pty Ltd : https://yescourse.com +// Submitted by Atul Bhouraskar +official.academy + +// Yola : https://www.yola.com/ +// Submitted by Stefano Rivera +yolasite.com + +// Yunohost : https://yunohost.org +// Submitted by Valentin Grimaud +ynh.fr +nohost.me +noho.st + +// ZaNiC : http://www.za.net/ +// Submitted by registry +za.net +za.org + +// ZAP-Hosting GmbH & Co. KG : https://zap-hosting.com +// Submitted by Julian Alker +zap.cloud + +// Zeabur : https://zeabur.com/ +// Submitted by Zeabur Team +zeabur.app + +// Zerops : https://zerops.io/ +// Submitted by Zerops Team +*.zerops.app + +// Zine EOOD : https://zine.bg/ +// Submitted by Martin Angelov +bss.design + +// Zitcom A/S : https://www.zitcom.dk +// Submitted by Emil Stahl +basicserver.io +virtualserver.io +enterprisecloud.nu + +// Zone.ID: https://zone.id +// Submitted by Gx1.org +zone.id +nett.to + +// ZoneABC : https://zoneabc.net +// Submitted by ZoneABC Team +zabc.net + +// ===END PRIVATE DOMAINS=== diff --git a/vendor/bundle/ruby/3.3.0/gems/public_suffix-7.0.2/lib/public_suffix.rb b/vendor/bundle/ruby/3.3.0/gems/public_suffix-7.0.2/lib/public_suffix.rb new file mode 100644 index 0000000..cf303ca --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/public_suffix-7.0.2/lib/public_suffix.rb @@ -0,0 +1,177 @@ +# frozen_string_literal: true + +# = Public Suffix +# +# Domain name parser based on the Public Suffix List. +# +# Copyright (c) 2009-2026 Simone Carletti + +require_relative "public_suffix/domain" +require_relative "public_suffix/version" +require_relative "public_suffix/errors" +require_relative "public_suffix/rule" +require_relative "public_suffix/list" + +# PublicSuffix is a Ruby domain name parser based on the Public Suffix List. +# +# The [Public Suffix List](https://publicsuffix.org) is a cross-vendor initiative +# to provide an accurate list of domain name suffixes. +# +# The Public Suffix List is an initiative of the Mozilla Project, +# but is maintained as a community resource. It is available for use in any software, +# but was originally created to meet the needs of browser manufacturers. +module PublicSuffix + + DOT = "." + BANG = "!" + STAR = "*" + + # Parses +name+ and returns the {PublicSuffix::Domain} instance. + # + # @example Parse a valid domain + # PublicSuffix.parse("google.com") + # # => # + # + # @example Parse a valid subdomain + # PublicSuffix.parse("www.google.com") + # # => # + # + # @example Parse a fully qualified domain + # PublicSuffix.parse("google.com.") + # # => # + # + # @example Parse a fully qualified domain (subdomain) + # PublicSuffix.parse("www.google.com.") + # # => # + # + # @example Parse an invalid (unlisted) domain + # PublicSuffix.parse("x.yz") + # # => # + # + # @example Parse an invalid (unlisted) domain with strict checking (without applying the default * rule) + # PublicSuffix.parse("x.yz", default_rule: nil) + # # => PublicSuffix::DomainInvalid: `x.yz` is not a valid domain + # + # @example Parse an URL (not supported, only domains) + # PublicSuffix.parse("http://www.google.com") + # # => PublicSuffix::DomainInvalid: http://www.google.com is not expected to contain a scheme + # + # + # @param name [#to_s] The domain name or fully qualified domain name to parse. + # @param list [PublicSuffix::List] The rule list to search, defaults to the default {PublicSuffix::List} + # @param ignore_private [Boolean] + # @return [PublicSuffix::Domain] + # + # @raise [PublicSuffix::DomainInvalid] If domain is not a valid domain. + # @raise [PublicSuffix::DomainNotAllowed] If a rule for +domain+ is found, but the rule doesn't allow +domain+. + def self.parse(name, list: List.default, default_rule: list.default_rule, ignore_private: false) + what = normalize(name) + raise what if what.is_a?(DomainInvalid) + + rule = list.find(what, default: default_rule, ignore_private: ignore_private) + + # rubocop:disable Style/IfUnlessModifier + if rule.nil? + raise DomainInvalid, "`#{what}` is not a valid domain" + end + if rule.decompose(what).last.nil? + raise DomainNotAllowed, "`#{what}` is not allowed according to Registry policy" + end + + # rubocop:enable Style/IfUnlessModifier + + decompose(what, rule) + end + + # Checks whether +domain+ is assigned and allowed, without actually parsing it. + # + # This method doesn't care whether domain is a domain or subdomain. + # The validation is performed using the default {PublicSuffix::List}. + # + # @example Validate a valid domain + # PublicSuffix.valid?("example.com") + # # => true + # + # @example Validate a valid subdomain + # PublicSuffix.valid?("www.example.com") + # # => true + # + # @example Validate a not-listed domain + # PublicSuffix.valid?("example.tldnotlisted") + # # => true + # + # @example Validate a not-listed domain with strict checking (without applying the default * rule) + # PublicSuffix.valid?("example.tldnotlisted") + # # => true + # PublicSuffix.valid?("example.tldnotlisted", default_rule: nil) + # # => false + # + # @example Validate a fully qualified domain + # PublicSuffix.valid?("google.com.") + # # => true + # PublicSuffix.valid?("www.google.com.") + # # => true + # + # @example Check an URL (which is not a valid domain) + # PublicSuffix.valid?("http://www.example.com") + # # => false + # + # + # @param name [#to_s] The domain name or fully qualified domain name to validate. + # @param ignore_private [Boolean] + # @return [Boolean] + def self.valid?(name, list: List.default, default_rule: list.default_rule, ignore_private: false) + what = normalize(name) + return false if what.is_a?(DomainInvalid) + + rule = list.find(what, default: default_rule, ignore_private: ignore_private) + + !rule.nil? && !rule.decompose(what).last.nil? + end + + # Attempt to parse the name and returns the domain, if valid. + # + # This method doesn't raise. Instead, it returns nil if the domain is not valid for whatever reason. + # + # @param name [#to_s] The domain name or fully qualified domain name to parse. + # @param list [PublicSuffix::List] The rule list to search, defaults to the default {PublicSuffix::List} + # @param ignore_private [Boolean] + # @return [String] + def self.domain(name, **options) + parse(name, **options).domain + rescue PublicSuffix::Error + nil + end + + + # private + + def self.decompose(name, rule) + left, right = rule.decompose(name) + + parts = left.split(DOT) + # If we have 0 parts left, there is just a tld and no domain or subdomain + # If we have 1 part left, there is just a tld, domain and not subdomain + # If we have 2 parts left, the last part is the domain, the other parts (combined) are the subdomain + tld = right + sld = parts.empty? ? nil : parts.pop + trd = parts.empty? ? nil : parts.join(DOT) + + Domain.new(tld, sld, trd) + end + + # Pretend we know how to deal with user input. + def self.normalize(name) + name = name.to_s.dup + name.strip! + name.chomp!(DOT) + name.downcase! + + return DomainInvalid.new("Name is blank") if name.empty? + return DomainInvalid.new("Name starts with a dot") if name.start_with?(DOT) + return DomainInvalid.new(format("%s is not expected to contain a scheme", name)) if name.include?("://") + + name + end + +end diff --git a/vendor/bundle/ruby/3.3.0/gems/public_suffix-7.0.2/lib/public_suffix/domain.rb b/vendor/bundle/ruby/3.3.0/gems/public_suffix-7.0.2/lib/public_suffix/domain.rb new file mode 100644 index 0000000..f7ca556 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/public_suffix-7.0.2/lib/public_suffix/domain.rb @@ -0,0 +1,235 @@ +# frozen_string_literal: true + +# = Public Suffix +# +# Domain name parser based on the Public Suffix List. +# +# Copyright (c) 2009-2026 Simone Carletti + +module PublicSuffix + + # Domain represents a domain name, composed by a TLD, SLD and TRD. + class Domain + + # Splits a string into the labels, that is the dot-separated parts. + # + # The input is not validated, but it is assumed to be a valid domain name. + # + # @example + # + # name_to_labels('example.com') + # # => ['example', 'com'] + # + # name_to_labels('example.co.uk') + # # => ['example', 'co', 'uk'] + # + # @param name [String, #to_s] The domain name to split. + # @return [Array] + def self.name_to_labels(name) + name.to_s.split(DOT) + end + + + attr_reader :tld, :sld, :trd + + # Creates and returns a new {PublicSuffix::Domain} instance. + # + # @overload initialize(tld) + # Initializes with a +tld+. + # @param [String] tld The TLD (extension) + # @overload initialize(tld, sld) + # Initializes with a +tld+ and +sld+. + # @param [String] tld The TLD (extension) + # @param [String] sld The TRD (domain) + # @overload initialize(tld, sld, trd) + # Initializes with a +tld+, +sld+ and +trd+. + # @param [String] tld The TLD (extension) + # @param [String] sld The SLD (domain) + # @param [String] trd The TRD (subdomain) + # + # @yield [self] Yields on self. + # @yieldparam [PublicSuffix::Domain] self The newly creates instance + # + # @example Initialize with a TLD + # PublicSuffix::Domain.new("com") + # # => # + # + # @example Initialize with a TLD and SLD + # PublicSuffix::Domain.new("com", "example") + # # => # + # + # @example Initialize with a TLD, SLD and TRD + # PublicSuffix::Domain.new("com", "example", "wwww") + # # => # + # + def initialize(*args) + @tld, @sld, @trd = args + yield(self) if block_given? + end + + # Returns a string representation of this object. + # + # @return [String] + def to_s + name + end + + # Returns an array containing the domain parts. + # + # @return [Array] + # + # @example + # + # PublicSuffix::Domain.new("google.com").to_a + # # => [nil, "google", "com"] + # + # PublicSuffix::Domain.new("www.google.com").to_a + # # => [nil, "google", "com"] + # + def to_a + [@trd, @sld, @tld] + end + + # Returns the full domain name. + # + # @return [String] + # + # @example Gets the domain name of a domain + # PublicSuffix::Domain.new("com", "google").name + # # => "google.com" + # + # @example Gets the domain name of a subdomain + # PublicSuffix::Domain.new("com", "google", "www").name + # # => "www.google.com" + # + def name + [@trd, @sld, @tld].compact.join(DOT) + end + + # Returns a domain-like representation of this object + # if the object is a {#domain?}, nil otherwise. + # + # PublicSuffix::Domain.new("com").domain + # # => nil + # + # PublicSuffix::Domain.new("com", "google").domain + # # => "google.com" + # + # PublicSuffix::Domain.new("com", "google", "www").domain + # # => "www.google.com" + # + # This method doesn't validate the input. It handles the domain + # as a valid domain name and simply applies the necessary transformations. + # + # This method returns a FQD, not just the domain part. + # To get the domain part, use #sld (aka second level domain). + # + # PublicSuffix::Domain.new("com", "google", "www").domain + # # => "google.com" + # + # PublicSuffix::Domain.new("com", "google", "www").sld + # # => "google" + # + # @see #domain? + # @see #subdomain + # + # @return [String] + def domain + [@sld, @tld].join(DOT) if domain? + end + + # Returns a subdomain-like representation of this object + # if the object is a {#subdomain?}, nil otherwise. + # + # PublicSuffix::Domain.new("com").subdomain + # # => nil + # + # PublicSuffix::Domain.new("com", "google").subdomain + # # => nil + # + # PublicSuffix::Domain.new("com", "google", "www").subdomain + # # => "www.google.com" + # + # This method doesn't validate the input. It handles the domain + # as a valid domain name and simply applies the necessary transformations. + # + # This method returns a FQD, not just the subdomain part. + # To get the subdomain part, use #trd (aka third level domain). + # + # PublicSuffix::Domain.new("com", "google", "www").subdomain + # # => "www.google.com" + # + # PublicSuffix::Domain.new("com", "google", "www").trd + # # => "www" + # + # @see #subdomain? + # @see #domain + # + # @return [String] + def subdomain + [@trd, @sld, @tld].join(DOT) if subdomain? + end + + # Checks whether self looks like a domain. + # + # This method doesn't actually validate the domain. + # It only checks whether the instance contains + # a value for the {#tld} and {#sld} attributes. + # + # @example + # + # PublicSuffix::Domain.new("com").domain? + # # => false + # + # PublicSuffix::Domain.new("com", "google").domain? + # # => true + # + # PublicSuffix::Domain.new("com", "google", "www").domain? + # # => true + # + # # This is an invalid domain, but returns true + # # because this method doesn't validate the content. + # PublicSuffix::Domain.new("com", nil).domain? + # # => true + # + # @see #subdomain? + # + # @return [Boolean] + def domain? + !(@tld.nil? || @sld.nil?) + end + + # Checks whether self looks like a subdomain. + # + # This method doesn't actually validate the subdomain. + # It only checks whether the instance contains + # a value for the {#tld}, {#sld} and {#trd} attributes. + # If you also want to validate the domain, + # use {#valid_subdomain?} instead. + # + # @example + # + # PublicSuffix::Domain.new("com").subdomain? + # # => false + # + # PublicSuffix::Domain.new("com", "google").subdomain? + # # => false + # + # PublicSuffix::Domain.new("com", "google", "www").subdomain? + # # => true + # + # # This is an invalid domain, but returns true + # # because this method doesn't validate the content. + # PublicSuffix::Domain.new("com", "example", nil).subdomain? + # # => true + # + # @see #domain? + # + # @return [Boolean] + def subdomain? + !(@tld.nil? || @sld.nil? || @trd.nil?) + end + + end + +end diff --git a/vendor/bundle/ruby/3.3.0/gems/public_suffix-7.0.2/lib/public_suffix/errors.rb b/vendor/bundle/ruby/3.3.0/gems/public_suffix-7.0.2/lib/public_suffix/errors.rb new file mode 100644 index 0000000..a5d8653 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/public_suffix-7.0.2/lib/public_suffix/errors.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +# = Public Suffix +# +# Domain name parser based on the Public Suffix List. +# +# Copyright (c) 2009-2026 Simone Carletti + +module PublicSuffix + + class Error < StandardError + end + + # Raised when trying to parse an invalid name. + # A name is considered invalid when no rule is found in the definition list. + # + # @example + # + # PublicSuffix.parse("nic.test") + # # => PublicSuffix::DomainInvalid + # + # PublicSuffix.parse("http://www.nic.it") + # # => PublicSuffix::DomainInvalid + # + class DomainInvalid < Error + end + + # Raised when trying to parse a name that matches a suffix. + # + # @example + # + # PublicSuffix.parse("nic.do") + # # => PublicSuffix::DomainNotAllowed + # + # PublicSuffix.parse("www.nic.do") + # # => PublicSuffix::Domain + # + class DomainNotAllowed < DomainInvalid + end + +end diff --git a/vendor/bundle/ruby/3.3.0/gems/public_suffix-7.0.2/lib/public_suffix/list.rb b/vendor/bundle/ruby/3.3.0/gems/public_suffix-7.0.2/lib/public_suffix/list.rb new file mode 100644 index 0000000..f3ec170 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/public_suffix-7.0.2/lib/public_suffix/list.rb @@ -0,0 +1,247 @@ +# frozen_string_literal: true + +# = Public Suffix +# +# Domain name parser based on the Public Suffix List. +# +# Copyright (c) 2009-2026 Simone Carletti + +module PublicSuffix + + # A {PublicSuffix::List} is a collection of one + # or more {PublicSuffix::Rule}. + # + # Given a {PublicSuffix::List}, + # you can add or remove {PublicSuffix::Rule}, + # iterate all items in the list or search for the first rule + # which matches a specific domain name. + # + # # Create a new list + # list = PublicSuffix::List.new + # + # # Push two rules to the list + # list << PublicSuffix::Rule.factory("it") + # list << PublicSuffix::Rule.factory("com") + # + # # Get the size of the list + # list.size + # # => 2 + # + # # Search for the rule matching given domain + # list.find("example.com") + # # => # + # list.find("example.org") + # # => nil + # + # You can create as many {PublicSuffix::List} you want. + # The {PublicSuffix::List.default} rule list is used + # to tokenize and validate a domain. + # + class List + + DEFAULT_LIST_PATH = File.expand_path("../../data/list.txt", __dir__) + + # Gets the default rule list. + # + # Initializes a new {PublicSuffix::List} parsing the content + # of {PublicSuffix::List.default_list_content}, if required. + # + # @return [PublicSuffix::List] + def self.default(**options) + @default ||= parse(File.read(DEFAULT_LIST_PATH), **options) + end + + # Sets the default rule list to +value+. + # + # @param value [PublicSuffix::List] the new list + # @return [PublicSuffix::List] + def self.default=(value) + @default = value + end + + # Parse given +input+ treating the content as Public Suffix List. + # + # See http://publicsuffix.org/format/ for more details about input format. + # + # @param input [#each_line] the list to parse + # @param private_domains [Boolean] whether to ignore the private domains section + # @return [PublicSuffix::List] + def self.parse(input, private_domains: true) + comment_token = "//" + private_token = "===BEGIN PRIVATE DOMAINS===" + section = nil # 1 == ICANN, 2 == PRIVATE + + new do |list| + input.each_line do |line| + line.strip! + case # rubocop:disable Style/EmptyCaseCondition + + # skip blank lines + when line.empty? + next + + # include private domains or stop scanner + when line.include?(private_token) + break if !private_domains + + section = 2 + + # skip comments + when line.start_with?(comment_token) # rubocop:disable Lint/DuplicateBranch + next + + else + list.add(Rule.factory(line, private: section == 2)) + + end + end + end + end + + + # Initializes an empty {PublicSuffix::List}. + # + # @yield [self] Yields on self. + # @yieldparam [PublicSuffix::List] self The newly created instance. + def initialize + @rules = {} + yield(self) if block_given? + end + + + # Checks whether two lists are equal. + # + # List one is equal to two, if two is an instance of + # {PublicSuffix::List} and each +PublicSuffix::Rule::*+ + # in list one is available in list two, in the same order. + # + # @param other [PublicSuffix::List] the List to compare + # @return [Boolean] + def ==(other) + return false unless other.is_a?(List) + + equal?(other) || @rules == other.rules + end + alias eql? == + + # Iterates each rule in the list. + def each(&block) + Enumerator.new do |y| + @rules.each do |key, node| + y << entry_to_rule(node, key) + end + end.each(&block) + end + + + # Adds the given object to the list and optionally refreshes the rule index. + # + # @param rule [PublicSuffix::Rule::*] the rule to add to the list + # @return [self] + def add(rule) + @rules[rule.value] = rule_to_entry(rule) + self + end + alias << add + + # Gets the number of rules in the list. + # + # @return [Integer] + def size + @rules.size + end + + # Checks whether the list is empty. + # + # @return [Boolean] + def empty? + @rules.empty? + end + + # Removes all rules. + # + # @return [self] + def clear + @rules.clear + self + end + + # Finds and returns the rule corresponding to the longest public suffix for the hostname. + # + # @param name [#to_s] the hostname + # @param default [PublicSuffix::Rule::*] the default rule to return in case no rule matches + # @return [PublicSuffix::Rule::*] + def find(name, default: default_rule, **options) + rule = select(name, **options).inject do |l, r| + return r if r.instance_of?(Rule::Exception) + + l.length > r.length ? l : r + end + rule || default + end + + # Selects all the rules matching given hostame. + # + # If `ignore_private` is set to true, the algorithm will skip the rules that are flagged as + # private domain. Note that the rules will still be part of the loop. + # If you frequently need to access lists ignoring the private domains, + # you should create a list that doesn't include these domains setting the + # `private_domains: false` option when calling {.parse}. + # + # Note that this method is currently private, as you should not rely on it. Instead, + # the public interface is {#find}. The current internal algorithm allows to return all + # matching rules, but different data structures may not be able to do it, and instead would + # return only the match. For this reason, you should rely on {#find}. + # + # @param name [#to_s] the hostname + # @param ignore_private [Boolean] + # @return [Array] + def select(name, ignore_private: false) + name = name.to_s + + parts = name.split(DOT).reverse! + index = 0 + query = parts[index] + rules = [] + + loop do + match = @rules[query] + rules << entry_to_rule(match, query) if !match.nil? && (ignore_private == false || match.private == false) + + index += 1 + break if index >= parts.size + + query = parts[index] + DOT + query + end + + rules + end + private :select + + # Gets the default rule. + # + # @see PublicSuffix::Rule.default_rule + # + # @return [PublicSuffix::Rule::*] + def default_rule + PublicSuffix::Rule.default + end + + + protected + + attr_reader :rules + + + private + + def entry_to_rule(entry, value) + entry.type.new(value: value, length: entry.length, private: entry.private) + end + + def rule_to_entry(rule) + Rule::Entry.new(rule.class, rule.length, rule.private) + end + + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/public_suffix-7.0.2/lib/public_suffix/rule.rb b/vendor/bundle/ruby/3.3.0/gems/public_suffix-7.0.2/lib/public_suffix/rule.rb new file mode 100644 index 0000000..27c04ac --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/public_suffix-7.0.2/lib/public_suffix/rule.rb @@ -0,0 +1,350 @@ +# frozen_string_literal: true + +# = Public Suffix +# +# Domain name parser based on the Public Suffix List. +# +# Copyright (c) 2009-2026 Simone Carletti + +module PublicSuffix + + # A Rule is a special object which holds a single definition + # of the Public Suffix List. + # + # There are 3 types of rules, each one represented by a specific + # subclass within the +PublicSuffix::Rule+ namespace. + # + # To create a new Rule, use the {PublicSuffix::Rule#factory} method. + # + # PublicSuffix::Rule.factory("ar") + # # => # + # + module Rule + + # @api internal + Entry = Struct.new(:type, :length, :private) # rubocop:disable Lint/StructNewOverride + + # = Abstract rule class + # + # This represent the base class for a Rule definition + # in the {Public Suffix List}[https://publicsuffix.org]. + # + # This is intended to be an Abstract class + # and you shouldn't create a direct instance. The only purpose + # of this class is to expose a common interface + # for all the available subclasses. + # + # * {PublicSuffix::Rule::Normal} + # * {PublicSuffix::Rule::Exception} + # * {PublicSuffix::Rule::Wildcard} + # + # ## Properties + # + # A rule is composed by 4 properties: + # + # value - A normalized version of the rule name. + # The normalization process depends on rule tpe. + # + # Here's an example + # + # PublicSuffix::Rule.factory("*.google.com") + # # + # + # ## Rule Creation + # + # The best way to create a new rule is passing the rule name + # to the PublicSuffix::Rule.factory method. + # + # PublicSuffix::Rule.factory("com") + # # => PublicSuffix::Rule::Normal + # + # PublicSuffix::Rule.factory("*.com") + # # => PublicSuffix::Rule::Wildcard + # + # This method will detect the rule type and create an instance + # from the proper rule class. + # + # ## Rule Usage + # + # A rule describes the composition of a domain name and explains how to tokenize + # the name into tld, sld and trd. + # + # To use a rule, you first need to be sure the name you want to tokenize + # can be handled by the current rule. + # You can use the #match? method. + # + # rule = PublicSuffix::Rule.factory("com") + # + # rule.match?("google.com") + # # => true + # + # rule.match?("google.com") + # # => false + # + # Rule order is significant. A name can match more than one rule. + # See the {Public Suffix Documentation}[http://publicsuffix.org/format/] + # to learn more about rule priority. + # + # When you have the right rule, you can use it to tokenize the domain name. + # + # rule = PublicSuffix::Rule.factory("com") + # + # rule.decompose("google.com") + # # => ["google", "com"] + # + # rule.decompose("www.google.com") + # # => ["www.google", "com"] + # + # @abstract + # + class Base + + # @return [String] the rule definition + attr_reader :value + + # @return [String] the length of the rule + attr_reader :length + + # @return [Boolean] true if the rule is a private domain + attr_reader :private + + + # Initializes a new rule from the content. + # + # @param content [String] the content of the rule + # @param private [Boolean] + def self.build(content, private: false) + new(value: content, private: private) + end + + # Initializes a new rule. + # + # @param value [String] + # @param private [Boolean] + def initialize(value:, length: nil, private: false) + @value = value.to_s + @length = length || (@value.count(DOT) + 1) + @private = private + end + + # Checks whether this rule is equal to other. + # + # @param other [PublicSuffix::Rule::*] The rule to compare + # @return [Boolean] true if this rule and other are instances of the same class + # and has the same value, false otherwise. + def ==(other) + equal?(other) || (self.class == other.class && value == other.value) + end + alias eql? == + + # Checks if this rule matches +name+. + # + # A domain name is said to match a rule if and only if + # all of the following conditions are met: + # + # - When the domain and rule are split into corresponding labels, + # that the domain contains as many or more labels than the rule. + # - Beginning with the right-most labels of both the domain and the rule, + # and continuing for all labels in the rule, one finds that for every pair, + # either they are identical, or that the label from the rule is "*". + # + # @see https://publicsuffix.org/list/ + # + # @example + # PublicSuffix::Rule.factory("com").match?("example.com") + # # => true + # PublicSuffix::Rule.factory("com").match?("example.net") + # # => false + # + # @param name [String] the domain name to check + # @return [Boolean] + def match?(name) + # NOTE: it works because of the assumption there are no + # rules like foo.*.com. If the assumption is incorrect, + # we need to properly walk the input and skip parts according + # to wildcard component. + diff = name.chomp(value) + diff.empty? || diff.end_with?(DOT) + end + + # @abstract + def parts + raise NotImplementedError + end + + # @abstract + # @param domain [#to_s] The domain name to decompose + # @return [Array] + def decompose(*) + raise NotImplementedError + end + + end + + # Normal represents a standard rule (e.g. com). + class Normal < Base + + # Gets the original rule definition. + # + # @return [String] The rule definition. + def rule + value + end + + # Decomposes the domain name according to rule properties. + # + # @param domain [#to_s] The domain name to decompose + # @return [Array] The array with [trd + sld, tld]. + def decompose(domain) + suffix = parts.join('\.') + matches = domain.to_s.match(/^(.*)\.(#{suffix})$/) + matches ? matches[1..2] : [nil, nil] + end + + # dot-split rule value and returns all rule parts + # in the order they appear in the value. + # + # @return [Array] + def parts + @value.split(DOT) + end + + end + + # Wildcard represents a wildcard rule (e.g. *.co.uk). + class Wildcard < Base + + # Initializes a new rule from the content. + # + # @param content [String] the content of the rule + # @param private [Boolean] + def self.build(content, private: false) + new(value: content.to_s[2..], private: private) + end + + # Initializes a new rule. + # + # @param value [String] + # @param length [Integer] + # @param private [Boolean] + def initialize(value:, length: nil, private: false) + super + length or @length += 1 # * counts as 1 + end + + # Gets the original rule definition. + # + # @return [String] The rule definition. + def rule + value == "" ? STAR : STAR + DOT + value + end + + # Decomposes the domain name according to rule properties. + # + # @param domain [#to_s] The domain name to decompose + # @return [Array] The array with [trd + sld, tld]. + def decompose(domain) + suffix = ([".*?"] + parts).join('\.') + matches = domain.to_s.match(/^(.*)\.(#{suffix})$/) + matches ? matches[1..2] : [nil, nil] + end + + # dot-split rule value and returns all rule parts + # in the order they appear in the value. + # + # @return [Array] + def parts + @value.split(DOT) + end + + end + + # Exception represents an exception rule (e.g. !parliament.uk). + class Exception < Base + + # Initializes a new rule from the content. + # + # @param content [#to_s] the content of the rule + # @param private [Boolean] + def self.build(content, private: false) + new(value: content.to_s[1..], private: private) + end + + # Gets the original rule definition. + # + # @return [String] The rule definition. + def rule + BANG + value + end + + # Decomposes the domain name according to rule properties. + # + # @param domain [#to_s] The domain name to decompose + # @return [Array] The array with [trd + sld, tld]. + def decompose(domain) + suffix = parts.join('\.') + matches = domain.to_s.match(/^(.*)\.(#{suffix})$/) + matches ? matches[1..2] : [nil, nil] + end + + # dot-split rule value and returns all rule parts + # in the order they appear in the value. + # The leftmost label is not considered a label. + # + # See http://publicsuffix.org/format/: + # If the prevailing rule is a exception rule, + # modify it by removing the leftmost label. + # + # @return [Array] + def parts + @value.split(DOT)[1..] + end + + end + + + # Takes the +name+ of the rule, detects the specific rule class + # and creates a new instance of that class. + # The +name+ becomes the rule +value+. + # + # @example Creates a Normal rule + # PublicSuffix::Rule.factory("ar") + # # => # + # + # @example Creates a Wildcard rule + # PublicSuffix::Rule.factory("*.ar") + # # => # + # + # @example Creates an Exception rule + # PublicSuffix::Rule.factory("!congresodelalengua3.ar") + # # => # + # + # @param content [#to_s] the content of the rule + # @return [PublicSuffix::Rule::*] A rule instance. + def self.factory(content, private: false) + case content.to_s[0, 1] + when STAR + Wildcard + when BANG + Exception + else + Normal + end.build(content, private: private) + end + + # The default rule to use if no rule match. + # + # The default rule is "*". From https://publicsuffix.org/list/: + # + # > If no rules match, the prevailing rule is "*". + # + # @return [PublicSuffix::Rule::Wildcard] The default rule. + def self.default + factory(STAR) + end + + end + +end diff --git a/vendor/bundle/ruby/3.3.0/gems/public_suffix-7.0.2/lib/public_suffix/version.rb b/vendor/bundle/ruby/3.3.0/gems/public_suffix-7.0.2/lib/public_suffix/version.rb new file mode 100644 index 0000000..2b08b66 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/public_suffix-7.0.2/lib/public_suffix/version.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +# = Public Suffix +# +# Domain name parser based on the Public Suffix List. +# +# Copyright (c) 2009-2026 Simone Carletti + +module PublicSuffix + + # @return [String] the current library version + VERSION = "7.0.2" + +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/History.rdoc b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/History.rdoc new file mode 100644 index 0000000..51e9f71 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/History.rdoc @@ -0,0 +1,2454 @@ +=== 13.2.1 + +* Suppressed "internal:array:52:in 'Array#each'" from backtrace by @hsbt in #554 +* Bump actions/configure-pages from 4 to 5 by @dependabot in #553 + +=== 13.2.0 + +* Fix rule example to be correct by @zenspider in #525 +* Switch to use test-unit by @hsbt in #536 +* Removed redundant block by @hsbt in #537 +* Use Struct instead of OpenStruct. by @hsbt in #545 +* Accept FileList object as directory task's target by @gemmaro in #530 +* Fix exception when exception has nil backtrace by @janbiedermann in #451 +* Add TruffleRuby on CI by @andrykonchin in #551 + +=== 13.1.0 + +* Added dependabot.yml for actions by @hsbt in #416 +* Add Ruby 3.1 to the CI matrix by @petergoldstein in #415 +* (Performance) Remove unnecessary I/O syscalls for FileTasks by @da2x in #393 +* Skip test failure with JRuby by @hsbt in #418 +* Remove bin/rdoc by @tnir in #421 +* Remove bin/rake by @tnir in #422 +* Remove bin/bundle by @tnir in #425 +* Apply RuboCop linting for Ruby 2.3 by @tnir in #423 +* Update rubocop to work with Ruby 2.4 compatible by @tnir in #424 +* chore: fix typo in comments by @tnir in #429 +* Use 'test' as workflow name on Actions by @tnir in #427 +* docs: update CONTRIBUTING.rdoc by @tnir in #428 +* Add RuboCop job to Actions by @tnir in #426 +* Lock minitest-5.15.0 for Ruby 2.2 by @hsbt in #442 +* Eagerly require set in thread_pool.rb by @jeremyevans in #440 +* Avoid creating an unnecessary thread pool by @jeremyevans in #441 +* Add credit for maintenance in Rake 12/13 by @tnir in #443 +* Sh fully echoes commands which error exit by @MarkDBlackwell in #147 +* Correct RuboCop offenses by @deivid-rodriguez in #444 +* [StepSecurity] ci: Harden GitHub Actions by @step-security-bot in #450 +* Add ruby 3.2 to test matrix by @hanneskaeufler in #458 +* Missing 'do' on example by @zzak in #467 +* Try to use dependabot automerge by @hsbt in #470 +* Rewrite auto-merge feature for dependabot by @hsbt in #471 +* Update bundler in Dependabot by @ono-max in #472 +* Fix grammar in help text by @mebezac in #381 +* Try to use ruby/ruby/.github/workflows/ruby_versions.yml@master by @hsbt in #475 +* Use GitHub Pages Action for generating rdoc page by @hsbt in #477 +* Support #detailed_message when task failed by @ksss in #486 +* Debug at stop when task fail by @ksss in #489 +* Drop to support Ruby 2.2 by @hsbt in #492 +* Bump up setup-ruby by @hsbt in #497 +* Update development dependencies by @hsbt in #505 + +=== 13.0.6 + +* Additional fix for #389 + Pull request #390 by hsbt + +=== 13.0.5 + +* Fixed the regression of #388 + Pull request #389 by hsbt + +=== 13.0.4 + +* Fix rake test loader swallowing useful error information. + Pull request #367 by deivid-rodriguez +* Add -C/--directory option the same as GNU make. + Pull request #376 by nobu + +=== 13.0.3 + +* Fix breaking change of execution order on TestTask. + Pull request #368 by ysakasin + +=== 13.0.2 + +==== Enhancements + +* Fix tests to work with current FileUtils + Pull Request #358 by jeremyevans +* Simplify default rake test loader + Pull Request #357 by deivid-rodriguez +* Update rdoc + Pull Request #366 by bahasalien +* Update broken links to rake articles from Avdi in README + Pull Request #360 by svl7 + +=== 13.0.1 + +==== Bug fixes + +* Fixed bug: Reenabled task raises previous exception on second invokation + Pull Request #271 by thorsteneckel +* Fix an incorrectly resolved arg pattern + Pull Request #327 by mjbellantoni + +=== 13.0.0 + +==== Enhancements + +* Follows recent changes on keyword arguments in ruby 2.7. + Pull Request #326 by nobu +* Make `PackageTask` be able to omit parent directory while packing files + Pull Request #310 by tonytonyjan +* Add order only dependency + Pull Request #269 by take-cheeze + +==== Compatibility changes + +* Drop old ruby versions(< 2.2) + +=== 12.3.3 + +==== Bug fixes + +* Use the application's name in error message if a task is not found. + Pull Request #303 by tmatilai + +==== Enhancements: + +* Use File.open explicitly. + +=== 12.3.2 + +==== Bug fixes + +* Fixed test fails caused by 2.6 warnings. + Pull Request #297 by hsbt + +==== Enhancements: + +* Rdoc improvements. + Pull Request #293 by colby-swandale +* Improve multitask performance. + Pull Request #273 by jsm +* Add alias `prereqs`. + Pull Request #268 by take-cheeze + +=== 12.3.1 + +==== Bug fixes + +* Support did_you_mean >= v1.2.0 which has a breaking change on formatters. + Pull request #262 by FUJI Goro. + +==== Enhancements: + +* Don't run task if it depends on already invoked but failed task. + Pull request #252 by Gonzalo Rodriguez. +* Make space trimming consistent for all task arguments. + Pull request #259 by Gonzalo Rodriguez. +* Removes duplicated inclusion of Rake::DSL in tests. + Pull request #254 by Gonzalo Rodriguez. +* Re-raise a LoadError that didn't come from require in the test loader. + Pull request #250 by Dylan Thacker-Smith. + +=== 12.3.0 + +==== Compatibility Changes + +* Bump `required_ruby_version` to Ruby 2.0.0. Rake has already + removed support for Ruby 1.9.x. + +==== Enhancements: + +* Support `test-bundled-gems` task on ruby core. + +=== 12.2.1 + +==== Bug fixes + +* Fixed to break Capistrano::Application on capistrano3. + +=== 12.2.0 + +==== Enhancements: + +* Make rake easier to use as a library + Pull request #211 by @drbrain +* Fix quadratic performance in FileTask#out_of_date? + Pull request #224 by @doudou +* Clarify output when printing nested exception traces + Pull request #232 by @urbanautomaton + +==== Bug fixes + +* Account for a file that match 2 or more patterns. + Pull request #231 by @styd + +=== 12.1.0 + +==== Enhancements: + +* Added did_you_mean feature for invalid rake task. + Pull request #221 by @xtina-starr +* Enabled to dependency chained by extensions. Pull request #39 by Petr Skocik. +* Make all of string literals to frozen objects on Ruby 2.4 or later. + +==== Bug fixes + +* Typo fixes in rakefile.rdoc. Pull request #180 by Yuta Kurotaki. +* Fix unexpected behavior of file task with dryrun option. + Pull request #183 by @aycabta. +* Make LoadError from running tests more obvious. Pull request #195 + by Eric Hodel. +* Fix unexpected TypeError with hash style option. Pull request #202 + by Kuniaki IGARASHI. + +=== 12.0.0 + +==== Compatibility Changes + +* Removed arguments on clear #157 by Jesse Bowes +* Removed `rake/contrib` packages. These are extracted to `rake-contrib` gem. +* Removed deprecated method named `last\_comment`. + +==== Enhancements: + +* Re-use trace option on `cleanup` task. #164 by Brian Henderson +* Actions adore keyword arguments #174 by Josh Cheek +* Rake::TaskArguments#key? alias of #has_key? #175 by Paul Annesley + +=== 11.3.0 / 2016-09-20 + +==== Enhancements: + +* Remove to reference `Fixnum` constant. Pull request #160 by nobu + +=== 11.2.2 / 2016-06-12 + +==== Bug fixes + +* Fix unexpected behavior with multiple dependencies on Rake::TestTask + +=== 11.2.1 / 2016-06-12 + +==== Bug fixes + +* Fix regression of dependencies handling on Rake::TestTask. Report #139 + +=== 11.2.0 / 2016-06-11 + +==== Bug fixes + +* Fix unexpected cut-out behavior on task description using triple dots + and exclamation. Report #106 from Stephan Kämper and Pull request #134 by Lee +* Fix empty argument assignment with `with_defaults` option. Pull request #135 + by bakunyo +* Ignore to use `hwprefs` on Darwin platform. Use sysctl now. Report #128 + +==== Enhancements + +* Spawn options for sh Pull equest #138 by Eric Hodel. +* Allow to specify dependencies(prerequisites) for Rake::TestTask + Pull request #117 by Tim Maslyuchenko +* Use Bundler task instead of hoe for gem release. +* Remove explicitly load to rubygems for Ruby 1.8. +* Unify to declare `Rake::VERSION`. +* Support xz format for PackageTask. + +=== 11.1.2 / 2016-03-28 + +==== Bug fixes + +* Remove `-W` option when Rake::TestTask#verbose enabled. It's misunderstanding + specification change with Rake 11. Partly revert #67 + +=== 11.1.1 / 2016-03-14 + +==== Bug fixes + +* Use `-W` instead of `--verbose` when Rake::TestTask#verbose enabled. + JRuby doesn't have `--verbose` option. + +=== 11.1.0 / 2016-03-11 + +==== Compatibility Changes + +* Revert to remove `last\_comment`. It will remove Rake 12. + +=== 11.0.1 / 2016-03-09 + +==== Bug fixes + +* Fixed packaging manifest. + +=== 11.0.0 / 2016-03-09 + +==== Bug fixes + +* Correctly handle bad encoding in exception messages. Pull request #113 + by Tomer Brisker +* Fix verbose option at TestTask. Pull request #67 by Mike Blumtritt + +==== Enhancements + +* Make FileList#exclude more analogous to FileList#include. +* Use IO.open instead of Open3.popen3 for CPU counter. +* Make Rake::Task#already_invoked publicly accessible. + Pull request #93 by Joe Rafaniello +* Lookup prerequisites with same name outside of scope instead of + matching self. Pull request #96 by Sandy Vanderbleek +* Make FileList#pathmap behave like String#pathmap. + Pull request #61 by Daniel Tamai +* Add fetch method to task arguments. + Pull request #12 by Chris Keathley +* Use ruby warnings by default. Pull request #97 by Harold Giménez + +==== Compatibility Changes + +* Removed to support Ruby 1.8.x +* Removed constant named `RAKEVERSION` +* Removed Rake::AltSystem +* Removed Rake::RubyForgePublisher +* Removed Rake::TaskManager#last\_comment. Use last\_description. +* Removed Rake::TaskLib#paste +* Removed Top-level SshDirPublisher, SshFreshDirPublisher, SshFilePublisher + and CompositePublisher from lib/rake/contrib/publisher.rb +* Removed "rake/runtest.rb" + +=== 10.5.0 / 2016-01-13 + +==== Enhancements + +* Removed monkey patching for Ruby 1.8. Pull request #46 by Pablo Herrero. +* Inheritance class of Rake::FileList returns always self class. + Pull request #74 by Thomas Scholz + +=== 10.4.2 / 2014-12-02 + +==== Bug fixes + +* Rake no longer edits ARGV. This allows you to re-exec rake from a rake + task. Pull requset #9 by Matt Palmer. +* Documented how Rake::DSL#desc handles sentences in task descriptions. + Issue #7 by Raza Sayed. +* Fixed test error on 1.9.3 with legacy RubyGems. Issue #8 by Matt Palmer. +* Deleted duplicated History entry. Pull request #10 by Yuji Yamamoto. + +=== 10.4.1 / 2014-12-01 + +==== Bug fixes + +* Reverted fix for #277 as it caused numerous issues for rake users. + rails/spring issue #366 by Gustavo Dutra. + +=== 10.4.0 / 2014-11-22 + +==== Enhancements + +* Upgraded to minitest 5. Pull request #292 by Teo Ljungberg. +* Added support for Pathname in rake tasks. Pull request #271 by Randy + Coulman. +* Rake now ignores falsy dependencies which allows for easier programmatic + creation of tasks. Pull request #273 by Manav. +* Rake no longer edits ARGV. This allows you to re-exec rake from a rake + task. Issue #277 by Matt Palmer. +* Etc.nprocessors is used for counting the number of CPUs. + +==== Bug fixes + +* Updated rake manpage. Issue #283 by Nathan Long, pull request #291 by + skittleys. +* Add Rake::LATE to allow rebuilding of files that depend on deleted files. + Bug #286, pull request #287 by David Grayson. +* Fix relinking of files when repackaging. Bug #276 by Muenze. +* Fixed some typos. Pull request #280 by Jed Northridge. +* Try counting CPUs via cpuinfo if host_os was not matched. Pull request + #282 by Edouard B. + +=== 10.3.2 / 2014-05-15 + +==== Bug fixes + +* Rake no longer infinitely loops when showing exception causes that refer to + each other. Bug #272 by Chris Bandy. +* Fixed documentation typos. Bug #275 by Jake Worth. + +=== 10.3.1 / 2014-04-17 + +==== Bug fixes + +* Really stop reporting an error when cleaning already-deleted files. Pull + request #269 by Randy Coulman +* Fixed infinite loop when cleaning already-deleted files on windows. + +=== 10.3 / 2014-04-15 + +==== Enhancements + +* Added --build-all option to rake which treats all file prerequisites as + out-of-date. Pull request #254 by Andrew Gilbert. +* Added Rake::NameSpace#scope. Issue #263 by Jon San Miguel. + +==== Bug fixes + +* Suppress org.jruby package files in rake error messages for JRuby users. + Issue #213 by Charles Nutter. +* Fixed typo, removed extra "h". Pull request #267 by Hsing-Hui Hsu. +* Rake no longer reports an error when cleaning already-deleted files. Pull + request #266 by Randy Coulman. +* Consume stderr while determining CPU count to avoid hang. Issue #268 by + Albert Sun. + +=== 10.2.2 / 2014-03-27 + +==== Bug fixes + +* Restored Ruby 1.8.7 compatibility + +=== 10.2.1 / 2014-03-25 + +==== Bug fixes + +* File tasks including a ':' are now top-level tasks again. Issue #262 by + Josh Holtrop. +* Use sysctl for CPU count for all BSDs. Pull request #261 by Joshua Stein. +* Fixed CPU detection for unknown platforms. + +=== 10.2.0 / 2014-03-24 + +==== Enhancements + +* Rake now requires Ruby 1.9 or newer. For me, this is a breaking change, but + it seems that Jim planned to release it with Rake 10.2. See also pull + request #247 by Philip Arndt. +* Rake now allows you to declare tasks under a namespace like: + + task 'a:b' do ... end + + Pull request #232 by Judson Lester. +* Task#source defaults to the first prerequisite in non-rule tasks. Pull + request #215 by Avdi Grimm. +* Rake now automatically rebuilds and reloads imported files. Pull request + #209 by Randy Coulman. +* The rake task arguments can contain escaped commas. Pull request #214 by + Filip Hrbek. +* Rake now prints the exception class on errors. Patch #251 by David Cornu. + +==== Bug fixes + +* Fixed typos. Pull request #256 by Valera Rozuvan, #250 via Jake Worth, #260 + by Zachary Scott. +* Fixed documentation for calling tasks with arguments. Pull request #235 by + John Varghese. +* Clarified `rake -f` usage message. Pull request #252 by Marco Pfatschbacher. +* Fixed a test failure on windows. Pull request #231 by Hiroshi Shirosaki. +* Fixed corrupted rake.1.gz. Pull request #225 by Michel Boaventura. +* Fixed bug in can\_detect\_signals? in test. Patch from #243 by Alexey + Borzenkov. + +=== 10.1.1 + +* Use http://github.com/jimweirich/rake instead of http://rake.rubyforge.org for + canonical project url. + +=== 10.1.0 + +==== Changes + +===== New Features + +* Add support for variable length task argument lists. If more actual + arguments are supplied than named arguments, then the extra + arguments values will be in args.extras. + +* Application name is not displayed in the help banner. (Previously + "rake" was hardcoded, now rake-based applications can display their + own names). + +===== Bug Fixes + +Bug fixes include: + +* Fix backtrace suppression issues. + +* Rules now explicit get task arguments passed to them. + +* Rename FileList#exclude? to FileList#exclude\_from\_list? to avoid + conflict with new Rails method. + +* Clean / Clobber tasks now report failure to remove files. + +* Plus heaps of internal code cleanup. + +==== Thanks + +As usual, it was input from users that drove a lot of these changes. +The following people contributed patches, made suggestions or made +otherwise helpful comments. Thanks to ... + +* Michael Nikitochkin (general code cleanup) +* Vipul A M (general code cleanup) +* Dennis Bell (variable length task argument lists) +* Jacob Swanner (rules arguments) +* Rafael Rosa Fu (documentation typo) +* Stuart Nelson (install.rb fixes) +* Lee Hambley (application name in help banner) + +-- Jim Weirich + +=== 10.0.3 + + "Jim, when will Rake reach version 1.0?" + +Over the past several years I've been asked that question at +conferences, panels and over twitter. Due to historical reasons (or +maybe just plain laziness) Rake has (incorrectly) been treating the +second digit of the version as the major release number. So in my head +Rake was already at version 9. + +Well, it's time to fix things. This next version of Rake drops old, +crufty, backwards compatibility hacks such as top level constants, DSL +methods defined in Object and numerous other features that are just no +longer desired. It's also time to drop the leading zero from the +version number as well and call this new version of rake what it +really is: Version 10. + +So, welcome to Rake 10.0! + +Rake 10 is actually feature identical to the latest version of Rake 9 +(that would be the version spelled 0.9.3), *except* that Rake 10 drops +all the sundry deprecated features that have accumulated over the years. + +If your Rakefile is up to date and current with all the new features +of Rake 10, you are ready to go. If your Rakefile still uses a few +deprecated feeatures, feel free to use Rake 9 (0.9.3) with the same +feature set. Just be aware that future features will be in Rake 10 +family line. + +==== Changes + +As mentioned above, there are no new features in Rake 10. However, +there are a number of features missing: + +* Classic namespaces are now gone. Rake is no longer able to reflect + the options settings in the global variables ($rakefile, $show\_tasks, + $show\_prereqs, $trace, $dryrun and $silent). The + --classic-namespace option is no longer supported. + +* Global constants are no longer supported. This includes + Task, FileTask, FileCreationTask and + RakeApp). The constant missing hook to warn about using + global rake constants has been removed. + +* The Rake DSL methods (task, file, directory, etc) are in their own + module (Rake::DSL). The stub versions of these methods (that printed + warnings) in Object have been removed. However, the DSL methods are + added to the top-level main object. Since main is + not in the inheritance tree, the presence of the DSL methods in main + should be low impact on other libraries. + + If you want to use the Rake DSL commands from your own code, just + include Rake::DSL into your own classes and modules. + +* The deprecated syntax for task arguments (the one using + :needs) has been removed. + +* The --reduce-compat flag has been removed (it's not needed + anymore). + +* The deprecated rake/sys.rb library has been removed. + +* The deprecated rake/rdoctask.rb library has been removed. + RDoc supplies its own rake task now. + +* The deprecated rake/gempackagetask.rb library has been + removed. Gem supplies its own package task now. + +There is one small behavioral change: + +* Non-file tasks now always report the current time as their time + stamp. This is different from the previous behavior where non-file + tasks reported current time only if there were no prerequisites, and + the max prerequisite timestamp otherwise. This lead to inconsistent + and surprising behavior when adding prerequisites to tasks that in + turn were prequisites to file tasks. The new behavior is more + consistent and predictable. + +==== Changes (from 0.9.3, 0.9.4, 0.9.5) + +Since Rake 10 includes the changes from the last version of Rake 9, +we'll repeat the changes for versions 0.9.3 through 0.9.5 here. + +===== New Features (in 0.9.3) + +* Multitask tasks now use a thread pool. Use -j to limit the number of + available threads. + +* Use -m to turn regular tasks into multitasks (use at your own risk). + +* You can now do "Rake.add_rakelib 'dir'" in your Rakefile to + programatically add rake task libraries. + +* You can specific backtrace suppression patterns (see + --suppress-backtrace) + +* Directory tasks can now take prerequisites and actions + +* Use --backtrace to request a full backtrace without the task trace. + +* You can say "--backtrace=stdout" and "--trace=stdout" to route trace + output to standard output rather than standard error. + +* Optional 'phony' target (enable with 'require 'rake/phony'") for + special purpose builds. + +* Task#clear now clears task comments as well as actions and + prerequisites. Task#clear_comment will specifically target comments. + +* The --all option will force -T and -D to consider all the tasks, + with and without descriptions. + +===== Bug Fixes (in 0.9.3) + +* Semi-colons in windows rakefile paths now work. + +* Improved Control-C support when invoking multiple test suites. + +* egrep method now reads files in text mode (better support for + Windows) + +* Better deprecation line number reporting. + +* The -W option now works with all tasks, whether they have a + description or not. + +* File globs in rake should not be sorted alphabetically, independent + of file system and platform. + +* Numerous internal improvements. + +* Documentation typos and fixes. + +===== Bug Fixes (in 0.9.4) + +* Exit status with failing tests is not correctly set to non-zero. + +* Simplified syntax for phony task (for older versions of RDoc). + +* Stand alone FileList usage gets glob function (without loading in + extra dependencies) + +===== Bug Fixes (in 0.9.5) + +* --trace and --backtrace no longer swallow following task names. + +==== Thanks + +As usual, it was input from users that drove a lot of these changes. The +following people contributed patches, made suggestions or made +otherwise helpful comments. Thanks to ... + +* Aaron Patterson +* Dylan Smith +* Jo Liss +* Jonas Pfenniger +* Kazuki Tsujimoto +* Michael Bishop +* Michael Elufimov +* NAKAMURA Usaku +* Ryan Davis +* Sam Grönblom +* Sam Phippen +* Sergio Wong +* Tay Ray Chuan +* grosser +* quix + +Also, many thanks to Eric Hodel for assisting with getting this release +out the door. + +-- Jim Weirich + +=== 10.0.2 + +==== Changes + +===== Bug Fixes + +* --trace and --backtrace no longer swallow following task names. + +==== Thanks + +As usual, it was input from users that drove a lot of these changes. The +following people contributed patches, made suggestions or made +otherwise helpful comments. Thanks to ... + +* Aaron Patterson +* Dylan Smith +* Jo Liss +* Jonas Pfenniger +* Kazuki Tsujimoto +* Michael Bishop +* Michael Elufimov +* NAKAMURA Usaku +* Ryan Davis +* Sam Grönblom +* Sam Phippen +* Sergio Wong +* Tay Ray Chuan +* grosser +* quix + +Also, many thanks to Eric Hodel for assisting with getting this release +out the door. + +-- Jim Weirich + +=== 10.0.1 + +==== Changes + +===== Bug Fixes + +* Exit status with failing tests is not correctly set to non-zero. + +* Simplified syntax for phony task (for older versions of RDoc). + +* Stand alone FileList usage gets glob function (without loading in + extra dependencies) + +==== Thanks + +As usual, it was input from users that drove a lot of these changes. The +following people contributed patches, made suggestions or made +otherwise helpful comments. Thanks to ... + +* Aaron Patterson +* Dylan Smith +* Jo Liss +* Jonas Pfenniger +* Kazuki Tsujimoto +* Michael Bishop +* Michael Elufimov +* NAKAMURA Usaku +* Ryan Davis +* Sam Grönblom +* Sam Phippen +* Sergio Wong +* Tay Ray Chuan +* grosser +* quix + +Also, many thanks to Eric Hodel for assisting with getting this release +out the door. + +-- Jim Weirich + +=== 10.0.0 + + "Jim, when will Rake reach version 1.0?" + +Over the past several years I've been asked that question at +conferences, panels and over twitter. Due to historical reasons (or +maybe just plain laziness) Rake has (incorrectly) been treating the +second digit of the version as the major release number. So in my head +Rake was already at version 9. + +Well, it's time to fix things. This next version of Rake drops old, +crufty, backwards compatibility hacks such as top level constants, DSL +methods defined in Object and numerous other features that are just no +longer desired. It's also time to drop the leading zero from the +version number as well and call this new version of rake what it +really is: Version 10. + +So, welcome to Rake 10.0! + +Rake 10 is actually feature identical to the latest version of Rake 9 +(that would be the version spelled 0.9.3), *except* that Rake 10 drops +all the sundry deprecated features that have accumulated over the years. + +If your Rakefile is up to date and current with all the new features +of Rake 10, you are ready to go. If your Rakefile still uses a few +deprecated feeatures, feel free to use Rake 9 (0.9.3) with the same +feature set. Just be aware that future features will be in Rake 10 +family line. + +==== Changes in 10.0 + +As mentioned above, there are no new features in Rake 10. However, +there are a number of features missing: + +* Classic namespaces are now gone. Rake is no longer able to reflect + the options settings in the global variables ($rakefile, $show\_tasks, + $show\_prereqs, $trace, $dryrun and $silent). The + --classic-namespace option is no longer supported. + +* Global constants are no longer supported. This includes + Task, FileTask, FileCreationTask and + RakeApp). The constant missing hook to warn about using + global rake constants has been removed. + +* The Rake DSL methods (task, file, directory, etc) are in their own + module (Rake::DSL). The stub versions of these methods (that printed + warnings) in Object have been removed. However, the DSL methods are + added to the top-level main object. Since main is + not in the inheritance tree, the presence of the DSL methods in main + should be low impact on other libraries. + + If you want to use the Rake DSL commands from your own code, just + include Rake::DSL into your own classes and modules. + +* The deprecated syntax for task arguments (the one using + :needs) has been removed. + +* The --reduce-compat flag has been removed (it's not needed + anymore). + +* The deprecated rake/sys.rb library has been removed. + +* The deprecated rake/rdoctask.rb library has been removed. + RDoc supplies its own rake task now. + +* The deprecated rake/gempackagetask.rb library has been + removed. Gem supplies its own package task now. + +There is one small behavioral change: + +* Non-file tasks now always report the current time as their time + stamp. This is different from the previous behavior where non-file + tasks reported current time only if there were no prerequisites, and + the max prerequisite timestamp otherwise. This lead to inconsistent + and surprising behavior when adding prerequisites to tasks that in + turn were prequisites to file tasks. The new behavior is more + consistent and predictable. + +==== Changes (from 0.9.3) + +Since Rake 10 includes the changes from the last version of Rake 9, +we'll repeat the changes for version 0.9.3 here. + +===== New Features + +* Multitask tasks now use a thread pool. Use -j to limit the number of + available threads. + +* Use -m to turn regular tasks into multitasks (use at your own risk). + +* You can now do "Rake.add_rakelib 'dir'" in your Rakefile to + programatically add rake task libraries. + +* You can specific backtrace suppression patterns (see + --suppress-backtrace) + +* Directory tasks can now take prerequisites and actions + +* Use --backtrace to request a full backtrace without the task trace. + +* You can say "--backtrace=stdout" and "--trace=stdout" to route trace + output to standard output rather than standard error. + +* Optional 'phony' target (enable with 'require 'rake/phony'") for + special purpose builds. + +* Task#clear now clears task comments as well as actions and + prerequisites. Task#clear_comment will specifically target comments. + +* The --all option will force -T and -D to consider all the tasks, + with and without descriptions. + +===== Bug Fixes + +* Semi-colons in windows rakefile paths now work. + +* Improved Control-C support when invoking multiple test suites. + +* egrep method now reads files in text mode (better support for + Windows) + +* Better deprecation line number reporting. + +* The -W option now works with all tasks, whether they have a + description or not. + +* File globs in rake should not be sorted alphabetically, independent + of file system and platform. + +* Numerous internal improvements. + +* Documentation typos and fixes. + + +==== Thanks + +As usual, it was input from users that drove a lot of these changes. The +following people contributed patches, made suggestions or made +otherwise helpful comments. Thanks to ... + +* Aaron Patterson +* Dylan Smith +* Jo Liss +* Jonas Pfenniger +* Kazuki Tsujimoto +* Michael Bishop +* Michael Elufimov +* NAKAMURA Usaku +* Ryan Davis +* Sam Grönblom +* Sam Phippen +* Sergio Wong +* Tay Ray Chuan +* grosser +* quix + +Also, many thanks to Eric Hodel for assisting with getting this release +out the door. + +-- Jim Weirich + +=== 0.9.6 + +Rake version 0.9.6 contains a number of fixes mainly for merging +Rake into the Ruby source tree and fixing tests. + +==== Changes + +===== Bug Fixes (0.9.6) + +* Better trace output when using a multi-threaded Rakefile. +* Arg parsing is now consistent for tasks and multitasks. +* Skip exit code test in versions of Ruby that don't support it well. + +Changes for better integration with the Ruby source tree: + +* Fix version literal for Ruby source tree build. +* Better loading of libraries for testing in Ruby build. +* Use the ruby version provided by Ruby's tests. + +==== Thanks + +As usual, it was input from users that drove a alot of these changes. The +following people either contributed patches, made suggestions or made +otherwise helpful comments. Thanks to ... + +* Aaron Patterson +* Dylan Smith +* Jo Liss +* Jonas Pfenniger +* Kazuki Tsujimoto +* Michael Bishop +* Michael Elufimov +* NAKAMURA Usaku +* Ryan Davis +* Sam Grönblom +* Sam Phippen +* Sergio Wong +* Tay Ray Chuan +* grosser +* quix + +Also, many thanks to Eric Hodel for assisting with getting this release +out the door. + +-- Jim Weirich + +=== 0.9.5 + +Rake version 0.9.5 contains a number of bug fixes. + +==== Changes + +===== Bug Fixes (0.9.5) + +* --trace and --backtrace no longer swallow following task names. + +==== Thanks + +As usual, it was input from users that drove a alot of these changes. The +following people either contributed patches, made suggestions or made +otherwise helpful comments. Thanks to ... + +* Aaron Patterson +* Dylan Smith +* Jo Liss +* Jonas Pfenniger +* Kazuki Tsujimoto +* Michael Bishop +* Michael Elufimov +* NAKAMURA Usaku +* Ryan Davis +* Sam Grönblom +* Sam Phippen +* Sergio Wong +* Tay Ray Chuan +* grosser +* quix + +Also, many thanks to Eric Hodel for assisting with getting this release +out the door. + +-- Jim Weirich + +=== 0.9.4 + +Rake version 0.9.4 contains a number of bug fixes. + +==== Changes + +===== Bug Fixes (0.9.4) + +* Exit status with failing tests is not correctly set to non-zero. + +* Simplified syntax for phony task (for older versions of RDoc). + +* Stand alone FileList usage gets glob function (without loading in + extra dependencies) + +==== Thanks + +As usual, it was input from users that drove a alot of these changes. The +following people either contributed patches, made suggestions or made +otherwise helpful comments. Thanks to ... + +* Aaron Patterson +* Dylan Smith +* Jo Liss +* Jonas Pfenniger +* Kazuki Tsujimoto +* Michael Bishop +* Michael Elufimov +* NAKAMURA Usaku +* Ryan Davis +* Sam Grönblom +* Sam Phippen +* Sergio Wong +* Tay Ray Chuan +* grosser +* quix + +Also, many thanks to Eric Hodel for assisting with getting this release +out the door. + +-- Jim Weirich + +=== 0.9.3 + +Rake version 0.9.3 contains some new, backwards compatible features and +a number of bug fixes. + +==== Changes + +===== New Features + +* Multitask tasks now use a thread pool. Use -j to limit the number of + available threads. + +* Use -m to turn regular tasks into multitasks (use at your own risk). + +* You can now do "Rake.add_rakelib 'dir'" in your Rakefile to + programatically add rake task libraries. + +* You can specific backtrace suppression patterns (see + --suppress-backtrace) + +* Directory tasks can now take prerequisites and actions + +* Use --backtrace to request a full backtrace without the task trace. + +* You can say "--backtrace=stdout" and "--trace=stdout" to route trace + output to standard output rather than standard error. + +* Optional 'phony' target (enable with 'require 'rake/phony'") for + special purpose builds. + +* Task#clear now clears task comments as well as actions and + prerequisites. Task#clear_comment will specifically target comments. + +* The --all option will force -T and -D to consider all the tasks, + with and without descriptions. + +===== Bug Fixes + +* Semi-colons in windows rakefile paths now work. + +* Improved Control-C support when invoking multiple test suites. + +* egrep method now reads files in text mode (better support for + Windows) + +* Better deprecation line number reporting. + +* The -W option now works with all tasks, whether they have a + description or not. + +* File globs in rake should not be sorted alphabetically, independent + of file system and platform. + +* Numerous internal improvements. + +* Documentation typos and fixes. + +==== Thanks + +As usual, it was input from users that drove a alot of these changes. The +following people either contributed patches, made suggestions or made +otherwise helpful comments. Thanks to ... + +* Aaron Patterson +* Dylan Smith +* Jo Liss +* Jonas Pfenniger +* Kazuki Tsujimoto +* Michael Bishop +* Michael Elufimov +* NAKAMURA Usaku +* Ryan Davis +* Sam Grönblom +* Sam Phippen +* Sergio Wong +* Tay Ray Chuan +* grosser +* quix + +Also, many thanks to Eric Hodel for assisting with getting this release +out the door. + +-- Jim Weirich + +=== Rake 0.9.2.2 + +Rake version 0.9.2.2 is mainly bug fixes. + +==== Changes + +* The rake test loader now removes arguments it has processed. Issue #51 +* Rake::TaskArguments now responds to #values\_at +* RakeFileUtils.verbose_flag = nil silences output the same as 0.8.7 +* Rake tests are now directory-independent +* Rake tests are no longer require flexmock +* Commands constant is no longer polluting top level namespace. +* Show only the interesting portion of the backtrace by default (James M. Lawrence). +* Added --reduce-compat option to remove backward compatible DSL hacks (James M. Lawrence). + +==== Thanks + +As usual, it was input from users that drove a alot of these changes. The +following people either contributed patches, made suggestions or made +otherwise helpful comments. Thanks to ... + +* James M. Lawrence (quix) +* Roger Pack +* Cezary Baginski +* Sean Scot August Moon +* R.T. Lechow +* Alex Chaffee +* James Tucker +* Matthias Lüdtke +* Santiago Pastorino + +Also, bit thanks to Eric Hodel for assisting with getting this release +out the door (where "assisting" includes, but is not by any means +limited to, "pushing" me to get it done). + +-- Jim Weirich + +=== 0.9.2 + +Rake version 0.9.2 has a few small fixes. See below for details. + +==== Changes + +* Support for Ruby 1.8.6 was fixed. +* Global DSL warnings now honor --no-deprecate + +==== Thanks + +As usual, it was input from users that drove a alot of these changes. The +following people either contributed patches, made suggestions or made +otherwise helpful comments. Thanks to ... + +* James M. Lawrence (quix) +* Roger Pack +* Cezary Baginski +* Sean Scot August Moon +* R.T. Lechow +* Alex Chaffee +* James Tucker +* Matthias Lüdtke +* Santiago Pastorino + +Also, bit thanks to Eric Hodel for assisting with getting this release +out the door (where "assisting" includes, but is not by any means +limited to, "pushing" me to get it done). + +-- Jim Weirich + +=== 0.9.1 + +Rake version 0.9.1 has a number of bug fixes and enhancments (see +below for more details). Additionally, the internals have be slightly +restructured and improved. + +==== Changes + +Rake 0.9.1 adds back the global DSL methods, but with deprecation +messages. This allows Rake 0.9.1 to be used with older rakefiles with +warning messages. + +==== Thanks + +As usual, it was input from users that drove a alot of these changes. The +following people either contributed patches, made suggestions or made +otherwise helpful comments. Thanks to ... + +* James M. Lawrence (quix) +* Roger Pack +* Cezary Baginski +* Sean Scot August Moon +* R.T. Lechow +* Alex Chaffee +* James Tucker +* Matthias Lüdtke +* Santiago Pastorino + +Also, bit thanks to Eric Hodel for assisting with getting this release +out the door (where "assisting" includes, but is not by any means +limited to, "pushing" me to get it done). + +-- Jim Weirich + +=== 0.9.0 + +Rake version 0.9.0 has a number of bug fixes and enhancments (see +below for more details). Additionally, the internals have be slightly +restructured and improved. + +==== Changes + +===== New Features / Enhancements / Bug Fixes in Version 0.9.0 + +* Rake now warns when the deprecated :needs syntax used (and suggests + the proper syntax in the warning). + +* Moved Rake DSL commands to top level ruby object 'main'. Rake DSL + commands are no longer private methods in Object. (Suggested by + James M. Lawrence/quix) + +* Rake now uses case-insensitive comparisons to find the Rakefile on Windows. + Based on patch by Roger Pack. + +* Rake now requires (instead of loads) files in the test task. Patch by Cezary + Baginski. + +* Fixed typos. Patches by Sean Scot August Moon and R.T. Lechow. + +* Rake now prints the Rakefile directory only when it's different from the + current directory. Patch by Alex Chaffee. + +* Improved rakefile_location discovery on Windows. Patch by James Tucker. + +* Rake now recognizes "Windows Server" as a windows system. Patch by Matthias + Lüdtke + +* Rake::RDocTask is deprecated. Use RDoc::Task from RDoc 2.4.2+ (require + 'rdoc/task') + +* Rake::GemPackageTask is deprecated. Use Gem::PackageTask (require + 'rubygems/package\_task') + +* Rake now outputs various messages to $stderr instead of $stdout. + +* Rake no longer emits warnings for Config. Patch by Santiago Pastorino. + +* Removed Rake's DSL methods from the top level scope. If you need to + call 'task :xzy' in your code, include Rake::DSL into your class, or + put the code in a Rake::DSL.environment do ... end block. + +* Split rake.rb into individual files. + +* Support for the --where (-W) flag for showing where a task is defined. + +* Fixed quoting in test task. + (http://onestepback.org/redmine/issues/show/44, + http://www.pivotaltracker.com/story/show/1223138) + +* Fixed the silent option parsing problem. + (http://onestepback.org/redmine/issues/show/47) + +* Fixed :verbose=>false flag on sh and ruby commands. + +* Rake command line options may be given by default in a RAKEOPT + environment variable. + +* Errors in Rake will now display the task invocation chain in effect + at the time of the error. + +* Accepted change by warnickr to not expand test patterns in shell + (allowing more files in the test suite). + +* Fixed that file tasks did not perform prereq lookups in scope + (Redmine #57). + +==== Thanks + +As usual, it was input from users that drove a alot of these changes. The +following people either contributed patches, made suggestions or made +otherwise helpful comments. Thanks to ... + +* James M. Lawrence (quix) +* Roger Pack +* Cezary Baginski +* Sean Scot August Moon +* R.T. Lechow +* Alex Chaffee +* James Tucker +* Matthias Lüdtke +* Santiago Pastorino + +Also, bit thanks to Eric Hodel for assisting with getting this release +out the door (where "assisting" includes, but is not by any means +limited to, "pushing" me to get it done). + +-- Jim Weirich + + +=== 0.8.7 + +Rake version 0.8.5 introduced greatly improved support for executing +commands on Windows. The "sh" command now has the same semantics on +Windows that it has on Unix based platforms. + +Rake version 0.8.6 includes minor fixes the the RDoc generation. +Rake version 0.8.7 includes a minor fix for JRuby running on windows. + +==== Changes + +===== New Features / Enhancements in Version 0.8.5 + +* Improved implementation of the Rake system command for Windows. + (patch from James M. Lawrence/quix) + +* Support for Ruby 1.9's improved system command. (patch from James + M. Lawrence/quix) + +* Rake now includes the configured extension when invoking an + executable (Config::CONFIG['EXEEXT]) + +===== Bug Fixes in Version 0.8.5 + +* Environment variable keys are now correctly cased (it matters in + some implementations). + +==== Thanks + +As usual, it was input from users that drove a alot of these changes. The +following people either contributed patches, made suggestions or made +otherwise helpful comments. Thanks to ... + +* Charles Nutter + +-- Jim Weirich + +=== 0.8.6 + +Rake version 0.8.5 introduced greatly improved support for executing +commands on Windows. The "sh" command now has the same semantics on +Windows that it has on Unix based platforms. + +Rake version 0.8.5 includes minor fixes the the RDoc generation. + +==== Thanks + +As usual, it was input from users that drove a alot of these changes. The +following people either contributed patches, made suggestions or made +otherwise helpful comments. Thanks to ... + +* James M. Lawrence/quix +* Luis Lavena + +-- Jim Weirich + +=== 0.8.5 + +Rake version 0.8.5 is a new release of Rake with greatly improved +support for executing commands on Windows. The "sh" command now has +the same semantics on Windows that it has on Unix based platforms. + +==== Changes + +===== New Features / Enhancements in Version 0.8.5 + +* Improved implementation of the Rake system command for Windows. + (patch from James M. Lawrence/quix) + +* Support for Ruby 1.9's improved system command. (patch from James + M. Lawrence/quix) + +* Rake now includes the configured extension when invoking an + executable (Config::CONFIG['EXEEXT]) + +===== Bug Fixes in Version 0.8.5 + +* Environment variable keys are now correctly cased (it matters in + some implementations). + +==== Thanks + +As usual, it was input from users that drove a alot of these changes. The +following people either contributed patches, made suggestions or made +otherwise helpful comments. Thanks to ... + +* James M. Lawrence/quix +* Luis Lavena + +-- Jim Weirich + +=== 0.8.4 + +Rake version 0.8.4 is a bug-fix release of rake. + +NOTE: The version of Rake that comes with Ruby 1.9 has diverged + slightly from the core Rake code base. Rake 0.8.4 will work + with Ruby 1.9, but is not a strict upgrade for the Rake that + comes with Ruby 1.9. A (near) future release of Rake will unify + those two codebases. + +==== Letter Writing Campaign + +Thanks to Aaron Patterson (@tenderlove) and Eric Hodel (@drbrain) for +their encouraging support in organizing a letter writing campaign to +lobby for the "Warning Free" release of rake 0.8.4. A special callout +goes to Jonathan D. Lord, Sr (Dr. Wingnut) whose postcard was the +first to actually reach me. (see +http://tenderlovemaking.com/2009/02/26/we-need-a-new-version-of-rake/ +for details) + +==== Changes + +===== New Features / Enhancements in Version 0.8.4 + +* Case is preserved on rakefile names. (patch from James + M. Lawrence/quix) + +* Improved Rakefile case insensitivity testing (patch from Luis + Lavena). + +* Windows system dir search order is now: HOME, HOMEDRIVE + HOMEPATH, + APPDATA, USERPROFILE (patch from Luis Lavena) + +* MingGW is now recognized as a windows platform. (patch from Luis + Lavena) + +===== Bug Fixes in Version 0.8.4 + +* Removed reference to manage_gem to fix the warning produced by the + gem package task. + +* Fixed stray ARGV option problem that was interfering with + Test::Unit::Runner. (patch from Pivotal Labs) + +===== Infrastructure Improvements in Version 0.8.4 + +* Numerous fixes to the windows test suite (patch from Luis Lavena). + +* Improved Rakefile case insensitivity testing (patch from Luis + Lavena). + +* Better support for windows paths in the test task (patch from Simon + Chiang/bahuvrihi) + +==== Thanks + +As usual, it was input from users that drove a alot of these changes. The +following people either contributed patches, made suggestions or made +otherwise helpful comments. Thanks to ... + +* James M. Lawrence/quix +* Luis Lavena +* Pivotal Labs +* Simon Chiang/bahuvrihi + +-- Jim Weirich + +=== 0.8.3 + +Rake version 0.8.3 is a bug-fix release of rake. + +==== Changes + +===== Bug Fixes in Version 0.8.3 + +* Enhanced the system directory detection in windows. We now check + HOMEDRIVE/HOMEPATH and USERPROFILE if APPDATA isn't found. (Patch + supplied by James Tucker). Rake no long aborts if it can't find the + directory. + +* Added fix to handle ruby installations in directories with spaces in + their name. + +==== Thanks + +As usual, it was input from users that drove a alot of these changes. The +following people either contributed patches, made suggestions or made +otherwise helpful comments. Thanks to ... + +* Edwin Pratomo +* Gavin Stark +* Adam Q. Salter +* Adam Majer +* Emanuel Indermühle +* Ittay Dror +* Bheeshmar Redheendran (for spending an afternoon with me debugging + windows issues) + +-- Jim Weirich + + +=== 0.8.2 + +Rake version 0.8.2 is a new release of rake that includes a number of +new features and numerous bug fixes. + +==== Changes + +===== New Features in Version 0.8.2 + +* Switched from getoptlong to optparse (patches supplied by Edwin + Pratomo). + +* The -T option will now attempt to dynamically sense the size of the + terminal. The -T output will only self-truncate if the output is a + tty. However, if RAKE_COLUMNS is explicitly set, it will be honored + in any case. (Patch provided by Gavin Stark). + +* The following public methods have been added to rake task objects: + + * task.clear -- Clear both the prerequisites and actions of the + target rake task. + * task.clear_prerequisites -- Clear all the existing prerequisites + from the target rake task. + * task.clear_actions -- Clear all the existing actions from the + target rake task. + * task.reenable -- Re-enable a task, allowing its actions to be + executed again if the task is invoked. + +* Changed RDoc test task to have no default template. This makes it + easier for the tempate to pick up the template from the environment. + +* Default values for task arguments can easily be specified with the + :with_defaults method. (Idea for default argument merging supplied + by (Adam Q. Salter) + +===== Bug Fixes in Version 0.8.2 + +* Fixed bug in package task so that it will include the subdir + directory in the package for testing. (Bug found by Adam Majer) + +* Fixed filename dependency order bug in test\_inspect\_pending and + test\_to\_s\_pending. (Bug found by Adam Majer) + +* Fixed check for file utils options to make them immune to the + symbol/string differences. (Patch supplied by Edwin Pratomo) + +* Fixed bug with rules involving multiple source, where only the first + dependency of a rule has any effect (Patch supplied by Emanuel + Indermühle) + +* FileList#clone and FileList#dup have better sematics w.r.t. taint + and freeze. + +* Changed from using Mutex to Monitor. Evidently Mutex causes thread + join errors when Ruby is compiled with -disable-pthreads. (Patch + supplied by Ittay Dror) + +* Fixed bug in makefile parser that had problems with extra spaces in + file task names. (Patch supplied by Ittay Dror) + +==== Other changes in Version 0.8.2 + +* Added ENV var to rake's own Rakefile to prevent OS X from including + extended attribute junk in the rake package tar file. (Bug found by + Adam Majer) + +* Added a performance patch for reading large makefile dependency + files. (Patch supplied by Ittay Dror) + +==== Task Argument Examples + +Prior to version 0.8.0, rake was only able to handle command line +arguments of the form NAME=VALUE that were passed into Rake via the +ENV hash. Many folks had asked for some kind of simple command line +arguments, perhaps using "--" to separate regular task names from +argument values on the command line. The problem is that there was no +easy way to associate positional arguments on the command line with +different tasks. Suppose both tasks :a and :b expect a command line +argument: does the first value go with :a? What if :b is run first? +Should it then get the first command line argument. + +Rake 0.8.0 solves this problem by explicitly passing values directly +to the tasks that need them. For example, if I had a release task +that required a version number, I could say: + + rake release[0.8.2] + +And the string "0.8.2" will be passed to the :release task. Multiple +arguments can be passed by separating them with a comma, for example: + + rake name[john,doe] + +Just a few words of caution. The rake task name and its arguments +need to be a single command line argument to rake. This generally +means no spaces. If spaces are needed, then the entire rake + +argument string should be quoted. Something like this: + + rake "name[billy bob, smith]" + +(Quoting rules vary between operating systems and shells, so make sure +you consult the proper docs for your OS/shell). + +===== Tasks that Expect Parameters + +Parameters are only given to tasks that are setup to expect them. In +order to handle named parameters, the task declaration syntax for +tasks has been extended slightly. + +For example, a task that needs a first name and last name might be +declared as: + + task :name, :first_name, :last_name + +The first argument is still the name of the task (:name in this case). +The next to argumements are the names of the parameters expected by +:name (:first_name and :last_name in the example). + +To access the values of the parameters, the block defining the task +behaviour can now accept a second parameter: + + task :name, :first_name, :last_name do |t, args| + puts "First name is #{args.first_name}" + puts "Last name is #{args.last_name}" + end + +The first argument of the block "t" is always bound to the current +task object. The second argument "args" is an open-struct like object +that allows access to the task arguments. Extra command line +arguments to a task are ignored. Missing command line arguments are +given the nil value. + +==== Thanks + +As usual, it was input from users that drove a alot of these changes. The +following people either contributed patches, made suggestions or made +otherwise helpful comments. Thanks to ... + +* Edwin Pratomo +* Gavin Stark +* Adam Q. Salter +* Adam Majer +* Emanuel Indermühle +* Ittay Dror +* Bheeshmar Redheendran (for spending an afternoon with me debugging + windows issues) + +-- Jim Weirich + +=== 0.8.0/0.8.1 + +Rake version 0.8.0 is a new release of rake that includes serveral new +features. + +==== Changes + +===== New Features in Version 0.8.0 + +* Tasks can now receive command line parameters. See the examples + below for more details. + +* Comments are limited to 80 columns on output, but full comments can + be seen by using the -D parameter. (feature suggested by Jamis + Buck). + +* Explicit exit(n) calls will now set the exit status to n. (patch + provided by Stephen Touset). + +* Rake is now compatible with Ruby 1.9. + +Version 0.8.1 is a minor update that includes additional Ruby 1.9 +compatibility fixes. + +==== Task Argument Examples + +Prior to version 0.8.0, rake was only able to handle command line +arguments of the form NAME=VALUE that were passed into Rake via the +ENV hash. Many folks had asked for some kind of simple command line +arguments, perhaps using "--" to separate regular task names from +argument values on the command line. The problem is that there was no +easy way to associate positional arguments on the command line with +different tasks. Suppose both tasks :a and :b expect a command line +argument: does the first value go with :a? What if :b is run first? +Should it then get the first command line argument. + +Rake 0.8.0 solves this problem by explicitly passing values directly +to the tasks that need them. For example, if I had a release task +that required a version number, I could say: + + rake release[0.8.0] + +And the string "0.8.0" will be passed to the :release task. Multiple +arguments can be passed by separating them with a comma, for example: + + rake name[john,doe] + +Just a few words of caution. The rake task name and its arguments +need to be a single command line argument to rake. This generally +means no spaces. If spaces are needed, then the entire rake + +argument string should be quoted. Something like this: + + rake "name[billy bob, smith]" + +(Quoting rules vary between operating systems and shells, so make sure +you consult the proper docs for your OS/shell). + +===== Tasks that Expect Parameters + +Parameters are only given to tasks that are setup to expect them. In +order to handle named parameters, the task declaration syntax for +tasks has been extended slightly. + +For example, a task that needs a first name and last name might be +declared as: + + task :name, :first_name, :last_name + +The first argument is still the name of the task (:name in this case). +The next to argumements are the names of the parameters expected by +:name (:first_name and :last_name in the example). + +To access the values of the parameters, the block defining the task +behaviour can now accept a second parameter: + + task :name, :first_name, :last_name do |t, args| + puts "First name is #{args.first_name}" + puts "Last name is #{args.last_name}" + end + +The first argument of the block "t" is always bound to the current +task object. The second argument "args" is an open-struct like object +that allows access to the task arguments. Extra command line +arguments to a task are ignored. Missing command line arguments are +given the nil value. + +==== Thanks + +As usual, it was input from users that drove a alot of these changes. The +following people either contributed patches, made suggestions or made +otherwise helpful comments. Thanks to ... + +* Jamis Buck (for comment formatting suggestions) +* Stephen Touset (for exit status patch). + +-- Jim Weirich + + +=== 0.7.3 + +Rake version 0.7.3 is a minor release that includes some refactoring to better +support custom Rake applications. + +==== Changes + +===== New Features in Version 0.7.3 + +* Added the +init+ and +top_level+ methods to make the creation of custom Rake applications a bit easier. E.g. + + gem 'rake', ">= 0.7.3" + require 'rake' + + Rake.application.init('myrake') + + task :default do + something_interesting + end + + Rake.application.top_level + +==== Thanks + +As usual, it was input from users that drove a alot of these changes. The +following people either contributed patches, made suggestions or made +otherwise helpful comments. Thanks to ... + +-- Jim Weirich + + +=== 0.7.2 + + +Version 0.7.2 supplies a bug fix and a few minor enhancements. In +particular, the new version fixes an incompatibility with the soon to +be released Ruby 1.8.6. We strongly recommend upgrading to Rake 0.7.2 +in order to be compatible with the new version of Ruby. + +==== Changes + +===== Bug Fixes in 0.7.2 + +There are quite a number of bug fixes in the new 0.7.2 version of +Rake: + +* Removed dependency on internal fu_xxx functions from FileUtils. + +* Error messages are now send to stderr rather than stdout (from + Payton Quackenbush). + +* Better error handling on invalid command line arguments (from Payton + Quackenbush). + +* Fixed some bugs where the application object was going to the global + appliation instead of using its own data. + +* Fixed the method name leak from FileUtils (bug found by Glenn + Vanderburg). + +* Added test for noop, bad_option and verbose flags to sh command. + +* Added a description to the gem task in GemPackageTask. + +* Fixed a bug when rules have multiple prerequisites (patch by Joel + VanderWerf) + +* Added the handful of RakeFileUtils to the private method as well. + +===== New Features in 0.7.2 + +The following new features are available in Rake version 0.7.2: + +* Added square and curly bracket patterns to FileList#include (Tilman + Sauerbeck). + +* FileLists can now pass a block to FileList#exclude to exclude files + based on calculated values. + +* Added plain filename support to rule dependents (suggested by Nobu + Nakada). + +* Added pathmap support to rule dependents. In other words, if a + pathmap format (beginning with a '%') is given as a Rake rule + dependent, then the name of the depend will be the name of the + target with the pathmap format applied. + +* Added a 'tasks' method to a namespace to get a list of tasks + associated with the namespace. + +* Added tar_command and zip_command options to the Package task. + +* The clean task will no longer delete 'core' if it is a directory. + +===== Internal Rake Improvements + +The following changes will are mainly internal improvements and +refactorings and have little effect on the end user. But they may be +of interest to the general public. + +* Added rcov task and updated unit testing for better code coverage. + +* Added a 'shame' task to the Rakefile. + +* Added rake_extension to handle detection of extension collisions. + +* Added a protected 'require "rubygems"' to test/test_application to + unbreak cruisecontrol.rb. + +* Removed rake\_dup. Now we just simply rescue a bad dup. + +* Refactored the FileList reject logic to remove duplication. + +* Removed if \_\_FILE\_\_ at the end of the rake.rb file. + +==== Thanks + +As usual, it was input from users that drove a alot of these changes. +The following people either contributed patches, made suggestions or +made otherwise helpful comments. Thanks to ... + +* Payton Quackenbush -- For several error handling improvements. + +* Glenn Vanderburg -- For finding and fixing the method name leak from + FileUtils. + +* Joel VanderWerf -- for finding and fixing a bug in the handling of + multiple prerequisites. + +* Tilman Sauerbeck -- For some enhancing FileList to support more + advanced file globbing. + +* Nobu Nakada -- For suggesting plain file name support to rule dependents. + +-- Jim Weirich + +=== 0.7.1 + +Version 0.7.1 supplies a bug fix and a few minor enhancements. + +==== Changes + +===== Bug Fixes in 0.7.1 + +* Changes in the exception reported for the FileUtils.ln caused + safe_ln to fail with a NotImplementedError. Rake 0.7.1 will now + catch that error or any StandardError and properly fall back to + using +cp+. + +===== New Features in 0.7.1 + +* You can filter the results of the --task option by supplying an + optional regular expression. This allows the user to easily find a + particular task name in a long list of possible names. + +* Transforming procs in a rule may now return a list of prerequisites. + This allows more flexible rule formation. + +* FileList and String now support a +pathmap+ melthod that makes the + transforming paths a bit easier. See the API docs for +pathmap+ for + details. + +* The -f option without a value will disable the search for a + Rakefile. This allows the Rakefile to be defined entirely in a + library (and loaded with the -r option). The current working + directory is not changed when this is done. + +==== Thanks + +As usual, it was input from users that drove a alot of these changes. +The following people either contributed patches, made suggestions or +made otherwise helpful comments. Thanks to ... + +* James Britt and Assaph Mehr for reporting and helping to debug the + safe_ln issue. + +-- Jim Weirich + + +=== 0.7.0 + +These changes for Rake have been brewing for a long time. Here they +are, I hope you enjoy them. + +==== Changes + +===== New Features + +* Name space support for task names (see below). +* Prerequisites can be executed in parallel (see below). +* Added safe_ln support for openAFS (via Ludvig Omholt). +* RDoc defaults to internal (in-process) invocation. The old behavior + is still available by setting the +external+ flag to true. +* Rakefiles are now loaded with the expanded path to prevent + accidental pollution from the Ruby load path. +* Task objects my now be used in prerequisite lists directly. +* Task objects (in addition to task names) may now be included in the + prerequisite list of a task. +* Internals cleanup and refactoring. + +===== Bug Fixes + +* Compatibility fixes for Ruby 1.8.4 FileUtils changes. + +===== Namespaces + +Tasks can now be nested inside their own namespaces. Tasks within one +namespace will not accidentally interfer with tasks named in a different +namespace. + +For example: + + namespace "main" do + task :build do + # Build the main program + end + end + + namespace "samples" do + task :build do + # Build the sample programs + end + end + + task :build_all => ["main:build", "samples:build"] + +Even though both tasks are named :build, they are separate tasks in +their own namespaces. The :build_all task (defined in the toplevel +namespace) references both build tasks in its prerequisites. + +You may invoke each of the individual build tasks with the following +commands: + + rake main:build + rake samples:build + +Or invoke both via the :build_all command: + + rake build_all + +Namespaces may be nested arbitrarily. Since the name of file tasks +correspond to the name of a file in the external file system, +FileTasks are not affected by the namespaces. + +See the Rakefile format documentation (in the Rake API documents) for +more information. + +===== Parallel Tasks + +Sometimes you have several tasks that can be executed in parallel. By +specifying these tasks as prerequisites to a +multitask+ task. + +In the following example the tasks copy\_src, copy\_doc and copy\_bin +will all execute in parallel in their own thread. + + multitask :copy_files => [:copy_src, :copy_doc, :copy_bin] do + puts "All Copies Complete" + end + +==== Thanks + +As usual, it was input from users that drove a alot of these changes. +The following people either contributed patches, made suggestions or +made otherwise helpful comments. Thanks to ... + +* Doug Young (inspiration for the parallel task) +* David Heinemeier Hansson (for --trace message enhancement and for + pushing for namespace support). +* Ludvig Omholt (for the openAFS fix) + +-- Jim Weirich + +=== 0.6.1 + +* Rebuilt 0.6.0 gem without signing. + +=== 0.6.0 + +Its time for some long requested enhancements and lots of bug fixes +... And a whole new web page. + +==== New Web Page + +The primary documentation for rake has moved from the RubyForge based +wiki to its own Hieraki based web site. Constant spam on the wiki +made it a difficult to keep clean. The new site will be easier to +update and organize. + +Check out the new documentation at: http://docs.rubyrake.org + +We will be adding new documentation to the site as time goes on. + +In addition to the new docs page, make sure you check out Martin +Fowlers article on rake at http://martinfowler.com/articles/rake.html + +==== Changes + +===== New Features + +* Multiple prerequisites on Rake rules now allowed. However, keep the + following in mind: + + 1. All the prerequisites of a rule must be available before a rule + is triggered, where "enabled" means (a) an existing file, (b) a + defined rule, or (c) another rule which also must be + trigger-able. + 2. Rules are checked in order of definition, so it is important to + order your rules properly. If a file can be created by two + different rules, put the more specific rule first (otherwise the + more general rule will trigger first and the specific one will + never be triggered). + 3. The source method now returns the name of the first + prerequisite listed in the rule. sources returns the + names of all the rule prerequisites, ordered as they are defined + in the rule. If the task has other prerequisites not defined in + the rule (but defined in an explicit task definition), then they + will _not_ be included in the sources list. + +* FileLists may now use the egrep command. This popular enhancement + is now a core part of the FileList object. If you want to get a + list of all your to-dos, fixmes and TBD comments, add the following + to your Rakefile. + + desc "Look for TODO and FIXME tags in the code" + task :todo do + FileList['**/*.rb'].egrep /#.*(FIXME|TODO|TBD)/ + end + +* The investigation method was added to task object to dump + out some important values. This makes it a bit easier to debug Rake + tasks. + + For example, if you are having problems with a particular task, just + print it out: + + task :huh do + puts Rake::Task['huh'].investigation + end + +* The Rake::TestTask class now supports a "ruby\_opts" option to pass + arbitrary ruby options to a test subprocess. + +===== Some Incompatibilities + +* When using the ruby command to start a Ruby subprocess, the + Ruby interpreter that is currently running rake is used by default. + This makes it easier to use rake in an environment with multiple + ruby installation. (Previously, the first ruby command found in the + PATH was used). + + If you wish to chose a different Ruby interpreter, you can + explicitly choose the interpreter via the sh command. + +* The major rake classes (Task, FileTask, FileCreationTask, RakeApp) + have been moved out of the toplevel scope and are now accessible as + Rake::Task, Rake::FileTask, Rake::FileCreationTask and + Rake::Application. If your Rakefile + directly references any one of these tasks, you may: + + 1. Update your Rakefile to use the new classnames + 2. Use the --classic-namespace option on the rake command to get the + old behavior, + 3. Add require 'rake/classic_namespace' to the + Rakefile to get the old behavior. + + rake will print a rather annoying warning whenever a + deprecated class name is referenced without enabling classic + namespace. + +===== Bug Fixes + +* Several unit tests and functional tests were fixed to run better + under windows. + +* Directory tasks are now a specialized version of a File task. A + directory task will only be triggered if it doesn't exist. It will + not be triggered if it is out of date w.r.t. any of its + prerequisites. + +* Fixed a bug in the Rake::GemPackageTask class so that the gem now + properly contains the platform name. + +* Fixed a bug where a prerequisite on a file task would cause + an exception if the prerequisite did not exist. + +==== Thanks + +As usual, it was input from users that drove a alot of these changes. +The following people either contributed patches, made suggestions or +made otherwise helpful comments. Thanks to ... + +* Greg Fast (better ruby_opt test options) +* Kelly Felkins (requested by better namespace support) +* Martin Fowler (suggested Task.investigation) +* Stuart Jansen (send initial patch for multiple prerequisites). +* Masao Mutch (better support for non-ruby Gem platforms) +* Philipp Neubeck (patch for file task exception fix) + +-- Jim Weirich + +=== 0.5.4 + +Time for some minor bug fixes and small enhancements + +==== Changes + +Here are the changes for version 0.5.4 ... + +* Added double quotes to the test runner. This allows the location of + the tests (and runner) to be in a directory path that contains + spaces (e.g. "C:/Program Files/ruby/bin"). +* Added .svn to default ignore list. Now subversion project metadata + is automatically ignored by Rake's FileList. +* Updated FileList#include to support nested arrays and filelists. + FileLists are flat lists of file names. Using a FileList in an + include will flatten out the nested file names. + +== Thanks + +As usual, it was input from users that drove a alot of these changes. +Thanks to ... + +* Tilman Sauerbeck for the nested FileList suggestion. +* Josh Knowles for pointing out the spaces in directory name problem. + +-- Jim Weirich + +=== 0.5.3 + +Although it has only been two weeks since the last release, we have +enough updates to the Rake program to make it time for another +release. + +==== Changes + +Here are the changes for version 0.5.3 ... + +* FileLists have been extensively changed so that they mimic the + behavior of real arrays even more closely. In particular, + operations on FileLists that return a new collection (e.g. collect, + reject) will now return a FileList rather than an array. In + addition, several places where FileLists were not properly expanded + before use have been fixed. +* A method (+ext+) to simplify the handling of file extensions was + added to String and to Array. +* The 'testrb' script in test/unit tends to silently swallow syntax + errors in test suites. Because of that, the default test loader is + now a rake-provided script. You can still use 'testrb' by setting + the loader flag in the test task to :testrb. (See the API documents + for TestTask for all the loader flag values). +* FileUtil methods (e.g. cp, mv, install) are now declared to be + private. This will cut down on the interference with user defined + methods of the same name. +* Fixed the verbose flag in the TestTask so that the test code is + controlled by the flag. Also shortened up some failure messages. + (Thanks to Tobias Luetke for the suggestion). +* Rules will now properly detect a task that can generate a source + file. Previously rules would only consider source files that were + already present. +* Added an +import+ command that allows Rake to dynamically import + dependendencies into a running Rake session. The +import+ command + can run tasks to update the dependency file before loading them. + Dependency files can be in rake or make format, allowing rake to + work with tools designed to generate dependencies for make. + +==== Thanks + +As usual, it was input from users that drove a alot of these changes. +Thanks to ... + +* Brian Gernhardt for the rules fix (especially for the patience to + explain the problem to me until I got what he was talking about). +* Stefan Lang for pointing out problems in the dark corners of the + FileList implementation. +* Alexey Verkhovsky pointing out the silently swallows syntax errors + in tests. +* Tobias Luetke for beautifying the test task output. +* Sam Roberts for some of the ideas behind dependency loading. + +-- Jim Weirich + + +=== 0.5.0 + +It has been a long time in coming, but we finally have a new version +of Rake available. + +==== Changes + +* Fixed documentation that was lacking the Rake module name (Tilman + Sauerbeck). +* Added tar.gz and tar.bz2 support to package task (Tilman Sauerbeck). +* Recursive rules are now supported (Tilman Sauerbeck). +* Added warning option for the Test Task (requested by Eric Hodel). +* The jamis rdoc template is only used if it exists. +* Added fix for Ruby 1.8.2 test/unit and rails problem. +* Added contributed rake man file (Jani Monoses). +* Added Brian Candler's fix for problems in --trace and --dry-run + mode. + +==== Thanks + +Lots of people provided input to this release. Thanks to Tilman +Sauerbeck for numerous patches, documentation fixes and suggestions. +And for also pushing me to get this release out. Also, thanks to +Brian Candler for the finding and fixing --trace/dry-run fix. That +was an obscure bug. Also to Eric Hodel for some good suggestions. + +-- Jim Weirich + +=== 0.4.15 + +==== Changes + +Version 0.4.15 is a bug fix update for the Ruby 1.8.2 compatibility +changes. This release includes: + +* Fixed a bug that prevented the TESTOPTS flag from working with the + revised for 1.8.2 test task. +* Updated the docs on --trace to indicate that it also enables a full + backtrace on errors. +* Several fixes for new warnings generated. + +==== Mini-Roadmap + +I will continue to issue Rake updates in the 0.4.xx series as new +Ruby-1.8.2 issues become manifest. Once the codebase stabilizes, I +will release a 0.5.0 version incorporating all the changes. If you +are not using Ruby-1.8.2 and wish to avoid version churn, I recommend +staying with a release prior to Rake-0.4.14. + +=== 0.4.14 + +Version 0.4.14 is a compatibility fix to allow Rake's test task to +work under Ruby 1.8.2. A change in the Test::Unit autorun feature +prevented Rake from running any tests. This release fixes the +problem. + +Rake 0.4.14 is the recommended release for anyone using Ruby 1.8.2. + +=== 0.4.13 + +* Fixed the dry-run flag so it is operating again. +* Multiple arguments to sh and ruby commands will not be interpreted + by the shell (patch provided by Jonathan Paisley). + +=== 0.4.12 + +* Added --silent (-s) to suppress the (in directory) rake message. + +=== 0.4.11 + +* Changed the "don't know how to rake" message (finally) +* Changes references to a literal "Rakefile" to reference the global + variable $rakefile (which contains the actual name of the rakefile). + +=== 0.4.10 + +* Added block support to the "sh" command, allowing users to take + special actions on the result of the system call. E.g. + + sh "shell_command" do |ok, res| + puts "Program returned #{res.exitstatus}" if ! ok + end + +=== 0.4.9 + +* Switched to Jamis Buck's RDoc template. +* Removed autorequire from Rake's gem spec. This prevents the Rake + libraries from loading while using rails. + +=== 0.4.8 + +* Added support for .rb versions of Rakefile. +* Removed \\\n's from test task. +* Fixed Ruby 1.9 compatibility issue with FileList. + +=== 0.4.7 + +* Fixed problem in FileList that caused Ruby 1.9 to go into infinite + recursion. Since to_a was removed from Object, it does not need to + added back into the list of methods to rewrite in FileList. (Thanks + to Kent Sibilev for pointing this out). + +=== 0.4.6 +* Removed test version of ln in FileUtils that prevented safe_ln from + using ln. + +=== 0.4.5 +* Upgraded comments in TestTask. +* FileList to_s and inspect now automatically resolve pending changes. +* FileList#exclude properly returns the FileList. + +=== 0.4.4 +* Fixed initialization problem with @comment. +* Now using multi -r technique in TestTask. Switch Rakefile back to + using the built-in test task macros because the rake runtime is no + longer needed. +* Added 'TEST=filename' and 'TESTOPTS=options' to the Test Task + macros. +* Allow a +test_files+ attribute in test tasks. This allows more + flexibility in specifying test files. + +=== 0.4.3 +* Fixed Comment leakage. + +=== 0.4.2 +* Added safe_ln that falls back to a copy if a file link is not supported. +* Package builder now uses safe\_ln. + +=== 0.4.1 +* Task comments are now additive, combined with "/". +* Works with (soon to be released) rubygems 0.6.2 (or 0.7.0) + +=== 0.4.0 +* FileList now uses deferred loading. The file system is not searched + until the first call that needs the file names. +* VAR=VALUE options are now accepted on the command line and are + treated like environment variables. The values may be tested in a + Rakefile by referencing ENV['VAR']. +* File.mtime is now used (instead of File.new().mtime). + +=== 0.3.2.x + +* Removed some hidden dependencies on rubygems. Tests now will test + gems only if they are installed. +* Removed Sys from some example files. I believe that is that last + reference to Sys outside of the contrib area. +* Updated all copyright notices to include 2004. + +=== 0.3.2 + +* GEM Installation now works with the application stub. + +=== 0.3.1 + +* FileLists now automatically ignore CVS, .bak, ! +* GEM Installation now works. + +=== 0.3.0 + +Promoted 0.2.10. + +=== 0.2.10 +General + +* Added title to Rake's rdocs +* Contrib packages are no longer included in the documentation. + +RDoc Issues + +* Removed default for the '--main' option +* Fixed rendering of the rdoc options +* Fixed clean/clobber confusion with rerdoc +* 'title' attribute added + +Package Task Library Issues + +* Version (or explicit :noversion) is required. +* +package_file+ attribute is now writable + +FileList Issues + +* Dropped bang version of exclude. Now using ant-like include/exclude semantics. +* Enabled the "yield self" idiom in FileList#initialize. + +=== 0.2.9 + +This version contains numerous changes as the RubyConf.new(2003) +presentation was being prepared. The changes include: + +* The monolithic rubyapp task library is in the process of being + dropped in favor of lighter weight task libraries. + +=== 0.2.7 + +* Added "desc" for task descriptions. +* -T will now display tasks with descriptions. +* -P will display tasks and prerequisites. +* Dropped the Sys module in favor of the 1.8.x FileUtils module. Sys + is still supported in the contrib area. + +=== 0.2.6 + +* Moved to RubyForge + +=== 0.2.5 + +* Switched to standard ruby app builder. +* Added no_match option to file matcher. + +=== 0.2.4 + +* Fixed indir, which neglected to actually change directories. + +=== 0.2.3 + +* Added rake module for a help target +* Added 'for\_files' to Sys +* Added a $rakefile constant +* Added test for selecting proper rule with multiple targets. diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/MIT-LICENSE b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/MIT-LICENSE new file mode 100644 index 0000000..4292f3b --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/MIT-LICENSE @@ -0,0 +1,21 @@ +Copyright (c) Jim Weirich + +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/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/README.rdoc b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/README.rdoc new file mode 100644 index 0000000..b31fe37 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/README.rdoc @@ -0,0 +1,155 @@ += RAKE -- Ruby Make + +home :: https://github.com/ruby/rake +bugs :: https://github.com/ruby/rake/issues +docs :: https://ruby.github.io/rake + +== Description + +Rake is a Make-like program implemented in Ruby. Tasks and dependencies are +specified in standard Ruby syntax. + +Rake has the following features: + +* Rakefiles (rake's version of Makefiles) are completely defined in + standard Ruby syntax. No XML files to edit. No quirky Makefile + syntax to worry about (is that a tab or a space?) + +* Users can specify tasks with prerequisites. + +* Rake supports rule patterns to synthesize implicit tasks. + +* Flexible FileLists that act like arrays but know about manipulating + file names and paths. + +* A library of prepackaged tasks to make building rakefiles easier. For example, + tasks for building tarballs. (Formerly + tasks for building RDoc, Gems, and publishing to FTP were included in rake but they're now + available in RDoc, RubyGems, and rake-contrib respectively.) + +* Supports parallel execution of tasks. + +== Installation + +=== Gem Installation + +Download and install rake with the following. + + gem install rake + +== Usage + +=== Simple Example + +First, you must write a "Rakefile" file which contains the build rules. Here's +a simple example: + + task default: %w[test] + + task :test do + ruby "test/unittest.rb" + end + +This Rakefile has two tasks: + +* A task named "test", which -- upon invocation -- will run a unit test file + in Ruby. +* A task named "default". This task does nothing by itself, but it has exactly + one dependency, namely the "test" task. Invoking the "default" task will + cause Rake to invoke the "test" task as well. + +Running the "rake" command without any options will cause it to run the +"default" task in the Rakefile: + + % ls + Rakefile test/ + % rake + (in /home/some_user/Projects/rake) + ruby test/unittest.rb + ....unit test output here... + +Type "rake --help" for all available options. + +== Resources + +=== Rake Information + +* {Rake command-line}[rdoc-ref:doc/command_line_usage.rdoc] +* {Writing Rakefiles}[rdoc-ref:doc/rakefile.rdoc] +* The original {Rake announcement}[rdoc-ref:doc/rational.rdoc] +* Rake {glossary}[rdoc-ref:doc/glossary.rdoc] + +=== Presentations and Articles about Rake + +* Avdi Grimm's rake series: + 1. {Rake Basics}[https://avdi.codes/rake-part-1-basics/] + 2. {Rake File Lists}[https://avdi.codes/rake-part-2-file-lists-2/] + 3. {Rake Rules}[https://avdi.codes/rake-part-3-rules/] + 4. {Rake Pathmap}[https://avdi.codes/rake-part-4-pathmap/] + 5. {File Operations}[https://avdi.codes/rake-part-5-file-operations/] + 6. {Clean and Clobber}[https://avdi.codes/rake-part-6-clean-and-clobber/] + 7. {MultiTask}[https://avdi.codes/rake-part-7-multitask/] +* {Jim Weirich's 2003 RubyConf presentation}[https://web.archive.org/web/20140221123354/http://onestepback.org/articles/buildingwithrake/] +* Martin Fowler's article on Rake: https://martinfowler.com/articles/rake.html + +== Other Make Re-envisionings ... + +Rake is a late entry in the make replacement field. Here are links to +other projects with similar (and not so similar) goals. + +* https://directory.fsf.org/wiki/Bras -- Bras, one of earliest + implementations of "make in a scripting language". +* http://www.a-a-p.org -- Make in Python +* https://ant.apache.org -- The Ant project +* https://search.cpan.org/search?query=PerlBuildSystem -- The Perl Build System +* https://www.rubydoc.info/gems/rant/0.5.7/frames -- Rant, another Ruby make tool. + +== Credits + +[Jim Weirich] Who originally created Rake. + +[Ryan Dlugosz] For the initial conversation that sparked Rake. + +[Nobuyoshi Nakada ] For the initial patch for rule support. + +[Tilman Sauerbeck ] For the recursive rule patch. + +[Eric Hodel] For aid in maintaining rake. + +[Hiroshi SHIBATA] Maintainer of Rake 10 and later + +== License + +Rake is available under an MIT-style license. + +:include: MIT-LICENSE + +--- + += Other stuff + +Author:: Jim Weirich +Requires:: Ruby 2.0.0 or later +License:: Copyright Jim Weirich. + Released under an MIT-style license. See the MIT-LICENSE + file included in the distribution. + +== Warranty + +This software is provided "as is" and without any express or implied +warranties, including, without limitation, the implied warranties of +merchantability and fitness for a particular purpose. + +== Historical + +Rake was originally created by Jim Weirich, who unfortunately passed away in +February 2014. This repository was originally hosted at +{github.com/jimweirich/rake}[https://github.com/jimweirich/rake/], however +with his passing, has been moved to {ruby/rake}[https://github.com/ruby/rake]. + +You can view Jim's last commit here: +https://github.com/jimweirich/rake/commit/336559f28f55bce418e2ebcc0a57548dcbac4025 + +You can {read more about Jim}[https://en.wikipedia.org/wiki/Jim_Weirich] at Wikipedia. + +Thank you for this great tool, Jim. We'll remember you. diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/doc/command_line_usage.rdoc b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/doc/command_line_usage.rdoc new file mode 100644 index 0000000..c1ff39d --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/doc/command_line_usage.rdoc @@ -0,0 +1,171 @@ += Rake Command Line Usage + +Rake is invoked from the command line using: + + % rake [options ...] [VAR=VALUE ...] [targets ...] + +Options are: + +[name=value] + Set the environment variable name to value + during the execution of the rake command. You can access + the value by using ENV['name']. + +[--all (-A)] + Used in combination with the -T and -D options, will force + those options to show all the tasks, even the ones without comments. + +[--backtrace{=_output_} (-n)] + Enable a full backtrace (i.e. like --trace, but without the task + tracing details). The _output_ parameter is optional, but if + specified it controls where the backtrace output is sent. If + _output_ is stdout, then backtrace output is directed to + standard output. If _output_ is stderr, or if it is + missing, then the backtrace output is sent to standard error. + +[--comments] + Used in combination with the -W options to force the output to + contain commented options only. This is the reverse of + --all. + +[--describe _pattern_ (-D)] + Describe the tasks (matching optional PATTERN), then exit. + +[--dry-run (-n)] + Do a dry run. Print the tasks invoked and executed, but do not + actually execute any of the actions. + +[--execute _code_ (-e)] + Execute some Ruby code and exit. + +[--execute-print _code_ (-p)] + Execute some Ruby code, print the result, and exit. + +[--execute-continue _code_ (-E)] + Execute some Ruby code, then continue with normal task processing. + +[--help (-H)] + Display some help text and exit. + +[--jobs _number_ (-j)] + + Specifies the maximum number of concurrent threads allowed. Rake + will allocate threads as needed up to this maximum number. + + If omitted, Rake will attempt to estimate the number of CPUs on + the system and add 4 to that number. + + The concurrent threads are used to execute the multitask + prerequisites. Also see the -m option which turns all + tasks into multitasks. + + Sample values: + (no -j) : Allow up to (# of CPUs + 4) number of threads + --jobs : Allow unlimited number of threads + --jobs=1 : Allow only one thread (the main thread) + --jobs=16 : Allow up to 16 concurrent threads + +[--job-stats _level_] + + Display job statistics at the completion of the run. By default, + this will display the requested number of active threads (from the + -j options) and the maximum number of threads in play at any given + time. + + If the optional _level_ is history, then a complete trace + of task history will be displayed on standard output. + +[--libdir _directory_ (-I)] + Add _directory_ to the list of directories searched for require. + +[--multitask (-m)] + Treat all tasks as multitasks. ('make/drake' semantics) + +[--nosearch (-N)] + Do not search for a Rakefile in parent directories. + +[--prereqs (-P)] + Display a list of all tasks and their immediate prerequisites. + +[--quiet (-q)] + Do not echo commands from FileUtils. + +[--rakefile _filename_ (-f)] + Use _filename_ as the name of the rakefile. The default rakefile + names are +rakefile+ and +Rakefile+ (with +rakefile+ taking + precedence). If the rakefile is not found in the current + directory, +rake+ will search parent directories for a match. The + directory where the Rakefile is found will become the current + directory for the actions executed in the Rakefile. + +[--rakelibdir _rakelibdir_ (-R)] + Auto-import any .rake files in RAKELIBDIR. (default is 'rakelib') + +[--require _name_ (-r)] + Require _name_ before executing the Rakefile. + +[--rules] + Trace the rules resolution. + +[--silent (-s)] + Like --quiet, but also suppresses the 'in directory' announcement. + +[--suppress-backtrace _pattern_ ] + Line matching the regular expression _pattern_ will be removed + from the backtrace output. Note that the --backtrace option is the + full backtrace without these lines suppressed. + +[--system (-g)] + Use the system wide (global) rakefiles. The project Rakefile is + ignored. By default, the system wide rakefiles are used only if no + project Rakefile is found. On Unix-like system, the system wide + rake files are located in $HOME/.rake. On a windows system they + are stored in $APPDATA/Rake. + +[--no-system (-G)] + Use the project level Rakefile, ignoring the system-wide (global) + rakefiles. + +[--tasks pattern (-T)] + Display a list of the major tasks and their comments. Comments + are defined using the "desc" command. If a pattern is given, then + only tasks matching the pattern are displayed. + +[--trace{=_output_} (-t)] + Turn on invoke/execute tracing. Also enable full backtrace on + errors. The _output_ parameter is optional, but if specified it + controls where the trace output is sent. If _output_ is + stdout, then trace output is directed to standard output. + If _output_ is stderr, or if it is missing, then trace + output is sent to standard error. + +[--verbose (-v)] + Echo the Sys commands to standard output. + +[--version (-V)] + Display the program version and exit. + +[--where pattern (-W)] + Display tasks that match pattern and the file and line + number where the task is defined. By default this option will + display all tasks, not just the tasks that have descriptions. + +[--no-deprecation-warnings (-X)] + Do not display the deprecation warnings. + +== Environment Variables + +[RAKEOPT] + Command line options can be specified in the RAKEOPT + environment variable. These options will be processed as if they + were given on the command line. This is useful for setting default + options that you want to use with every rake invocation. + + For example, setting: + export RAKEOPT="-s --trace" + + would cause rake to run silently with tracing enabled by default. + +In addition, any command line option of the form +VAR=VALUE will be added to the environment hash +ENV and may be tested in the Rakefile. diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/doc/example/Rakefile1 b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/doc/example/Rakefile1 new file mode 100644 index 0000000..39f8bcc --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/doc/example/Rakefile1 @@ -0,0 +1,38 @@ +# Example Rakefile -*- ruby -*- + +task :default => [:main] + +file "a.o" => ["a.c"] do |t| + src = t.name.sub(/\.o$/, '.c') + sh "gcc #{src} -c -o #{t.name}" +end + +file "b.o" => ["b.c"] do |t| + src = t.name.sub(/\.o$/, '.c') + sh "gcc #{src} -c -o #{t.name}" +end + +file "main.o" => ["main.c"] do |t| + src = t.name.sub(/\.o$/, '.c') + sh "gcc #{src} -c -o #{t.name}" +end + +OBJFILES = ["a.o", "b.o", "main.o"] +task :obj => OBJFILES + +file "main" => OBJFILES do |t| + sh "gcc -o #{t.name} main.o a.o b.o" +end + +task :clean do + rm_f FileList['*.o'] + Dir['*~'].each { |fn| rm_f fn } +end + +task :clobber => [:clean] do + rm_f "main" +end + +task :run => ["main"] do + sh "./main" +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/doc/example/Rakefile2 b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/doc/example/Rakefile2 new file mode 100644 index 0000000..35310ec --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/doc/example/Rakefile2 @@ -0,0 +1,35 @@ +# Example Rakefile -*- ruby -*- +# Using the power of Ruby + +task :default => [:main] + +def ext(fn, newext) + fn.sub(/\.[^.]+$/, newext) +end + +SRCFILES = Dir['*.c'] +OBJFILES = SRCFILES.collect { |fn| ext(fn,".o") } + +OBJFILES.each do |objfile| + srcfile = ext(objfile, ".c") + file objfile => [srcfile] do |t| + sh "gcc #{srcfile} -c -o #{t.name}" + end +end + +file "main" => OBJFILES do |t| + sh "gcc -o #{t.name} main.o a.o b.o" +end + +task :clean do + rm_f FileList['*.o'] + Dir['*~'].each { |fn| rm_f fn } +end + +task :clobber => [:clean] do + rm_f "main" +end + +task :run => ["main"] do + sh "./main" +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/doc/example/a.c b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/doc/example/a.c new file mode 100644 index 0000000..620e6f8 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/doc/example/a.c @@ -0,0 +1,6 @@ +#include + +void a() +{ + printf ("In function a\n"); +} diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/doc/example/b.c b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/doc/example/b.c new file mode 100644 index 0000000..9b24aa1 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/doc/example/b.c @@ -0,0 +1,6 @@ +#include + +void b() +{ + printf ("In function b\n"); +} diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/doc/example/main.c b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/doc/example/main.c new file mode 100644 index 0000000..a04558a --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/doc/example/main.c @@ -0,0 +1,11 @@ +#include + +extern void a(); +extern void b(); + +int main () +{ + a(); + b(); + return 0; +} diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/doc/glossary.rdoc b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/doc/glossary.rdoc new file mode 100644 index 0000000..9d592b0 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/doc/glossary.rdoc @@ -0,0 +1,42 @@ += Glossary + +action :: + Code to be executed in order to perform a task. Actions in a Rakefile are + specified in a code block. (Usually delimited by +do+/+end+ pairs.) + +execute :: + When a task is executed, all of its actions are performed in the order they + were defined. Note that, unlike invoke, execute always + executes the actions (without invoking or executing the prerequisites). + +file task (Rake::FileTask) :: + A file task is a task whose purpose is to create a file (which has the same + name as the task). When invoked, a file task will only execute if one or + more of the following conditions are true. + + 1. The associated file does not exist. + 2. A prerequisite has a later time stamp than the existing file. + + Because normal Tasks always have the current time as timestamp, a FileTask + that has a normal Task prerequisite will always execute. + +invoke :: + When a task is invoked, first we check to see if it has been invoked before. + If it has been, then nothing else is done. If this is the first time it has + been invoked, then we invoke each of its prerequisites. Finally, we check + to see if we need to execute the actions of this task by calling + Rake::Task#needed?. If the task is needed, we execute its actions. + + NOTE: Prerequisites are still invoked even if the task is not needed. + +prerequisites :: + Every task has a (possibly empty) set of prerequisites. A prerequisite P to + Task T is itself a task that must be invoked before Task T. + +rule :: + A rule is a recipe for synthesizing a task when no task is explicitly + defined. Rules generally synthesize file tasks. + +task (Rake::Task) :: + The basic unit of work in a Rakefile. A task has a name, a set of + prerequisites, and a list of actions to be performed. diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/doc/jamis.rb b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/doc/jamis.rb new file mode 100644 index 0000000..531aa75 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/doc/jamis.rb @@ -0,0 +1,592 @@ +# frozen_string_literal: true +module RDoc +module Page + +FONTS = "\"Bitstream Vera Sans\", Verdana, Arial, Helvetica, sans-serif" + +STYLE = < pre { + padding: 0.5em; + border: 1px dotted black; + background: #FFE; +} + +CSS + +XHTML_PREAMBLE = %{ + +} + +HEADER = XHTML_PREAMBLE + < + + %title% + + + + + + + +ENDHEADER + +FILE_PAGE = < + + + + +
File
%short_name%
+ + + + + + + + + +
Path:%full_path% +IF:cvsurl +  (CVS) +ENDIF:cvsurl +
Modified:%dtm_modified%
+
+ +
+HTML + +################################################################### + +CLASS_PAGE = < + %classmod%
%full_name% + + + + + + +IF:parent + + + + +ENDIF:parent +
In: +START:infiles +HREF:full_path_url:full_path: +IF:cvsurl + (CVS) +ENDIF:cvsurl +END:infiles +
Parent: +IF:par_url + +ENDIF:par_url +%parent% +IF:par_url + +ENDIF:par_url +
+ + + +HTML + +################################################################### + +METHOD_LIST = < +IF:diagram +
+ %diagram% +
+ENDIF:diagram + +IF:description +
%description%
+ENDIF:description + +IF:requires +
Required Files
+
    +START:requires +
  • HREF:aref:name:
  • +END:requires +
+ENDIF:requires + +IF:toc +
Contents
+ +ENDIF:toc + +IF:methods +
Methods
+
    +START:methods +
  • HREF:aref:name:
  • +END:methods +
+ENDIF:methods + +IF:includes +
Included Modules
+
    +START:includes +
  • HREF:aref:name:
  • +END:includes +
+ENDIF:includes + +START:sections +IF:sectitle + +IF:seccomment +
+%seccomment% +
+ENDIF:seccomment +ENDIF:sectitle + +IF:classlist +
Classes and Modules
+ %classlist% +ENDIF:classlist + +IF:constants +
Constants
+ +START:constants + + + + + +IF:desc + + + + +ENDIF:desc +END:constants +
%name%=%value%
 %desc%
+ENDIF:constants + +IF:attributes +
Attributes
+ +START:attributes + + + + + +END:attributes +
+IF:rw +[%rw%] +ENDIF:rw + %name%%a_desc%
+ENDIF:attributes + +IF:method_list +START:method_list +IF:methods +
%type% %category% methods
+START:methods +
+
+IF:callseq + %callseq% +ENDIF:callseq +IFNOT:callseq + %name%%params% +ENDIF:callseq +IF:codeurl +[ source ] +ENDIF:codeurl +
+IF:m_desc +
+ %m_desc% +
+ENDIF:m_desc +IF:aka +
+ This method is also aliased as +START:aka + %name% +END:aka +
+ENDIF:aka +IF:sourcecode +
+ +
+
+%sourcecode%
+
+
+
+ENDIF:sourcecode +
+END:methods +ENDIF:methods +END:method_list +ENDIF:method_list +END:sections + +HTML + +FOOTER = < + +ENDFOOTER + +BODY = HEADER + < + +
+ #{METHOD_LIST} +
+ + #{FOOTER} +ENDBODY + +########################## Source code ########################## + +SRC_PAGE = XHTML_PREAMBLE + < +%title% + + + + +
%code%
+ + +HTML + +########################## Index ################################ + +FR_INDEX_BODY = < + + + + + + + +
+START:entries +%name%
+END:entries +
+ +HTML + +CLASS_INDEX = FILE_INDEX +METHOD_INDEX = FILE_INDEX + +INDEX = XHTML_PREAMBLE + < + + %title% + + + + + + + + + +IF:inline_source + +ENDIF:inline_source +IFNOT:inline_source + + + + +ENDIF:inline_source + + <body bgcolor="white"> + Click <a href="html/index.html">here</a> for a non-frames + version of this page. + </body> + + + + +HTML + +end +end + + diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/doc/proto_rake.rdoc b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/doc/proto_rake.rdoc new file mode 100644 index 0000000..a9e33d1 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/doc/proto_rake.rdoc @@ -0,0 +1,127 @@ += Original Prototype Rake + +This is the original 100 line prototype rake program. + +--- + #!/usr/bin/env ruby + + require 'ftools' + + class Task + TASKS = Hash.new + + attr_reader :prerequisites + + def initialize(task_name) + @name = task_name + @prerequisites = [] + @actions = [] + end + + def enhance(deps=nil, &block) + @prerequisites |= deps if deps + @actions << block if block_given? + self + end + + def name + @name.to_s + end + + def invoke + @prerequisites.each { |n| Task[n].invoke } + execute if needed? + end + + def execute + return if @triggered + @triggered = true + @actions.collect { |act| result = act.call(self) }.last + end + + def needed? + true + end + + def timestamp + Time.now + end + + class << self + def [](task_name) + TASKS[intern(task_name)] or fail "Don't know how to rake #{task_name}" + end + + def define_task(args, &block) + case args + when Hash + fail "Too Many Target Names: #{args.keys.join(' ')}" if args.size > 1 + fail "No Task Name Given" if args.size < 1 + task_name = args.keys[0] + deps = args[task_name] + else + task_name = args + deps = [] + end + deps = deps.collect {|d| intern(d) } + get(task_name).enhance(deps, &block) + end + + def get(task_name) + name = intern(task_name) + TASKS[name] ||= self.new(name) + end + + def intern(task_name) + (Symbol === task_name) ? task_name : task_name.intern + end + end + end + + class FileTask < Task + def needed? + return true unless File.exist?(name) + latest_prereq = @prerequisites.collect{|n| Task[n].timestamp}.max + return false if latest_prereq.nil? + timestamp < latest_prereq + end + + def timestamp + File.new(name.to_s).mtime + end + end + + def task(args, &block) + Task.define_task(args, &block) + end + + def file(args, &block) + FileTask.define_task(args, &block) + end + + def sys(cmd) + puts cmd + system(cmd) or fail "Command Failed: [#{cmd}]" + end + + def rake + begin + here = Dir.pwd + while ! File.exist?("Rakefile") + Dir.chdir("..") + fail "No Rakefile found" if Dir.pwd == here + here = Dir.pwd + end + puts "(in #{Dir.pwd})" + load "./Rakefile" + ARGV.push("default") if ARGV.size == 0 + ARGV.each { |task_name| Task[task_name].invoke } + rescue Exception => ex + puts "rake aborted ... #{ex.message}" + puts ex.backtrace.find {|str| str =~ /Rakefile/ } || "" + end + end + + if __FILE__ == $0 then + rake + end diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/doc/rake.1 b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/doc/rake.1 new file mode 100644 index 0000000..c6bfa25 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/doc/rake.1 @@ -0,0 +1,156 @@ +.Dd June 12, 2016 +.Dt RAKE 1 +.Os rake 11.2.2 +.Sh NAME +.Nm rake +.Nd make-like build utility for Ruby +.Sh SYNOPSIS +.Nm +.Op Fl f Ar rakefile +.Op Ar options +.Ar targets ... +.Sh DESCRIPTION +.Nm +is a +.Xr make 1 Ns -like +build utility for Ruby. +Tasks and dependencies are specified in standard Ruby syntax. +.Sh OPTIONS +.Bl -tag -width Ds +.It Fl m , Fl -multitask +Treat all tasks as multitasks. +.It Fl B , Fl -build-all +Build all prerequisites, including those which are up\-to\-date. +.It Fl j , Fl -jobs Ar num_jobs +Specifies the maximum number of tasks to execute in parallel (default is number of CPU cores + 4). +.El +.Ss Modules +.Bl -tag -width Ds +.It Fl I , Fl -libdir Ar libdir +Include +.Ar libdir +in the search path for required modules. +.It Fl r , Fl -require Ar module +Require +.Ar module +before executing +.Pa rakefile . +.El +.Ss Rakefile location +.Bl -tag -width Ds +.It Fl f , Fl -rakefile Ar filename +Use +.Ar filename +as the rakefile to search for. +.It Fl N , Fl -no-search , Fl -nosearch +Do not search parent directories for the Rakefile. +.It Fl G , Fl -no-system , Fl -nosystem +Use standard project Rakefile search paths, ignore system wide rakefiles. +.It Fl R , Fl -rakelib Ar rakelibdir , Fl -rakelibdir Ar rakelibdir +Auto-import any .rake files in +.Ar rakelibdir +(default is +.Sq rakelib ) +.It Fl g , Fl -system +Use system-wide (global) rakefiles (usually +.Pa ~/.rake/*.rake ) . +.El +.Ss Debugging +.Bl -tag -width Ds +.It Fl -backtrace Ns = Ns Ar out +Enable full backtrace. +.Ar out +can be +.Dv stderr +(default) or +.Dv stdout . +.It Fl t , Fl -trace Ns = Ns Ar out +Turn on invoke/execute tracing, enable full backtrace. +.Ar out +can be +.Dv stderr +(default) or +.Dv stdout . +.It Fl -suppress-backtrace Ar pattern +Suppress backtrace lines matching regexp +.Ar pattern . +Ignored if +.Fl -trace +is on. +.It Fl -rules +Trace the rules resolution. +.It Fl n , Fl -dry-run +Do a dry run without executing actions. +.It Fl T , Fl -tasks Op Ar pattern +Display the tasks (matching optional +.Ar pattern ) +with descriptions, then exit. +.It Fl D , Fl -describe Op Ar pattern +Describe the tasks (matching optional +.Ar pattern ) , +then exit. +.It Fl W , Fl -where Op Ar pattern +Describe the tasks (matching optional +.Ar pattern ) , +then exit. +.It Fl P , Fl -prereqs +Display the tasks and dependencies, then exit. +.It Fl e , Fl -execute Ar code +Execute some Ruby code and exit. +.It Fl p , Fl -execute-print Ar code +Execute some Ruby code, print the result, then exit. +.It Fl E , Fl -execute-continue Ar code +Execute some Ruby code, then continue with normal task processing. +.El +.Ss Information +.Bl -tag -width Ds +.It Fl v , Fl -verbose +Log message to standard output. +.It Fl q , Fl -quiet +Do not log messages to standard output. +.It Fl s , Fl -silent +Like +.Fl -quiet , +but also suppresses the +.Sq in directory +announcement. +.It Fl X , Fl -no-deprecation-warnings +Disable the deprecation warnings. +.It Fl -comments +Show commented tasks only +.It Fl A , Fl -all +Show all tasks, even uncommented ones (in combination with +.Fl T +or +.Fl D ) +.It Fl -job-stats Op Ar level +Display job statistics. +If +.Ar level +is +.Sq history , +displays a complete job list. +.It Fl V , Fl -version +Display the program version. +.It Fl h , Fl H , Fl -help +Display a help message. +.El +.Sh SEE ALSO +The complete documentation for +.Nm rake +has been installed at +.Pa /usr/share/doc/rake-doc/html/index.html . +It is also available online at +.Lk https://ruby.github.io/rake . +.Sh AUTHORS +.An -nosplit +.Nm +was written by +.An Jim Weirich Aq Mt jim@weirichhouse.org . +.Pp +This manual was created by +.An Caitlin Matos Aq Mt caitlin.matos@zoho.com +for the Debian project (but may be used by others). +It was inspired by the manual by +.An Jani Monoses Aq Mt jani@iv.ro +for the Ubuntu project. diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/doc/rakefile.rdoc b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/doc/rakefile.rdoc new file mode 100644 index 0000000..4014306 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/doc/rakefile.rdoc @@ -0,0 +1,622 @@ += Rakefile Format + +First of all, there is no special format for a Rakefile. A Rakefile +contains executable Ruby code. Anything legal in a ruby script is +allowed in a Rakefile. + +Now that we understand there is no special syntax in a Rakefile, there +are some conventions that are used in a Rakefile that are a little +unusual in a typical Ruby program. Since a Rakefile is tailored to +specifying tasks and actions, the idioms used in a Rakefile are +designed to support that. + +So, what goes into a Rakefile? + +== Tasks + +Tasks are the main unit of work in a Rakefile. Tasks have a name +(usually given as a symbol or a string), a list of prerequisites (more +symbols or strings) and a list of actions (given as a block). + +=== Simple Tasks + +A task is declared by using the +task+ method. +task+ takes a single +parameter that is the name of the task. + + task :name + +=== Tasks with Prerequisites + +Any prerequisites are given as a list (enclosed in square brackets) +following the name and an arrow (=>). + + task name: [:prereq1, :prereq2] + +*NOTE:* Although this syntax looks a little funky, it is legal +Ruby. We are constructing a hash where the key is :name and the value +for that key is the list of prerequisites. It is equivalent to the +following ... + + hash = Hash.new + hash[:name] = [:prereq1, :prereq2] + task(hash) + +You can also use strings for task names and prerequisites, rake doesn't care. +This is the same task definition: + + task 'name' => %w[prereq1 prereq2] + +As is this: + + task name: %w[prereq1 prereq2] + +We'll prefer this style for regular tasks with prerequisites throughout the +rest of the document. Using an array of strings for the prerequisites means +you will need to make fewer changes if you need to move tasks into namespaces +or perform other refactorings. + +=== Tasks with Actions + +Actions are defined by passing a block to the +task+ method. Any Ruby +code can be placed in the block. The block may reference the task +object via the block parameter. + + task name: [:prereq1, :prereq2] do |t| + # actions (may reference t) + end + +=== Multiple Definitions + +A task may be specified more than once. Each specification adds its +prerequisites and actions to the existing definition. This allows one +part of a rakefile to specify the actions and a different rakefile +(perhaps separately generated) to specify the dependencies. + +For example, the following is equivalent to the single task +specification given above. + + task :name + task name: :prereq1 + task name: %w[prereq2] + task :name do |t| + # actions + end + +== File Tasks + +Some tasks are designed to create a file from one or more other files. +Tasks that generate these files may be skipped if the file already +exists. File tasks are used to specify file creation tasks. + +File tasks are declared using the +file+ method (instead of the +task+ +method). In addition, file tasks are usually named with a string +rather than a symbol. + +The following file task creates a executable program (named +prog+) +given two object files named +a.o+ and +b.o+. The tasks +for creating +a.o+ and +b.o+ are not shown. + + file "prog" => ["a.o", "b.o"] do |t| + sh "cc -o #{t.name} #{t.prerequisites.join(' ')}" + end + +== Directory Tasks + +It is common to need to create directories upon demand. The ++directory+ convenience method is a short-hand for creating a FileTask +that creates the directory. For example, the following declaration +... + + directory "testdata/examples/doc" + +is equivalent to ... + + file "testdata" do |t| mkdir t.name end + file "testdata/examples" => ["testdata"] do |t| mkdir t.name end + file "testdata/examples/doc" => ["testdata/examples"] do |t| mkdir t.name end + +The +directory+ method does not accept prerequisites or actions, but +both prerequisites and actions can be added later. For example ... + + directory "testdata" + file "testdata" => ["otherdata"] + file "testdata" do + cp Dir["standard_data/*.data"], "testdata" + end + +== Tasks with Parallel Prerequisites + +Rake allows parallel execution of prerequisites using the following syntax: + + multitask copy_files: %w[copy_src copy_doc copy_bin] do + puts "All Copies Complete" + end + +In this example, +copy_files+ is a normal rake task. Its actions are +executed whenever all of its prerequisites are done. The big +difference is that the prerequisites (+copy_src+, +copy_bin+ and ++copy_doc+) are executed in parallel. Each of the prerequisites are +run in their own Ruby thread, possibly allowing faster overall runtime. + +=== Secondary Prerequisites + +If any of the primary prerequisites of a multitask have common secondary +prerequisites, all of the primary/parallel prerequisites will wait +until the common prerequisites have been run. + +For example, if the copy_xxx tasks have the +following prerequisites: + + task copy_src: :prep_for_copy + task copy_bin: :prep_for_copy + task copy_doc: :prep_for_copy + +Then the +prep_for_copy+ task is run before starting all the copies in +parallel. Once +prep_for_copy+ is complete, +copy_src+, +copy_bin+, +and +copy_doc+ are all run in parallel. Note that +prep_for_copy+ is +run only once, even though it is referenced in multiple threads. + +=== Thread Safety + +The Rake internal data structures are thread-safe with respect +to the multitask parallel execution, so there is no need for the user +to do extra synchronization for Rake's benefit. However, if there are +user data structures shared between the parallel prerequisites, the +user must do whatever is necessary to prevent race conditions. + +== Tasks with Arguments + +Prior to version 0.8.0, rake was only able to handle command line +arguments of the form NAME=VALUE that were passed into Rake via the +ENV hash. Many folks had asked for some kind of simple command line +arguments, perhaps using "--" to separate regular task names from +argument values on the command line. The problem is that there was no +easy way to associate positional arguments on the command line with +different tasks. Suppose both tasks :a and :b expect a command line +argument: does the first value go with :a? What if :b is run first? +Should it then get the first command line argument. + +Rake 0.8.0 solves this problem by explicitly passing values directly +to the tasks that need them. For example, if I had a release task +that required a version number, I could say: + + rake release[0.8.2] + +And the string "0.8.2" will be passed to the :release task. Multiple +arguments can be passed by separating them with a comma, for example: + + rake name[john,doe] + +Just a few words of caution. The rake task name and its arguments +need to be a single command line argument to rake. This generally +means no spaces. If spaces are needed, then the entire name + +argument string should be quoted. Something like this: + + rake "name[billy bob, smith]" + +(Quoting rules vary between operating systems and shells, so make sure +you consult the proper docs for your OS/shell). + +=== Tasks that Expect Parameters + +Parameters are only given to tasks that are setup to expect them. In +order to handle named parameters, the task declaration syntax for +tasks has been extended slightly. + +For example, a task that needs a first name and last name might be +declared as: + + task :name, [:first_name, :last_name] + +The first argument is still the name of the task (:name in this case). +The next two arguments are the names of the parameters expected by +:name in an array (:first_name and :last_name in the example). + +To access the values of the parameters, the block defining the task +behaviour can now accept a second parameter: + + task :name, [:first_name, :last_name] do |t, args| + puts "First name is #{args.first_name}" + puts "Last name is #{args.last_name}" + end + +The first argument of the block "t" is always bound to the current +task object. The second argument "args" is an open-struct like object +that allows access to the task arguments. Extra command line +arguments to a task are ignored. + +If you wish to specify default values for the arguments, you can use +the with_defaults method in the task body. Here is the above example +where we specify default values for the first and last names: + + task :name, [:first_name, :last_name] do |t, args| + args.with_defaults(:first_name => "John", :last_name => "Dough") + puts "First name is #{args.first_name}" + puts "Last name is #{args.last_name}" + end + +=== Tasks that Expect Parameters and Have Prerequisites + +Tasks that use parameters have a slightly different format for +prerequisites. Use the arrow notation to indicate the prerequisites +for tasks with arguments. For example: + + task :name, [:first_name, :last_name] => [:pre_name] do |t, args| + args.with_defaults(:first_name => "John", :last_name => "Dough") + puts "First name is #{args.first_name}" + puts "Last name is #{args.last_name}" + end + +=== Tasks that take Variable-length Parameters + +Tasks that need to handle a list of values as a parameter can use the +extras method of the args variable. This allows for tasks that can +loop over a variable number of values, and its compatible with named +parameters as well: + + task :email, [:message] do |t, args| + mail = Mail.new(args.message) + recipients = args.extras + recipients.each do |target| + mail.send_to(target) + end + end + +There is also the convenience method to_a that returns all parameters +in the sequential order they were given, including those associated +with named parameters. + +=== Deprecated Task Parameters Format + +There is an older format for declaring task parameters that omitted +the task argument array and used the :needs keyword to introduce the +dependencies. That format is still supported for compatibility, but +is not recommended for use. The older format may be dropped in future +versions of rake. + +== Accessing Task Programmatically + +Sometimes it is useful to manipulate tasks programmatically in a +Rakefile. To find a task object use Rake::Task.[]. + +=== Programmatic Task Example + +For example, the following Rakefile defines two tasks. The :doit task +simply prints a simple "DONE" message. The :dont class will lookup +the doit class and remove (clear) all of its prerequisites and +actions. + + task :doit do + puts "DONE" + end + + task :dont do + Rake::Task[:doit].clear + end + +Running this example: + + $ rake doit + (in /Users/jim/working/git/rake/x) + DONE + $ rake dont doit + (in /Users/jim/working/git/rake/x) + $ + +The ability to programmatically manipulate tasks gives rake very +powerful meta-programming capabilities w.r.t. task execution, but +should be used with caution. + +== Rules + +When a file is named as a prerequisite, but does not have a file task +defined for it, Rake will attempt to synthesize a task by looking at a +list of rules supplied in the Rakefile. + +Suppose we were trying to invoke task "mycode.o", but no task is +defined for it. But the rakefile has a rule that look like this ... + + rule '.o' => ['.c'] do |t| + sh "cc #{t.source} -c -o #{t.name}" + end + +This rule will synthesize any task that ends in ".o". It has a +prerequisite a source file with an extension of ".c" must exist. If +Rake is able to find a file named "mycode.c", it will automatically +create a task that builds "mycode.o" from "mycode.c". + +If the file "mycode.c" does not exist, rake will attempt +to recursively synthesize a rule for it. + +When a task is synthesized from a rule, the +source+ attribute of the +task is set to the matching source file. This allows us to write +rules with actions that reference the source file. + +=== Advanced Rules + +Any regular expression may be used as the rule pattern. Additionally, +a proc may be used to calculate the name of the source file. This +allows for complex patterns and sources. + +The following rule is equivalent to the example above. + + rule( /\.o$/ => [ + proc {|task_name| task_name.sub(/\.[^.]+$/, '.c') } + ]) do |t| + sh "cc #{t.source} -c -o #{t.name}" + end + +*NOTE:* Because of a _quirk_ in Ruby syntax, parenthesis are +required on *rule* when the first argument is a regular expression. + +The following rule might be used for Java files ... + + rule '.class' => [ + proc { |tn| tn.sub(/\.class$/, '.java').sub(/^classes\//, 'src/') } + ] do |t| + java_compile(t.source, t.name) + end + +*NOTE:* +java_compile+ is a hypothetical method that invokes the +java compiler. + +== Importing Dependencies + +Any ruby file (including other rakefiles) can be included with a +standard Ruby +require+ command. The rules and declarations in the +required file are just added to the definitions already accumulated. + +Because the files are loaded _before_ the rake targets are evaluated, +the loaded files must be "ready to go" when the rake command is +invoked. This makes generated dependency files difficult to use. By +the time rake gets around to updating the dependencies file, it is too +late to load it. + +The +import+ command addresses this by specifying a file to be loaded +_after_ the main rakefile is loaded, but _before_ any targets on the +command line are invoked. In addition, if the file name matches an +explicit task, that task is invoked before loading the file. This +allows dependency files to be generated and used in a single rake +command invocation. + +Example: + + require 'rake/loaders/makefile' + + file ".depends.mf" => [SRC_LIST] do |t| + sh "makedepend -f- -- #{CFLAGS} -- #{t.prerequisites} > #{t.name}" + end + + import ".depends.mf" + +If ".depends" does not exist, or is out of date w.r.t. the source +files, a new ".depends" file is generated using +makedepend+ before +loading. + +== Comments + +Standard Ruby comments (beginning with "#") can be used anywhere it is +legal in Ruby source code, including comments for tasks and rules. +However, if you wish a task to be described using the "-T" switch, +then you need to use the +desc+ command to describe the task. + +Example: + + desc "Create a distribution package" + task package: %w[ ... ] do ... end + +The "-T" switch (or "--tasks" if you like to spell things out) will +display a list of tasks that have a description. If you use +desc+ to +describe your major tasks, you have a semi-automatic way of generating +a summary of your Rake file. + + $ rake -T + (in /home/.../rake) + rake clean # Remove any temporary products. + rake clobber # Remove any generated file. + rake clobber_rdoc # Remove rdoc products + rake contrib_test # Run tests for contrib_test + rake default # Default Task + rake install # Install the application + rake lines # Count lines in the main rake file + rake rdoc # Build the rdoc HTML Files + rake rerdoc # Force a rebuild of the RDOC files + rake test # Run tests + rake testall # Run all test targets + +Only tasks with descriptions will be displayed with the "-T" switch. +Use "-P" (or "--prereqs") to get a list of all tasks and their +prerequisites. + +== Namespaces + +As projects grow (and along with it, the number of tasks), it is +common for task names to begin to clash. For example, if you might +have a main program and a set of sample programs built by a single +Rakefile. By placing the tasks related to the main program in one +namespace, and the tasks for building the sample programs in a +different namespace, the task names will not interfere with each other. + +For example: + + namespace "main" do + task :build do + # Build the main program + end + end + + namespace "samples" do + task :build do + # Build the sample programs + end + end + + task build: %w[main:build samples:build] + +Referencing a task in a separate namespace can be achieved by +prefixing the task name with the namespace and a colon +(e.g. "main:build" refers to the :build task in the +main+ namespace). +Nested namespaces are supported. + +Note that the name given in the +task+ command is always the unadorned +task name without any namespace prefixes. The +task+ command always +defines a task in the current namespace. + +=== FileTasks + +File task names are not scoped by the namespace command. Since the +name of a file task is the name of an actual file in the file system, +it makes little sense to include file task names in name space. +Directory tasks (created by the +directory+ command) are a type of +file task and are also not affected by namespaces. + +=== Name Resolution + +When looking up a task name, rake will start with the current +namespace and attempt to find the name there. If it fails to find a +name in the current namespace, it will search the parent namespaces +until a match is found (or an error occurs if there is no match). + +The "rake" namespace is a special implicit namespace that refers to +the toplevel names. + +If a task name begins with a "^" character, the name resolution will +start in the parent namespace. Multiple "^" characters are allowed. + +Here is an example file with multiple :run tasks and how various names +resolve in different locations. + + task :run + + namespace "one" do + task :run + + namespace "two" do + task :run + + # :run => "one:two:run" + # "two:run" => "one:two:run" + # "one:two:run" => "one:two:run" + # "one:run" => "one:run" + # "^run" => "one:run" + # "^^run" => "rake:run" (the top level task) + # "rake:run" => "rake:run" (the top level task) + end + + # :run => "one:run" + # "two:run" => "one:two:run" + # "^run" => "rake:run" + end + + # :run => "rake:run" + # "one:run" => "one:run" + # "one:two:run" => "one:two:run" + +== FileLists + +FileLists are the way Rake manages lists of files. You can treat a +FileList as an array of strings for the most part, but FileLists +support some additional operations. + +=== Creating a FileList + +Creating a file list is easy. Just give it the list of file names: + + fl = FileList['file1.rb', file2.rb'] + +Or give it a glob pattern: + + fl = FileList['*.rb'] + +== Odds and Ends + +=== do/end versus { } + +Blocks may be specified with either a +do+/+end+ pair, or with curly +braces in Ruby. We _strongly_ recommend using +do+/+end+ to specify the +actions for tasks and rules. Because the rakefile idiom tends to +leave off parentheses on the task/file/rule methods, unusual +ambiguities can arise when using curly braces. + +For example, suppose that the method +object_files+ returns a list of +object files in a project. Now we use +object_files+ as the +prerequisites in a rule specified with actions in curly braces. + + # DON'T DO THIS! + file "prog" => object_files { + # Actions are expected here (but it doesn't work)! + } + +Because curly braces have a higher precedence than +do+/+end+, the +block is associated with the +object_files+ method rather than the ++file+ method. + +This is the proper way to specify the task ... + + # THIS IS FINE + file "prog" => object_files do + # Actions go here + end + +== Rakefile Path + +When issuing the +rake+ command in a terminal, Rake will look +for a Rakefile in the current directory. If a Rakefile is not found, +it will search parent directories until one is found. + +For example, if a Rakefile resides in the +project/+ directory, +moving deeper into the project's directory tree will not have an adverse +effect on rake tasks: + + $ pwd + /home/user/project + + $ cd lib/foo/bar + $ pwd + /home/user/project/lib/foo/bar + + $ rake run_pwd + /home/user/project + +As far as rake is concerned, all tasks are run from the directory in +which the Rakefile resides. + +=== Multiple Rake Files + +Not all tasks need to be included in a single Rakefile. Additional +rake files (with the file extension "+.rake+") may be placed in ++rakelib+ directory located at the top level of a project (i.e. +the same directory that contains the main +Rakefile+). + +Also, rails projects may include additional rake files in the ++lib/tasks+ directory. + +=== Clean and Clobber Tasks + +Through require 'rake/clean' Rake provides +clean+ and +clobber+ +tasks: + ++clean+ :: + Clean up the project by deleting scratch files and backup files. Add files + to the +CLEAN+ FileList to have the +clean+ target handle them. + ++clobber+ :: + Clobber all generated and non-source files in a project. The task depends + on +clean+, so all the +CLEAN+ files will be deleted as well as files in the + +CLOBBER+ FileList. The intent of this task is to return a project to its + pristine, just unpacked state. + +You can add file names or glob patterns to both the +CLEAN+ and +CLOBBER+ +lists. + +=== Phony Task + +The phony task can be used as a dependency to allow file-based tasks to use +non-file-based-tasks as prerequisites without forcing them to rebuild. You +can require 'rake/phony' to add the +phony+ task. + +---- + +== See + +* README.rdoc -- Main documentation for Rake. diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/doc/rational.rdoc b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/doc/rational.rdoc new file mode 100644 index 0000000..0e1c338 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/doc/rational.rdoc @@ -0,0 +1,151 @@ += Why rake? + +Ok, let me state from the beginning that I never intended to write this +code. I'm not convinced it is useful, and I'm not convinced anyone +would even be interested in it. All I can say is that Why's onion truck +must by been passing through the Ohio valley. + +What am I talking about? ... A Ruby version of Make. + +See, I can sense you cringing already, and I agree. The world certainly +doesn't need yet another reworking of the "make" program. I mean, we +already have "ant". Isn't that enough? + +It started yesterday. I was helping a coworker fix a problem in one of +the Makefiles we use in our project. Not a particularly tough problem, +but during the course of the conversation I began lamenting some of the +shortcomings of make. In particular, in one of my makefiles I wanted to +determine the name of a file dynamically and had to resort to some +simple scripting (in Ruby) to make it work. "Wouldn't it be nice if you +could just use Ruby inside a Makefile" I said. + +My coworker (a recent convert to Ruby) agreed, but wondered what it +would look like. So I sketched the following on the whiteboard... + + "What if you could specify the make tasks in Ruby, like this ..." + + task "build" do + java_compile(...args, etc ...) + end + + "The task function would register "build" as a target to be made, + and the block would be the action executed whenever the build + system determined that it was time to do the build target." + +We agreed that would be cool, but writing make from scratch would be WAY +too much work. And that was the end of that! + +... Except I couldn't get the thought out of my head. What exactly +would be needed to make the about syntax work as a make file? Hmmm, you +would need to register the tasks, you need some way of specifying +dependencies between tasks, and some way of kicking off the process. +Hey! What if we did ... and fifteen minutes later I had a working +prototype of Ruby make, complete with dependencies and actions. + +I showed the code to my coworker and we had a good laugh. It was just +about a page worth of code that reproduced an amazing amount of the +functionality of make. We were both truly stunned with the power of +Ruby. + +But it didn't do everything make did. In particular, it didn't have +timestamp based file dependencies (where a file is rebuilt if any of its +prerequisite files have a later timestamp). Obviously THAT would be a +pain to add and so Ruby Make would remain an interesting experiment. + +... Except as I walked back to my desk, I started thinking about what +file based dependencies would really need. Rats! I was hooked again, +and by adding a new class and two new methods, file/timestamp +dependencies were implemented. + +Ok, now I was really hooked. Last night (during CSI!) I massaged the +code and cleaned it up a bit. The result is a bare-bones replacement +for make in exactly 100 lines of code. + +For the curious, you can see it at ... +* doc/proto_rake.rdoc + +Oh, about the name. When I wrote the example Ruby Make task on my +whiteboard, my coworker exclaimed "Oh! I have the perfect name: Rake ... +Get it? Ruby-Make. Rake!" He said he envisioned the tasks as leaves +and Rake would clean them up ... or something like that. Anyways, the +name stuck. + +Some quick examples ... + +A simple task to delete backup files ... + + task :clean do + Dir['*~'].each {|fn| rm fn rescue nil} + end + +Note that task names are symbols (they are slightly easier to type +than quoted strings ... but you may use quoted string if you would +rather). Rake makes the methods of the FileUtils module directly +available, so we take advantage of the rm command. Also note +the use of "rescue nil" to trap and ignore errors in the rm +command. + +To run it, just type "rake clean". Rake will automatically find a +Rakefile in the current directory (or above!) and will invoke the +targets named on the command line. If there are no targets explicitly +named, rake will invoke the task "default". + +Here's another task with dependencies ... + + task :clobber => [:clean] do + rm_r "tempdir" + end + +Task :clobber depends upon task :clean, so :clean will be run before +:clobber is executed. + +Files are specified by using the "file" command. It is similar to the +task command, except that the task name represents a file, and the task +will be run only if the file doesn't exist, or if its modification time +is earlier than any of its prerequisites. + +Here is a file based dependency that will compile "hello.cc" to +"hello.o". + + file "hello.cc" + file "hello.o" => ["hello.cc"] do |t| + srcfile = t.name.sub(/\.o$/, ".cc") + sh %{g++ #{srcfile} -c -o #{t.name}} + end + +I normally specify file tasks with string (rather than symbols). Some +file names can't be represented by symbols. Plus it makes the +distinction between them more clear to the casual reader. + +Currently writing a task for each and every file in the project would be +tedious at best. I envision a set of libraries to make this job +easier. For instance, perhaps something like this ... + + require 'rake/ctools' + Dir['*.c'].each do |fn| + c_source_file(fn) + end + +where "c_source_file" will create all the tasks need to compile all the +C source files in a directory. Any number of useful libraries could be +created for rake. + +That's it. There's no documentation (other than whats in this +message). Does this sound interesting to anyone? If so, I'll continue +to clean it up and write it up and publish it on RAA. Otherwise, I'll +leave it as an interesting exercise and a tribute to the power of Ruby. + +Why /might/ rake be interesting to Ruby programmers. I don't know, +perhaps ... + +* No weird make syntax (only weird Ruby syntax :-) +* No need to edit or read XML (a la ant) +* Platform independent build scripts. +* Will run anywhere Ruby exists, so no need to have "make" installed. + If you stay away from the "sys" command and use things like + 'ftools', you can have a perfectly platform independent + build script. Also rake is only 100 lines of code, so it can + easily be packaged along with the rest of your code. + +So ... Sorry for the long rambling message. Like I said, I never +intended to write this code at all. diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/exe/rake b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/exe/rake new file mode 100755 index 0000000..a00975f --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/exe/rake @@ -0,0 +1,27 @@ +#!/usr/bin/env ruby + +#-- +# Copyright (c) 2003, 2004, 2005, 2006, 2007 Jim Weirich +# +# 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. +#++ + +require "rake" + +Rake.application.run diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake.rb b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake.rb new file mode 100644 index 0000000..2006fba --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: true +#-- +# Copyright 2003-2010 by Jim Weirich (jim.weirich@gmail.com) +# +# 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. +#++ + +module Rake; end + +require_relative "rake/version" + +require "rbconfig" +require "fileutils" +require "singleton" +require "monitor" +require "optparse" + +require_relative "rake/ext/string" + +require_relative "rake/win32" + +require_relative "rake/linked_list" +require_relative "rake/cpu_counter" +require_relative "rake/scope" +require_relative "rake/task_argument_error" +require_relative "rake/rule_recursion_overflow_error" +require_relative "rake/rake_module" +require_relative "rake/trace_output" +require_relative "rake/pseudo_status" +require_relative "rake/task_arguments" +require_relative "rake/invocation_chain" +require_relative "rake/task" +require_relative "rake/file_task" +require_relative "rake/file_creation_task" +require_relative "rake/multi_task" +require_relative "rake/dsl_definition" +require_relative "rake/file_utils_ext" +require_relative "rake/file_list" +require_relative "rake/default_loader" +require_relative "rake/early_time" +require_relative "rake/late_time" +require_relative "rake/name_space" +require_relative "rake/task_manager" +require_relative "rake/application" +require_relative "rake/backtrace" + +# :stopdoc: +# +# Some top level Constants. + +FileList = Rake::FileList +RakeFileUtils = Rake::FileUtilsExt diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/application.rb b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/application.rb new file mode 100644 index 0000000..87ae47b --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/application.rb @@ -0,0 +1,854 @@ +# frozen_string_literal: true +require "optparse" + +require_relative "task_manager" +require_relative "file_list" +require_relative "thread_pool" +require_relative "thread_history_display" +require_relative "trace_output" +require_relative "win32" + +module Rake + + CommandLineOptionError = Class.new(StandardError) + + ## + # Rake main application object. When invoking +rake+ from the + # command line, a Rake::Application object is created and run. + + class Application + include TaskManager + include TraceOutput + + # The name of the application (typically 'rake') + attr_reader :name + + # The original directory where rake was invoked. + attr_reader :original_dir + + # Name of the actual rakefile used. + attr_reader :rakefile + + # Number of columns on the terminal + attr_accessor :terminal_columns + + # List of the top level task names (task names from the command line). + attr_reader :top_level_tasks + + # Override the detected TTY output state (mostly for testing) + attr_writer :tty_output + + DEFAULT_RAKEFILES = [ + "rakefile", + "Rakefile", + "rakefile.rb", + "Rakefile.rb" + ].freeze + + # Initialize a Rake::Application object. + def initialize + super + @name = "rake" + @rakefiles = DEFAULT_RAKEFILES.dup + @rakefile = nil + @pending_imports = [] + @imported = [] + @loaders = {} + @default_loader = Rake::DefaultLoader.new + @original_dir = Dir.pwd + @top_level_tasks = [] + add_loader("rb", DefaultLoader.new) + add_loader("rf", DefaultLoader.new) + add_loader("rake", DefaultLoader.new) + @tty_output = STDOUT.tty? + @terminal_columns = ENV["RAKE_COLUMNS"].to_i + + set_default_options + end + + # Run the Rake application. The run method performs the following + # three steps: + # + # * Initialize the command line options (+init+). + # * Define the tasks (+load_rakefile+). + # * Run the top level tasks (+top_level+). + # + # If you wish to build a custom rake command, you should call + # +init+ on your application. Then define any tasks. Finally, + # call +top_level+ to run your top level tasks. + def run(argv = ARGV) + standard_exception_handling do + init "rake", argv + load_rakefile + top_level + end + end + + # Initialize the command line parameters and app name. + def init(app_name="rake", argv = ARGV) + standard_exception_handling do + @name = app_name + begin + args = handle_options argv + rescue ArgumentError + # Backward compatibility for capistrano + args = handle_options + end + load_debug_at_stop_feature + collect_command_line_tasks(args) + end + end + + def load_debug_at_stop_feature + return unless ENV["RAKE_DEBUG"] + require "debug/session" + DEBUGGER__::start no_sigint_hook: true, nonstop: true + Rake::Task.prepend Module.new { + def execute(*) + exception = DEBUGGER__::SESSION.capture_exception_frames(/(exe|bin|lib)\/rake/) do + super + end + + if exception + STDERR.puts exception.message + DEBUGGER__::SESSION.enter_postmortem_session exception + raise exception + end + end + } + rescue LoadError + end + private :load_debug_at_stop_feature + + # Find the rakefile and then load it and any pending imports. + def load_rakefile + standard_exception_handling do + raw_load_rakefile + end + end + + # Run the top level tasks of a Rake application. + def top_level + run_with_threads do + if options.show_tasks + display_tasks_and_comments + elsif options.show_prereqs + display_prerequisites + else + top_level_tasks.each { |task_name| invoke_task(task_name) } + end + end + end + + # Run the given block with the thread startup and shutdown. + def run_with_threads + thread_pool.gather_history if options.job_stats == :history + + yield + + thread_pool.join if defined?(@thread_pool) + if options.job_stats + stats = thread_pool.statistics + puts "Maximum active threads: #{stats[:max_active_threads]} + main" + puts "Total threads in play: #{stats[:total_threads_in_play]} + main" + end + ThreadHistoryDisplay.new(thread_pool.history).show if + options.job_stats == :history + end + + # Add a loader to handle imported files ending in the extension + # +ext+. + def add_loader(ext, loader) + ext = ".#{ext}" unless ext =~ /^\./ + @loaders[ext] = loader + end + + # Application options from the command line + def options + @options ||= Struct.new( + :always_multitask, :backtrace, :build_all, :dryrun, + :ignore_deprecate, :ignore_system, :job_stats, :load_system, + :nosearch, :rakelib, :show_all_tasks, :show_prereqs, + :show_task_pattern, :show_tasks, :silent, :suppress_backtrace_pattern, + :thread_pool_size, :trace, :trace_output, :trace_rules + ).new + end + + # Return the thread pool used for multithreaded processing. + def thread_pool # :nodoc: + @thread_pool ||= ThreadPool.new(options.thread_pool_size || Rake.suggested_thread_count-1) + end + + # internal ---------------------------------------------------------------- + + # Invokes a task with arguments that are extracted from +task_string+ + def invoke_task(task_string) # :nodoc: + name, args = parse_task_string(task_string) + t = self[name] + t.invoke(*args) + end + + def parse_task_string(string) # :nodoc: + /^([^\[]+)(?:\[(.*)\])$/ =~ string.to_s + + name = $1 + remaining_args = $2 + + return string, [] unless name + return name, [] if remaining_args.empty? + + args = [] + + begin + /\s*((?:[^\\,]|\\.)*?)\s*(?:,\s*(.*))?$/ =~ remaining_args + + remaining_args = $2 + args << $1.gsub(/\\(.)/, '\1') + end while remaining_args + + return name, args + end + + # Provide standard exception handling for the given block. + def standard_exception_handling # :nodoc: + yield + rescue SystemExit + # Exit silently with current status + raise + rescue OptionParser::InvalidOption => ex + $stderr.puts ex.message + exit(false) + rescue Exception => ex + # Exit with error message + display_error_message(ex) + exit_because_of_exception(ex) + end + + # Exit the program because of an unhandled exception. + # (may be overridden by subclasses) + def exit_because_of_exception(ex) # :nodoc: + exit(false) + end + + # Display the error message that caused the exception. + def display_error_message(ex) # :nodoc: + trace "#{name} aborted!" + display_exception_details(ex) + trace "Tasks: #{ex.chain}" if has_chain?(ex) + trace "(See full trace by running task with --trace)" unless + options.backtrace + end + + def display_exception_details(ex) # :nodoc: + display_exception_details_seen << ex + + display_exception_message_details(ex) + display_exception_backtrace(ex) if ex.backtrace + display_cause_details(ex.cause) if has_cause?(ex) + end + + def display_cause_details(ex) # :nodoc: + return if display_exception_details_seen.include? ex + + trace "\nCaused by:" + display_exception_details(ex) + end + + def display_exception_details_seen # :nodoc: + Thread.current[:rake_display_exception_details_seen] ||= [] + end + + def has_cause?(ex) # :nodoc: + ex.respond_to?(:cause) && ex.cause + end + + def display_exception_message_details(ex) # :nodoc: + if ex.instance_of?(RuntimeError) + trace ex.message + elsif ex.respond_to?(:detailed_message) + trace "#{ex.class.name}: #{ex.detailed_message(highlight: false)}" + else + trace "#{ex.class.name}: #{ex.message}" + end + end + + def display_exception_backtrace(ex) # :nodoc: + if options.backtrace + trace ex.backtrace.join("\n") + else + trace Backtrace.collapse(ex.backtrace).join("\n") + end + end + + # Warn about deprecated usage. + # + # Example: + # Rake.application.deprecate("import", "Rake.import", caller.first) + # + def deprecate(old_usage, new_usage, call_site) # :nodoc: + unless options.ignore_deprecate + $stderr.puts "WARNING: '#{old_usage}' is deprecated. " + + "Please use '#{new_usage}' instead.\n" + + " at #{call_site}" + end + end + + # Does the exception have a task invocation chain? + def has_chain?(exception) # :nodoc: + exception.respond_to?(:chain) && exception.chain + end + private :has_chain? + + # True if one of the files in RAKEFILES is in the current directory. + # If a match is found, it is copied into @rakefile. + def have_rakefile # :nodoc: + @rakefiles.each do |fn| + if File.exist?(fn) + others = FileList.glob(fn, File::FNM_CASEFOLD) + return others.size == 1 ? others.first : fn + elsif fn == "" + return fn + end + end + return nil + end + + # True if we are outputting to TTY, false otherwise + def tty_output? # :nodoc: + @tty_output + end + + # We will truncate output if we are outputting to a TTY or if we've been + # given an explicit column width to honor + def truncate_output? # :nodoc: + tty_output? || @terminal_columns.nonzero? + end + + # Display the tasks and comments. + def display_tasks_and_comments # :nodoc: + displayable_tasks = tasks.select { |t| + (options.show_all_tasks || t.comment) && + t.name =~ options.show_task_pattern + } + case options.show_tasks + when :tasks + width = displayable_tasks.map { |t| t.name_with_args.length }.max || 10 + if truncate_output? + max_column = terminal_width - name.size - width - 7 + else + max_column = nil + end + + displayable_tasks.each do |t| + printf("#{name} %-#{width}s # %s\n", + t.name_with_args, + max_column ? truncate(t.comment, max_column) : t.comment) + end + when :describe + displayable_tasks.each do |t| + puts "#{name} #{t.name_with_args}" + comment = t.full_comment || "" + comment.split("\n").each do |line| + puts " #{line}" + end + puts + end + when :lines + displayable_tasks.each do |t| + t.locations.each do |loc| + printf "#{name} %-30s %s\n", t.name_with_args, loc + end + end + else + fail "Unknown show task mode: '#{options.show_tasks}'" + end + end + + def terminal_width # :nodoc: + if @terminal_columns.nonzero? + result = @terminal_columns + else + result = unix? ? dynamic_width : 80 + end + (result < 10) ? 80 : result + rescue + 80 + end + + # Calculate the dynamic width of the + def dynamic_width # :nodoc: + @dynamic_width ||= (dynamic_width_stty.nonzero? || dynamic_width_tput) + end + + def dynamic_width_stty # :nodoc: + %x{stty size 2>/dev/null}.split[1].to_i + end + + def dynamic_width_tput # :nodoc: + %x{tput cols 2>/dev/null}.to_i + end + + def unix? # :nodoc: + RbConfig::CONFIG["host_os"] =~ + /(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris|irix|hpux)/i + end + + def windows? # :nodoc: + Win32.windows? + end + + def truncate(string, width) # :nodoc: + if string.nil? + "" + elsif string.length <= width + string + else + (string[0, width - 3] || "") + "..." + end + end + + # Display the tasks and prerequisites + def display_prerequisites # :nodoc: + tasks.each do |t| + puts "#{name} #{t.name}" + t.prerequisites.each { |pre| puts " #{pre}" } + end + end + + def trace(*strings) # :nodoc: + options.trace_output ||= $stderr + trace_on(options.trace_output, *strings) + end + + def sort_options(options) # :nodoc: + options.sort_by { |opt| + opt.select { |o| o.is_a?(String) && o =~ /^-/ }.map(&:downcase).sort.reverse + } + end + private :sort_options + + # A list of all the standard options used in rake, suitable for + # passing to OptionParser. + def standard_rake_options # :nodoc: + sort_options( + [ + ["--all", "-A", + "Show all tasks, even uncommented ones (in combination with -T or -D)", + lambda { |value| + options.show_all_tasks = value + } + ], + ["--backtrace=[OUT]", + "Enable full backtrace. OUT can be stderr (default) or stdout.", + lambda { |value| + options.backtrace = true + select_trace_output(options, "backtrace", value) + } + ], + ["--build-all", "-B", + "Build all prerequisites, including those which are up-to-date.", + lambda { |value| + options.build_all = true + } + ], + ["--comments", + "Show commented tasks only", + lambda { |value| + options.show_all_tasks = !value + } + ], + ["--describe", "-D [PATTERN]", + "Describe the tasks (matching optional PATTERN), then exit.", + lambda { |value| + select_tasks_to_show(options, :describe, value) + } + ], + ["--directory", "-C [DIRECTORY]", + "Change to DIRECTORY before doing anything.", + lambda { |value| + Dir.chdir value + @original_dir = Dir.pwd + } + ], + ["--dry-run", "-n", + "Do a dry run without executing actions.", + lambda { |value| + Rake.verbose(true) + Rake.nowrite(true) + options.dryrun = true + options.trace = true + } + ], + ["--execute", "-e CODE", + "Execute some Ruby code and exit.", + lambda { |value| + eval(value) + exit + } + ], + ["--execute-print", "-p CODE", + "Execute some Ruby code, print the result, then exit.", + lambda { |value| + puts eval(value) + exit + } + ], + ["--execute-continue", "-E CODE", + "Execute some Ruby code, " + + "then continue with normal task processing.", + lambda { |value| eval(value) } + ], + ["--jobs", "-j [NUMBER]", + "Specifies the maximum number of tasks to execute in parallel. " + + "(default is number of CPU cores + 4)", + lambda { |value| + if value.nil? || value == "" + value = Float::INFINITY + elsif value =~ /^\d+$/ + value = value.to_i + else + value = Rake.suggested_thread_count + end + value = 1 if value < 1 + options.thread_pool_size = value - 1 + } + ], + ["--job-stats [LEVEL]", + "Display job statistics. " + + "LEVEL=history displays a complete job list", + lambda { |value| + if value =~ /^history/i + options.job_stats = :history + else + options.job_stats = true + end + } + ], + ["--libdir", "-I LIBDIR", + "Include LIBDIR in the search path for required modules.", + lambda { |value| $:.push(value) } + ], + ["--multitask", "-m", + "Treat all tasks as multitasks.", + lambda { |value| options.always_multitask = true } + ], + ["--no-search", "--nosearch", + "-N", "Do not search parent directories for the Rakefile.", + lambda { |value| options.nosearch = true } + ], + ["--prereqs", "-P", + "Display the tasks and dependencies, then exit.", + lambda { |value| options.show_prereqs = true } + ], + ["--quiet", "-q", + "Do not log messages to standard output.", + lambda { |value| Rake.verbose(false) } + ], + ["--rakefile", "-f [FILENAME]", + "Use FILENAME as the rakefile to search for.", + lambda { |value| + value ||= "" + @rakefiles.clear + @rakefiles << value + } + ], + ["--rakelibdir", "--rakelib", "-R RAKELIBDIR", + "Auto-import any .rake files in RAKELIBDIR. " + + "(default is 'rakelib')", + lambda { |value| + options.rakelib = value.split(File::PATH_SEPARATOR) + } + ], + ["--require", "-r MODULE", + "Require MODULE before executing rakefile.", + lambda { |value| + begin + require value + rescue LoadError => ex + begin + rake_require value + rescue LoadError + raise ex + end + end + } + ], + ["--rules", + "Trace the rules resolution.", + lambda { |value| options.trace_rules = true } + ], + ["--silent", "-s", + "Like --quiet, but also suppresses the " + + "'in directory' announcement.", + lambda { |value| + Rake.verbose(false) + options.silent = true + } + ], + ["--suppress-backtrace PATTERN", + "Suppress backtrace lines matching regexp PATTERN. " + + "Ignored if --trace is on.", + lambda { |value| + options.suppress_backtrace_pattern = Regexp.new(value) + } + ], + ["--system", "-g", + "Using system wide (global) rakefiles " + + "(usually '~/.rake/*.rake').", + lambda { |value| options.load_system = true } + ], + ["--no-system", "--nosystem", "-G", + "Use standard project Rakefile search paths, " + + "ignore system wide rakefiles.", + lambda { |value| options.ignore_system = true } + ], + ["--tasks", "-T [PATTERN]", + "Display the tasks (matching optional PATTERN) " + + "with descriptions, then exit. " + + "-AT combination displays all the tasks, including those without descriptions.", + lambda { |value| + select_tasks_to_show(options, :tasks, value) + } + ], + ["--trace=[OUT]", "-t", + "Turn on invoke/execute tracing, enable full backtrace. " + + "OUT can be stderr (default) or stdout.", + lambda { |value| + options.trace = true + options.backtrace = true + select_trace_output(options, "trace", value) + Rake.verbose(true) + } + ], + ["--verbose", "-v", + "Log message to standard output.", + lambda { |value| Rake.verbose(true) } + ], + ["--version", "-V", + "Display the program version.", + lambda { |value| + puts "rake, version #{Rake::VERSION}" + exit + } + ], + ["--where", "-W [PATTERN]", + "Describe the tasks (matching optional PATTERN), then exit.", + lambda { |value| + select_tasks_to_show(options, :lines, value) + options.show_all_tasks = true + } + ], + ["--no-deprecation-warnings", "-X", + "Disable the deprecation warnings.", + lambda { |value| + options.ignore_deprecate = true + } + ], + ]) + end + + def select_tasks_to_show(options, show_tasks, value) # :nodoc: + options.show_tasks = show_tasks + options.show_task_pattern = Regexp.new(value || "") + Rake::TaskManager.record_task_metadata = true + end + private :select_tasks_to_show + + def select_trace_output(options, trace_option, value) # :nodoc: + value = value.strip unless value.nil? + case value + when "stdout" + options.trace_output = $stdout + when "stderr", nil + options.trace_output = $stderr + else + fail CommandLineOptionError, + "Unrecognized --#{trace_option} option '#{value}'" + end + end + private :select_trace_output + + # Read and handle the command line options. Returns the command line + # arguments that we didn't understand, which should (in theory) be just + # task names and env vars. + def handle_options(argv) # :nodoc: + set_default_options + + OptionParser.new do |opts| + opts.banner = "#{Rake.application.name} [-f rakefile] {options} targets..." + opts.separator "" + opts.separator "Options are ..." + + opts.on_tail("-h", "--help", "-H", "Display this help message.") do + puts opts + exit + end + + standard_rake_options.each { |args| opts.on(*args) } + opts.environment("RAKEOPT") + end.parse(argv) + end + + # Similar to the regular Ruby +require+ command, but will check + # for *.rake files in addition to *.rb files. + def rake_require(file_name, paths=$LOAD_PATH, loaded=$LOADED_FEATURES) # :nodoc: + fn = file_name + ".rake" + return false if loaded.include?(fn) + paths.each do |path| + full_path = File.join(path, fn) + if File.exist?(full_path) + Rake.load_rakefile(full_path) + loaded << fn + return true + end + end + fail LoadError, "Can't find #{file_name}" + end + + def find_rakefile_location # :nodoc: + here = Dir.pwd + until (fn = have_rakefile) + Dir.chdir("..") + return nil if Dir.pwd == here || options.nosearch + here = Dir.pwd + end + [fn, here] + ensure + Dir.chdir(Rake.original_dir) + end + + def print_rakefile_directory(location) # :nodoc: + $stderr.puts "(in #{Dir.pwd})" unless + options.silent or original_dir == location + end + + def raw_load_rakefile # :nodoc: + rakefile, location = find_rakefile_location + if (!options.ignore_system) && + (options.load_system || rakefile.nil?) && + system_dir && File.directory?(system_dir) + print_rakefile_directory(location) + glob("#{system_dir}/*.rake") do |name| + add_import name + end + else + fail "No Rakefile found (looking for: #{@rakefiles.join(', ')})" if + rakefile.nil? + @rakefile = rakefile + Dir.chdir(location) + print_rakefile_directory(location) + Rake.load_rakefile(File.expand_path(@rakefile)) if + @rakefile && @rakefile != "" + options.rakelib.each do |rlib| + glob("#{rlib}/*.rake") do |name| + add_import name + end + end + end + load_imports + end + + def glob(path, &block) # :nodoc: + FileList.glob(path.tr("\\", "/")).each(&block) + end + private :glob + + # The directory path containing the system wide rakefiles. + def system_dir # :nodoc: + @system_dir ||= ENV["RAKE_SYSTEM"] || standard_system_dir + end + + # The standard directory containing system wide rake files. + if Win32.windows? + def standard_system_dir #:nodoc: + Win32.win32_system_dir + end + else + def standard_system_dir #:nodoc: + File.join(Dir.home, ".rake") + end + end + private :standard_system_dir + + # Collect the list of tasks on the command line. If no tasks are + # given, return a list containing only the default task. + # Environmental assignments are processed at this time as well. + # + # `args` is the list of arguments to peruse to get the list of tasks. + # It should be the command line that was given to rake, less any + # recognised command-line options, which OptionParser.parse will + # have taken care of already. + def collect_command_line_tasks(args) # :nodoc: + @top_level_tasks = [] + args.each do |arg| + if arg =~ /^(\w+)=(.*)$/m + ENV[$1] = $2 + else + @top_level_tasks << arg unless arg =~ /^-/ + end + end + @top_level_tasks.push(default_task_name) if @top_level_tasks.empty? + end + + # Default task name ("default"). + # (May be overridden by subclasses) + def default_task_name # :nodoc: + "default" + end + + # Add a file to the list of files to be imported. + def add_import(fn) # :nodoc: + @pending_imports << fn + end + + # Load the pending list of imported files. + def load_imports # :nodoc: + while fn = @pending_imports.shift + next if @imported.member?(fn) + fn_task = lookup(fn) and fn_task.invoke + ext = File.extname(fn) + loader = @loaders[ext] || @default_loader + loader.load(fn) + if fn_task = lookup(fn) and fn_task.needed? + fn_task.reenable + fn_task.invoke + loader.load(fn) + end + @imported << fn + end + end + + def rakefile_location(backtrace=caller) # :nodoc: + backtrace.map { |t| t[/([^:]+):/, 1] } + + re = /^#{@rakefile}$/ + re = /#{re.source}/i if windows? + + backtrace.find { |str| str =~ re } || "" + end + + def set_default_options # :nodoc: + options.always_multitask = false + options.backtrace = false + options.build_all = false + options.dryrun = false + options.ignore_deprecate = false + options.ignore_system = false + options.job_stats = false + options.load_system = false + options.nosearch = false + options.rakelib = %w[rakelib] + options.show_all_tasks = false + options.show_prereqs = false + options.show_task_pattern = nil + options.show_tasks = nil + options.silent = false + options.suppress_backtrace_pattern = nil + options.thread_pool_size = Rake.suggested_thread_count + options.trace = false + options.trace_output = $stderr + options.trace_rules = false + end + + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/backtrace.rb b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/backtrace.rb new file mode 100644 index 0000000..c87f2f9 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/backtrace.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true +module Rake + module Backtrace # :nodoc: all + SYS_KEYS = RbConfig::CONFIG.keys.grep(/(?:[a-z]prefix|libdir)\z/) + SYS_PATHS = RbConfig::CONFIG.values_at(*SYS_KEYS).uniq + + [ File.join(File.dirname(__FILE__), "..") ] + + SUPPRESSED_PATHS = SYS_PATHS. + map { |s| s.tr("\\", "/") }. + map { |f| File.expand_path(f) }. + reject { |s| s.nil? || s =~ /^ *$/ } + SUPPRESSED_PATHS_RE = SUPPRESSED_PATHS.map { |f| Regexp.quote(f) }.join("|") + SUPPRESSED_PATHS_RE << "|^" + SUPPRESSED_PATHS_RE << "|^org\\/jruby\\/\\w+\\.java" if + Object.const_defined?(:RUBY_ENGINE) and RUBY_ENGINE == "jruby" + + SUPPRESS_PATTERN = %r!(\A(#{SUPPRESSED_PATHS_RE})|bin/rake:\d+)!i + + def self.collapse(backtrace) + pattern = Rake.application.options.suppress_backtrace_pattern || + SUPPRESS_PATTERN + backtrace.reject { |elem| elem =~ pattern } + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/clean.rb b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/clean.rb new file mode 100644 index 0000000..c49adf9 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/clean.rb @@ -0,0 +1,78 @@ +# frozen_string_literal: true +# The 'rake/clean' file defines two file lists (CLEAN and CLOBBER) and +# two rake tasks (:clean and :clobber). +# +# [:clean] Clean up the project by deleting scratch files and backup +# files. Add files to the CLEAN file list to have the :clean +# target handle them. +# +# [:clobber] Clobber all generated and non-source files in a project. +# The task depends on :clean, so all the clean files will +# be deleted as well as files in the CLOBBER file list. +# The intent of this task is to return a project to its +# pristine, just unpacked state. + +require_relative "../rake" + +# :stopdoc: + +module Rake + module Cleaner + extend FileUtils + + module_function + + def cleanup_files(file_names) + file_names.each do |file_name| + cleanup(file_name) + end + end + + def cleanup(file_name, **opts) + begin + opts = { verbose: Rake.application.options.trace }.merge(opts) + rm_r file_name, **opts + rescue StandardError => ex + puts "Failed to remove #{file_name}: #{ex}" unless file_already_gone?(file_name) + end + end + + def file_already_gone?(file_name) + return false if File.exist?(file_name) + + path = file_name + prev = nil + + while path = File.dirname(path) + return false if cant_be_deleted?(path) + break if [prev, "."].include?(path) + prev = path + end + true + end + private_class_method :file_already_gone? + + def cant_be_deleted?(path_name) + File.exist?(path_name) && + (!File.readable?(path_name) || !File.executable?(path_name)) + end + private_class_method :cant_be_deleted? + end +end + +CLEAN = ::Rake::FileList["**/*~", "**/*.bak", "**/core"] +CLEAN.clear_exclude.exclude { |fn| + fn.pathmap("%f").downcase == "core" && File.directory?(fn) +} + +desc "Remove any temporary products." +task :clean do + Rake::Cleaner.cleanup_files(CLEAN) +end + +CLOBBER = ::Rake::FileList.new + +desc "Remove any generated files." +task clobber: [:clean] do + Rake::Cleaner.cleanup_files(CLOBBER) +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/cloneable.rb b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/cloneable.rb new file mode 100644 index 0000000..eddb77e --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/cloneable.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true +module Rake + ## + # Mixin for creating easily cloned objects. + + module Cloneable # :nodoc: + # The hook that is invoked by 'clone' and 'dup' methods. + def initialize_copy(source) + super + source.instance_variables.each do |var| + src_value = source.instance_variable_get(var) + value = src_value.clone rescue src_value + instance_variable_set(var, value) + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/cpu_counter.rb b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/cpu_counter.rb new file mode 100644 index 0000000..75cc0d0 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/cpu_counter.rb @@ -0,0 +1,122 @@ +# frozen_string_literal: true +module Rake + + # Based on a script at: + # http://stackoverflow.com/questions/891537/ruby-detect-number-of-cpus-installed + class CpuCounter # :nodoc: all + def self.count + new.count_with_default + end + + def count_with_default(default=4) + count || default + rescue StandardError + default + end + + begin + require "etc" + rescue LoadError + else + if Etc.respond_to?(:nprocessors) + def count + return Etc.nprocessors + end + end + end + end +end + +unless Rake::CpuCounter.method_defined?(:count) + Rake::CpuCounter.class_eval <<-'end;', __FILE__, __LINE__+1 + require 'rbconfig' + + def count + if RUBY_PLATFORM == 'java' + count_via_java_runtime + else + case RbConfig::CONFIG['host_os'] + when /linux/ + count_via_cpuinfo + when /darwin|bsd/ + count_via_sysctl + when /mswin|mingw/ + count_via_win32 + else + # Try everything + count_via_win32 || + count_via_sysctl || + count_via_cpuinfo + end + end + end + + def count_via_java_runtime + Java::Java.lang.Runtime.getRuntime.availableProcessors + rescue StandardError + nil + end + + def count_via_win32 + # Get-CimInstance introduced in PowerShell 3 or earlier: https://learn.microsoft.com/en-us/previous-versions/powershell/module/cimcmdlets/get-ciminstance?view=powershell-3.0 + result = run_win32( + 'powershell -command "Get-CimInstance -ClassName Win32_Processor -Property NumberOfCores ' \ + '| Select-Object -Property NumberOfCores"' + ) + if !result || $?.exitstatus != 0 + # fallback to deprecated wmic for older systems + result = run_win32("wmic cpu get NumberOfCores") + end + + # powershell: "\nNumberOfCores\n-------------\n 4\n\n\n" + # wmic: "NumberOfCores \n\n4 \n\n\n\n" + result.scan(/\d+/).map(&:to_i).reduce(:+) if result + rescue StandardError + nil + end + + def count_via_cpuinfo + open('/proc/cpuinfo') { |f| f.readlines }.grep(/processor/).size + rescue StandardError + nil + end + + def count_via_sysctl + run 'sysctl', '-n', 'hw.ncpu' + end + + def run(command, *args) + cmd = resolve_command(command) + if cmd + IO.popen [cmd, *args] do |io| + io.read.to_i + end + else + nil + end + end + + def run_win32(command, *args) + IO.popen(command, &:read) + rescue Errno::ENOENT + nil + end + + def resolve_command(command) + look_for_command("/usr/sbin", command) || + look_for_command("/sbin", command) || + in_path_command(command) + end + + def look_for_command(dir, command) + path = File.join(dir, command) + File.exist?(path) ? path : nil + end + + def in_path_command(command) + IO.popen ['which', command] do |io| + io.eof? ? nil : command + end + end + end; +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/default_loader.rb b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/default_loader.rb new file mode 100644 index 0000000..d3b4650 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/default_loader.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true +module Rake + + # Default Rakefile loader used by +import+. + class DefaultLoader + + ## + # Loads a rakefile into the current application from +fn+ + + def load(fn) + Rake.load_rakefile(File.expand_path(fn)) + end + end + +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/dsl_definition.rb b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/dsl_definition.rb new file mode 100644 index 0000000..3799068 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/dsl_definition.rb @@ -0,0 +1,196 @@ +# frozen_string_literal: true +# Rake DSL functions. +require_relative "file_utils_ext" + +module Rake + + ## + # DSL is a module that provides #task, #desc, #namespace, etc. Use this + # when you'd like to use rake outside the top level scope. + # + # For a Rakefile you run from the command line this module is automatically + # included. + + module DSL + + #-- + # Include the FileUtils file manipulation functions in the top + # level module, but mark them private so that they don't + # unintentionally define methods on other objects. + #++ + + include FileUtilsExt + private(*FileUtils.instance_methods(false)) + private(*FileUtilsExt.instance_methods(false)) + + private + + # :call-seq: + # task(task_name) + # task(task_name: dependencies) + # task(task_name, arguments => dependencies) + # + # Declare a basic task. The +task_name+ is always the first argument. If + # the task name contains a ":" it is defined in that namespace. + # + # The +dependencies+ may be a single task name or an Array of task names. + # The +argument+ (a single name) or +arguments+ (an Array of names) define + # the arguments provided to the task. + # + # The task, argument and dependency names may be either symbols or + # strings. + # + # A task with a single dependency: + # + # task clobber: %w[clean] do + # rm_rf "html" + # end + # + # A task with an argument and a dependency: + # + # task :package, [:version] => :test do |t, args| + # # ... + # end + # + # To invoke this task from the command line: + # + # $ rake package[1.2.3] + # + def task(*args, &block) # :doc: + Rake::Task.define_task(*args, &block) + end + + # Declare a file task. + # + # Example: + # file "config.cfg" => ["config.template"] do + # open("config.cfg", "w") do |outfile| + # open("config.template") do |infile| + # while line = infile.gets + # outfile.puts line + # end + # end + # end + # end + # + def file(*args, &block) # :doc: + Rake::FileTask.define_task(*args, &block) + end + + # Declare a file creation task. + # (Mainly used for the directory command). + def file_create(*args, &block) + Rake::FileCreationTask.define_task(*args, &block) + end + + # Declare a set of files tasks to create the given directories on + # demand. + # + # Example: + # directory "testdata/doc" + # + def directory(*args, &block) # :doc: + args = args.flat_map { |arg| arg.is_a?(FileList) ? arg.to_a.flatten : arg } + result = file_create(*args, &block) + dir, _ = *Rake.application.resolve_args(args) + dir = Rake.from_pathname(dir) + Rake.each_dir_parent(dir) do |d| + file_create d do |t| + mkdir_p t.name unless File.exist?(t.name) + end + end + result + end + + # Declare a task that performs its prerequisites in + # parallel. Multitasks does *not* guarantee that its prerequisites + # will execute in any given order (which is obvious when you think + # about it) + # + # Example: + # multitask deploy: %w[deploy_gem deploy_rdoc] + # + def multitask(*args, &block) # :doc: + Rake::MultiTask.define_task(*args, &block) + end + + # Create a new rake namespace and use it for evaluating the given + # block. Returns a NameSpace object that can be used to lookup + # tasks defined in the namespace. + # + # Example: + # + # ns = namespace "nested" do + # # the "nested:run" task + # task :run + # end + # task_run = ns[:run] # find :run in the given namespace. + # + # Tasks can also be defined in a namespace by using a ":" in the task + # name: + # + # task "nested:test" do + # # ... + # end + # + def namespace(name=nil, &block) # :doc: + name = name.to_s if name.kind_of?(Symbol) + name = name.to_str if name.respond_to?(:to_str) + unless name.kind_of?(String) || name.nil? + raise ArgumentError, "Expected a String or Symbol for a namespace name" + end + Rake.application.in_namespace(name, &block) + end + + # Declare a rule for auto-tasks. + # + # Example: + # rule '.o' => '.c' do |t| + # sh 'cc', '-c', '-o', t.name, t.source + # end + # + def rule(*args, &block) # :doc: + Rake::Task.create_rule(*args, &block) + end + + # Describes the next rake task. Duplicate descriptions are discarded. + # Descriptions are shown with rake -T (up to the first + # sentence) and rake -D (the entire description). + # + # Example: + # desc "Run the Unit Tests" + # task test: [:build] do + # # ... run tests + # end + # + def desc(description) # :doc: + Rake.application.last_description = description + end + + # Import the partial Rakefiles +fn+. Imported files are loaded + # _after_ the current file is completely loaded. This allows the + # import statement to appear anywhere in the importing file, and yet + # allowing the imported files to depend on objects defined in the + # importing file. + # + # A common use of the import statement is to include files + # containing dependency declarations. + # + # See also the --rakelibdir command line option. + # + # Example: + # import ".depend", "my_rules" + # + def import(*fns) # :doc: + fns.each do |fn| + Rake.application.add_import(fn) + end + end + end + extend FileUtilsExt +end + +# Extend the main object with the DSL commands. This allows top-level +# calls to task, etc. to work from a Rakefile without polluting the +# object inheritance tree. +self.extend Rake::DSL diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/early_time.rb b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/early_time.rb new file mode 100644 index 0000000..80cc6bf --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/early_time.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true +module Rake + + # EarlyTime is a fake timestamp that occurs _before_ any other time value. + class EarlyTime + include Comparable + include Singleton + + ## + # The EarlyTime always comes before +other+! + + def <=>(other) + -1 + end + + def to_s # :nodoc: + "" + end + end + + EARLY = EarlyTime.instance +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/ext/core.rb b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/ext/core.rb new file mode 100644 index 0000000..226f212 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/ext/core.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true +class Module + # Check for an existing method in the current class before extending. If + # the method already exists, then a warning is printed and the extension is + # not added. Otherwise the block is yielded and any definitions in the + # block will take effect. + # + # Usage: + # + # class String + # rake_extension("xyz") do + # def xyz + # ... + # end + # end + # end + # + def rake_extension(method) # :nodoc: + if method_defined?(method) + $stderr.puts "WARNING: Possible conflict with Rake extension: " + + "#{self}##{method} already exists" + else + yield + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/ext/string.rb b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/ext/string.rb new file mode 100644 index 0000000..c82f532 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/ext/string.rb @@ -0,0 +1,176 @@ +# frozen_string_literal: true +require_relative "core" + +class String + + rake_extension("ext") do + # Replace the file extension with +newext+. If there is no extension on + # the string, append the new extension to the end. If the new extension + # is not given, or is the empty string, remove any existing extension. + # + # +ext+ is a user added method for the String class. + # + # This String extension comes from Rake + def ext(newext="") + return self.dup if [".", ".."].include? self + if newext != "" + newext = "." + newext unless newext =~ /^\./ + end + self.chomp(File.extname(self)) << newext + end + end + + rake_extension("pathmap") do + # Explode a path into individual components. Used by +pathmap+. + # + # This String extension comes from Rake + def pathmap_explode + head, tail = File.split(self) + return [self] if head == self + return [tail] if head == "." || tail == "/" + return [head, tail] if head == "/" + return head.pathmap_explode + [tail] + end + protected :pathmap_explode + + # Extract a partial path from the path. Include +n+ directories from the + # front end (left hand side) if +n+ is positive. Include |+n+| + # directories from the back end (right hand side) if +n+ is negative. + # + # This String extension comes from Rake + def pathmap_partial(n) + dirs = File.dirname(self).pathmap_explode + partial_dirs = + if n > 0 + dirs[0...n] + elsif n < 0 + dirs.reverse[0...-n].reverse + else + "." + end + File.join(partial_dirs) + end + protected :pathmap_partial + + # Perform the pathmap replacement operations on the given path. The + # patterns take the form 'pat1,rep1;pat2,rep2...'. + # + # This String extension comes from Rake + def pathmap_replace(patterns, &block) + result = self + patterns.split(";").each do |pair| + pattern, replacement = pair.split(",") + pattern = Regexp.new(pattern) + if replacement == "*" && block_given? + result = result.sub(pattern, &block) + elsif replacement + result = result.sub(pattern, replacement) + else + result = result.sub(pattern, "") + end + end + result + end + protected :pathmap_replace + + # Map the path according to the given specification. The specification + # controls the details of the mapping. The following special patterns are + # recognized: + # + # %p :: The complete path. + # %f :: The base file name of the path, with its file extension, + # but without any directories. + # %n :: The file name of the path without its file extension. + # %d :: The directory list of the path. + # %x :: The file extension of the path. An empty string if there + # is no extension. + # %X :: Everything *but* the file extension. + # %s :: The alternate file separator if defined, otherwise use # + # the standard file separator. + # %% :: A percent sign. + # + # The %d specifier can also have a numeric prefix (e.g. '%2d'). + # If the number is positive, only return (up to) +n+ directories in the + # path, starting from the left hand side. If +n+ is negative, return (up + # to) +n+ directories from the right hand side of the path. + # + # Examples: + # + # 'a/b/c/d/file.txt'.pathmap("%2d") => 'a/b' + # 'a/b/c/d/file.txt'.pathmap("%-2d") => 'c/d' + # + # Also the %d, %p, %f, %n, + # %x, and %X operators can take a pattern/replacement + # argument to perform simple string substitutions on a particular part of + # the path. The pattern and replacement are separated by a comma and are + # enclosed by curly braces. The replacement spec comes after the % + # character but before the operator letter. (e.g. "%{old,new}d"). + # Multiple replacement specs should be separated by semi-colons (e.g. + # "%{old,new;src,bin}d"). + # + # Regular expressions may be used for the pattern, and back refs may be + # used in the replacement text. Curly braces, commas and semi-colons are + # excluded from both the pattern and replacement text (let's keep parsing + # reasonable). + # + # For example: + # + # "src/org/onestepback/proj/A.java".pathmap("%{^src,class}X.class") + # + # returns: + # + # "class/org/onestepback/proj/A.class" + # + # If the replacement text is '*', then a block may be provided to perform + # some arbitrary calculation for the replacement. + # + # For example: + # + # "/path/to/file.TXT".pathmap("%X%{.*,*}x") { |ext| + # ext.downcase + # } + # + # Returns: + # + # "/path/to/file.txt" + # + # This String extension comes from Rake + def pathmap(spec=nil, &block) + return self if spec.nil? + result = "".dup + spec.scan(/%\{[^}]*\}-?\d*[sdpfnxX%]|%-?\d+d|%.|[^%]+/) do |frag| + case frag + when "%f" + result << File.basename(self) + when "%n" + result << File.basename(self).ext + when "%d" + result << File.dirname(self) + when "%x" + result << File.extname(self) + when "%X" + result << self.ext + when "%p" + result << self + when "%s" + result << (File::ALT_SEPARATOR || File::SEPARATOR) + when "%-" + # do nothing + when "%%" + result << "%" + when /%(-?\d+)d/ + result << pathmap_partial($1.to_i) + when /^%\{([^}]*)\}(\d*[dpfnxX])/ + patterns, operator = $1, $2 + result << pathmap("%" + operator).pathmap_replace(patterns, &block) + when /^%/ + fail ArgumentError, "Unknown pathmap specifier #{frag} in '#{spec}'" + else + result << frag + end + end + result + end + end + +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/file_creation_task.rb b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/file_creation_task.rb new file mode 100644 index 0000000..3df254c --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/file_creation_task.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true +require_relative "file_task" +require_relative "early_time" + +module Rake + + # A FileCreationTask is a file task that when used as a dependency will be + # needed if and only if the file has not been created. Once created, it is + # not re-triggered if any of its dependencies are newer, nor does trigger + # any rebuilds of tasks that depend on it whenever it is updated. + # + class FileCreationTask < FileTask + # Is this file task needed? Yes if it doesn't exist. + def needed? + !File.exist?(name) + end + + # Time stamp for file creation task. This time stamp is earlier + # than any other time stamp. + def timestamp + Rake::EARLY + end + end + +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/file_list.rb b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/file_list.rb new file mode 100644 index 0000000..76078d2 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/file_list.rb @@ -0,0 +1,435 @@ +# frozen_string_literal: true +require_relative "cloneable" +require_relative "file_utils_ext" +require_relative "ext/string" + +module Rake + + ## + # A FileList is essentially an array with a few helper methods defined to + # make file manipulation a bit easier. + # + # FileLists are lazy. When given a list of glob patterns for possible files + # to be included in the file list, instead of searching the file structures + # to find the files, a FileList holds the pattern for latter use. + # + # This allows us to define a number of FileList to match any number of + # files, but only search out the actual files when then FileList itself is + # actually used. The key is that the first time an element of the + # FileList/Array is requested, the pending patterns are resolved into a real + # list of file names. + # + class FileList + + include Cloneable + + # == Method Delegation + # + # The lazy evaluation magic of FileLists happens by implementing all the + # array specific methods to call +resolve+ before delegating the heavy + # lifting to an embedded array object (@items). + # + # In addition, there are two kinds of delegation calls. The regular kind + # delegates to the @items array and returns the result directly. Well, + # almost directly. It checks if the returned value is the @items object + # itself, and if so will return the FileList object instead. + # + # The second kind of delegation call is used in methods that normally + # return a new Array object. We want to capture the return value of these + # methods and wrap them in a new FileList object. We enumerate these + # methods in the +SPECIAL_RETURN+ list below. + + # List of array methods (that are not in +Object+) that need to be + # delegated. + ARRAY_METHODS = (Array.instance_methods - Object.instance_methods).map(&:to_s) + + # List of additional methods that must be delegated. + MUST_DEFINE = %w[inspect <=>] + + # List of methods that should not be delegated here (we define special + # versions of them explicitly below). + MUST_NOT_DEFINE = %w[to_a to_ary partition * <<] + + # List of delegated methods that return new array values which need + # wrapping. + SPECIAL_RETURN = %w[ + map collect sort sort_by select find_all reject grep + compact flatten uniq values_at + + - & | + ] + + DELEGATING_METHODS = (ARRAY_METHODS + MUST_DEFINE - MUST_NOT_DEFINE).map(&:to_s).sort.uniq + + # Now do the delegation. + DELEGATING_METHODS.each do |sym| + if SPECIAL_RETURN.include?(sym) + ln = __LINE__ + 1 + class_eval %{ + def #{sym}(*args, &block) + resolve + result = @items.send(:#{sym}, *args, &block) + self.class.new.import(result) + end + }, __FILE__, ln + else + ln = __LINE__ + 1 + class_eval %{ + def #{sym}(*args, &block) + resolve + result = @items.send(:#{sym}, *args, &block) + result.object_id == @items.object_id ? self : result + end + }, __FILE__, ln + end + end + + GLOB_PATTERN = %r{[*?\[\{]} + + # Create a file list from the globbable patterns given. If you wish to + # perform multiple includes or excludes at object build time, use the + # "yield self" pattern. + # + # Example: + # file_list = FileList.new('lib/**/*.rb', 'test/test*.rb') + # + # pkg_files = FileList.new('lib/**/*') do |fl| + # fl.exclude(/\bCVS\b/) + # end + # + def initialize(*patterns) + @pending_add = [] + @pending = false + @exclude_patterns = DEFAULT_IGNORE_PATTERNS.dup + @exclude_procs = DEFAULT_IGNORE_PROCS.dup + @items = [] + patterns.each { |pattern| include(pattern) } + yield self if block_given? + end + + # Add file names defined by glob patterns to the file list. If an array + # is given, add each element of the array. + # + # Example: + # file_list.include("*.java", "*.cfg") + # file_list.include %w( math.c lib.h *.o ) + # + def include(*filenames) + # TODO: check for pending + filenames.each do |fn| + if fn.respond_to? :to_ary + include(*fn.to_ary) + else + @pending_add << Rake.from_pathname(fn) + end + end + @pending = true + self + end + alias :add :include + + # Register a list of file name patterns that should be excluded from the + # list. Patterns may be regular expressions, glob patterns or regular + # strings. In addition, a block given to exclude will remove entries that + # return true when given to the block. + # + # Note that glob patterns are expanded against the file system. If a file + # is explicitly added to a file list, but does not exist in the file + # system, then an glob pattern in the exclude list will not exclude the + # file. + # + # Examples: + # FileList['a.c', 'b.c'].exclude("a.c") => ['b.c'] + # FileList['a.c', 'b.c'].exclude(/^a/) => ['b.c'] + # + # If "a.c" is a file, then ... + # FileList['a.c', 'b.c'].exclude("a.*") => ['b.c'] + # + # If "a.c" is not a file, then ... + # FileList['a.c', 'b.c'].exclude("a.*") => ['a.c', 'b.c'] + # + def exclude(*patterns, &block) + patterns.each do |pat| + if pat.respond_to? :to_ary + exclude(*pat.to_ary) + else + @exclude_patterns << Rake.from_pathname(pat) + end + end + @exclude_procs << block if block_given? + resolve_exclude unless @pending + self + end + + # Clear all the exclude patterns so that we exclude nothing. + def clear_exclude + @exclude_patterns = [] + @exclude_procs = [] + self + end + + # A FileList is equal through array equality. + def ==(array) + to_ary == array + end + + # Return the internal array object. + def to_a + resolve + @items + end + + # Return the internal array object. + def to_ary + to_a + end + + # Lie about our class. + def is_a?(klass) + klass == Array || super(klass) + end + alias kind_of? is_a? + + # Redefine * to return either a string or a new file list. + def *(other) + result = @items * other + case result + when Array + self.class.new.import(result) + else + result + end + end + + def <<(obj) + resolve + @items << Rake.from_pathname(obj) + self + end + + # Resolve all the pending adds now. + def resolve + if @pending + @pending = false + @pending_add.each do |fn| resolve_add(fn) end + @pending_add = [] + resolve_exclude + end + self + end + + def resolve_add(fn) # :nodoc: + case fn + when GLOB_PATTERN + add_matching(fn) + else + self << fn + end + end + private :resolve_add + + def resolve_exclude # :nodoc: + reject! { |fn| excluded_from_list?(fn) } + self + end + private :resolve_exclude + + # Return a new FileList with the results of running +sub+ against each + # element of the original list. + # + # Example: + # FileList['a.c', 'b.c'].sub(/\.c$/, '.o') => ['a.o', 'b.o'] + # + def sub(pat, rep) + inject(self.class.new) { |res, fn| res << fn.sub(pat, rep) } + end + + # Return a new FileList with the results of running +gsub+ against each + # element of the original list. + # + # Example: + # FileList['lib/test/file', 'x/y'].gsub(/\//, "\\") + # => ['lib\\test\\file', 'x\\y'] + # + def gsub(pat, rep) + inject(self.class.new) { |res, fn| res << fn.gsub(pat, rep) } + end + + # Same as +sub+ except that the original file list is modified. + def sub!(pat, rep) + each_with_index { |fn, i| self[i] = fn.sub(pat, rep) } + self + end + + # Same as +gsub+ except that the original file list is modified. + def gsub!(pat, rep) + each_with_index { |fn, i| self[i] = fn.gsub(pat, rep) } + self + end + + # Apply the pathmap spec to each of the included file names, returning a + # new file list with the modified paths. (See String#pathmap for + # details.) + def pathmap(spec=nil, &block) + collect { |fn| fn.pathmap(spec, &block) } + end + + # Return a new FileList with String#ext method applied to + # each member of the array. + # + # This method is a shortcut for: + # + # array.collect { |item| item.ext(newext) } + # + # +ext+ is a user added method for the Array class. + def ext(newext="") + collect { |fn| fn.ext(newext) } + end + + # Grep each of the files in the filelist using the given pattern. If a + # block is given, call the block on each matching line, passing the file + # name, line number, and the matching line of text. If no block is given, + # a standard emacs style file:linenumber:line message will be printed to + # standard out. Returns the number of matched items. + def egrep(pattern, *options) + matched = 0 + each do |fn| + begin + File.open(fn, "r", *options) do |inf| + count = 0 + inf.each do |line| + count += 1 + if pattern.match(line) + matched += 1 + if block_given? + yield fn, count, line + else + puts "#{fn}:#{count}:#{line}" + end + end + end + end + rescue StandardError => ex + $stderr.puts "Error while processing '#{fn}': #{ex}" + end + end + matched + end + + # Return a new file list that only contains file names from the current + # file list that exist on the file system. + def existing + select { |fn| File.exist?(fn) }.uniq + end + + # Modify the current file list so that it contains only file name that + # exist on the file system. + def existing! + resolve + @items = @items.select { |fn| File.exist?(fn) }.uniq + self + end + + # FileList version of partition. Needed because the nested arrays should + # be FileLists in this version. + def partition(&block) # :nodoc: + resolve + result = @items.partition(&block) + [ + self.class.new.import(result[0]), + self.class.new.import(result[1]), + ] + end + + # Convert a FileList to a string by joining all elements with a space. + def to_s + resolve + self.join(" ") + end + + # Add matching glob patterns. + def add_matching(pattern) + self.class.glob(pattern).each do |fn| + self << fn unless excluded_from_list?(fn) + end + end + private :add_matching + + # Should the given file name be excluded from the list? + # + # NOTE: This method was formerly named "exclude?", but Rails + # introduced an exclude? method as an array method and setup a + # conflict with file list. We renamed the method to avoid + # confusion. If you were using "FileList#exclude?" in your user + # code, you will need to update. + def excluded_from_list?(fn) + return true if @exclude_patterns.any? do |pat| + case pat + when Regexp + fn =~ pat + when GLOB_PATTERN + flags = File::FNM_PATHNAME + # Ruby <= 1.9.3 does not support File::FNM_EXTGLOB + flags |= File::FNM_EXTGLOB if defined? File::FNM_EXTGLOB + File.fnmatch?(pat, fn, flags) + else + fn == pat + end + end + @exclude_procs.any? { |p| p.call(fn) } + end + + DEFAULT_IGNORE_PATTERNS = [ + /(^|[\/\\])CVS([\/\\]|$)/, + /(^|[\/\\])\.svn([\/\\]|$)/, + /\.bak$/, + /~$/ + ] + DEFAULT_IGNORE_PROCS = [ + proc { |fn| fn =~ /(^|[\/\\])core$/ && !File.directory?(fn) } + ] + + def import(array) # :nodoc: + @items = array + self + end + + class << self + # Create a new file list including the files listed. Similar to: + # + # FileList.new(*args) + def [](*args) + new(*args) + end + + # Get a sorted list of files matching the pattern. This method + # should be preferred to Dir[pattern] and Dir.glob(pattern) because + # the files returned are guaranteed to be sorted. + def glob(pattern, *args) + Dir.glob(pattern, *args).sort + end + end + end +end + +module Rake + class << self + + # Yield each file or directory component. + def each_dir_parent(dir) # :nodoc: + old_length = nil + while dir != "." && dir.length != old_length + yield(dir) + old_length = dir.length + dir = File.dirname(dir) + end + end + + # Convert Pathname and Pathname-like objects to strings; + # leave everything else alone + def from_pathname(path) # :nodoc: + path = path.to_path if path.respond_to?(:to_path) + path = path.to_str if path.respond_to?(:to_str) + path + end + end +end # module Rake diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/file_task.rb b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/file_task.rb new file mode 100644 index 0000000..8c398bc --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/file_task.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true +require_relative "task" +require_relative "early_time" + +module Rake + + # A FileTask is a task that includes time based dependencies. If any of a + # FileTask's prerequisites have a timestamp that is later than the file + # represented by this task, then the file must be rebuilt (using the + # supplied actions). + # + class FileTask < Task + + # Is this file task needed? Yes if it doesn't exist, or if its time stamp + # is out of date. + def needed? + begin + out_of_date?(File.mtime(name)) || @application.options.build_all + rescue Errno::ENOENT + true + end + end + + # Time stamp for file task. + def timestamp + begin + File.mtime(name) + rescue Errno::ENOENT + Rake::LATE + end + end + + private + + # Are there any prerequisites with a later time than the given time stamp? + def out_of_date?(stamp) + all_prerequisite_tasks.any? { |prereq| + prereq_task = application[prereq, @scope] + if prereq_task.instance_of?(Rake::FileTask) + prereq_task.timestamp > stamp || @application.options.build_all + else + prereq_task.timestamp > stamp + end + } + end + + # ---------------------------------------------------------------- + # Task class methods. + # + class << self + # Apply the scope to the task name according to the rules for this kind + # of task. File based tasks ignore the scope when creating the name. + def scope_name(scope, task_name) + Rake.from_pathname(task_name) + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/file_utils.rb b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/file_utils.rb new file mode 100644 index 0000000..1510d95 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/file_utils.rb @@ -0,0 +1,132 @@ +# frozen_string_literal: true +require "rbconfig" +require "fileutils" + +#-- +# This a FileUtils extension that defines several additional commands to be +# added to the FileUtils utility functions. +module FileUtils + # Path to the currently running Ruby program + RUBY = ENV["RUBY"] || File.join( + RbConfig::CONFIG["bindir"], + RbConfig::CONFIG["ruby_install_name"] + RbConfig::CONFIG["EXEEXT"]). + sub(/.*\s.*/m, '"\&"') + + # Run the system command +cmd+. If multiple arguments are given the command + # is run directly (without the shell, same semantics as Kernel::exec and + # Kernel::system). + # + # It is recommended you use the multiple argument form over interpolating + # user input for both usability and security reasons. With the multiple + # argument form you can easily process files with spaces or other shell + # reserved characters in them. With the multiple argument form your rake + # tasks are not vulnerable to users providing an argument like + # ; rm # -rf /. + # + # If a block is given, upon command completion the block is called with an + # OK flag (true on a zero exit status) and a Process::Status object. + # Without a block a RuntimeError is raised when the command exits non-zero. + # + # Examples: + # + # sh 'ls -ltr' + # + # sh 'ls', 'file with spaces' + # + # # check exit status after command runs + # sh %{grep pattern file} do |ok, res| + # if !ok + # puts "pattern not found (status = #{res.exitstatus})" + # end + # end + # + def sh(*cmd, &block) + options = (Hash === cmd.last) ? cmd.pop : {} + shell_runner = block_given? ? block : create_shell_runner(cmd) + + set_verbose_option(options) + verbose = options.delete :verbose + noop = options.delete(:noop) || Rake::FileUtilsExt.nowrite_flag + + Rake.rake_output_message sh_show_command cmd if verbose + + unless noop + res = (Hash === cmd.last) ? system(*cmd) : system(*cmd, options) + status = $? + status = Rake::PseudoStatus.new(1) if !res && status.nil? + shell_runner.call(res, status) + end + end + + def create_shell_runner(cmd) # :nodoc: + show_command = sh_show_command cmd + lambda do |ok, status| + ok or + fail "Command failed with status (#{status.exitstatus}): " + + "[#{show_command}]" + end + end + private :create_shell_runner + + def sh_show_command(cmd) # :nodoc: + cmd = cmd.dup + + if Hash === cmd.first + env = cmd.first + env = env.map { |name, value| "#{name}=#{value}" }.join " " + cmd[0] = env + end + + cmd.join " " + end + private :sh_show_command + + def set_verbose_option(options) # :nodoc: + unless options.key? :verbose + options[:verbose] = + (Rake::FileUtilsExt.verbose_flag == Rake::FileUtilsExt::DEFAULT) || + Rake::FileUtilsExt.verbose_flag + end + end + private :set_verbose_option + + # Run a Ruby interpreter with the given arguments. + # + # Example: + # ruby %{-pe '$_.upcase!' 1 + sh(RUBY, *args, **options, &block) + else + sh("#{RUBY} #{args.first}", **options, &block) + end + end + + LN_SUPPORTED = [true] + + # Attempt to do a normal file link, but fall back to a copy if the link + # fails. + def safe_ln(*args, **options) + if LN_SUPPORTED[0] + begin + return options.empty? ? ln(*args) : ln(*args, **options) + rescue StandardError, NotImplementedError + LN_SUPPORTED[0] = false + end + end + options.empty? ? cp(*args) : cp(*args, **options) + end + + # Split a file path into individual directory names. + # + # Example: + # split_all("a/b/c") => ['a', 'b', 'c'] + # + def split_all(path) + head, tail = File.split(path) + return [tail] if head == "." || tail == "/" + return [head, tail] if head == "/" + return split_all(head) + [tail] + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/file_utils_ext.rb b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/file_utils_ext.rb new file mode 100644 index 0000000..687d805 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/file_utils_ext.rb @@ -0,0 +1,134 @@ +# frozen_string_literal: true +require_relative "file_utils" + +module Rake + # + # FileUtilsExt provides a custom version of the FileUtils methods + # that respond to the verbose and nowrite + # commands. + # + module FileUtilsExt + include FileUtils + + class << self + attr_accessor :verbose_flag, :nowrite_flag + end + + DEFAULT = Object.new + + FileUtilsExt.verbose_flag = DEFAULT + FileUtilsExt.nowrite_flag = false + + FileUtils.commands.each do |name| + opts = FileUtils.options_of name + default_options = [] + if opts.include?("verbose") + default_options << "verbose: FileUtilsExt.verbose_flag" + end + if opts.include?("noop") + default_options << "noop: FileUtilsExt.nowrite_flag" + end + + next if default_options.empty? + module_eval(<<-EOS, __FILE__, __LINE__ + 1) + def #{name}(*args, **options, &block) + super(*args, + #{default_options.join(', ')}, + **options, &block) + end + EOS + end + + # Get/set the verbose flag controlling output from the FileUtils + # utilities. If verbose is true, then the utility method is + # echoed to standard output. + # + # Examples: + # verbose # return the current value of the + # # verbose flag + # verbose(v) # set the verbose flag to _v_. + # verbose(v) { code } # Execute code with the verbose flag set + # # temporarily to _v_. Return to the + # # original value when code is done. + def verbose(value=nil) + oldvalue = FileUtilsExt.verbose_flag + FileUtilsExt.verbose_flag = value unless value.nil? + if block_given? + begin + yield + ensure + FileUtilsExt.verbose_flag = oldvalue + end + end + FileUtilsExt.verbose_flag + end + + # Get/set the nowrite flag controlling output from the FileUtils + # utilities. If verbose is true, then the utility method is + # echoed to standard output. + # + # Examples: + # nowrite # return the current value of the + # # nowrite flag + # nowrite(v) # set the nowrite flag to _v_. + # nowrite(v) { code } # Execute code with the nowrite flag set + # # temporarily to _v_. Return to the + # # original value when code is done. + def nowrite(value=nil) + oldvalue = FileUtilsExt.nowrite_flag + FileUtilsExt.nowrite_flag = value unless value.nil? + if block_given? + begin + yield + ensure + FileUtilsExt.nowrite_flag = oldvalue + end + end + oldvalue + end + + # Use this function to prevent potentially destructive ruby code + # from running when the :nowrite flag is set. + # + # Example: + # + # when_writing("Building Project") do + # project.build + # end + # + # The following code will build the project under normal + # conditions. If the nowrite(true) flag is set, then the example + # will print: + # + # DRYRUN: Building Project + # + # instead of actually building the project. + # + def when_writing(msg=nil) + if FileUtilsExt.nowrite_flag + $stderr.puts "DRYRUN: #{msg}" if msg + else + yield + end + end + + # Send the message to the default rake output (which is $stderr). + def rake_output_message(message) + $stderr.puts(message) + end + + # Check that the options do not contain options not listed in + # +optdecl+. An ArgumentError exception is thrown if non-declared + # options are found. + def rake_check_options(options, *optdecl) + h = options.dup + optdecl.each do |name| + h.delete name + end + raise ArgumentError, "no such option: #{h.keys.join(' ')}" unless + h.empty? + end + + extend self + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/invocation_chain.rb b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/invocation_chain.rb new file mode 100644 index 0000000..44a9954 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/invocation_chain.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true +module Rake + + # InvocationChain tracks the chain of task invocations to detect + # circular dependencies. + class InvocationChain < LinkedList + + # Is the invocation already in the chain? + def member?(invocation) + head == invocation || tail.member?(invocation) + end + + # Append an invocation to the chain of invocations. It is an error + # if the invocation already listed. + def append(invocation) + if member?(invocation) + fail RuntimeError, "Circular dependency detected: #{to_s} => #{invocation}" + end + conj(invocation) + end + + # Convert to string, ie: TOP => invocation => invocation + def to_s + "#{prefix}#{head}" + end + + # Class level append. + def self.append(invocation, chain) + chain.append(invocation) + end + + private + + def prefix + "#{tail} => " + end + + # Null object for an empty chain. + class EmptyInvocationChain < LinkedList::EmptyLinkedList + @parent = InvocationChain + + def member?(obj) + false + end + + def append(invocation) + conj(invocation) + end + + def to_s + "TOP" + end + end + + EMPTY = EmptyInvocationChain.new + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/invocation_exception_mixin.rb b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/invocation_exception_mixin.rb new file mode 100644 index 0000000..b0d307a --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/invocation_exception_mixin.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true +module Rake + module InvocationExceptionMixin + # Return the invocation chain (list of Rake tasks) that were in + # effect when this exception was detected by rake. May be null if + # no tasks were active. + def chain + @rake_invocation_chain ||= nil + end + + # Set the invocation chain in effect when this exception was + # detected. + def chain=(value) + @rake_invocation_chain = value + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/late_time.rb b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/late_time.rb new file mode 100644 index 0000000..8fe0249 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/late_time.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true +module Rake + # LateTime is a fake timestamp that occurs _after_ any other time value. + class LateTime + include Comparable + include Singleton + + def <=>(other) + 1 + end + + def to_s + "" + end + end + + LATE = LateTime.instance +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/linked_list.rb b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/linked_list.rb new file mode 100644 index 0000000..11fa46f --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/linked_list.rb @@ -0,0 +1,112 @@ +# frozen_string_literal: true +module Rake + + # Polylithic linked list structure used to implement several data + # structures in Rake. + class LinkedList + include Enumerable + attr_reader :head, :tail + + # Polymorphically add a new element to the head of a list. The + # type of head node will be the same list type as the tail. + def conj(item) + self.class.cons(item, self) + end + + # Is the list empty? + # .make guards against a list being empty making any instantiated LinkedList + # object not empty by default + # You should consider overriding this method if you implement your own .make method + def empty? + false + end + + # Lists are structurally equivalent. + def ==(other) + current = self + while !current.empty? && !other.empty? + return false if current.head != other.head + current = current.tail + other = other.tail + end + current.empty? && other.empty? + end + + # Convert to string: LL(item, item...) + def to_s + items = map(&:to_s).join(", ") + "LL(#{items})" + end + + # Same as +to_s+, but with inspected items. + def inspect + items = map(&:inspect).join(", ") + "LL(#{items})" + end + + # For each item in the list. + def each + current = self + while !current.empty? + yield(current.head) + current = current.tail + end + self + end + + # Make a list out of the given arguments. This method is + # polymorphic + def self.make(*args) + # return an EmptyLinkedList if there are no arguments + return empty if !args || args.empty? + + # build a LinkedList by starting at the tail and iterating + # through each argument + # inject takes an EmptyLinkedList to start + args.reverse.inject(empty) do |list, item| + list = cons(item, list) + list # return the newly created list for each item in the block + end + end + + # Cons a new head onto the tail list. + def self.cons(head, tail) + new(head, tail) + end + + # The standard empty list class for the given LinkedList class. + def self.empty + self::EMPTY + end + + protected + + def initialize(head, tail=EMPTY) + @head = head + @tail = tail + end + + # Represent an empty list, using the Null Object Pattern. + # + # When inheriting from the LinkedList class, you should implement + # a type specific Empty class as well. Make sure you set the class + # instance variable @parent to the associated list class (this + # allows conj, cons and make to work polymorphically). + class EmptyLinkedList < LinkedList + @parent = LinkedList + + def initialize + end + + def empty? + true + end + + def self.cons(head, tail) + @parent.cons(head, tail) + end + end + + EMPTY = EmptyLinkedList.new + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/loaders/makefile.rb b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/loaders/makefile.rb new file mode 100644 index 0000000..46f4bea --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/loaders/makefile.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true +module Rake + + # Makefile loader to be used with the import file loader. Use this to + # import dependencies from make dependency tools: + # + # require 'rake/loaders/makefile' + # + # file ".depends.mf" => [SRC_LIST] do |t| + # sh "makedepend -f- -- #{CFLAGS} -- #{t.prerequisites} > #{t.name}" + # end + # + # import ".depends.mf" + # + # See {Importing Dependencies}[link:doc/rakefile_rdoc.html#label-Importing+Dependencies] + # for further details. + + class MakefileLoader + include Rake::DSL + + SPACE_MARK = "\0" # :nodoc: + + # Load the makefile dependencies in +fn+. + def load(fn) # :nodoc: + lines = File.read fn + lines.gsub!(/\\ /, SPACE_MARK) + lines.gsub!(/#[^\n]*\n/m, "") + lines.gsub!(/\\\n/, " ") + lines.each_line do |line| + process_line(line) + end + end + + private + + # Process one logical line of makefile data. + def process_line(line) # :nodoc: + file_tasks, args = line.split(":", 2) + return if args.nil? + dependents = args.split.map { |d| respace(d) } + file_tasks.scan(/\S+/) do |file_task| + file_task = respace(file_task) + file file_task => dependents + end + end + + def respace(str) # :nodoc: + str.tr SPACE_MARK, " " + end + end + + # Install the handler + Rake.application.add_loader("mf", MakefileLoader.new) +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/multi_task.rb b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/multi_task.rb new file mode 100644 index 0000000..3ae363c --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/multi_task.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true +module Rake + + # Same as a regular task, but the immediate prerequisites are done in + # parallel using Ruby threads. + # + class MultiTask < Task + private + + def invoke_prerequisites(task_args, invocation_chain) # :nodoc: + invoke_prerequisites_concurrently(task_args, invocation_chain) + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/name_space.rb b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/name_space.rb new file mode 100644 index 0000000..32f8139 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/name_space.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true +## +# The NameSpace class will lookup task names in the scope defined by a +# +namespace+ command. + +class Rake::NameSpace + + ## + # Create a namespace lookup object using the given task manager + # and the list of scopes. + + def initialize(task_manager, scope_list) + @task_manager = task_manager + @scope = scope_list.dup + end + + ## + # Lookup a task named +name+ in the namespace. + + def [](name) + @task_manager.lookup(name, @scope) + end + + ## + # The scope of the namespace (a LinkedList) + + def scope + @scope.dup + end + + ## + # Return the list of tasks defined in this and nested namespaces. + + def tasks + @task_manager.tasks_in_scope(@scope) + end + +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/packagetask.rb b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/packagetask.rb new file mode 100644 index 0000000..80a4acf --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/packagetask.rb @@ -0,0 +1,222 @@ +# frozen_string_literal: true +# Define a package task library to aid in the definition of +# redistributable package files. + +require_relative "../rake" +require_relative "tasklib" + +module Rake + + # Create a packaging task that will package the project into + # distributable files (e.g zip archive or tar files). + # + # The PackageTask will create the following targets: + # + # +:package+ :: + # Create all the requested package files. + # + # +:clobber_package+ :: + # Delete all the package files. This target is automatically + # added to the main clobber target. + # + # +:repackage+ :: + # Rebuild the package files from scratch, even if they are not out + # of date. + # + # "package_dir/name-version.tgz" :: + # Create a gzipped tar package (if need_tar is true). + # + # "package_dir/name-version.tar.gz" :: + # Create a gzipped tar package (if need_tar_gz is true). + # + # "package_dir/name-version.tar.bz2" :: + # Create a bzip2'd tar package (if need_tar_bz2 is true). + # + # "package_dir/name-version.zip" :: + # Create a zip package archive (if need_zip is true). + # + # Example: + # + # Rake::PackageTask.new("rake", "1.2.3") do |p| + # p.need_tar = true + # p.package_files.include("lib/**/*.rb") + # end + # + class PackageTask < TaskLib + # Name of the package (from the GEM Spec). + attr_accessor :name + + # Version of the package (e.g. '1.3.2'). + attr_accessor :version + + # Directory used to store the package files (default is 'pkg'). + attr_accessor :package_dir + + # True if a gzipped tar file (tgz) should be produced (default is + # false). + attr_accessor :need_tar + + # True if a gzipped tar file (tar.gz) should be produced (default + # is false). + attr_accessor :need_tar_gz + + # True if a bzip2'd tar file (tar.bz2) should be produced (default + # is false). + attr_accessor :need_tar_bz2 + + # True if a xz'd tar file (tar.xz) should be produced (default is false) + attr_accessor :need_tar_xz + + # True if a zip file should be produced (default is false) + attr_accessor :need_zip + + # List of files to be included in the package. + attr_accessor :package_files + + # Tar command for gzipped or bzip2ed archives. The default is 'tar'. + attr_accessor :tar_command + + # Zip command for zipped archives. The default is 'zip'. + attr_accessor :zip_command + + # True if parent directory should be omitted (default is false) + attr_accessor :without_parent_dir + + # Create a Package Task with the given name and version. Use +:noversion+ + # as the version to build a package without a version or to provide a + # fully-versioned package name. + + def initialize(name=nil, version=nil) + init(name, version) + yield self if block_given? + define unless name.nil? + end + + # Initialization that bypasses the "yield self" and "define" step. + def init(name, version) + @name = name + @version = version + @package_files = Rake::FileList.new + @package_dir = "pkg" + @need_tar = false + @need_tar_gz = false + @need_tar_bz2 = false + @need_tar_xz = false + @need_zip = false + @tar_command = "tar" + @zip_command = "zip" + @without_parent_dir = false + end + + # Create the tasks defined by this task library. + def define + fail "Version required (or :noversion)" if @version.nil? + @version = nil if :noversion == @version + + desc "Build all the packages" + task :package + + desc "Force a rebuild of the package files" + task repackage: [:clobber_package, :package] + + desc "Remove package products" + task :clobber_package do + rm_r package_dir rescue nil + end + + task clobber: [:clobber_package] + + [ + [need_tar, tgz_file, "z"], + [need_tar_gz, tar_gz_file, "z"], + [need_tar_bz2, tar_bz2_file, "j"], + [need_tar_xz, tar_xz_file, "J"] + ].each do |need, file, flag| + if need + task package: ["#{package_dir}/#{file}"] + file "#{package_dir}/#{file}" => + [package_dir_path] + package_files do + chdir(working_dir) { sh @tar_command, "#{flag}cvf", file, target_dir } + mv "#{package_dir_path}/#{target_dir}", package_dir if without_parent_dir + end + end + end + + if need_zip + task package: ["#{package_dir}/#{zip_file}"] + file "#{package_dir}/#{zip_file}" => + [package_dir_path] + package_files do + chdir(working_dir) { sh @zip_command, "-r", zip_file, target_dir } + mv "#{package_dir_path}/#{zip_file}", package_dir if without_parent_dir + end + end + + directory package_dir_path => @package_files do + @package_files.each do |fn| + f = File.join(package_dir_path, fn) + fdir = File.dirname(f) + mkdir_p(fdir) unless File.exist?(fdir) + if File.directory?(fn) + mkdir_p(f) + else + rm_f f + safe_ln(fn, f) + end + end + end + self + end + + # The name of this package + + def package_name + @version ? "#{@name}-#{@version}" : @name + end + + # The directory this package will be built in + + def package_dir_path + "#{package_dir}/#{package_name}" + end + + # The package name with .tgz added + + def tgz_file + "#{package_name}.tgz" + end + + # The package name with .tar.gz added + + def tar_gz_file + "#{package_name}.tar.gz" + end + + # The package name with .tar.bz2 added + + def tar_bz2_file + "#{package_name}.tar.bz2" + end + + # The package name with .tar.xz added + + def tar_xz_file + "#{package_name}.tar.xz" + end + + # The package name with .zip added + + def zip_file + "#{package_name}.zip" + end + + def working_dir + without_parent_dir ? package_dir_path : package_dir + end + + # target directory relative to working_dir + def target_dir + without_parent_dir ? "." : package_name + end + end + +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/phony.rb b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/phony.rb new file mode 100644 index 0000000..8f62b7c --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/phony.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true +# Defines a :phony task that you can use as a dependency. This allows +# file-based tasks to use non-file-based tasks as prerequisites +# without forcing them to rebuild. +# +# See FileTask#out_of_date? and Task#timestamp for more info. + +require_relative "../rake" + +task :phony + +Rake::Task[:phony].tap do |task| + def task.timestamp # :nodoc: + Time.at 0 + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/private_reader.rb b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/private_reader.rb new file mode 100644 index 0000000..2815ce6 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/private_reader.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true +module Rake + + # Include PrivateReader to use +private_reader+. + module PrivateReader # :nodoc: all + + def self.included(base) + base.extend(ClassMethods) + end + + module ClassMethods + + # Declare a list of private accessors + def private_reader(*names) + attr_reader(*names) + private(*names) + end + end + + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/promise.rb b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/promise.rb new file mode 100644 index 0000000..f45af4f --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/promise.rb @@ -0,0 +1,100 @@ +# frozen_string_literal: true +module Rake + + # A Promise object represents a promise to do work (a chore) in the + # future. The promise is created with a block and a list of + # arguments for the block. Calling value will return the value of + # the promised chore. + # + # Used by ThreadPool. + # + class Promise # :nodoc: all + NOT_SET = Object.new.freeze # :nodoc: + + attr_accessor :recorder + + # Create a promise to do the chore specified by the block. + def initialize(args, &block) + @mutex = Mutex.new + @result = NOT_SET + @error = NOT_SET + @args = args + @block = block + end + + # Return the value of this promise. + # + # If the promised chore is not yet complete, then do the work + # synchronously. We will wait. + def value + unless complete? + stat :sleeping_on, item_id: object_id + @mutex.synchronize do + stat :has_lock_on, item_id: object_id + chore + stat :releasing_lock_on, item_id: object_id + end + end + error? ? raise(@error) : @result + end + + # If no one else is working this promise, go ahead and do the chore. + def work + stat :attempting_lock_on, item_id: object_id + if @mutex.try_lock + stat :has_lock_on, item_id: object_id + chore + stat :releasing_lock_on, item_id: object_id + @mutex.unlock + else + stat :bailed_on, item_id: object_id + end + end + + private + + # Perform the chore promised + def chore + if complete? + stat :found_completed, item_id: object_id + return + end + stat :will_execute, item_id: object_id + begin + @result = @block.call(*@args) + rescue Exception => e + @error = e + end + stat :did_execute, item_id: object_id + discard + end + + # Do we have a result for the promise + def result? + !@result.equal?(NOT_SET) + end + + # Did the promise throw an error + def error? + !@error.equal?(NOT_SET) + end + + # Are we done with the promise + def complete? + result? || error? + end + + # free up these items for the GC + def discard + @args = nil + @block = nil + end + + # Record execution statistics if there is a recorder + def stat(*args) + @recorder.call(*args) if @recorder + end + + end + +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/pseudo_status.rb b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/pseudo_status.rb new file mode 100644 index 0000000..8b3c989 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/pseudo_status.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true +module Rake + + ## + # Exit status class for times the system just gives us a nil. + class PseudoStatus # :nodoc: all + attr_reader :exitstatus + + def initialize(code=0) + @exitstatus = code + end + + def to_i + @exitstatus << 8 + end + + def >>(n) + to_i >> n + end + + def stopped? + false + end + + def exited? + true + end + end + +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/rake_module.rb b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/rake_module.rb new file mode 100644 index 0000000..03c2956 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/rake_module.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true +require "rake/application" + +module Rake + + class << self + # Current Rake Application + def application + @application ||= Rake::Application.new + end + + # Set the current Rake application object. + def application=(app) + @application = app + end + + def suggested_thread_count # :nodoc: + @cpu_count ||= Rake::CpuCounter.count + @cpu_count + 4 + end + + # Return the original directory where the Rake application was started. + def original_dir + application.original_dir + end + + # Load a rakefile. + def load_rakefile(path) + load(path) + end + + # Add files to the rakelib list + def add_rakelib(*files) + application.options.rakelib ||= [] + application.options.rakelib.concat(files) + end + + # Make +block_application+ the default rake application inside a block so + # you can load rakefiles into a different application. + # + # This is useful when you want to run rake tasks inside a library without + # running rake in a sub-shell. + # + # Example: + # + # Dir.chdir 'other/directory' + # + # other_rake = Rake.with_application do |rake| + # rake.load_rakefile + # end + # + # puts other_rake.tasks + + def with_application(block_application = Rake::Application.new) + orig_application = Rake.application + + Rake.application = block_application + + yield block_application + + block_application + ensure + Rake.application = orig_application + end + end + +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/rake_test_loader.rb b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/rake_test_loader.rb new file mode 100644 index 0000000..05d89fc --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/rake_test_loader.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require_relative "file_list" + +# Load the test files from the command line. +argv = ARGV.select do |argument| + case argument + when /^-/ then + argument + when /\*/ then + Rake::FileList[argument].to_a.each do |file| + require File.expand_path file + end + + false + else + path = File.expand_path argument + + abort "\nFile does not exist: #{path}\n\n" unless File.exist?(path) + + require path + + false + end +end + +ARGV.replace argv diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/rule_recursion_overflow_error.rb b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/rule_recursion_overflow_error.rb new file mode 100644 index 0000000..a51e774 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/rule_recursion_overflow_error.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true +module Rake + + # Error indicating a recursion overflow error in task selection. + class RuleRecursionOverflowError < StandardError + def initialize(*args) + super + @targets = [] + end + + def add_target(target) + @targets << target + end + + def message + super + ": [" + @targets.reverse.join(" => ") + "]" + end + end + +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/scope.rb b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/scope.rb new file mode 100644 index 0000000..fc1eb6c --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/scope.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true +module Rake + class Scope < LinkedList # :nodoc: all + + # Path for the scope. + def path + map(&:to_s).reverse.join(":") + end + + # Path for the scope + the named path. + def path_with_task_name(task_name) + "#{path}:#{task_name}" + end + + # Trim +n+ innermost scope levels from the scope. In no case will + # this trim beyond the toplevel scope. + def trim(n) + result = self + while n > 0 && !result.empty? + result = result.tail + n -= 1 + end + result + end + + # Scope lists always end with an EmptyScope object. See Null + # Object Pattern) + class EmptyScope < EmptyLinkedList + @parent = Scope + + def path + "" + end + + def path_with_task_name(task_name) + task_name + end + end + + # Singleton null object for an empty scope. + EMPTY = EmptyScope.new + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/task.rb b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/task.rb new file mode 100644 index 0000000..e61ccb6 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/task.rb @@ -0,0 +1,434 @@ +# frozen_string_literal: true +require_relative "invocation_exception_mixin" + +module Rake + + ## + # A Task is the basic unit of work in a Rakefile. Tasks have associated + # actions (possibly more than one) and a list of prerequisites. When + # invoked, a task will first ensure that all of its prerequisites have an + # opportunity to run and then it will execute its own actions. + # + # Tasks are not usually created directly using the new method, but rather + # use the +file+ and +task+ convenience methods. + # + class Task + # List of prerequisites for a task. + attr_reader :prerequisites + alias prereqs prerequisites + + # List of order only prerequisites for a task. + attr_reader :order_only_prerequisites + + # List of actions attached to a task. + attr_reader :actions + + # Application owning this task. + attr_accessor :application + + # Array of nested namespaces names used for task lookup by this task. + attr_reader :scope + + # File/Line locations of each of the task definitions for this + # task (only valid if the task was defined with the detect + # location option set). + attr_reader :locations + + # Has this task already been invoked? Already invoked tasks + # will be skipped unless you reenable them. + attr_reader :already_invoked + + # Return task name + def to_s + name + end + + def inspect # :nodoc: + "<#{self.class} #{name} => [#{prerequisites.join(', ')}]>" + end + + # List of sources for task. + attr_writer :sources + def sources + if defined?(@sources) + @sources + else + prerequisites + end + end + + # List of prerequisite tasks + def prerequisite_tasks + (prerequisites + order_only_prerequisites).map { |pre| lookup_prerequisite(pre) } + end + + def lookup_prerequisite(prerequisite_name) # :nodoc: + scoped_prerequisite_task = application[prerequisite_name, @scope] + if scoped_prerequisite_task == self + unscoped_prerequisite_task = application[prerequisite_name] + end + unscoped_prerequisite_task || scoped_prerequisite_task + end + private :lookup_prerequisite + + # List of all unique prerequisite tasks including prerequisite tasks' + # prerequisites. + # Includes self when cyclic dependencies are found. + def all_prerequisite_tasks + seen = {} + collect_prerequisites(seen) + seen.values + end + + def collect_prerequisites(seen) # :nodoc: + prerequisite_tasks.each do |pre| + next if seen[pre.name] + seen[pre.name] = pre + pre.collect_prerequisites(seen) + end + end + protected :collect_prerequisites + + # First source from a rule (nil if no sources) + def source + sources.first + end + + # Create a task named +task_name+ with no actions or prerequisites. Use + # +enhance+ to add actions and prerequisites. + def initialize(task_name, app) + @name = task_name.to_s + @prerequisites = [] + @actions = [] + @already_invoked = false + @comments = [] + @lock = Monitor.new + @application = app + @scope = app.current_scope + @arg_names = nil + @locations = [] + @invocation_exception = nil + @order_only_prerequisites = [] + end + + # Enhance a task with prerequisites or actions. Returns self. + def enhance(deps=nil, &block) + @prerequisites |= deps if deps + @actions << block if block_given? + self + end + + # Name of the task, including any namespace qualifiers. + def name + @name.to_s + end + + # Name of task with argument list description. + def name_with_args # :nodoc: + if arg_description + "#{name}#{arg_description}" + else + name + end + end + + # Argument description (nil if none). + def arg_description # :nodoc: + @arg_names ? "[#{arg_names.join(',')}]" : nil + end + + # Name of arguments for this task. + def arg_names + @arg_names || [] + end + + # Reenable the task, allowing its tasks to be executed if the task + # is invoked again. + def reenable + @already_invoked = false + @invocation_exception = nil + end + + # Clear the existing prerequisites, actions, comments, and arguments of a rake task. + def clear + clear_prerequisites + clear_actions + clear_comments + clear_args + self + end + + # Clear the existing prerequisites of a rake task. + def clear_prerequisites + prerequisites.clear + self + end + + # Clear the existing actions on a rake task. + def clear_actions + actions.clear + self + end + + # Clear the existing comments on a rake task. + def clear_comments + @comments = [] + self + end + + # Clear the existing arguments on a rake task. + def clear_args + @arg_names = nil + self + end + + # Invoke the task if it is needed. Prerequisites are invoked first. + def invoke(*args) + task_args = TaskArguments.new(arg_names, args) + invoke_with_call_chain(task_args, InvocationChain::EMPTY) + end + + # Same as invoke, but explicitly pass a call chain to detect + # circular dependencies. + # + # If multiple tasks depend on this + # one in parallel, they will all fail if the first execution of + # this task fails. + def invoke_with_call_chain(task_args, invocation_chain) + new_chain = Rake::InvocationChain.append(self, invocation_chain) + @lock.synchronize do + begin + if application.options.trace + application.trace "** Invoke #{name} #{format_trace_flags}" + end + + if @already_invoked + if @invocation_exception + if application.options.trace + application.trace "** Previous invocation of #{name} failed #{format_trace_flags}" + end + raise @invocation_exception + else + return + end + end + + @already_invoked = true + + invoke_prerequisites(task_args, new_chain) + execute(task_args) if needed? + rescue Exception => ex + add_chain_to(ex, new_chain) + @invocation_exception = ex + raise ex + end + end + end + protected :invoke_with_call_chain + + def add_chain_to(exception, new_chain) # :nodoc: + exception.extend(InvocationExceptionMixin) unless + exception.respond_to?(:chain) + exception.chain = new_chain if exception.chain.nil? + end + private :add_chain_to + + # Invoke all the prerequisites of a task. + def invoke_prerequisites(task_args, invocation_chain) # :nodoc: + if application.options.always_multitask + invoke_prerequisites_concurrently(task_args, invocation_chain) + else + prerequisite_tasks.each { |p| + prereq_args = task_args.new_scope(p.arg_names) + p.invoke_with_call_chain(prereq_args, invocation_chain) + } + end + end + + # Invoke all the prerequisites of a task in parallel. + def invoke_prerequisites_concurrently(task_args, invocation_chain)# :nodoc: + futures = prerequisite_tasks.map do |p| + prereq_args = task_args.new_scope(p.arg_names) + application.thread_pool.future(p) do |r| + r.invoke_with_call_chain(prereq_args, invocation_chain) + end + end + # Iterate in reverse to improve performance related to thread waiting and switching + futures.reverse_each(&:value) + end + + # Format the trace flags for display. + def format_trace_flags + flags = [] + flags << "first_time" unless @already_invoked + flags << "not_needed" unless needed? + flags.empty? ? "" : "(" + flags.join(", ") + ")" + end + private :format_trace_flags + + # Execute the actions associated with this task. + def execute(args=nil) + args ||= EMPTY_TASK_ARGS + if application.options.dryrun + application.trace "** Execute (dry run) #{name}" + return + end + application.trace "** Execute #{name}" if application.options.trace + application.enhance_with_matching_rule(name) if @actions.empty? + if opts = Hash.try_convert(args) and !opts.empty? + @actions.each { |act| act.call(self, args, **opts) } + else + @actions.each { |act| act.call(self, args) } + end + end + + # Is this task needed? + def needed? + true + end + + # Timestamp for this task. Basic tasks return the current time for their + # time stamp. Other tasks can be more sophisticated. + def timestamp + Time.now + end + + # Add a description to the task. The description can consist of an option + # argument list (enclosed brackets) and an optional comment. + def add_description(description) + return unless description + comment = description.strip + add_comment(comment) if comment && !comment.empty? + end + + def comment=(comment) # :nodoc: + add_comment(comment) + end + + def add_comment(comment) # :nodoc: + return if comment.nil? + @comments << comment unless @comments.include?(comment) + end + private :add_comment + + # Full collection of comments. Multiple comments are separated by + # newlines. + def full_comment + transform_comments("\n") + end + + # First line (or sentence) of all comments. Multiple comments are + # separated by a "/". + def comment + transform_comments(" / ") { |c| first_sentence(c) } + end + + # Transform the list of comments as specified by the block and + # join with the separator. + def transform_comments(separator, &block) + if @comments.empty? + nil + else + block ||= lambda { |c| c } + @comments.map(&block).join(separator) + end + end + private :transform_comments + + # Get the first sentence in a string. The sentence is terminated + # by the first period, exclamation mark, or the end of the line. + # Decimal points do not count as periods. + def first_sentence(string) + string.split(/(?<=\w)(\.|!)[ \t]|(\.$|!)|\n/).first + end + private :first_sentence + + # Set the names of the arguments for this task. +args+ should be + # an array of symbols, one for each argument name. + def set_arg_names(args) + @arg_names = args.map(&:to_sym) + end + + # Return a string describing the internal state of a task. Useful for + # debugging. + def investigation + result = "------------------------------\n".dup + result << "Investigating #{name}\n" + result << "class: #{self.class}\n" + result << "task needed: #{needed?}\n" + result << "timestamp: #{timestamp}\n" + result << "pre-requisites: \n" + prereqs = prerequisite_tasks + prereqs.sort! { |a, b| a.timestamp <=> b.timestamp } + prereqs.each do |p| + result << "--#{p.name} (#{p.timestamp})\n" + end + latest_prereq = prerequisite_tasks.map(&:timestamp).max + result << "latest-prerequisite time: #{latest_prereq}\n" + result << "................................\n\n" + return result + end + + # Format dependencies parameter to pass to task. + def self.format_deps(deps) + deps = [deps] unless deps.respond_to?(:to_ary) + deps.map { |d| Rake.from_pathname(d).to_s } + end + + # Add order only dependencies. + def |(deps) + @order_only_prerequisites |= Task.format_deps(deps) - @prerequisites + self + end + + # ---------------------------------------------------------------- + # Rake Module Methods + # + class << self + + # Clear the task list. This cause rake to immediately forget all the + # tasks that have been assigned. (Normally used in the unit tests.) + def clear + Rake.application.clear + end + + # List of all defined tasks. + def tasks + Rake.application.tasks + end + + # Return a task with the given name. If the task is not currently + # known, try to synthesize one from the defined rules. If no rules are + # found, but an existing file matches the task name, assume it is a file + # task with no dependencies or actions. + def [](task_name) + Rake.application[task_name] + end + + # TRUE if the task name is already defined. + def task_defined?(task_name) + Rake.application.lookup(task_name) != nil + end + + # Define a task given +args+ and an option block. If a rule with the + # given name already exists, the prerequisites and actions are added to + # the existing task. Returns the defined task. + def define_task(*args, &block) + Rake.application.define_task(self, *args, &block) + end + + # Define a rule for synthesizing tasks. + def create_rule(*args, &block) + Rake.application.create_rule(*args, &block) + end + + # Apply the scope to the task name according to the rules for + # this kind of task. Generic tasks will accept the scope as + # part of the name. + def scope_name(scope, task_name) + scope.path_with_task_name(task_name) + end + + end # class << Rake::Task + end # class Rake::Task +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/task_argument_error.rb b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/task_argument_error.rb new file mode 100644 index 0000000..ef20076 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/task_argument_error.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true +module Rake + + # Error indicating an ill-formed task declaration. + class TaskArgumentError < ArgumentError + end + +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/task_arguments.rb b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/task_arguments.rb new file mode 100644 index 0000000..24abebc --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/task_arguments.rb @@ -0,0 +1,113 @@ +# frozen_string_literal: true +module Rake + + ## + # TaskArguments manage the arguments passed to a task. + # + class TaskArguments + include Enumerable + + # Argument names + attr_reader :names + + # Create a TaskArgument object with a list of argument +names+ and a set + # of associated +values+. +parent+ is the parent argument object. + def initialize(names, values, parent=nil) + @names = names + @parent = parent + @hash = {} + @values = values + names.each_with_index { |name, i| + next if values[i].nil? || values[i] == "" + @hash[name.to_sym] = values[i] + } + end + + # Retrieve the complete array of sequential values + def to_a + @values.dup + end + + # Retrieve the list of values not associated with named arguments + def extras + @values[@names.length..-1] || [] + end + + # Create a new argument scope using the prerequisite argument + # names. + def new_scope(names) + values = names.map { |n| self[n] } + self.class.new(names, values + extras, self) + end + + # Find an argument value by name or index. + def [](index) + lookup(index.to_sym) + end + + # Specify a hash of default values for task arguments. Use the + # defaults only if there is no specific value for the given + # argument. + def with_defaults(defaults) + @hash = defaults.merge(@hash) + end + + # Enumerates the arguments and their values + def each(&block) + @hash.each(&block) + end + + # Extracts the argument values at +keys+ + def values_at(*keys) + keys.map { |k| lookup(k) } + end + + # Returns the value of the given argument via method_missing + def method_missing(sym, *args) + lookup(sym.to_sym) + end + + # Returns a Hash of arguments and their values + def to_hash + @hash.dup + end + + def to_s # :nodoc: + inspect + end + + def inspect # :nodoc: + inspection = @hash.map do |k,v| + "#{k.to_s}: #{v.to_s}" + end.join(", ") + + "#<#{self.class} #{inspection}>" + end + + # Returns true if +key+ is one of the arguments + def has_key?(key) + @hash.has_key?(key) + end + alias key? has_key? + + def fetch(*args, &block) + @hash.fetch(*args, &block) + end + + def deconstruct_keys(keys) + keys ? @hash.slice(*keys) : to_hash + end + + protected + + def lookup(name) # :nodoc: + if @hash.has_key?(name) + @hash[name] + elsif @parent + @parent.lookup(name) + end + end + end + + EMPTY_TASK_ARGS = TaskArguments.new([], []) # :nodoc: +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/task_manager.rb b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/task_manager.rb new file mode 100644 index 0000000..838779c --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/task_manager.rb @@ -0,0 +1,331 @@ +# frozen_string_literal: true +module Rake + + # The TaskManager module is a mixin for managing tasks. + module TaskManager + # Track the last comment made in the Rakefile. + attr_accessor :last_description + + def initialize # :nodoc: + super + @tasks = Hash.new + @rules = Array.new + @scope = Scope.make + @last_description = nil + end + + def create_rule(*args, &block) # :nodoc: + pattern, args, deps, order_only = resolve_args(args) + pattern = Regexp.new(Regexp.quote(pattern) + "$") if String === pattern + @rules << [pattern, args, deps, order_only, block] + end + + def define_task(task_class, *args, &block) # :nodoc: + task_name, arg_names, deps, order_only = resolve_args(args) + + original_scope = @scope + if String === task_name and + not task_class.ancestors.include? Rake::FileTask + task_name, *definition_scope = *(task_name.split(":").reverse) + @scope = Scope.make(*(definition_scope + @scope.to_a)) + end + + task_name = task_class.scope_name(@scope, task_name) + task = intern(task_class, task_name) + task.set_arg_names(arg_names) unless arg_names.empty? + if Rake::TaskManager.record_task_metadata + add_location(task) + task.add_description(get_description) + end + task.enhance(Task.format_deps(deps), &block) + task | order_only unless order_only.nil? + task + ensure + @scope = original_scope + end + + # Lookup a task. Return an existing task if found, otherwise + # create a task of the current type. + def intern(task_class, task_name) + @tasks[task_name.to_s] ||= task_class.new(task_name, self) + end + + # Find a matching task for +task_name+. + def [](task_name, scopes=nil) + task_name = task_name.to_s + self.lookup(task_name, scopes) or + enhance_with_matching_rule(task_name) or + synthesize_file_task(task_name) or + fail generate_message_for_undefined_task(task_name) + end + + def generate_message_for_undefined_task(task_name) + message = "Don't know how to build task '#{task_name}' "\ + "(See the list of available tasks with `#{Rake.application.name} --tasks`)" + message + generate_did_you_mean_suggestions(task_name) + end + + def generate_did_you_mean_suggestions(task_name) + return "" unless defined?(::DidYouMean::SpellChecker) + + suggestions = ::DidYouMean::SpellChecker.new(dictionary: @tasks.keys).correct(task_name.to_s) + if ::DidYouMean.respond_to?(:formatter)# did_you_mean v1.2.0 or later + ::DidYouMean.formatter.message_for(suggestions) + elsif defined?(::DidYouMean::Formatter) # before did_you_mean v1.2.0 + ::DidYouMean::Formatter.new(suggestions).to_s + else + "" + end + end + + def synthesize_file_task(task_name) # :nodoc: + return nil unless File.exist?(task_name) + define_task(Rake::FileTask, task_name) + end + + # Resolve the arguments for a task/rule. Returns a tuple of + # [task_name, arg_name_list, prerequisites, order_only_prerequisites]. + def resolve_args(args) + if args.last.is_a?(Hash) + deps = args.pop + resolve_args_with_dependencies(args, deps) + else + resolve_args_without_dependencies(args) + end + end + + # Resolve task arguments for a task or rule when there are no + # dependencies declared. + # + # The patterns recognized by this argument resolving function are: + # + # task :t + # task :t, [:a] + # + def resolve_args_without_dependencies(args) + task_name = args.shift + if args.size == 1 && args.first.respond_to?(:to_ary) + arg_names = args.first.to_ary + else + arg_names = args + end + [task_name, arg_names, [], nil] + end + private :resolve_args_without_dependencies + + # Resolve task arguments for a task or rule when there are + # dependencies declared. + # + # The patterns recognized by this argument resolving function are: + # + # task :t, order_only: [:e] + # task :t => [:d] + # task :t => [:d], order_only: [:e] + # task :t, [a] => [:d] + # task :t, [a] => [:d], order_only: [:e] + # + def resolve_args_with_dependencies(args, hash) # :nodoc: + fail "Task Argument Error" if + hash.size != 1 && + (hash.size != 2 || !hash.key?(:order_only)) + order_only = hash.delete(:order_only) + key, value = hash.map { |k, v| [k, v] }.first + if args.empty? + task_name = key + arg_names = [] + deps = value || [] + else + task_name = args.shift + arg_names = key || args.shift|| [] + deps = value || [] + end + deps = [deps] unless deps.respond_to?(:to_ary) + [task_name, arg_names, deps, order_only] + end + private :resolve_args_with_dependencies + + # If a rule can be found that matches the task name, enhance the + # task with the prerequisites and actions from the rule. Set the + # source attribute of the task appropriately for the rule. Return + # the enhanced task or nil of no rule was found. + def enhance_with_matching_rule(task_name, level=0) + fail Rake::RuleRecursionOverflowError, + "Rule Recursion Too Deep" if level >= 16 + @rules.each do |pattern, args, extensions, order_only, block| + if pattern && pattern.match(task_name) + task = attempt_rule(task_name, pattern, args, extensions, block, level) + task | order_only unless order_only.nil? + return task if task + end + end + nil + rescue Rake::RuleRecursionOverflowError => ex + ex.add_target(task_name) + fail ex + end + + # List of all defined tasks in this application. + def tasks + @tasks.values.sort_by { |t| t.name } + end + + # List of all the tasks defined in the given scope (and its + # sub-scopes). + def tasks_in_scope(scope) + prefix = scope.path + tasks.select { |t| + /^#{prefix}:/ =~ t.name + } + end + + # Clear all tasks in this application. + def clear + @tasks.clear + @rules.clear + end + + # Lookup a task, using scope and the scope hints in the task name. + # This method performs straight lookups without trying to + # synthesize file tasks or rules. Special scope names (e.g. '^') + # are recognized. If no scope argument is supplied, use the + # current scope. Return nil if the task cannot be found. + def lookup(task_name, initial_scope=nil) + initial_scope ||= @scope + task_name = task_name.to_s + if task_name =~ /^rake:/ + scopes = Scope.make + task_name = task_name.sub(/^rake:/, "") + elsif task_name =~ /^(\^+)/ + scopes = initial_scope.trim($1.size) + task_name = task_name.sub(/^(\^+)/, "") + else + scopes = initial_scope + end + lookup_in_scope(task_name, scopes) + end + + # Lookup the task name + def lookup_in_scope(name, scope) + loop do + tn = scope.path_with_task_name(name) + task = @tasks[tn] + return task if task + break if scope.empty? + scope = scope.tail + end + nil + end + private :lookup_in_scope + + # Return the list of scope names currently active in the task + # manager. + def current_scope + @scope + end + + # Evaluate the block in a nested namespace named +name+. Create + # an anonymous namespace if +name+ is nil. + def in_namespace(name) + name ||= generate_name + @scope = Scope.new(name, @scope) + ns = NameSpace.new(self, @scope) + yield(ns) + ns + ensure + @scope = @scope.tail + end + + private + + # Add a location to the locations field of the given task. + def add_location(task) + loc = find_location + task.locations << loc if loc + task + end + + # Find the location that called into the dsl layer. + def find_location + locations = caller + i = 0 + while locations[i] + return locations[i + 1] if locations[i] =~ /rake\/dsl_definition.rb/ + i += 1 + end + nil + end + + # Generate an anonymous namespace name. + def generate_name + @seed ||= 0 + @seed += 1 + "_anon_#{@seed}" + end + + def trace_rule(level, message) # :nodoc: + options.trace_output.puts "#{" " * level}#{message}" if + Rake.application.options.trace_rules + end + + # Attempt to create a rule given the list of prerequisites. + def attempt_rule(task_name, task_pattern, args, extensions, block, level) + sources = make_sources(task_name, task_pattern, extensions) + prereqs = sources.map { |source| + trace_rule level, "Attempting Rule #{task_name} => #{source}" + if File.exist?(source) || Rake::Task.task_defined?(source) + trace_rule level, "(#{task_name} => #{source} ... EXIST)" + source + elsif parent = enhance_with_matching_rule(source, level + 1) + trace_rule level, "(#{task_name} => #{source} ... ENHANCE)" + parent.name + else + trace_rule level, "(#{task_name} => #{source} ... FAIL)" + return nil + end + } + task = FileTask.define_task(task_name, { args => prereqs }, &block) + task.sources = prereqs + task + end + + # Make a list of sources from the list of file name extensions / + # translation procs. + def make_sources(task_name, task_pattern, extensions) + result = extensions.map { |ext| + case ext + when /%/ + task_name.pathmap(ext) + when %r{/} + ext + when /^\./ + source = task_name.sub(task_pattern, ext) + source == ext ? task_name.ext(ext) : source + when String, Symbol + ext.to_s + when Proc, Method + if ext.arity == 1 + ext.call(task_name) + else + ext.call + end + else + fail "Don't know how to handle rule dependent: #{ext.inspect}" + end + } + result.flatten + end + + # Return the current description, clearing it in the process. + def get_description + desc = @last_description + @last_description = nil + desc + end + + class << self + attr_accessor :record_task_metadata # :nodoc: + TaskManager.record_task_metadata = false + end + end + +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/tasklib.rb b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/tasklib.rb new file mode 100644 index 0000000..597a2d6 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/tasklib.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true +require_relative "../rake" + +module Rake + + # Base class for Task Libraries. + class TaskLib + include Cloneable + include Rake::DSL + end + +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/testtask.rb b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/testtask.rb new file mode 100644 index 0000000..7cf1ece --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/testtask.rb @@ -0,0 +1,189 @@ +# frozen_string_literal: true +require_relative "../rake" +require_relative "tasklib" + +module Rake + + # Create a task that runs a set of tests. + # + # Example: + # require "rake/testtask" + # + # Rake::TestTask.new do |t| + # t.libs << "test" + # t.test_files = FileList['test/test*.rb'] + # t.verbose = true + # end + # + # If rake is invoked with a "TEST=filename" command line option, + # then the list of test files will be overridden to include only the + # filename specified on the command line. This provides an easy way + # to run just one test. + # + # If rake is invoked with a "TESTOPTS=options" command line option, + # then the given options are passed to the test process after a + # '--'. This allows Test::Unit options to be passed to the test + # suite. + # + # Examples: + # + # rake test # run tests normally + # rake test TEST=just_one_file.rb # run just one test file. + # rake test TESTOPTS="-v" # run in verbose mode + # rake test TESTOPTS="--runner=fox" # use the fox test runner + # + class TestTask < TaskLib + + # Name of test task. (default is :test) + attr_accessor :name + + # List of directories added to $LOAD_PATH before running the + # tests. (default is 'lib') + attr_accessor :libs + + # True if verbose test output desired. (default is false) + attr_accessor :verbose + + # Test options passed to the test suite. An explicit + # TESTOPTS=opts on the command line will override this. (default + # is NONE) + attr_accessor :options + + # Request that the tests be run with the warning flag set. + # E.g. warning=true implies "ruby -w" used to run the tests. + # (default is true) + attr_accessor :warning + + # Glob pattern to match test files. (default is 'test/test*.rb') + attr_accessor :pattern + + # Style of test loader to use. Options are: + # + # * :rake -- Rake provided test loading script (default). + # * :testrb -- Ruby provided test loading script. + # * :direct -- Load tests using command line loader. + # + attr_accessor :loader + + # Array of command line options to pass to ruby when running test loader. + attr_accessor :ruby_opts + + # Description of the test task. (default is 'Run tests') + attr_accessor :description + + # Task prerequisites. + attr_accessor :deps + + # Explicitly define the list of test files to be included in a + # test. +list+ is expected to be an array of file names (a + # FileList is acceptable). If both +pattern+ and +test_files+ are + # used, then the list of test files is the union of the two. + def test_files=(list) + @test_files = list + end + + # Create a testing task. + def initialize(name=:test) + @name = name + @libs = ["lib"] + @pattern = nil + @options = nil + @test_files = nil + @verbose = false + @warning = true + @loader = :rake + @ruby_opts = [] + @description = "Run tests" + (@name == :test ? "" : " for #{@name}") + @deps = [] + if @name.is_a?(Hash) + @deps = @name.values.first + @name = @name.keys.first + end + yield self if block_given? + @pattern = "test/test*.rb" if @pattern.nil? && @test_files.nil? + define + end + + # Create the tasks defined by this task lib. + def define + desc @description + task @name => Array(deps) do + FileUtilsExt.verbose(@verbose) do + puts "Use TESTOPTS=\"--verbose\" to pass --verbose" \ + ", etc. to runners." if ARGV.include? "--verbose" + args = + "#{ruby_opts_string} #{run_code} " + + "#{file_list_string} #{option_list}" + ruby args do |ok, status| + if !ok && status.respond_to?(:signaled?) && status.signaled? + raise SignalException.new(status.termsig) + elsif !ok + status = "Command failed with status (#{status.exitstatus})" + details = ": [ruby #{args}]" + message = + if Rake.application.options.trace or @verbose + status + details + else + status + end + + fail message + end + end + end + end + self + end + + def option_list # :nodoc: + (ENV["TESTOPTS"] || + ENV["TESTOPT"] || + ENV["TEST_OPTS"] || + ENV["TEST_OPT"] || + @options || + "") + end + + def ruby_opts_string # :nodoc: + opts = @ruby_opts.dup + opts.unshift("-I\"#{lib_path}\"") unless @libs.empty? + opts.unshift("-w") if @warning + opts.join(" ") + end + + def lib_path # :nodoc: + @libs.join(File::PATH_SEPARATOR) + end + + def file_list_string # :nodoc: + file_list.map { |fn| "\"#{fn}\"" }.join(" ") + end + + def file_list # :nodoc: + if ENV["TEST"] + FileList[ENV["TEST"]] + else + result = [] + result += @test_files.to_a if @test_files + result += FileList[@pattern].to_a if @pattern + result + end + end + + def ruby_version # :nodoc: + RUBY_VERSION + end + + def run_code # :nodoc: + case @loader + when :direct + "-e \"ARGV.each{|f| require f}\"" + when :testrb + "-S testrb" + when :rake + "#{__dir__}/rake_test_loader.rb" + end + end + + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/thread_history_display.rb b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/thread_history_display.rb new file mode 100644 index 0000000..50e2bc8 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/thread_history_display.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true +require_relative "private_reader" + +module Rake + + class ThreadHistoryDisplay # :nodoc: all + include Rake::PrivateReader + + private_reader :stats, :items, :threads + + def initialize(stats) + @stats = stats + @items = { _seq_: 1 } + @threads = { _seq_: "A" } + end + + def show + puts "Job History:" + stats.each do |stat| + stat[:data] ||= {} + rename(stat, :thread, threads) + rename(stat[:data], :item_id, items) + rename(stat[:data], :new_thread, threads) + rename(stat[:data], :deleted_thread, threads) + printf("%8d %2s %-20s %s\n", + (stat[:time] * 1_000_000).round, + stat[:thread], + stat[:event], + stat[:data].map do |k, v| "#{k}:#{v}" end.join(" ")) + end + end + + private + + def rename(hash, key, renames) + if hash && hash[key] + original = hash[key] + value = renames[original] + unless value + value = renames[:_seq_] + renames[:_seq_] = renames[:_seq_].succ + renames[original] = value + end + hash[key] = value + end + end + end + +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/thread_pool.rb b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/thread_pool.rb new file mode 100644 index 0000000..ea9c0ae --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/thread_pool.rb @@ -0,0 +1,157 @@ +# frozen_string_literal: true + +require_relative "promise" +require "set" + +module Rake + + class ThreadPool # :nodoc: all + + # Creates a ThreadPool object. The +thread_count+ parameter is the size + # of the pool. + def initialize(thread_count) + @max_active_threads = [thread_count, 0].max + @threads = Set.new + @threads_mon = Monitor.new + @queue = Queue.new + @join_cond = @threads_mon.new_cond + + @history_start_time = nil + @history = [] + @history_mon = Monitor.new + @total_threads_in_play = 0 + end + + # Creates a future executed by the +ThreadPool+. + # + # The args are passed to the block when executing (similarly to + # Thread#new) The return value is an object representing + # a future which has been created and added to the queue in the + # pool. Sending #value to the object will sleep the + # current thread until the future is finished and will return the + # result (or raise an exception thrown from the future) + def future(*args, &block) + promise = Promise.new(args, &block) + promise.recorder = lambda { |*stats| stat(*stats) } + + @queue.enq promise + stat :queued, item_id: promise.object_id + start_thread + promise + end + + # Waits until the queue of futures is empty and all threads have exited. + def join + @threads_mon.synchronize do + begin + stat :joining + @join_cond.wait unless @threads.empty? + stat :joined + rescue Exception => e + stat :joined + $stderr.puts e + $stderr.print "Queue contains #{@queue.size} items. " + + "Thread pool contains #{@threads.count} threads\n" + $stderr.print "Current Thread #{Thread.current} status = " + + "#{Thread.current.status}\n" + $stderr.puts e.backtrace.join("\n") + @threads.each do |t| + $stderr.print "Thread #{t} status = #{t.status}\n" + $stderr.puts t.backtrace.join("\n") + end + raise e + end + end + end + + # Enable the gathering of history events. + def gather_history #:nodoc: + @history_start_time = Time.now if @history_start_time.nil? + end + + # Return a array of history events for the thread pool. + # + # History gathering must be enabled to be able to see the events + # (see #gather_history). Best to call this when the job is + # complete (i.e. after ThreadPool#join is called). + def history # :nodoc: + @history_mon.synchronize { @history.dup }. + sort_by { |i| i[:time] }. + each { |i| i[:time] -= @history_start_time } + end + + # Return a hash of always collected statistics for the thread pool. + def statistics # :nodoc: + { + total_threads_in_play: @total_threads_in_play, + max_active_threads: @max_active_threads, + } + end + + private + + # processes one item on the queue. Returns true if there was an + # item to process, false if there was no item + def process_queue_item #:nodoc: + return false if @queue.empty? + + # Even though we just asked if the queue was empty, it + # still could have had an item which by this statement + # is now gone. For this reason we pass true to Queue#deq + # because we will sleep indefinitely if it is empty. + promise = @queue.deq(true) + stat :dequeued, item_id: promise.object_id + promise.work + return true + + rescue ThreadError # this means the queue is empty + false + end + + def start_thread # :nodoc: + @threads_mon.synchronize do + next unless @threads.count < @max_active_threads + + t = Thread.new do + begin + loop do + break unless process_queue_item + end + ensure + @threads_mon.synchronize do + @threads.delete Thread.current + stat :ended, thread_count: @threads.count + @join_cond.broadcast if @threads.empty? + end + end + end + + @threads << t + stat( + :spawned, + new_thread: t.object_id, + thread_count: @threads.count) + @total_threads_in_play = @threads.count if + @threads.count > @total_threads_in_play + end + end + + def stat(event, data=nil) # :nodoc: + return if @history_start_time.nil? + info = { + event: event, + data: data, + time: Time.now, + thread: Thread.current.object_id, + } + @history_mon.synchronize { @history << info } + end + + # for testing only + + def __queue__ # :nodoc: + @queue + end + end + +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/trace_output.rb b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/trace_output.rb new file mode 100644 index 0000000..d713a09 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/trace_output.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true +module Rake + module TraceOutput # :nodoc: all + + # Write trace output to output stream +out+. + # + # The write is done as a single IO call (to print) to lessen the + # chance that the trace output is interrupted by other tasks also + # producing output. + def trace_on(out, *strings) + sep = $\ || "\n" + if strings.empty? + output = sep + else + output = strings.map { |s| + next if s.nil? + s.end_with?(sep) ? s : s + sep + }.join + end + out.print(output) + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/version.rb b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/version.rb new file mode 100644 index 0000000..01df37d --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/version.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true +module Rake + VERSION = "13.3.1" + + module Version # :nodoc: all + MAJOR, MINOR, BUILD, *OTHER = Rake::VERSION.split "." + + NUMBERS = [MAJOR, MINOR, BUILD, *OTHER] + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/win32.rb b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/win32.rb new file mode 100644 index 0000000..6e62031 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/lib/rake/win32.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true +require "rbconfig" + +module Rake + # Win 32 interface methods for Rake. Windows specific functionality + # will be placed here to collect that knowledge in one spot. + module Win32 # :nodoc: all + + # Error indicating a problem in locating the home directory on a + # Win32 system. + class Win32HomeError < RuntimeError + end + + class << self + # True if running on a windows system. + def windows? + RbConfig::CONFIG["host_os"] =~ %r!(msdos|mswin|djgpp|mingw|[Ww]indows)! + end + + # The standard directory containing system wide rake files on + # Win 32 systems. Try the following environment variables (in + # order): + # + # * HOME + # * HOMEDRIVE + HOMEPATH + # * APPDATA + # * USERPROFILE + # + # If the above are not defined, the return nil. + def win32_system_dir #:nodoc: + win32_shared_path = ENV["HOME"] + if win32_shared_path.nil? && ENV["HOMEDRIVE"] && ENV["HOMEPATH"] + win32_shared_path = ENV["HOMEDRIVE"] + ENV["HOMEPATH"] + end + + win32_shared_path ||= ENV["APPDATA"] + win32_shared_path ||= ENV["USERPROFILE"] + raise Win32HomeError, + "Unable to determine home path environment variable." if + win32_shared_path.nil? or win32_shared_path.empty? + normalize(File.join(win32_shared_path, "Rake")) + end + + # Normalize a win32 path so that the slashes are all forward slashes. + def normalize(path) + path.gsub(/\\/, "/") + end + + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/rake.gemspec b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/rake.gemspec new file mode 100644 index 0000000..5899790 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rake-13.3.1/rake.gemspec @@ -0,0 +1,101 @@ +# frozen_string_literal: true + +require_relative "lib/rake/version" + +Gem::Specification.new do |s| + s.name = "rake" + s.version = Rake::VERSION + s.authors = ["Hiroshi SHIBATA", "Eric Hodel", "Jim Weirich"] + s.email = ["hsbt@ruby-lang.org", "drbrain@segment7.net", ""] + + s.summary = "Rake is a Make-like program implemented in Ruby" + s.description = <<~DESCRIPTION + Rake is a Make-like program implemented in Ruby. Tasks and dependencies are + specified in standard Ruby syntax. + Rake has the following features: + * Rakefiles (rake's version of Makefiles) are completely defined in standard Ruby syntax. + No XML files to edit. No quirky Makefile syntax to worry about (is that a tab or a space?) + * Users can specify tasks with prerequisites. + * Rake supports rule patterns to synthesize implicit tasks. + * Flexible FileLists that act like arrays but know about manipulating file names and paths. + * Supports parallel execution of tasks. + DESCRIPTION + s.homepage = "https://github.com/ruby/rake" + s.licenses = ["MIT"] + + s.metadata = { + "bug_tracker_uri" => "https://github.com/ruby/rake/issues", + "changelog_uri" => "https://github.com/ruby/rake/releases", + "documentation_uri" => "https://ruby.github.io/rake", + "source_code_uri" => s.homepage + } + + s.files = [ + "History.rdoc", + "MIT-LICENSE", + "README.rdoc", + "doc/command_line_usage.rdoc", + "doc/example/Rakefile1", + "doc/example/Rakefile2", + "doc/example/a.c", + "doc/example/b.c", + "doc/example/main.c", + "doc/glossary.rdoc", + "doc/jamis.rb", + "doc/proto_rake.rdoc", + "doc/rake.1", + "doc/rakefile.rdoc", + "doc/rational.rdoc", + "exe/rake", + "lib/rake.rb", + "lib/rake/application.rb", + "lib/rake/backtrace.rb", + "lib/rake/clean.rb", + "lib/rake/cloneable.rb", + "lib/rake/cpu_counter.rb", + "lib/rake/default_loader.rb", + "lib/rake/dsl_definition.rb", + "lib/rake/early_time.rb", + "lib/rake/ext/core.rb", + "lib/rake/ext/string.rb", + "lib/rake/file_creation_task.rb", + "lib/rake/file_list.rb", + "lib/rake/file_task.rb", + "lib/rake/file_utils.rb", + "lib/rake/file_utils_ext.rb", + "lib/rake/invocation_chain.rb", + "lib/rake/invocation_exception_mixin.rb", + "lib/rake/late_time.rb", + "lib/rake/linked_list.rb", + "lib/rake/loaders/makefile.rb", + "lib/rake/multi_task.rb", + "lib/rake/name_space.rb", + "lib/rake/packagetask.rb", + "lib/rake/phony.rb", + "lib/rake/private_reader.rb", + "lib/rake/promise.rb", + "lib/rake/pseudo_status.rb", + "lib/rake/rake_module.rb", + "lib/rake/rake_test_loader.rb", + "lib/rake/rule_recursion_overflow_error.rb", + "lib/rake/scope.rb", + "lib/rake/task.rb", + "lib/rake/task_argument_error.rb", + "lib/rake/task_arguments.rb", + "lib/rake/task_manager.rb", + "lib/rake/tasklib.rb", + "lib/rake/testtask.rb", + "lib/rake/thread_history_display.rb", + "lib/rake/thread_pool.rb", + "lib/rake/trace_output.rb", + "lib/rake/version.rb", + "lib/rake/win32.rb", + "rake.gemspec" + ] + s.bindir = "exe" + s.executables = s.files.grep(%r{^exe/}) { |f| File.basename(f) } + s.require_paths = ["lib"] + + s.required_ruby_version = Gem::Requirement.new(">= 2.3") + s.rdoc_options = ["--main", "README.rdoc"] +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/LICENSE.txt b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/LICENSE.txt new file mode 100644 index 0000000..a009cae --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/LICENSE.txt @@ -0,0 +1,22 @@ +Copyright (C) 1993-2013 Yukihiro Matsumoto. 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. diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/NEWS.md b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/NEWS.md new file mode 100644 index 0000000..1462712 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/NEWS.md @@ -0,0 +1,843 @@ +# News + +## 3.4.4 - 2025-09-10 {#version-3-4-4} + +### Improvement + + * Accept `REXML::Document.new("")` for backward compatibility + * GH-296 + * GH-295 + * Patch by NAITOH Jun + * Reported by Joe Rafaniello + +### Thanks + + * NAITOH Jun + + * Joe Rafaniello + +## 3.4.3 - 2025-09-07 {#version-3-4-3} + +### Improvement + + * Reject no root element XML as an invalid XML + * GH-289 + * GH-291 + * Patch by NAITOH Jun + * Reported by Sutou Kouhei + +### Fixes + + * Fixed an issue with `IOSource#read_until` when reaching the end of a file + * GH-287 + * GH-288 + * Patch by NAITOH Jun + * Reported by Jason Thomas + +### Thanks + + * NAITOH Jun + + * Sutou Kouhei + + * Jason Thomas + +## 3.4.2 - 2025-08-26 {#version-3-4-2} + +### Improvement + + * Improved performance. + * GH-244 + * GH-245 + * GH-246 + * GH-249 + * GH-256 + * Patch by NAITOH Jun + + * Raise appropriate exception when failing to match start tag in DOCTYPE + * GH-247 + * Patch by NAITOH Jun + + * Deprecate accepting array as an element in XPath.match, first and each + * GH-252 + * Patch by tomoya ishida + + * Don't call needless encoding_updated + * GH-259 + * Patch by Sutou Kouhei + + * Reuse XPath::match + * GH-263 + * Patch by pboling + + * Cache redundant calls for doctype + * GH-264 + * Patch by pboling + + * Use Safe Navigation (&.) from Ruby 2.3 + * GH-265 + * Patch by pboling + + * Remove redundant return statements + * GH-266 + * Patch by pboling + + * Added XML declaration check & Source#skip_spaces method + * GH-282 + * Patch by NAITOH Jun + * Reported by Sofi Aberegg + +### Fixes + + * Fix docs typo + * GH-248 + * Patch by James Coleman + + * Fix reverse sort in xpath_parser + * GH-251 + * Patch by tomoya ishida + + * Fix duplicate responses in XPath following, following-sibling, preceding, preceding-sibling + * GH-255 + * Patch by NAITOH Jun + + * Fix wrong Encoding resolution + * GH-258 + * Patch by Sutou Kouhei + + * Handle nil when parsing fragment + * GH-267 + * GH-268 + * Patch by pboling + + * [Documentation] Use # to reference instance methods + * GH-269 + * GH-270 + * Patch by pboling + + * Fix & Deprecate REXML::Text#text_indent + * GH-273 + * GH-275 + * Patch by pboling + + * remove bundler from dev deps + * GH-276 + * GH-277 + * Patch by pboling + + * remove ostruct from dev deps + * GH-280 + * GH-281 + * Patch by pboling + +### Thanks + + * NAITOH Jun + + * tomoya ishida + + * James Coleman + + * pboling + + * Sutou Kouhei + + * Sofi Aberegg + +## 3.4.1 - 2025-02-16 {#version-3-4-1} + +### Improvement + + * Improved performance. + * GH-226 + * GH-227 + * GH-237 + * Patch by NAITOH Jun + +### Fixes + + * Fix serialization of ATTLIST is incorrect + * GH-233 + * GH-234 + * Patch by OlofKalufs + * Reported by OlofKalufs + +### Thanks + + * NAITOH Jun + + * OlofKalufs + +## 3.4.0 - 2024-12-15 {#version-3-4-0} + +### Improvement + + * Improved performance. + * GH-216 + * Patch by NAITOH Jun + + * JRuby: Improved parse performance. + * GH-219 + * Patch by João Duarte + + * Added support for reusing pull parser. + * GH-214 + * GH-220 + * Patch by Dmitry Pogrebnoy + + * Improved error handling when source is `IO`. + * GH-221 + * Patch by NAITOH Jun + +### Thanks + + * NAITOH Jun + + * João Duarte + + * Dmitry Pogrebnoy + +## 3.3.9 - 2024-10-24 {#version-3-3-9} + +### Improvements + + * Improved performance. + * GH-210 + * Patch by NAITOH Jun. + +### Fixes + + * Fixed a parse bug for text only invalid XML. + * GH-215 + * Patch by NAITOH Jun. + + * Fixed a parse bug that `�x...;` is accepted as a character + reference. + +### Thanks + + * NAITOH Jun + +## 3.3.8 - 2024-09-29 {#version-3-3-8} + +### Improvements + + * SAX2: Improve parse performance. + * GH-207 + * Patch by NAITOH Jun. + +### Fixes + + * Fixed a bug that unexpected attribute namespace conflict error for + the predefined "xml" namespace is reported. + * GH-208 + * Patch by KITAITI Makoto + +### Thanks + + * NAITOH Jun + + * KITAITI Makoto + +## 3.3.7 - 2024-09-04 {#version-3-3-7} + +### Improvements + + * Added local entity expansion limit methods + * GH-192 + * GH-202 + * Reported by takuya kodama. + * Patch by NAITOH Jun. + + * Removed explicit strscan dependency + * GH-204 + * Patch by Bo Anderson. + +### Thanks + + * takuya kodama + + * NAITOH Jun + + * Bo Anderson + +## 3.3.6 - 2024-08-22 {#version-3-3-6} + +### Improvements + + * Removed duplicated entity expansions for performance. + * GH-194 + * Patch by Viktor Ivarsson. + + * Improved namespace conflicted attribute check performance. It was + too slow for deep elements. + * Reported by l33thaxor. + +### Fixes + + * Fixed a bug that default entity expansions are counted for + security check. Default entity expansions should not be counted + because they don't have a security risk. + * GH-198 + * GH-199 + * Patch Viktor Ivarsson + + * Fixed a parser bug that parameter entity references in internal + subsets are expanded. It's not allowed in the XML specification. + * GH-191 + * Patch by NAITOH Jun. + + * Fixed a stream parser bug that user-defined entity references in + text aren't expanded. + * GH-200 + * Patch by NAITOH Jun. + +### Thanks + + * Viktor Ivarsson + + * NAITOH Jun + + * l33thaxor + +## 3.3.5 - 2024-08-12 {#version-3-3-5} + +### Fixes + + * Fixed a bug that `REXML::Security.entity_expansion_text_limit` + check has wrong text size calculation in SAX and pull parsers. + * GH-193 + * GH-195 + * Reported by Viktor Ivarsson. + * Patch by NAITOH Jun. + +### Thanks + + * Viktor Ivarsson + + * NAITOH Jun + +## 3.3.4 - 2024-08-01 {#version-3-3-4} + +### Fixes + + * Fixed a bug that `REXML::Security` isn't defined when + `REXML::Parsers::StreamParser` is used and + `rexml/parsers/streamparser` is only required. + * GH-189 + * Patch by takuya kodama. + +### Thanks + + * takuya kodama + +## 3.3.3 - 2024-08-01 {#version-3-3-3} + +### Improvements + + * Added support for detecting invalid XML that has unsupported + content before root element + * GH-184 + * Patch by NAITOH Jun. + + * Added support for `REXML::Security.entity_expansion_limit=` and + `REXML::Security.entity_expansion_text_limit=` in SAX2 and pull + parsers + * GH-187 + * Patch by NAITOH Jun. + + * Added more tests for invalid XMLs. + * GH-183 + * Patch by Watson. + + * Added more performance tests. + * Patch by Watson. + + * Improved parse performance. + * GH-186 + * Patch by tomoya ishida. + +### Thanks + + * NAITOH Jun + + * Watson + + * tomoya ishida + +## 3.3.2 - 2024-07-16 {#version-3-3-2} + +### Improvements + + * Improved parse performance. + * GH-160 + * Patch by NAITOH Jun. + + * Improved parse performance. + * GH-169 + * GH-170 + * GH-171 + * GH-172 + * GH-173 + * GH-174 + * GH-175 + * GH-176 + * GH-177 + * Patch by Watson. + + * Added support for raising a parse exception when an XML has extra + content after the root element. + * GH-161 + * Patch by NAITOH Jun. + + * Added support for raising a parse exception when an XML + declaration exists in wrong position. + * GH-162 + * Patch by NAITOH Jun. + + * Removed needless a space after XML declaration in pretty print mode. + * GH-164 + * Patch by NAITOH Jun. + + * Stopped to emit `:text` event after the root element. + * GH-167 + * Patch by NAITOH Jun. + +### Fixes + + * Fixed a bug that SAX2 parser doesn't expand predefined entities for + `characters` callback. + * GH-168 + * Patch by NAITOH Jun. + +### Thanks + + * NAITOH Jun + + * Watson + +## 3.3.1 - 2024-06-25 {#version-3-3-1} + +### Improvements + + * Added support for detecting malformed top-level comments. + * GH-145 + * Patch by Hiroya Fujinami. + + * Improved `REXML::Element#attribute` performance. + * GH-146 + * Patch by Hiroya Fujinami. + + * Added support for detecting malformed `` comments. + * GH-147 + * Patch by Hiroya Fujinami. + + * Added support for detecting unclosed `DOCTYPE`. + * GH-152 + * Patch by Hiroya Fujinami. + + * Added `changlog_uri` metadata to gemspec. + * GH-156 + * Patch by fynsta. + + * Improved parse performance. + * GH-157 + * GH-158 + * Patch by NAITOH Jun. + +### Fixes + + * Fixed a bug that large XML can't be parsed. + * GH-154 + * Patch by NAITOH Jun. + + * Fixed a bug that private constants are visible. + * GH-155 + * Patch by NAITOH Jun. + +### Thanks + + * Hiroya Fujinami + + * NAITOH Jun + + * fynsta + +## 3.3.0 - 2024-06-11 {#version-3-3-0} + +### Improvements + + * Added support for strscan 0.7.0 installed with Ruby 2.6. + * GH-142 + * Reported by Fernando Trigoso. + +### Thanks + + * Fernando Trigoso + +## 3.2.9 - 2024-06-09 {#version-3-2-9} + +### Improvements + + * Added support for old strscan. + * GH-132 + * Reported by Adam. + + * Improved attribute value parse performance. + * GH-135 + * Patch by NAITOH Jun. + + * Improved `REXML::Node#each_recursive` performance. + * GH-134 + * GH-139 + * Patch by Hiroya Fujinami. + + * Improved text parse performance. + * Reported by mprogrammer. + +### Thanks + + * Adam + * NAITOH Jun + * Hiroya Fujinami + * mprogrammer + +## 3.2.8 - 2024-05-16 {#version-3-2-8} + +### Fixes + + * Suppressed a warning + +## 3.2.7 - 2024-05-16 {#version-3-2-7} + +### Improvements + + * Improve parse performance by using `StringScanner`. + + * GH-106 + * GH-107 + * GH-108 + * GH-109 + * GH-112 + * GH-113 + * GH-114 + * GH-115 + * GH-116 + * GH-117 + * GH-118 + * GH-119 + * GH-121 + + * Patch by NAITOH Jun. + + * Improved parse performance when an attribute has many `>`s. + + * GH-126 + +### Fixes + + * XPath: Fixed a bug of `normalize_space(array)`. + + * GH-110 + * GH-111 + + * Patch by flatisland. + + * XPath: Fixed a bug that wrong position is used with nested path. + + * GH-110 + * GH-122 + + * Reported by jcavalieri. + * Patch by NAITOH Jun. + + * Fixed a bug that an exception message can't be generated for + invalid encoding XML. + + * GH-29 + * GH-123 + + * Reported by DuKewu. + * Patch by NAITOH Jun. + +### Thanks + + * NAITOH Jun + * flatisland + * jcavalieri + * DuKewu + +## 3.2.6 - 2023-07-27 {#version-3-2-6} + +### Improvements + + * Required Ruby 2.5 or later explicitly. + [GH-69][gh-69] + [Patch by Ivo Anjo] + + * Added documentation for maintenance cycle. + [GH-71][gh-71] + [Patch by Ivo Anjo] + + * Added tutorial. + [GH-77][gh-77] + [GH-78][gh-78] + [Patch by Burdette Lamar] + + * Improved performance and memory usage. + [GH-94][gh-94] + [Patch by fatkodima] + + * `REXML::Parsers::XPathParser#abbreviate`: Added support for + function arguments. + [GH-95][gh-95] + [Reported by pulver] + + * `REXML::Parsers::XPathParser#abbreviate`: Added support for string + literal that contains double-quote. + [GH-96][gh-96] + [Patch by pulver] + + * `REXML::Parsers::XPathParser#abbreviate`: Added missing `/` to + `:descendant_or_self/:self/:parent`. + [GH-97][gh-97] + [Reported by pulver] + + * `REXML::Parsers::XPathParser#abbreviate`: Added support for more patterns. + [GH-97][gh-97] + [Reported by pulver] + +### Fixes + + * Fixed a typo in NEWS. + [GH-72][gh-72] + [Patch by Spencer Goodman] + + * Fixed a typo in NEWS. + [GH-75][gh-75] + [Patch by Andrew Bromwich] + + * Fixed documents. + [GH-87][gh-87] + [Patch by Alexander Ilyin] + + * Fixed a bug that `Attriute` convert `'` and `'` even when + `attribute_quote: :quote` is used. + [GH-92][gh-92] + [Reported by Edouard Brière] + + * Fixed links in tutorial. + [GH-99][gh-99] + [Patch by gemmaro] + + +### Thanks + + * Ivo Anjo + + * Spencer Goodman + + * Andrew Bromwich + + * Burdette Lamar + + * Alexander Ilyin + + * Edouard Brière + + * fatkodima + + * pulver + + * gemmaro + +[gh-69]:https://github.com/ruby/rexml/issues/69 +[gh-71]:https://github.com/ruby/rexml/issues/71 +[gh-72]:https://github.com/ruby/rexml/issues/72 +[gh-75]:https://github.com/ruby/rexml/issues/75 +[gh-77]:https://github.com/ruby/rexml/issues/77 +[gh-87]:https://github.com/ruby/rexml/issues/87 +[gh-92]:https://github.com/ruby/rexml/issues/92 +[gh-94]:https://github.com/ruby/rexml/issues/94 +[gh-95]:https://github.com/ruby/rexml/issues/95 +[gh-96]:https://github.com/ruby/rexml/issues/96 +[gh-97]:https://github.com/ruby/rexml/issues/97 +[gh-98]:https://github.com/ruby/rexml/issues/98 +[gh-99]:https://github.com/ruby/rexml/issues/99 + +## 3.2.5 - 2021-04-05 {#version-3-2-5} + +### Improvements + + * Add more validations to XPath parser. + + * `require "rexml/document"` by default. + [GitHub#36][Patch by Koichi ITO] + + * Don't add `#dclone` method to core classes globally. + [GitHub#37][Patch by Akira Matsuda] + + * Add more documentations. + [Patch by Burdette Lamar] + + * Added `REXML::Elements#parent`. + [GitHub#52][Patch by Burdette Lamar] + +### Fixes + + * Fixed a bug that `REXML::DocType#clone` doesn't copy external ID + information. + + * Fixed round-trip vulnerability bugs. + See also: https://www.ruby-lang.org/en/news/2021/04/05/xml-round-trip-vulnerability-in-rexml-cve-2021-28965/ + [HackerOne#1104077][CVE-2021-28965][Reported by Juho Nurminen] + +### Thanks + + * Koichi ITO + + * Akira Matsuda + + * Burdette Lamar + + * Juho Nurminen + +## 3.2.4 - 2020-01-31 {#version-3-2-4} + +### Improvements + + * Don't use `taint` with Ruby 2.7 or later. + [GitHub#21][Patch by Jeremy Evans] + +### Fixes + + * Fixed a `elsif` typo. + [GitHub#22][Patch by Nobuyoshi Nakada] + +### Thanks + + * Jeremy Evans + + * Nobuyoshi Nakada + +## 3.2.3 - 2019-10-12 {#version-3-2-3} + +### Fixes + + * Fixed a bug that `REXML::XMLDecl#close` doesn't copy `@writethis`. + [GitHub#20][Patch by hirura] + +### Thanks + + * hirura + +## 3.2.2 - 2019-06-03 {#version-3-2-2} + +### Fixes + + * xpath: Fixed a bug for equality and relational expressions. + [GitHub#17][Reported by Mirko Budszuhn] + + * xpath: Fixed `boolean()` implementation. + + * xpath: Fixed `local_name()` with nonexistent node. + + * xpath: Fixed `number()` implementation with node set. + [GitHub#18][Reported by Mirko Budszuhn] + +### Thanks + + * Mirko Budszuhn + +## 3.2.1 - 2019-05-04 {#version-3-2-1} + +### Improvements + + * Improved error message. + [GitHub#12][Patch by FUJI Goro] + + * Improved error message. + [GitHub#16][Patch by ujihisa] + + * Improved documentation markup. + [GitHub#14][Patch by Alyssa Ross] + +### Fixes + + * Fixed a bug that `nil` variable value raises an unexpected exception. + [GitHub#13][Patch by Alyssa Ross] + +### Thanks + + * FUJI Goro + + * Alyssa Ross + + * ujihisa + +## 3.2.0 - 2019-01-01 {#version-3-2-0} + +### Fixes + + * Fixed a bug that no namespace attribute isn't matched with prefix. + + [ruby-list:50731][Reported by Yasuhiro KIMURA] + + * Fixed a bug that the default namespace is applied to attribute names. + + NOTE: It's a backward incompatible change. If your program has any + problem with this change, please report it. We may revert this fix. + + * `REXML::Attribute#prefix` returns `""` for no namespace attribute. + + * `REXML::Attribute#namespace` returns `""` for no namespace attribute. + +### Thanks + + * Yasuhiro KIMURA + +## 3.1.9 - 2018-12-20 {#version-3-1-9} + +### Improvements + + * Improved backward compatibility. + + Restored `REXML::Parsers::BaseParser::UNQME_STR` because it's used + by kramdown. + +## 3.1.8 - 2018-12-20 {#version-3-1-8} + +### Improvements + + * Added support for customizing quote character in prologue. + [GitHub#8][Bug #9367][Reported by Takashi Oguma] + + * You can use `"` as quote character by specifying `:quote` to + `REXML::Document#context[:prologue_quote]`. + + * You can use `'` as quote character by specifying `:apostrophe` + to `REXML::Document#context[:prologue_quote]`. + + * Added processing instruction target check. The target must not nil. + [GitHub#7][Reported by Ariel Zelivansky] + + * Added name check for element and attribute. + [GitHub#7][Reported by Ariel Zelivansky] + + * Stopped to use `Exception`. + [GitHub#9][Patch by Jean Boussier] + +### Fixes + + * Fixed a bug that `REXML::Text#clone` escapes value twice. + [ruby-dev:50626][Bug #15058][Reported by Ryosuke Nanba] + +### Thanks + + * Takashi Oguma + + * Ariel Zelivansky + + * Jean Boussier + + * Ryosuke Nanba diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/README.md b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/README.md new file mode 100644 index 0000000..e8ab508 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/README.md @@ -0,0 +1,57 @@ +# REXML + +REXML was inspired by the Electric XML library for Java, which features an easy-to-use API, small size, and speed. Hopefully, REXML, designed with the same philosophy, has these same features. I've tried to keep the API as intuitive as possible, and have followed the Ruby methodology for method naming and code flow, rather than mirroring the Java API. + +REXML supports both tree and stream document parsing. Stream parsing is faster (about 1.5 times as fast). However, with stream parsing, you don't get access to features such as XPath. + +## API + +See the [API documentation](https://ruby.github.io/rexml/). + +## Usage + +We'll start with parsing an XML document + +```ruby +require "rexml/document" +file = File.new( "mydoc.xml" ) +doc = REXML::Document.new file +``` + +Line 3 creates a new document and parses the supplied file. You can also do the following + +```ruby +require "rexml/document" +include REXML # so that we don't have to prefix everything with REXML::... +string = < + Text, text, text + +EOF +doc = Document.new string +``` + +So parsing a string is just as easy as parsing a file. + +## Support + +REXML support follows the same maintenance cycle as Ruby releases, as shown on . + +If you are running on an end-of-life Ruby, do not expect modern REXML releases to be compatible with it; in fact, it's recommended that you DO NOT use this gem, and instead use the REXML version that came bundled with your end-of-life Ruby version. + +The `required_ruby_version` on the gemspec is kept updated on a [best-effort basis](https://github.com/ruby/rexml/pull/70) by the community. +Up to version 3.2.5, this information was not set. That version [is known broken with at least Ruby < 2.3](https://github.com/ruby/rexml/issues/69). + +## Development + +After checking out the repo, run `rake test` to run the tests. + +To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). + +## Contributing + +Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/rexml. + +## License + +The gem is available as open source under the terms of the [BSD-2-Clause](LICENSE.txt). diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/doc/rexml/context.rdoc b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/doc/rexml/context.rdoc new file mode 100644 index 0000000..7ef01f7 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/doc/rexml/context.rdoc @@ -0,0 +1,143 @@ +== Element Context + +Notes: +- All code on this page presupposes that the following has been executed: + + require 'rexml/document' + +- For convenience, examples on this page use +REXML::Document.new+, not +REXML::Element.new+. + This is completely valid, because REXML::Document is a subclass of REXML::Element. + +The context for an element is a hash of processing directives +that influence the way \XML is read, stored, and written. +The context entries are: + +- +:respect_whitespace+: controls treatment of whitespace. +- +:compress_whitespace+: determines whether whitespace is compressed. +- +:ignore_whitespace_nodes+: determines whether whitespace-only nodes are to be ignored. +- +:raw+: controls treatment of special characters and entities. + +The default context for a new element is {}. +You can set the context at element-creation time: + + d = REXML::Document.new('', {compress_whitespace: :all, raw: :all}) + d.context # => {:compress_whitespace=>:all, :raw=>:all} + +You can reset the entire context by assigning a new hash: + + d.context = {ignore_whitespace_nodes: :all} + d.context # => {:ignore_whitespace_nodes=>:all} + +Or you can create or modify an individual entry: + + d.context[:raw] = :all + d.context # => {:ignore_whitespace_nodes=>:all, :raw=>:all} + +=== +:respect_whitespace+ + +Affects: +REXML::Element.new+, +REXML::Element.text=+. + +By default, all parsed whitespace is respected (that is, stored whitespace not compressed): + + xml_string = 'a b c d e f' + d = REXML::Document.new(xml_string) + d.to_s # => "a b c d e f" + +Use +:respect_whitespace+ with an array of element names +to specify the elements that _are_ to have their whitespace respected; +other elements' whitespace, and whitespace between elements, will be compressed. + +In this example: +foo+ and +baz+ will have their whitespace respected; ++bar+ and the space between elements will have their whitespace compressed: + + d = REXML::Document.new(xml_string, {respect_whitespace: ['foo', 'baz']}) + d.to_s # => "a b c d e f" + bar = d.root[2] # => ... + bar.text = 'X Y' + d.to_s # => "a b X Y e f" + +=== +:compress_whitespace+ + +Affects: +REXML::Element.new+, +REXML::Element.text=+. + +Use compress_whitespace: :all +to compress whitespace both within and between elements: + + xml_string = 'a b c d e f' + d = REXML::Document.new(xml_string, {compress_whitespace: :all}) + d.to_s # => "a b c d e f" + +Use +:compress_whitespace+ with an array of element names +to compress whitespace in those elements, +but not in other elements nor between elements. + +In this example, +foo+ and +baz+ will have their whitespace compressed; ++bar+ and the space between elements will not: + + d = REXML::Document.new(xml_string, {compress_whitespace: ['foo', 'baz']}) + d.to_s # => "a b c d e f" + foo = d.root[0] # => ... + foo.text= 'X Y' + d.to_s # => "X Y c d e f" + +=== +:ignore_whitespace_nodes+ + +Affects: +REXML::Element.new+. + +Use ignore_whitespace_nodes: :all to omit all whitespace-only elements. + +In this example, +bar+ has a text node, while nodes +foo+ and +baz+ do not: + + xml_string = ' BAR ' + d = REXML::Document.new(xml_string, {ignore_whitespace_nodes: :all}) + d.to_s # => " FOO BAZ " + root = d.root # => ... + foo = root[0] # => + bar = root[1] # => ... + baz = root[2] # => + foo.first.class # => NilClass + bar.first.class # => REXML::Text + baz.first.class # => NilClass + +Use +:ignore_whitespace_nodes+ with an array of element names +to specify the elements that are to have whitespace nodes ignored. + +In this example, +bar+ and +baz+ have text nodes, while node +foo+ does not. + + xml_string = ' BAR ' + d = REXML::Document.new(xml_string, {ignore_whitespace_nodes: ['foo']}) + d.to_s # => " BAR " + root = d.root # => ... + foo = root[0] # => + bar = root[1] # => ... + baz = root[2] # => ... + foo.first.class # => NilClass + bar.first.class # => REXML::Text + baz.first.class # => REXML::Text + +=== +:raw+ + +Affects: +Element.text=+, +Element.add_text+, +Text.to_s+. + +Parsing of +a+ elements is not affected by +raw+: + + xml_string = '0 < 11 > 0' + d = REXML::Document.new(xml_string, {:raw => ['a']}) + d.root.to_s # => "0 < 11 > 0" + a, b = *d.root.elements + a.to_s # => "0 < 1" + b.to_s # => "1 > 0" + +But Element#text= is affected: + + a.text = '0 < 1' + b.text = '1 > 0' + a.to_s # => "0 < 1" + b.to_s # => "1 &gt; 0" + +As is Element.add_text: + + a.add_text(' so 1 > 0') + b.add_text(' so 0 < 1') + a.to_s # => "0 < 1 so 1 > 0" + b.to_s # => "1 &gt; 0 so 0 &lt; 1" diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/doc/rexml/tasks/rdoc/child.rdoc b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/doc/rexml/tasks/rdoc/child.rdoc new file mode 100644 index 0000000..8953638 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/doc/rexml/tasks/rdoc/child.rdoc @@ -0,0 +1,87 @@ +== Class Child + +Class Child includes module Node; +see {Tasks for Node}[node_rdoc.html]. + +:include: ../tocs/child_toc.rdoc + +=== Relationships + +==== Task: Set the Parent + +Use method {Child#parent=}[../../../../REXML/Parent.html#method-i-parent-3D] +to set the parent: + + e0 = REXML::Element.new('foo') + e1 = REXML::Element.new('bar') + e1.parent # => nil + e1.parent = e0 + e1.parent # => + +==== Task: Insert Previous Sibling + +Use method {Child#previous_sibling=}[../../../../REXML/Parent.html#method-i-previous_sibling-3D] +to insert a previous sibling: + + xml_string = '' + d = REXML::Document.new(xml_string) + d.root.to_a # => [, ] + c = d.root[1] # => + b = REXML::Element.new('b') + c.previous_sibling = b + d.root.to_a # => [, , ] + +==== Task: Insert Next Sibling + +Use method {Child#next_sibling=}[../../../../REXML/Parent.html#method-i-next-sibling-3D] +to insert a previous sibling: + + xml_string = '' + d = REXML::Document.new(xml_string) + d.root.to_a # => [, ] + a = d.root[0] # => + b = REXML::Element.new('b') + a.next_sibling = b + d.root.to_a # => [, , ] + +=== Removal or Replacement + +==== Task: Remove Child from Parent + +Use method {Child#remove}[../../../../REXML/Parent.html#method-i-remove] +to remove a child from its parent; returns the removed child: + + xml_string = '' + d = REXML::Document.new(xml_string) + d.root.to_a # => [, , ] + b = d.root[1] # => + b.remove # => + d.root.to_a # => [, ] + +==== Task: Replace Child + +Use method {Child#replace_with}[../../../../REXML/Parent.html#method-i-replace] +to replace a child; +returns the replaced child: + + xml_string = '' + d = REXML::Document.new(xml_string) + d.root.to_a # => [, , ] + b = d.root[1] # => + d = REXML::Element.new('d') + b.replace_with(d) # => + d.root.to_a # => [, , ] + +=== Document + +==== Task: Get the Document + +Use method {Child#document}[../../../../REXML/Parent.html#method-i-document] +to get the document for the child: + + xml_string = '' + d = REXML::Document.new(xml_string) + d.root.to_a # => [, , ] + b = d.root[1] # => + b.document == d # => true + REXML::Child.new.document # => nil diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/doc/rexml/tasks/rdoc/document.rdoc b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/doc/rexml/tasks/rdoc/document.rdoc new file mode 100644 index 0000000..96d0335 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/doc/rexml/tasks/rdoc/document.rdoc @@ -0,0 +1,276 @@ +== Class Document + +Class Document has methods from its superclasses and included modules; +see: + +- {Tasks for Element}[element_rdoc.html]. +- {Tasks for Parent}[parent_rdoc.html]. +- {Tasks for Child}[child_rdoc.html]. +- {Tasks for Node}[node_rdoc.html]. +- {Module Enumerable}[https://docs.ruby-lang.org/en/master/Enumerable.html]. + +:include: ../tocs/document_toc.rdoc + +=== New Document + +==== Task: Create an Empty Document + +Use method {Document::new}[../../../../REXML/Document.html#method-c-new] +to create an empty document. + + d = REXML::Document.new + +==== Task: Parse a \String into a New Document + +Use method {Document::new}[../../../../REXML/Document.html#method-c-new] +to parse an XML string into a new document: + + xml_string = 'textmore' + d = REXML::Document.new(xml_string) + d.root # => ... + +==== Task: Parse an \IO Stream into a New Document + +Use method {Document::new}[../../../../REXML/Document.html#method-c-new] +to parse an XML \IO stream into a new document: + + xml_string = 'textmore' + File.write('t.xml', xml_string) + d = File.open('t.xml', 'r') do |file| + REXML::Document.new(file) + end + d.root # => ... + +==== Task: Create a Document from an Existing Document + +Use method {Document::new}[../../../../REXML/Document.html#method-c-new] +to create a document from an existing document. +The context and attributes are copied to the new document, +but not the children: + + xml_string = 'textmore' + d = REXML::Document.new(xml_string) + d.children # => [ ... ] + d.context = {raw: :all, compress_whitespace: :all} + d.add_attributes({'bar' => 0, 'baz' => 1}) + d1 = REXML::Document.new(d) + d1.context # => {:raw=>:all, :compress_whitespace=>:all} + d1.attributes # => {"bar"=>bar='0', "baz"=>baz='1'} + d1.children # => [] + +==== Task: Clone a Document + +Use method {Document#clone}[../../../../REXML/Document.html#method-i-clone] +to clone a document. +The context and attributes are copied to the new document, +but not the children: + + xml_string = 'textmore' + d = REXML::Document.new(xml_string) + d.children # => [ ... ] + d.context = {raw: :all, compress_whitespace: :all} + d.add_attributes({'bar' => 0, 'baz' => 1}) + d1 = d.clone # => < bar='0' baz='1'/> + d1.context # => {:raw=>:all, :compress_whitespace=>:all} + d1.attributes # => {"bar"=>bar='0', "baz"=>baz='1'} + d1.children # => [] + +=== Document Type + +==== Task: Get the Document Type + +Use method {Document#doctype}[../../../../REXML/Document.html#method-i-doctype] +to get the document type: + + d = REXML::Document.new('') + d.doctype.class # => REXML::DocType + d = REXML::Document.new('') + d.doctype.class # => nil + +==== Task: Set the Document Type + +Use method {document#add}[../../../../REXML/Document.html#method-i-add] +to add or replace the document type: + + d = REXML::Document.new('') + d.doctype.class # => nil + d.add(REXML::DocType.new('foo')) + d.doctype.class # => REXML::DocType + +=== XML Declaration + +==== Task: Get the XML Declaration + +Use method {document#xml_decl}[../../../../REXML/Document.html#method-i-xml_decl] +to get the XML declaration: + + d = REXML::Document.new('') + d.xml_decl.class # => REXML::XMLDecl + d.xml_decl # => + d = REXML::Document.new('') + d.xml_decl.class # => REXML::XMLDecl + d.xml_decl # => + +==== Task: Set the XML Declaration + +Use method {document#add}[../../../../REXML/Document.html#method-i-add] +to replace the XML declaration: + + d = REXML::Document.new('') + d.add(REXML::XMLDecl.new) + +=== Children + +==== Task: Add an Element Child + +Use method +{document#add_element}[../../../../REXML/Document.html#method-i-add_element] +to add an element to the document: + + d = REXML::Document.new('') + d.add_element(REXML::Element.new('root')) + d.children # => [] + +==== Task: Add a Non-Element Child + +Use method +{document#add}[../../../../REXML/Document.html#method-i-add] +to add a non-element to the document: + + xml_string = 'textmore' + d = REXML::Document.new(xml_string) + d.add(REXML::Text.new('foo')) + d.children # => [ ... , "foo"] + +=== Writing + +==== Task: Write to $stdout + +Use method +{document#write}[../../../../REXML/Document.html#method-i-write] +to write the document to $stdout: + + xml_string = 'textmore' + d = REXML::Document.new(xml_string) + d.write + +Output: + + textmore + +==== Task: Write to IO Stream + +Use method +{document#write}[../../../../REXML/Document.html#method-i-write] +to write the document to $stdout: + + xml_string = 'textmore' + d = REXML::Document.new(xml_string) + File.open('t.xml', 'w') do |file| + d.write(file) + end + p File.read('t.xml') + +Output: + + "textmore" + +==== Task: Write with No Indentation + +Use method +{document#write}[../../../../REXML/Document.html#method-i-write] +to write the document with no indentation: + + xml_string = '' + d = REXML::Document.new(xml_string) + d.write({indent: 0}) + +Output: + + + + + + + + + +==== Task: Write with Specified Indentation + +Use method +{document#write}[../../../../REXML/Document.html#method-i-write] +to write the document with a specified indentation: + + xml_string = '' + d = REXML::Document.new(xml_string) + d.write({indent: 2}) + +Output: + + + + + + + + + +=== Querying + +==== Task: Get the Document + +Use method +{document#document}[../../../../REXML/Document.html#method-i-document] +to get the document (+self+); overrides Element#document: + + xml_string = '' + d = REXML::Document.new(xml_string) + d.document == d # => true + +==== Task: Get the Encoding + +Use method +{document#document}[../../../../REXML/Document.html#method-i-document] +to get the document (+self+); overrides Element#document: + + xml_string = '' + d = REXML::Document.new(xml_string) + d.encoding # => "UTF-8" + +==== Task: Get the Node Type + +Use method +{document#node_type}[../../../../REXML/Document.html#method-i-node_type] +to get the node type (+:document+); overrides Element#node_type: + + xml_string = '' + d = REXML::Document.new(xml_string) + d.node_type # => :document + +==== Task: Get the Root Element + +Use method +{document#root}[../../../../REXML/Document.html#method-i-root] +to get the root element: + + xml_string = '' + d = REXML::Document.new(xml_string) + d.root # => ... + +==== Task: Determine Whether Stand-Alone + +Use method +{document#stand_alone?}[../../../../REXML/Document.html#method-i-stand_alone-3F] +to get the stand-alone value: + + d = REXML::Document.new('') + d.stand_alone? # => "yes" + +==== Task: Get the Version + +Use method +{document#version}[../../../../REXML/Document.html#method-i-version] +to get the version: + + d = REXML::Document.new('') + d.version # => "2.0" diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/doc/rexml/tasks/rdoc/element.rdoc b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/doc/rexml/tasks/rdoc/element.rdoc new file mode 100644 index 0000000..4b3609b --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/doc/rexml/tasks/rdoc/element.rdoc @@ -0,0 +1,602 @@ +== Class Element + +Class Element has methods from its superclasses and included modules; +see: + +- {Tasks for Parent}[parent_rdoc.html]. +- {Tasks for Child}[child_rdoc.html]. +- {Tasks for Node}[node_rdoc.html]. +- {Module Enumerable}[https://docs.ruby-lang.org/en/master/Enumerable.html]. + +:include: ../tocs/element_toc.rdoc + +=== New Element + +==== Task: Create a Default Element + +Use method +{Element::new}[../../../../REXML/Element.html#method-c-new] +with no arguments to create a default element: + + e = REXML::Element.new + e.name # => "UNDEFINED" + e.parent # => nil + e.context # => nil + +==== Task: Create a Named Element + +Use method +{Element::new}[../../../../REXML/Element.html#method-c-new] +with a string name argument +to create a named element: + + e = REXML::Element.new('foo') + e.name # => "foo" + e.parent # => nil + e.context # => nil + +==== Task: Create an Element with Name and Parent + +Use method +{Element::new}[../../../../REXML/Element.html#method-c-new] +with name and parent arguments +to create an element with name and parent: + + p = REXML::Parent.new + e = REXML::Element.new('foo', p) + e.name # => "foo" + e.parent # => #]> + e.context # => nil + +==== Task: Create an Element with Name, Parent, and Context + +Use method +{Element::new}[../../../../REXML/Element.html#method-c-new] +with name, parent, and context arguments +to create an element with name, parent, and context: + + p = REXML::Parent.new + e = REXML::Element.new('foo', p, {compress_whitespace: :all}) + e.name # => "foo" + e.parent # => #]> + e.context # => {:compress_whitespace=>:all} + +==== Task: Create a Shallow Clone + +Use method +{Element#clone}[../../../../REXML/Element.html#method-i-clone] +to create a shallow clone of an element, +copying only the name, attributes, and context: + + e0 = REXML::Element.new('foo', nil, {compress_whitespace: :all}) + e0.add_attribute(REXML::Attribute.new('bar', 'baz')) + e0.context = {compress_whitespace: :all} + e1 = e0.clone # => + e1.name # => "foo" + e1.context # => {:compress_whitespace=>:all} + +=== Attributes + +==== Task: Create and Add an Attribute + +Use method +{Element#add_attribute}[../../../../REXML/Element.html#method-i-add_attribute] +to create and add an attribute: + + e = REXML::Element.new + e.add_attribute('attr', 'value') # => "value" + e['attr'] # => "value" + e.add_attribute('attr', 'VALUE') # => "VALUE" + e['attr'] # => "VALUE" + +==== Task: Add an Existing Attribute + +Use method +{Element#add_attribute}[../../../../REXML/Element.html#method-i-add_attribute] +to add an existing attribute: + + e = REXML::Element.new + a = REXML::Attribute.new('attr', 'value') + e.add_attribute(a) + e['attr'] # => "value" + a = REXML::Attribute.new('attr', 'VALUE') + e.add_attribute(a) + e['attr'] # => "VALUE" + +==== Task: Add Multiple Attributes from a Hash + +Use method +{Element#add_attributes}[../../../../REXML/Element.html#method-i-add_attributes] +to add multiple attributes from a hash: + + e = REXML::Element.new + h = {'foo' => 0, 'bar' => 1} + e.add_attributes(h) + e['foo'] # => "0" + e['bar'] # => "1" + +==== Task: Add Multiple Attributes from an Array + +Use method +{Element#add_attributes}[../../../../REXML/Element.html#method-i-add_attributes] +to add multiple attributes from an array: + + e = REXML::Element.new + a = [['foo', 0], ['bar', 1]] + e.add_attributes(a) + e['foo'] # => "0" + e['bar'] # => "1" + +==== Task: Retrieve the Value for an Attribute Name + +Use method +{Element#[]}[../../../../REXML/Element.html#method-i-5B-5D] +to retrieve the value for an attribute name: + + e = REXML::Element.new + e.add_attribute('attr', 'value') # => "value" + e['attr'] # => "value" + +==== Task: Retrieve the Attribute Value for a Name and Namespace + +Use method +{Element#attribute}[../../../../REXML/Element.html#method-i-attribute] +to retrieve the value for an attribute name: + + xml_string = "" + d = REXML::Document.new(xml_string) + e = d.root + e.attribute("x") # => x='x' + e.attribute("x", "a") # => a:x='a:x' + +==== Task: Delete an Attribute + +Use method +{Element#delete_attribute}[../../../../REXML/Element.html#method-i-delete_attribute] +to remove an attribute: + + e = REXML::Element.new('foo') + e.add_attribute('bar', 'baz') + e.delete_attribute('bar') + e.delete_attribute('bar') + e['bar'] # => nil + +==== Task: Determine Whether the Element Has Attributes + +Use method +{Element#has_attributes?}[../../../../REXML/Element.html#method-i-has_attributes-3F] +to determine whether the element has attributes: + + e = REXML::Element.new('foo') + e.has_attributes? # => false + e.add_attribute('bar', 'baz') + e.has_attributes? # => true + +=== Children + +Element Children + +==== Task: Create and Add an Element + +Use method +{Element#add_element}[../../../../REXML/Element.html#method-i-add_element] +to create a new element and add it to this element: + + e0 = REXML::Element.new('foo') + e0.add_element('bar') + e0.children # => [] + +==== Task: Add an Existing Element + +Use method +{Element#add_element}[../../../../REXML/Element.html#method-i-add_element] +to add an element to this element: + + e0 = REXML::Element.new('foo') + e1 = REXML::Element.new('bar') + e0.add_element(e1) + e0.children # => [] + +==== Task: Create and Add an Element with Attributes + +Use method +{Element#add_element}[../../../../REXML/Element.html#method-i-add_element] +to create a new element with attributes, and add it to this element: + + e0 = REXML::Element.new('foo') + e0.add_element('bar', {'name' => 'value'}) + e0.children # => [] + +==== Task: Add an Existing Element with Added Attributes + +Use method +{Element#add_element}[../../../../REXML/Element.html#method-i-add_element] +to add an element to this element: + + e0 = REXML::Element.new('foo') + e1 = REXML::Element.new('bar') + e0.add_element(e1, {'name' => 'value'}) + e0.children # => [] + +==== Task: Delete a Specified Element + +Use method +{Element#delete_element}[../../../../REXML/Element.html#method-i-delete_element] +to remove a specified element from this element: + + e0 = REXML::Element.new('foo') + e1 = REXML::Element.new('bar') + e0.add_element(e1) + e0.children # => [] + e0.delete_element(e1) + e0.children # => [] + +==== Task: Delete an Element by Index + +Use method +{Element#delete_element}[../../../../REXML/Element.html#method-i-delete_element] +to remove an element from this element by index: + + e0 = REXML::Element.new('foo') + e1 = REXML::Element.new('bar') + e0.add_element(e1) + e0.children # => [] + e0.delete_element(1) + e0.children # => [] + +==== Task: Delete an Element by XPath + +Use method +{Element#delete_element}[../../../../REXML/Element.html#method-i-delete_element] +to remove an element from this element by XPath: + + e0 = REXML::Element.new('foo') + e1 = REXML::Element.new('bar') + e0.add_element(e1) + e0.children # => [] + e0.delete_element('//bar/') + e0.children # => [] + +==== Task: Determine Whether Element Children + +Use method +{Element#has_elements?}[../../../../REXML/Element.html#method-i-has_elements-3F] +to determine whether the element has element children: + + e0 = REXML::Element.new('foo') + e0.has_elements? # => false + e0.add_element(REXML::Element.new('bar')) + e0.has_elements? # => true + +==== Task: Get Element Descendants by XPath + +Use method +{Element#get_elements}[../../../../REXML/Element.html#method-i-get_elements] +to fetch all element descendant children by XPath: + + xml_string = <<-EOT + + + + + + EOT + d = REXML::Document.new(xml_string) + d.root.get_elements('//a') # => [ ... , ] + +==== Task: Get Next Element Sibling + +Use method +{Element#next_element}[../../../../REXML/Element.html#method-i-next_element] +to retrieve the next element sibling: + + d = REXML::Document.new 'text' + d.root.elements['b'].next_element #-> + d.root.elements['c'].next_element #-> nil + +==== Task: Get Previous Element Sibling + +Use method +{Element#previous_element}[../../../../REXML/Element.html#method-i-previous_element] +to retrieve the previous element sibling: + + d = REXML::Document.new 'text' + d.root.elements['c'].previous_element #-> + d.root.elements['b'].previous_element #-> nil + +Text Children + +==== Task: Add a Text Node + +Use method +{Element#add_text}[../../../../REXML/Element.html#method-i-add_text] +to add a text node to the element: + + d = REXML::Document.new('foobar') + e = d.root + e.add_text(REXML::Text.new('baz')) + e.to_a # => ["foo", , "bar", "baz"] + e.add_text(REXML::Text.new('baz')) + e.to_a # => ["foo", , "bar", "baz", "baz"] + +==== Task: Replace the First Text Node + +Use method +{Element#text=}[../../../../REXML/Element.html#method-i-text-3D] +to replace the first text node in the element: + + d = REXML::Document.new('textmore') + e = d.root + e.to_a # => [, "text", , "more", ] + e.text = 'oops' + e.to_a # => [, "oops", , "more", ] + +==== Task: Remove the First Text Node + +Use method +{Element#text=}[../../../../REXML/Element.html#method-i-text-3D] +to remove the first text node in the element: + + d = REXML::Document.new('textmore') + e = d.root + e.to_a # => [, "text", , "more", ] + e.text = nil + e.to_a # => [, , "more", ] + +==== Task: Retrieve the First Text Node + +Use method +{Element#get_text}[../../../../REXML/Element.html#method-i-get_text] +to retrieve the first text node in the element: + + d = REXML::Document.new('textmore') + e = d.root + e.to_a # => [, "text", , "more", ] + e.get_text # => "text" + +==== Task: Retrieve a Specific Text Node + +Use method +{Element#get_text}[../../../../REXML/Element.html#method-i-get_text] +to retrieve the first text node in a specified element: + + d = REXML::Document.new "some text this is bold! more text" + e = d.root + e.get_text('//root') # => "some text " + e.get_text('//b') # => "this is bold!" + +==== Task: Determine Whether the Element has Text Nodes + +Use method +{Element#has_text?}[../../../../REXML/Element.html#method-i-has_text-3F] +to determine whether the element has text: + + e = REXML::Element.new('foo') + e.has_text? # => false + e.add_text('bar') + e.has_text? # => true + +Other Children + +==== Task: Get the Child at a Given Index + +Use method +{Element#[]}[../../../../REXML/Element.html#method-i-5B-5D] +to retrieve the child at a given index: + + d = REXML::Document.new '>textmore' + e = d.root + e[0] # => + e[1] # => "text" + e[2] # => + +==== Task: Get All CDATA Children + +Use method +{Element#cdatas}[../../../../REXML/Element.html#method-i-cdatas] +to retrieve all CDATA children: + + xml_string = <<-EOT + + + + + EOT + d = REXML::Document.new(xml_string) + d.root.cdatas # => ["foo", "bar"] + +==== Task: Get All Comment Children + +Use method +{Element#comments}[../../../../REXML/Element.html#method-i-comments] +to retrieve all comment children: + + xml_string = <<-EOT + + + + + EOT + d = REXML::Document.new(xml_string) + d.root.comments.map {|comment| comment.to_s } # => ["foo", "bar"] + +==== Task: Get All Processing Instruction Children + +Use method +{Element#instructions}[../../../../REXML/Element.html#method-i-instructions] +to retrieve all processing instruction children: + + xml_string = <<-EOT + + + + + EOT + d = REXML::Document.new(xml_string) + instructions = d.root.instructions.map {|instruction| instruction.to_s } + instructions # => ["", ""] + +==== Task: Get All Text Children + +Use method +{Element#texts}[../../../../REXML/Element.html#method-i-texts] +to retrieve all text children: + + xml_string = 'textmore' + d = REXML::Document.new(xml_string) + d.root.texts # => ["text", "more"] + +=== Namespaces + +==== Task: Add a Namespace + +Use method +{Element#add_namespace}[../../../../REXML/Element.html#method-i-add_namespace] +to add a namespace to the element: + + e = REXML::Element.new('foo') + e.add_namespace('bar') + e.namespaces # => {"xmlns"=>"bar"} + +==== Task: Delete the Default Namespace + +Use method +{Element#delete_namespace}[../../../../REXML/Element.html#method-i-delete_namespace] +to remove the default namespace from the element: + + d = REXML::Document.new "" + d.to_s # => "" + d.root.delete_namespace # => + d.to_s # => "" + +==== Task: Delete a Specific Namespace + +Use method +{Element#delete_namespace}[../../../../REXML/Element.html#method-i-delete_namespace] +to remove a specific namespace from the element: + + d = REXML::Document.new "" + d.to_s # => "" + d.root.delete_namespace # => + d.to_s # => "" + d.root.delete_namespace('foo') + d.to_s # => "" + +==== Task: Get a Namespace URI + +Use method +{Element#namespace}[../../../../REXML/Element.html#method-i-namespace] +to retrieve a specific namespace URI for the element: + + xml_string = <<-EOT + + + + + + + EOT + d = REXML::Document.new(xml_string) + b = d.elements['//b'] + b.namespace # => "1" + b.namespace('y') # => "2" + +==== Task: Retrieve Namespaces + +Use method +{Element#namespaces}[../../../../REXML/Element.html#method-i-namespaces] +to retrieve all namespaces for the element: + + xml_string = '' + d = REXML::Document.new(xml_string) + d.root.attributes.namespaces # => {"xmlns"=>"foo", "x"=>"bar", "y"=>"twee"} + +==== Task: Retrieve Namespace Prefixes + +Use method +{Element#prefixes}[../../../../REXML/Element.html#method-i-prefixes] +to retrieve all prefixes (namespace names) for the element: + + xml_string = <<-EOT + + + + + + + EOT + d = REXML::Document.new(xml_string, {compress_whitespace: :all}) + d.elements['//a'].prefixes # => ["x", "y"] + d.elements['//b'].prefixes # => ["x", "y"] + d.elements['//c'].prefixes # => ["x", "y", "z"] + +=== Iteration + +==== Task: Iterate Over Elements + +Use method +{Element#each_element}[../../../../REXML/Element.html#method-i-each_element] +to iterate over element children: + + d = REXML::Document.new 'bbd' + d.root.each_element {|e| p e } + +Output: + + ... + ... + ... + + +==== Task: Iterate Over Elements Having a Specified Attribute + +Use method +{Element#each_element_with_attribute}[../../../../REXML/Element.html#method-i-each_element_with_attribute] +to iterate over element children that have a specified attribute: + + d = REXML::Document.new '' + a = d.root + a.each_element_with_attribute('id') {|e| p e } + +Output: + + + + + +==== Task: Iterate Over Elements Having a Specified Attribute and Value + +Use method +{Element#each_element_with_attribute}[../../../../REXML/Element.html#method-i-each_element_with_attribute] +to iterate over element children that have a specified attribute and value: + + d = REXML::Document.new '' + a = d.root + a.each_element_with_attribute('id', '1') {|e| p e } + +Output: + + + + +==== Task: Iterate Over Elements Having Specified Text + +Use method +{Element#each_element_with_text}[../../../../REXML/Element.html#method-i-each_element_with_text] +to iterate over element children that have specified text: + + +=== Context + +#whitespace +#ignore_whitespace_nodes +#raw + +=== Other Getters + +#document +#root +#root_node +#node_type +#xpath +#inspect diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/doc/rexml/tasks/rdoc/node.rdoc b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/doc/rexml/tasks/rdoc/node.rdoc new file mode 100644 index 0000000..d5d2e12 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/doc/rexml/tasks/rdoc/node.rdoc @@ -0,0 +1,97 @@ +== Module Node + +:include: ../tocs/node_toc.rdoc + +=== Siblings + +==== Task: Find Previous Sibling + +Use method +{Node.previous_sibling_node}[../../../../REXML/Node.html#method-i-previous_sibling] +to retrieve the previous sibling: + + d = REXML::Document.new('') + b = d.root[1] # => + b.previous_sibling_node # => + +==== Task: Find Next Sibling + +Use method +{Node.next_sibling_node}[../../../../REXML/Node.html#method-i-next_sibling] +to retrieve the next sibling: + + d = REXML::Document.new('') + b = d.root[1] # => + b.next_sibling_node # => + +=== Position + +==== Task: Find Own Index Among Siblings + +Use method +{Node.index_in_parent}[../../../../REXML/Node.html#method-i-index_in_parent] +to retrieve the 1-based index of this node among its siblings: + + d = REXML::Document.new('') + b = d.root[1] # => + b.index_in_parent # => 2 + +=== Recursive Traversal + +==== Task: Traverse Each Recursively + +Use method +{Node.each_recursive}[../../../../REXML/Node.html#method-i-each_recursive] +to traverse a tree of nodes recursively: + + xml_string = '' + d = REXML::Document.new(xml_string) + d.root.each_recursive {|node| p node } + +Output: + + ... + ... + + ... + + +=== Recursive Search + +==== Task: Traverse Each Recursively + +Use method +{Node.find_first_recursive}[../../../../REXML/Node.html#method-i-find_first_recursive] +to search a tree of nodes recursively: + + xml_string = '' + d = REXML::Document.new(xml_string) + d.root.find_first_recursive {|node| node.name == 'c' } # => + +=== Representation + +==== Task: Represent a String + +Use method {Node.to_s}[../../../../REXML/Node.html#method-i-to_s] +to represent the node as a string: + + xml_string = '' + d = REXML::Document.new(xml_string) + d.root.to_s # => "" + +=== Parent? + +==== Task: Determine Whether the Node is a Parent + +Use method {Node.parent?}[../../../../REXML/Node.html#method-i-parent-3F] +to determine whether the node is a parent; +class Text derives from Node: + + d = REXML::Document.new('textmore') + t = d.root[1] # => "text" + t.parent? # => false + +Class Parent also derives from Node, but overrides this method: + + p = REXML::Parent.new + p.parent? # => true diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/doc/rexml/tasks/rdoc/parent.rdoc b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/doc/rexml/tasks/rdoc/parent.rdoc new file mode 100644 index 0000000..54f1dbe --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/doc/rexml/tasks/rdoc/parent.rdoc @@ -0,0 +1,267 @@ +== Class Parent + +Class Parent has methods from its superclasses and included modules; +see: + +- {Tasks for Child}[child_rdoc.html]. +- {Tasks for Node}[node_rdoc.html]. +- {Module Enumerable}[https://docs.ruby-lang.org/en/master/Enumerable.html]. + +:include: ../tocs/parent_toc.rdoc + +=== Queries + +==== Task: Get the Count of Children + +Use method {Parent#size}[../../../../REXML/Parent.html#method-i-size] +(or its alias +length+) to get the count of the parent's children: + + p = REXML::Parent.new + p.size # => 0 + xml_string = '' + d = REXML::Document.new(xml_string) + d.root.size # => 3 + +==== Task: Get the Child at a Given Index + +Use method {Parent#[]}[../../../../REXML/Parent.html#method-i-5B-5D] +to get the child at a given index: + + xml_string = '' + d = REXML::Document.new(xml_string) + d.root[1] # => + d.root[-1] # => + d.root[50] # => nil + +==== Task: Get the Index of a Given Child + +Use method {Parent#index}[../../../../REXML/Parent.html#method-i-index] +to get the index (0-based offset) of a child: + + d = REXML::Document.new('') + root = d.root + e0 = REXML::Element.new('foo') + e1 = REXML::Element.new('bar') + root.add(e0) # => + root.add(e1) # => + root.add(e0) # => + root.add(e1) # => + root.index(e0) # => 0 + root.index(e1) # => 1 + +==== Task: Get the Children + +Use method {Parent#children}[../../../../REXML/Parent.html#method-i-children] +(or its alias +to_a+) to get the parent's children: + + xml_string = '' + d = REXML::Document.new(xml_string) + d.root.children # => [, , ] + +==== Task: Determine Whether the Node is a Parent + +Use method {Parent#parent?}[../../../../REXML/Parent.html#method-i-parent-3F] +to determine whether the node is a parent; +class Text derives from Node: + + d = REXML::Document.new('textmore') + t = d.root[1] # => "text" + t.parent? # => false + +Class Parent also derives from Node, but overrides this method: + + p = REXML::Parent.new + p.parent? # => true + +=== Additions + +==== Task: Add a Child at the Beginning + +Use method {Parent#unshift}[../../../../REXML/Parent.html#method-i-unshift] +to add a child as at the beginning of the children: + + xml_string = '' + d = REXML::Document.new(xml_string) + d.root.children # => [, , ] + d.root.unshift REXML::Element.new('d') + d.root.children # => [, , , ] + +==== Task: Add a Child at the End + +Use method {Parent#<<}[../../../../REXML/Parent.html#method-i-3C-3C] +(or an alias +push+ or +add+) to add a child as at the end of the children: + + xml_string = '' + d = REXML::Document.new(xml_string) + d.root.children # => [, , ] + d.root << REXML::Element.new('d') + d.root.children # => [, , , ] + +==== Task: Replace a Child with Another Child + +Use method {Parent#replace}[../../../../REXML/Parent.html#method-i-replace] + + xml_string = '' + d = REXML::Document.new(xml_string) + d.root.children # => [, , ] + b = d.root[1] # => + d.replace_child(b, REXML::Element.new('d')) + d.root.children # => [, ] + +==== Task: Replace Multiple Children with Another Child + +Use method {Parent#[]=}[../../../../REXML/Parent.html#method-i-parent-5B-5D-3D] +to replace multiple consecutive children with another child: + + xml_string = '' + d = REXML::Document.new(xml_string) + d.root.children # => [, , , ] + d.root[1, 2] = REXML::Element.new('x') + d.root.children # => [, , ] + d.root[1, 5] = REXML::Element.new('x') + d.root.children # => [, ] # BUG? + +==== Task: Insert Child Before a Given Child + +Use method {Parent#insert_before}[../../../../REXML/Parent.html#method-i-insert_before] +to insert a child immediately before a given child: + + xml_string = '' + d = REXML::Document.new(xml_string) + d.root.children # => [, , ] + b = d.root[1] # => + x = REXML::Element.new('x') + d.root.insert_before(b, x) + d.root.children # => [, , , ] + +==== Task: Insert Child After a Given Child + +Use method {Parent#insert_after}[../../../../REXML/Parent.html#method-i-insert_after] +to insert a child immediately after a given child: + + xml_string = '' + d = REXML::Document.new(xml_string) + d.root.children # => [, , ] + b = d.root[1] # => + x = REXML::Element.new('x') + d.root.insert_after(b, x) + d.root.children # => [, , , ] + +=== Deletions + +==== Task: Remove a Given Child + +Use method {Parent#delete}[../../../../REXML/Parent.html#method-i-delete] +to remove all occurrences of a given child: + + d = REXML::Document.new('') + a = REXML::Element.new('a') + b = REXML::Element.new('b') + d.root.add(a) + d.root.add(b) + d.root.add(a) + d.root.add(b) + d.root.children # => [, , , ] + d.root.delete(b) + d.root.children # => [, ] + +==== Task: Remove the Child at a Specified Offset + +Use method {Parent#delete_at}[../../../../REXML/Parent.html#method-i-delete_at] +to remove the child at a specified offset: + + d = REXML::Document.new('') + a = REXML::Element.new('a') + b = REXML::Element.new('b') + d.root.add(a) + d.root.add(b) + d.root.add(a) + d.root.add(b) + d.root.children # => [, , , ] + d.root.delete_at(2) + d.root.children # => [, , ] + +==== Task: Remove Children That Meet Specified Criteria + +Use method {Parent#delete_if}[../../../../REXML/Parent.html#method-i-delete_if] +to remove children that meet criteria specified in the given block: + + d = REXML::Document.new('') + d.root.add(REXML::Element.new('x')) + d.root.add(REXML::Element.new('xx')) + d.root.add(REXML::Element.new('xxx')) + d.root.add(REXML::Element.new('xxxx')) + d.root.children # => [, , , ] + d.root.delete_if {|child| child.name.size.odd? } + d.root.children # => [, ] + +=== Iterations + +==== Task: Iterate Over Children + +Use method {Parent#each_child}[../../../../REXML/Parent.html#method-i-each_child] +(or its alias +each+) to iterate over all children: + + xml_string = '' + d = REXML::Document.new(xml_string) + d.root.children # => [, , ] + d.root.each_child {|child| p child } + +Output: + + + + + +==== Task: Iterate Over Child Indexes + +Use method {Parent#each_index}[../../../../REXML/Parent.html#method-i-each_index] +to iterate over all child indexes: + + xml_string = '' + d = REXML::Document.new(xml_string) + d.root.children # => [, , ] + d.root.each_index {|child| p child } + +Output: + + 0 + 1 + 2 + +=== Clones + +==== Task: Clone Deeply + +Use method {Parent#deep_clone}[../../../../REXML/Parent.html#method-i-deep_clone] +to clone deeply; that is, to clone every nested node that is a Parent object: + + xml_string = <<-EOT + + + + Everyday Italian + Giada De Laurentiis + 2005 + 30.00 + + + Harry Potter + J K. Rowling + 2005 + 29.99 + + + Learning XML + Erik T. Ray + 2003 + 39.95 + + + EOT + d = REXML::Document.new(xml_string) + root = d.root + shallow = root.clone + deep = root.deep_clone + shallow.to_s.size # => 12 + deep.to_s.size # => 590 diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/doc/rexml/tasks/tocs/child_toc.rdoc b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/doc/rexml/tasks/tocs/child_toc.rdoc new file mode 100644 index 0000000..a2083a0 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/doc/rexml/tasks/tocs/child_toc.rdoc @@ -0,0 +1,12 @@ +Tasks on this page: + +- {Relationships}[#label-Relationships] + - {Task: Set the Parent}[#label-Task-3A+Set+the+Parent] + - {Task: Insert Previous Sibling}[#label-Task-3A+Insert+Previous+Sibling] + - {Task: Insert Next Sibling}[#label-Task-3A+Insert+Next+Sibling] +- {Removal or Replacement}[#label-Removal+or+Replacement] + - {Task: Remove Child from Parent}[#label-Task-3A+Remove+Child+from+Parent] + - {Task: Replace Child}[#label-Task-3A+Replace+Child] +- {Document}[#label-Document] + - {Task: Get the Document}[#label-Task-3A+Get+the+Document] + diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/doc/rexml/tasks/tocs/document_toc.rdoc b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/doc/rexml/tasks/tocs/document_toc.rdoc new file mode 100644 index 0000000..5db055f --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/doc/rexml/tasks/tocs/document_toc.rdoc @@ -0,0 +1,30 @@ +Tasks on this page: + +- {New Document}[#label-New+Document] + - {Task: Create an Empty Document}[#label-Task-3A+Create+an+Empty+Document] + - {Task: Parse a String into a New Document}[#label-Task-3A+Parse+a+String+into+a+New+Document] + - {Task: Parse an IO Stream into a New Document}[#label-Task-3A+Parse+an+IO+Stream+into+a+New+Document] + - {Task: Create a Document from an Existing Document}[#label-Task-3A+Create+a+Document+from+an+Existing+Document] + - {Task: Clone a Document}[#label-Task-3A+Clone+a+Document] +- {Document Type}[#label-Document+Type] + - {Task: Get the Document Type}[#label-Task-3A+Get+the+Document+Type] + - {Task: Set the Document Type}[#label-Task-3A+Set+the+Document+Type] +- {XML Declaration}[#label-XML+Declaration] + - {Task: Get the XML Declaration}[#label-Task-3A+Get+the+XML+Declaration] + - {Task: Set the XML Declaration}[#label-Task-3A+Set+the+XML+Declaration] +- {Children}[#label-Children] + - {Task: Add an Element Child}[#label-Task-3A+Add+an+Element+Child] + - {Task: Add a Non-Element Child}[#label-Task-3A+Add+a+Non-Element+Child] +- {Writing}[#label-Writing] + - {Task: Write to $stdout}[#label-Task-3A+Write+to+-24stdout] + - {Task: Write to IO Stream}[#label-Task-3A+Write+to+IO+Stream] + - {Task: Write with No Indentation}[#label-Task-3A+Write+with+No+Indentation] + - {Task: Write with Specified Indentation}[#label-Task-3A+Write+with+Specified+Indentation] +- {Querying}[#label-Querying] + - {Task: Get the Document}[#label-Task-3A+Get+the+Document] + - {Task: Get the Encoding}[#label-Task-3A+Get+the+Encoding] + - {Task: Get the Node Type}[#label-Task-3A+Get+the+Node+Type] + - {Task: Get the Root Element}[#label-Task-3A+Get+the+Root+Element] + - {Task: Determine Whether Stand-Alone}[#label-Task-3A+Determine+Whether+Stand-Alone] + - {Task: Get the Version}[#label-Task-3A+Get+the+Version] + diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/doc/rexml/tasks/tocs/element_toc.rdoc b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/doc/rexml/tasks/tocs/element_toc.rdoc new file mode 100644 index 0000000..60a504a --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/doc/rexml/tasks/tocs/element_toc.rdoc @@ -0,0 +1,55 @@ +Tasks on this page: + +- {New Element}[#label-New+Element] + - {Task: Create a Default Element}[#label-Task-3A+Create+a+Default+Element] + - {Task: Create a Named Element}[#label-Task-3A+Create+a+Named+Element] + - {Task: Create an Element with Name and Parent}[#label-Task-3A+Create+an+Element+with+Name+and+Parent] + - {Task: Create an Element with Name, Parent, and Context}[#label-Task-3A+Create+an+Element+with+Name-2C+Parent-2C+and+Context] + - {Task: Create a Shallow Clone}[#label-Task-3A+Create+a+Shallow+Clone] +- {Attributes}[#label-Attributes] + - {Task: Create and Add an Attribute}[#label-Task-3A+Create+and+Add+an+Attribute] + - {Task: Add an Existing Attribute}[#label-Task-3A+Add+an+Existing+Attribute] + - {Task: Add Multiple Attributes from a Hash}[#label-Task-3A+Add+Multiple+Attributes+from+a+Hash] + - {Task: Add Multiple Attributes from an Array}[#label-Task-3A+Add+Multiple+Attributes+from+an+Array] + - {Task: Retrieve the Value for an Attribute Name}[#label-Task-3A+Retrieve+the+Value+for+an+Attribute+Name] + - {Task: Retrieve the Attribute Value for a Name and Namespace}[#label-Task-3A+Retrieve+the+Attribute+Value+for+a+Name+and+Namespace] + - {Task: Delete an Attribute}[#label-Task-3A+Delete+an+Attribute] + - {Task: Determine Whether the Element Has Attributes}[#label-Task-3A+Determine+Whether+the+Element+Has+Attributes] +- {Children}[#label-Children] + - {Task: Create and Add an Element}[#label-Task-3A+Create+and+Add+an+Element] + - {Task: Add an Existing Element}[#label-Task-3A+Add+an+Existing+Element] + - {Task: Create and Add an Element with Attributes}[#label-Task-3A+Create+and+Add+an+Element+with+Attributes] + - {Task: Add an Existing Element with Added Attributes}[#label-Task-3A+Add+an+Existing+Element+with+Added+Attributes] + - {Task: Delete a Specified Element}[#label-Task-3A+Delete+a+Specified+Element] + - {Task: Delete an Element by Index}[#label-Task-3A+Delete+an+Element+by+Index] + - {Task: Delete an Element by XPath}[#label-Task-3A+Delete+an+Element+by+XPath] + - {Task: Determine Whether Element Children}[#label-Task-3A+Determine+Whether+Element+Children] + - {Task: Get Element Descendants by XPath}[#label-Task-3A+Get+Element+Descendants+by+XPath] + - {Task: Get Next Element Sibling}[#label-Task-3A+Get+Next+Element+Sibling] + - {Task: Get Previous Element Sibling}[#label-Task-3A+Get+Previous+Element+Sibling] + - {Task: Add a Text Node}[#label-Task-3A+Add+a+Text+Node] + - {Task: Replace the First Text Node}[#label-Task-3A+Replace+the+First+Text+Node] + - {Task: Remove the First Text Node}[#label-Task-3A+Remove+the+First+Text+Node] + - {Task: Retrieve the First Text Node}[#label-Task-3A+Retrieve+the+First+Text+Node] + - {Task: Retrieve a Specific Text Node}[#label-Task-3A+Retrieve+a+Specific+Text+Node] + - {Task: Determine Whether the Element has Text Nodes}[#label-Task-3A+Determine+Whether+the+Element+has+Text+Nodes] + - {Task: Get the Child at a Given Index}[#label-Task-3A+Get+the+Child+at+a+Given+Index] + - {Task: Get All CDATA Children}[#label-Task-3A+Get+All+CDATA+Children] + - {Task: Get All Comment Children}[#label-Task-3A+Get+All+Comment+Children] + - {Task: Get All Processing Instruction Children}[#label-Task-3A+Get+All+Processing+Instruction+Children] + - {Task: Get All Text Children}[#label-Task-3A+Get+All+Text+Children] +- {Namespaces}[#label-Namespaces] + - {Task: Add a Namespace}[#label-Task-3A+Add+a+Namespace] + - {Task: Delete the Default Namespace}[#label-Task-3A+Delete+the+Default+Namespace] + - {Task: Delete a Specific Namespace}[#label-Task-3A+Delete+a+Specific+Namespace] + - {Task: Get a Namespace URI}[#label-Task-3A+Get+a+Namespace+URI] + - {Task: Retrieve Namespaces}[#label-Task-3A+Retrieve+Namespaces] + - {Task: Retrieve Namespace Prefixes}[#label-Task-3A+Retrieve+Namespace+Prefixes] +- {Iteration}[#label-Iteration] + - {Task: Iterate Over Elements}[#label-Task-3A+Iterate+Over+Elements] + - {Task: Iterate Over Elements Having a Specified Attribute}[#label-Task-3A+Iterate+Over+Elements+Having+a+Specified+Attribute] + - {Task: Iterate Over Elements Having a Specified Attribute and Value}[#label-Task-3A+Iterate+Over+Elements+Having+a+Specified+Attribute+and+Value] + - {Task: Iterate Over Elements Having Specified Text}[#label-Task-3A+Iterate+Over+Elements+Having+Specified+Text] +- {Context}[#label-Context] +- {Other Getters}[#label-Other+Getters] + diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/doc/rexml/tasks/tocs/master_toc.rdoc b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/doc/rexml/tasks/tocs/master_toc.rdoc new file mode 100644 index 0000000..0214f6b --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/doc/rexml/tasks/tocs/master_toc.rdoc @@ -0,0 +1,135 @@ +== Tasks + +=== {Child}[../../tasks/rdoc/child_rdoc.html] +- {Relationships}[../../tasks/rdoc/child_rdoc.html#label-Relationships] + - {Task: Set the Parent}[../../tasks/rdoc/child_rdoc.html#label-Task-3A+Set+the+Parent] + - {Task: Insert Previous Sibling}[../../tasks/rdoc/child_rdoc.html#label-Task-3A+Insert+Previous+Sibling] + - {Task: Insert Next Sibling}[../../tasks/rdoc/child_rdoc.html#label-Task-3A+Insert+Next+Sibling] +- {Removal or Replacement}[../../tasks/rdoc/child_rdoc.html#label-Removal+or+Replacement] + - {Task: Remove Child from Parent}[../../tasks/rdoc/child_rdoc.html#label-Task-3A+Remove+Child+from+Parent] + - {Task: Replace Child}[../../tasks/rdoc/child_rdoc.html#label-Task-3A+Replace+Child] +- {Document}[../../tasks/rdoc/child_rdoc.html#label-Document] + - {Task: Get the Document}[../../tasks/rdoc/child_rdoc.html#label-Task-3A+Get+the+Document] + +=== {Document}[../../tasks/rdoc/document_rdoc.html] +- {New Document}[../../tasks/rdoc/document_rdoc.html#label-New+Document] + - {Task: Create an Empty Document}[../../tasks/rdoc/document_rdoc.html#label-Task-3A+Create+an+Empty+Document] + - {Task: Parse a String into a New Document}[../../tasks/rdoc/document_rdoc.html#label-Task-3A+Parse+a+String+into+a+New+Document] + - {Task: Parse an IO Stream into a New Document}[../../tasks/rdoc/document_rdoc.html#label-Task-3A+Parse+an+IO+Stream+into+a+New+Document] + - {Task: Create a Document from an Existing Document}[../../tasks/rdoc/document_rdoc.html#label-Task-3A+Create+a+Document+from+an+Existing+Document] + - {Task: Clone a Document}[../../tasks/rdoc/document_rdoc.html#label-Task-3A+Clone+a+Document] +- {Document Type}[../../tasks/rdoc/document_rdoc.html#label-Document+Type] + - {Task: Get the Document Type}[../../tasks/rdoc/document_rdoc.html#label-Task-3A+Get+the+Document+Type] + - {Task: Set the Document Type}[../../tasks/rdoc/document_rdoc.html#label-Task-3A+Set+the+Document+Type] +- {XML Declaration}[../../tasks/rdoc/document_rdoc.html#label-XML+Declaration] + - {Task: Get the XML Declaration}[../../tasks/rdoc/document_rdoc.html#label-Task-3A+Get+the+XML+Declaration] + - {Task: Set the XML Declaration}[../../tasks/rdoc/document_rdoc.html#label-Task-3A+Set+the+XML+Declaration] +- {Children}[../../tasks/rdoc/document_rdoc.html#label-Children] + - {Task: Add an Element Child}[../../tasks/rdoc/document_rdoc.html#label-Task-3A+Add+an+Element+Child] + - {Task: Add a Non-Element Child}[../../tasks/rdoc/document_rdoc.html#label-Task-3A+Add+a+Non-Element+Child] +- {Writing}[../../tasks/rdoc/document_rdoc.html#label-Writing] + - {Task: Write to $stdout}[../../tasks/rdoc/document_rdoc.html#label-Task-3A+Write+to+-24stdout] + - {Task: Write to IO Stream}[../../tasks/rdoc/document_rdoc.html#label-Task-3A+Write+to+IO+Stream] + - {Task: Write with No Indentation}[../../tasks/rdoc/document_rdoc.html#label-Task-3A+Write+with+No+Indentation] + - {Task: Write with Specified Indentation}[../../tasks/rdoc/document_rdoc.html#label-Task-3A+Write+with+Specified+Indentation] +- {Querying}[../../tasks/rdoc/document_rdoc.html#label-Querying] + - {Task: Get the Document}[../../tasks/rdoc/document_rdoc.html#label-Task-3A+Get+the+Document] + - {Task: Get the Encoding}[../../tasks/rdoc/document_rdoc.html#label-Task-3A+Get+the+Encoding] + - {Task: Get the Node Type}[../../tasks/rdoc/document_rdoc.html#label-Task-3A+Get+the+Node+Type] + - {Task: Get the Root Element}[../../tasks/rdoc/document_rdoc.html#label-Task-3A+Get+the+Root+Element] + - {Task: Determine Whether Stand-Alone}[../../tasks/rdoc/document_rdoc.html#label-Task-3A+Determine+Whether+Stand-Alone] + - {Task: Get the Version}[../../tasks/rdoc/document_rdoc.html#label-Task-3A+Get+the+Version] + +=== {Element}[../../tasks/rdoc/element_rdoc.html] +- {New Element}[../../tasks/rdoc/element_rdoc.html#label-New+Element] + - {Task: Create a Default Element}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Create+a+Default+Element] + - {Task: Create a Named Element}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Create+a+Named+Element] + - {Task: Create an Element with Name and Parent}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Create+an+Element+with+Name+and+Parent] + - {Task: Create an Element with Name, Parent, and Context}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Create+an+Element+with+Name-2C+Parent-2C+and+Context] + - {Task: Create a Shallow Clone}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Create+a+Shallow+Clone] +- {Attributes}[../../tasks/rdoc/element_rdoc.html#label-Attributes] + - {Task: Create and Add an Attribute}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Create+and+Add+an+Attribute] + - {Task: Add an Existing Attribute}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Add+an+Existing+Attribute] + - {Task: Add Multiple Attributes from a Hash}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Add+Multiple+Attributes+from+a+Hash] + - {Task: Add Multiple Attributes from an Array}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Add+Multiple+Attributes+from+an+Array] + - {Task: Retrieve the Value for an Attribute Name}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Retrieve+the+Value+for+an+Attribute+Name] + - {Task: Retrieve the Attribute Value for a Name and Namespace}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Retrieve+the+Attribute+Value+for+a+Name+and+Namespace] + - {Task: Delete an Attribute}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Delete+an+Attribute] + - {Task: Determine Whether the Element Has Attributes}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Determine+Whether+the+Element+Has+Attributes] +- {Children}[../../tasks/rdoc/element_rdoc.html#label-Children] + - {Task: Create and Add an Element}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Create+and+Add+an+Element] + - {Task: Add an Existing Element}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Add+an+Existing+Element] + - {Task: Create and Add an Element with Attributes}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Create+and+Add+an+Element+with+Attributes] + - {Task: Add an Existing Element with Added Attributes}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Add+an+Existing+Element+with+Added+Attributes] + - {Task: Delete a Specified Element}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Delete+a+Specified+Element] + - {Task: Delete an Element by Index}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Delete+an+Element+by+Index] + - {Task: Delete an Element by XPath}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Delete+an+Element+by+XPath] + - {Task: Determine Whether Element Children}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Determine+Whether+Element+Children] + - {Task: Get Element Descendants by XPath}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Get+Element+Descendants+by+XPath] + - {Task: Get Next Element Sibling}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Get+Next+Element+Sibling] + - {Task: Get Previous Element Sibling}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Get+Previous+Element+Sibling] + - {Task: Add a Text Node}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Add+a+Text+Node] + - {Task: Replace the First Text Node}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Replace+the+First+Text+Node] + - {Task: Remove the First Text Node}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Remove+the+First+Text+Node] + - {Task: Retrieve the First Text Node}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Retrieve+the+First+Text+Node] + - {Task: Retrieve a Specific Text Node}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Retrieve+a+Specific+Text+Node] + - {Task: Determine Whether the Element has Text Nodes}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Determine+Whether+the+Element+has+Text+Nodes] + - {Task: Get the Child at a Given Index}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Get+the+Child+at+a+Given+Index] + - {Task: Get All CDATA Children}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Get+All+CDATA+Children] + - {Task: Get All Comment Children}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Get+All+Comment+Children] + - {Task: Get All Processing Instruction Children}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Get+All+Processing+Instruction+Children] + - {Task: Get All Text Children}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Get+All+Text+Children] +- {Namespaces}[../../tasks/rdoc/element_rdoc.html#label-Namespaces] + - {Task: Add a Namespace}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Add+a+Namespace] + - {Task: Delete the Default Namespace}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Delete+the+Default+Namespace] + - {Task: Delete a Specific Namespace}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Delete+a+Specific+Namespace] + - {Task: Get a Namespace URI}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Get+a+Namespace+URI] + - {Task: Retrieve Namespaces}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Retrieve+Namespaces] + - {Task: Retrieve Namespace Prefixes}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Retrieve+Namespace+Prefixes] +- {Iteration}[../../tasks/rdoc/element_rdoc.html#label-Iteration] + - {Task: Iterate Over Elements}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Iterate+Over+Elements] + - {Task: Iterate Over Elements Having a Specified Attribute}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Iterate+Over+Elements+Having+a+Specified+Attribute] + - {Task: Iterate Over Elements Having a Specified Attribute and Value}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Iterate+Over+Elements+Having+a+Specified+Attribute+and+Value] + - {Task: Iterate Over Elements Having Specified Text}[../../tasks/rdoc/element_rdoc.html#label-Task-3A+Iterate+Over+Elements+Having+Specified+Text] +- {Context}[../../tasks/rdoc/element_rdoc.html#label-Context] +- {Other Getters}[../../tasks/rdoc/element_rdoc.html#label-Other+Getters] + +=== {Node}[../../tasks/rdoc/node_rdoc.html] +- {Siblings}[../../tasks/rdoc/node_rdoc.html#label-Siblings] + - {Task: Find Previous Sibling}[../../tasks/rdoc/node_rdoc.html#label-Task-3A+Find+Previous+Sibling] + - {Task: Find Next Sibling}[../../tasks/rdoc/node_rdoc.html#label-Task-3A+Find+Next+Sibling] +- {Position}[../../tasks/rdoc/node_rdoc.html#label-Position] + - {Task: Find Own Index Among Siblings}[../../tasks/rdoc/node_rdoc.html#label-Task-3A+Find+Own+Index+Among+Siblings] +- {Recursive Traversal}[../../tasks/rdoc/node_rdoc.html#label-Recursive+Traversal] + - {Task: Traverse Each Recursively}[../../tasks/rdoc/node_rdoc.html#label-Task-3A+Traverse+Each+Recursively] +- {Recursive Search}[../../tasks/rdoc/node_rdoc.html#label-Recursive+Search] + - {Task: Traverse Each Recursively}[../../tasks/rdoc/node_rdoc.html#label-Task-3A+Traverse+Each+Recursively] +- {Representation}[../../tasks/rdoc/node_rdoc.html#label-Representation] + - {Task: Represent a String}[../../tasks/rdoc/node_rdoc.html#label-Task-3A+Represent+a+String] +- {Parent?}[../../tasks/rdoc/node_rdoc.html#label-Parent-3F] + - {Task: Determine Whether the Node is a Parent}[../../tasks/rdoc/node_rdoc.html#label-Task-3A+Determine+Whether+the+Node+is+a+Parent] + +=== {Parent}[../../tasks/rdoc/parent_rdoc.html] +- {Queries}[../../tasks/rdoc/parent_rdoc.html#label-Queries] + - {Task: Get the Count of Children}[../../tasks/rdoc/parent_rdoc.html#label-Task-3A+Get+the+Count+of+Children] + - {Task: Get the Child at a Given Index}[../../tasks/rdoc/parent_rdoc.html#label-Task-3A+Get+the+Child+at+a+Given+Index] + - {Task: Get the Index of a Given Child}[../../tasks/rdoc/parent_rdoc.html#label-Task-3A+Get+the+Index+of+a+Given+Child] + - {Task: Get the Children}[../../tasks/rdoc/parent_rdoc.html#label-Task-3A+Get+the+Children] + - {Task: Determine Whether the Node is a Parent}[../../tasks/rdoc/parent_rdoc.html#label-Task-3A+Determine+Whether+the+Node+is+a+Parent] +- {Additions}[../../tasks/rdoc/parent_rdoc.html#label-Additions] + - {Task: Add a Child at the Beginning}[../../tasks/rdoc/parent_rdoc.html#label-Task-3A+Add+a+Child+at+the+Beginning] + - {Task: Add a Child at the End}[../../tasks/rdoc/parent_rdoc.html#label-Task-3A+Add+a+Child+at+the+End] + - {Task: Replace a Child with Another Child}[../../tasks/rdoc/parent_rdoc.html#label-Task-3A+Replace+a+Child+with+Another+Child] + - {Task: Replace Multiple Children with Another Child}[../../tasks/rdoc/parent_rdoc.html#label-Task-3A+Replace+Multiple+Children+with+Another+Child] + - {Task: Insert Child Before a Given Child}[../../tasks/rdoc/parent_rdoc.html#label-Task-3A+Insert+Child+Before+a+Given+Child] + - {Task: Insert Child After a Given Child}[../../tasks/rdoc/parent_rdoc.html#label-Task-3A+Insert+Child+After+a+Given+Child] +- {Deletions}[../../tasks/rdoc/parent_rdoc.html#label-Deletions] + - {Task: Remove a Given Child}[../../tasks/rdoc/parent_rdoc.html#label-Task-3A+Remove+a+Given+Child] + - {Task: Remove the Child at a Specified Offset}[../../tasks/rdoc/parent_rdoc.html#label-Task-3A+Remove+the+Child+at+a+Specified+Offset] + - {Task: Remove Children That Meet Specified Criteria}[../../tasks/rdoc/parent_rdoc.html#label-Task-3A+Remove+Children+That+Meet+Specified+Criteria] +- {Iterations}[../../tasks/rdoc/parent_rdoc.html#label-Iterations] + - {Task: Iterate Over Children}[../../tasks/rdoc/parent_rdoc.html#label-Task-3A+Iterate+Over+Children] + - {Task: Iterate Over Child Indexes}[../../tasks/rdoc/parent_rdoc.html#label-Task-3A+Iterate+Over+Child+Indexes] +- {Clones}[../../tasks/rdoc/parent_rdoc.html#label-Clones] + - {Task: Clone Deeply}[../../tasks/rdoc/parent_rdoc.html#label-Task-3A+Clone+Deeply] + diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/doc/rexml/tasks/tocs/node_toc.rdoc b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/doc/rexml/tasks/tocs/node_toc.rdoc new file mode 100644 index 0000000..d9114fa --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/doc/rexml/tasks/tocs/node_toc.rdoc @@ -0,0 +1,16 @@ +Tasks on this page: + +- {Siblings}[#label-Siblings] + - {Task: Find Previous Sibling}[#label-Task-3A+Find+Previous+Sibling] + - {Task: Find Next Sibling}[#label-Task-3A+Find+Next+Sibling] +- {Position}[#label-Position] + - {Task: Find Own Index Among Siblings}[#label-Task-3A+Find+Own+Index+Among+Siblings] +- {Recursive Traversal}[#label-Recursive+Traversal] + - {Task: Traverse Each Recursively}[#label-Task-3A+Traverse+Each+Recursively] +- {Recursive Search}[#label-Recursive+Search] + - {Task: Traverse Each Recursively}[#label-Task-3A+Traverse+Each+Recursively] +- {Representation}[#label-Representation] + - {Task: Represent a String}[#label-Task-3A+Represent+a+String] +- {Parent?}[#label-Parent-3F] + - {Task: Determine Whether the Node is a Parent}[#label-Task-3A+Determine+Whether+the+Node+is+a+Parent] + diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/doc/rexml/tasks/tocs/parent_toc.rdoc b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/doc/rexml/tasks/tocs/parent_toc.rdoc new file mode 100644 index 0000000..68fc0b7 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/doc/rexml/tasks/tocs/parent_toc.rdoc @@ -0,0 +1,25 @@ +Tasks on this page: + +- {Queries}[#label-Queries] + - {Task: Get the Count of Children}[#label-Task-3A+Get+the+Count+of+Children] + - {Task: Get the Child at a Given Index}[#label-Task-3A+Get+the+Child+at+a+Given+Index] + - {Task: Get the Index of a Given Child}[#label-Task-3A+Get+the+Index+of+a+Given+Child] + - {Task: Get the Children}[#label-Task-3A+Get+the+Children] + - {Task: Determine Whether the Node is a Parent}[#label-Task-3A+Determine+Whether+the+Node+is+a+Parent] +- {Additions}[#label-Additions] + - {Task: Add a Child at the Beginning}[#label-Task-3A+Add+a+Child+at+the+Beginning] + - {Task: Add a Child at the End}[#label-Task-3A+Add+a+Child+at+the+End] + - {Task: Replace a Child with Another Child}[#label-Task-3A+Replace+a+Child+with+Another+Child] + - {Task: Replace Multiple Children with Another Child}[#label-Task-3A+Replace+Multiple+Children+with+Another+Child] + - {Task: Insert Child Before a Given Child}[#label-Task-3A+Insert+Child+Before+a+Given+Child] + - {Task: Insert Child After a Given Child}[#label-Task-3A+Insert+Child+After+a+Given+Child] +- {Deletions}[#label-Deletions] + - {Task: Remove a Given Child}[#label-Task-3A+Remove+a+Given+Child] + - {Task: Remove the Child at a Specified Offset}[#label-Task-3A+Remove+the+Child+at+a+Specified+Offset] + - {Task: Remove Children That Meet Specified Criteria}[#label-Task-3A+Remove+Children+That+Meet+Specified+Criteria] +- {Iterations}[#label-Iterations] + - {Task: Iterate Over Children}[#label-Task-3A+Iterate+Over+Children] + - {Task: Iterate Over Child Indexes}[#label-Task-3A+Iterate+Over+Child+Indexes] +- {Clones}[#label-Clones] + - {Task: Clone Deeply}[#label-Task-3A+Clone+Deeply] + diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/doc/rexml/tutorial.rdoc b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/doc/rexml/tutorial.rdoc new file mode 100644 index 0000000..c85a70d --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/doc/rexml/tutorial.rdoc @@ -0,0 +1,1358 @@ += \REXML Tutorial + +== Why \REXML? + +- Ruby's \REXML library is part of the Ruby distribution, + so using it requires no gem installations. +- \REXML is fully maintained. +- \REXML is mature, having been in use for long years. + +== To Include, or Not to Include? + +REXML is a module. +To use it, you must require it: + + require 'rexml' # => true + +If you do not also include it, you must fully qualify references to REXML: + + REXML::Document # => REXML::Document + +If you also include the module, you may optionally omit REXML::: + + include REXML + Document # => REXML::Document + REXML::Document # => REXML::Document + +== Preliminaries + +All examples here assume that the following code has been executed: + + require 'rexml' + include REXML + +The source XML for many examples here is from file +{books.xml}[https://www.w3schools.com/xml/books.xml] at w3schools.com. +You may find it convenient to open that page in a new tab +(Ctrl-click in some browsers). + +Note that your browser may display the XML with modified whitespace +and without the XML declaration, which in this case is: + + + +For convenience, we capture the XML into a string variable: + + require 'open-uri' + source_string = URI.open('https://www.w3schools.com/xml/books.xml').read + +And into a file: + + File.write('source_file.xml', source_string) + +Throughout these examples, variable +doc+ will hold only the document +derived from these sources: + + doc = Document.new(source_string) + +== Parsing \XML \Source + +=== Parsing a Document + +Use method REXML::Document::new to parse XML source. + +The source may be a string: + + doc = Document.new(source_string) + +Or an \IO stream: + + doc = File.open('source_file.xml', 'r') do |io| + Document.new(io) + end + +Method URI.open returns a StringIO object, +so the source can be from a web page: + + require 'open-uri' + io = URI.open("https://www.w3schools.com/xml/books.xml") + io.class # => StringIO + doc = Document.new(io) + +For any of these sources, the returned object is an REXML::Document: + + doc # => ... + doc.class # => REXML::Document + +Note: 'UNDEFINED' is the "name" displayed for a document, +even though doc.name returns an empty string "". + +A parsed document may produce \REXML objects of many classes, +but the two that are likely to be of greatest interest are +REXML::Document and REXML::Element. +These two classes are covered in great detail in this tutorial. + +=== Context (Parsing Options) + +The context for parsing a document is a hash that influences +the way the XML is read and stored. + +The context entries are: + +- +:respect_whitespace+: controls treatment of whitespace. +- +:compress_whitespace+: determines whether whitespace is compressed. +- +:ignore_whitespace_nodes+: determines whether whitespace-only nodes are to be ignored. +- +:raw+: controls treatment of special characters and entities. + +See {Element Context}[../context_rdoc.html]. + +== Exploring the Document + +An REXML::Document object represents an XML document. + +The object inherits from its ancestor classes: + +- REXML::Child (includes module REXML::Node) + - REXML::Parent (includes module {Enumerable}[rdoc-ref:Enumerable]). + - REXML::Element (includes module REXML::Namespace). + - REXML::Document + +This section covers only those properties and methods that are unique to a document +(that is, not inherited or included). + +=== Document Properties + +A document has several properties (other than its children); + +- Document type. +- Node type. +- Name. +- Document. +- XPath + +[Document Type] + + A document may have a document type: + + my_xml = '' + my_doc = Document.new(my_xml) + doc_type = my_doc.doctype + doc_type.class # => REXML::DocType + doc_type.to_s # => "" + +[Node Type] + + A document also has a node type (always +:document+): + + doc.node_type # => :document + +[Name] + + A document has a name (always an empty string): + + doc.name # => "" + +[Document] + + \Method REXML::Document#document returns +self+: + + doc.document == doc # => true + + An object of a different class (\REXML::Element or \REXML::Child) + may have a document, which is the document to which the object belongs; + if so, that document will be an \REXML::Document object. + + doc.root.document.class # => REXML::Document + +[XPath] + + \method REXML::Element#xpath returns the string xpath to the element, + relative to its most distant ancestor: + + doc.root.class # => REXML::Element + doc.root.xpath # => "/bookstore" + doc.root.texts.first # => "\n\n" + doc.root.texts.first.xpath # => "/bookstore/text()" + + If there is no ancestor, returns the expanded name of the element: + + Element.new('foo').xpath # => "foo" + +=== Document Children + +A document may have children of these types: + +- XML declaration. +- Root element. +- Text. +- Processing instructions. +- Comments. +- CDATA. + +[XML Declaration] + + A document may an XML declaration, which is stored as an REXML::XMLDecl object: + + doc.xml_decl # => + doc.xml_decl.class # => REXML::XMLDecl + + Document.new('').xml_decl # => + + my_xml = '"' + my_doc = Document.new(my_xml) + xml_decl = my_doc.xml_decl + xml_decl.to_s # => "" + + The version, encoding, and stand-alone values may be retrieved separately: + + my_doc.version # => "1.0" + my_doc.encoding # => "UTF-8" + my_doc.stand_alone? # => "yes" + +[Root Element] + + A document may have a single element child, called the _root_ _element_, + which is stored as an REXML::Element object; + it may be retrieved with method +root+: + + doc.root # => ... + doc.root.class # => REXML::Element + + Document.new('').root # => nil + +[Text] + + A document may have text passages, each of which is stored + as an REXML::Text object: + + doc.texts.each {|t| p [t.class, t] } + + Output: + + [REXML::Text, "\n"] + +[Processing Instructions] + + A document may have processing instructions, which are stored + as REXML::Instruction objects: + + + + Output: + + [REXML::Instruction, ] + [REXML::Instruction, ] + +[Comments] + + A document may have comments, which are stored + as REXML::Comment objects: + + my_xml = <<-EOT + + + EOT + my_doc = Document.new(my_xml) + my_doc.comments.each {|c| p [c.class, c] } + + Output: + + [REXML::Comment, # ... , @string="foo">] + [REXML::Comment, # ... , @string="bar">] + +[CDATA] + + A document may have CDATA entries, which are stored + as REXML::CData objects: + + my_xml = <<-EOT + + + EOT + my_doc = Document.new(my_xml) + my_doc.cdatas.each {|cd| p [cd.class, cd] } + + Output: + + [REXML::CData, "foo"] + [REXML::CData, "bar"] + +The payload of a document is a tree of nodes, descending from the root element: + + doc.root.children.each do |child| + p [child, child.class] + end + +Output: + + [REXML::Text, "\n\n"] + [REXML::Element, ... ] + [REXML::Text, "\n\n"] + [REXML::Element, ... ] + [REXML::Text, "\n\n"] + [REXML::Element, ... ] + [REXML::Text, "\n\n"] + [REXML::Element, ... ] + [REXML::Text, "\n\n"] + +== Exploring an Element + +An REXML::Element object represents an XML element. + +The object inherits from its ancestor classes: + +- REXML::Child (includes module REXML::Node) + - REXML::Parent (includes module {Enumerable}[rdoc-ref:Enumerable]). + - REXML::Element (includes module REXML::Namespace). + +This section covers methods: + +- Defined in REXML::Element itself. +- Inherited from REXML::Parent and REXML::Child. +- Included from REXML::Node. + +=== Inside the Element + +[Brief String Representation] + + Use method REXML::Element#inspect to retrieve a brief string representation. + + doc.root.inspect # => " ... " + + The ellipsis (...) indicates that the element has children. + When there are no children, the ellipsis is omitted: + + Element.new('foo').inspect # => "" + + If the element has attributes, those are also included: + + doc.root.elements.first.inspect # => " ... " + +[Extended String Representation] + + Use inherited method REXML::Child.bytes to retrieve an extended + string representation. + + doc.root.bytes # => "\n\n\n Everyday Italian\n Giada De Laurentiis\n 2005\n 30.00\n\n\n\n Harry Potter\n J K. Rowling\n 2005\n 29.99\n\n\n\n XQuery Kick Start\n James McGovern\n Per Bothner\n Kurt Cagle\n James Linn\n Vaidyanathan Nagarajan\n 2003\n 49.99\n\n\n\n Learning XML\n Erik T. Ray\n 2003\n 39.95\n\n\n" + +[Node Type] + + Use method REXML::Element#node_type to retrieve the node type (always +:element+): + + doc.root.node_type # => :element + +[Raw Mode] + + Use method REXML::Element#raw to retrieve whether (+true+ or +nil+) + raw mode is set. + + doc.root.raw # => nil + +[Context] + + Use method REXML::Element#context to retrieve the context hash + (see {Element Context}[../context_rdoc.html]): + + doc.root.context # => {} + +=== Relationships + +An element may have: + +- Ancestors. +- Siblings. +- Children. + +==== Ancestors + +[Containing Document] + + Use method REXML::Element#document to retrieve the containing document, if any: + + ele = doc.root.elements.first # => ... + ele.document # => ... + ele = Element.new('foo') # => + ele.document # => nil + +[Root Element] + + Use method REXML::Element#root to retrieve the root element: + + ele = doc.root.elements.first # => ... + ele.root # => ... + ele = Element.new('foo') # => + ele.root # => + +[Root Node] + + Use method REXML::Element#root_node to retrieve the most distant ancestor, + which is the containing document, if any, otherwise the root element: + + ele = doc.root.elements.first # => ... + ele.root_node # => ... + ele = Element.new('foo') # => + ele.root_node # => + +[Parent] + + Use inherited method REXML::Child#parent to retrieve the parent + + ele = doc.root # => ... + ele.parent # => ... + ele = doc.root.elements.first # => ... + ele.parent # => ... + + Use included method REXML::Node#index_in_parent to retrieve the index + of the element among all of its parents children (not just the element children). + Note that while the index for doc.root.elements[n] is 1-based, + the returned index is 0-based. + + doc.root.children # => + # ["\n\n", + # ... , + # "\n\n", + # ... , + # "\n\n", + # ... , + # "\n\n", + # ... , + # "\n\n"] + ele = doc.root.elements[1] # => ... + ele.index_in_parent # => 2 + ele = doc.root.elements[2] # => ... + ele.index_in_parent# => 4 + +==== Siblings + +[Next Element] + + Use method REXML::Element#next_element to retrieve the first following + sibling that is itself an element (+nil+ if there is none): + + ele = doc.root.elements[1] + while ele do + p [ele.class, ele] + ele = ele.next_element + end + p ele + + Output: + + [REXML::Element, ... ] + [REXML::Element, ... ] + [REXML::Element, ... ] + [REXML::Element, ... ] + +[Previous Element] + + Use method REXML::Element#previous_element to retrieve the first preceding + sibling that is itself an element (+nil+ if there is none): + + ele = doc.root.elements[4] + while ele do + p [ele.class, ele] + ele = ele.previous_element + end + p ele + + Output: + + [REXML::Element, ... ] + [REXML::Element, ... ] + [REXML::Element, ... ] + [REXML::Element, ... ] + +[Next Node] + + Use included method REXML::Node.next_sibling_node + (or its alias next_sibling) to retrieve the first following node + regardless of its class: + + node = doc.root.children[0] + while node do + p [node.class, node] + node = node.next_sibling + end + p node + + Output: + + [REXML::Text, "\n\n"] + [REXML::Element, ... ] + [REXML::Text, "\n\n"] + [REXML::Element, ... ] + [REXML::Text, "\n\n"] + [REXML::Element, ... ] + [REXML::Text, "\n\n"] + [REXML::Element, ... ] + [REXML::Text, "\n\n"] + +[Previous Node] + + Use included method REXML::Node.previous_sibling_node + (or its alias previous_sibling) to retrieve the first preceding node + regardless of its class: + + node = doc.root.children[-1] + while node do + p [node.class, node] + node = node.previous_sibling + end + p node + + Output: + + [REXML::Text, "\n\n"] + [REXML::Element, ... ] + [REXML::Text, "\n\n"] + [REXML::Element, ... ] + [REXML::Text, "\n\n"] + [REXML::Element, ... ] + [REXML::Text, "\n\n"] + [REXML::Element, ... ] + [REXML::Text, "\n\n"] + +==== Children + +[Child Count] + + Use inherited method REXML::Parent.size to retrieve the count + of nodes (of all types) in the element: + + doc.root.size # => 9 + +[Child Nodes] + + Use inherited method REXML::Parent.children to retrieve an array + of the child nodes (of all types): + + doc.root.children # => + # ["\n\n", + # ... , + # "\n\n", + # ... , + # "\n\n", + # ... , + # "\n\n", + # ... , + # "\n\n"] + +[Child at Index] + + Use method REXML::Element#[] to retrieve the child at a given numerical index, + or +nil+ if there is no such child: + + doc.root[0] # => "\n\n" + doc.root[1] # => ... + doc.root[7] # => ... + doc.root[8] # => "\n\n" + + doc.root[-1] # => "\n\n" + doc.root[-2] # => ... + + doc.root[50] # => nil + +[Index of Child] + + Use method REXML::Parent#index to retrieve the zero-based child index + of the given object, or #size - 1 if there is no such child: + + ele = doc.root # => ... + ele.index(ele[0]) # => 0 + ele.index(ele[1]) # => 1 + ele.index(ele[7]) # => 7 + ele.index(ele[8]) # => 8 + + ele.index(ele[-1]) # => 8 + ele.index(ele[-2]) # => 7 + + ele.index(ele[50]) # => 8 + +[Element Children] + + Use method REXML::Element#has_elements? to retrieve whether the element + has element children: + + doc.root.has_elements? # => true + REXML::Element.new('foo').has_elements? # => false + + Use method REXML::Element#elements to retrieve the REXML::Elements object + containing the element children: + + eles = doc.root.elements + eles # => # ... > + eles.size # => 4 + eles.each {|e| p [e.class], e } + + Output: + + [ ... , + ... , + ... , + ... + ] + +Note that while in this example, all the element children of the root element are +elements of the same name, 'book', that is not true of all documents; +a root element (or any other element) may have any mixture of child elements. + +[CDATA Children] + + Use method REXML::Element#cdatas to retrieve a frozen array of CDATA children: + + my_xml = <<-EOT + + + + + EOT + my_doc = REXML::Document.new(my_xml) + cdatas my_doc.root.cdatas + cdatas.frozen? # => true + cdatas.map {|cd| cd.class } # => [REXML::CData, REXML::CData] + +[Comment Children] + + Use method REXML::Element#comments to retrieve a frozen array of comment children: + + my_xml = <<-EOT + + + + + EOT + my_doc = REXML::Document.new(my_xml) + comments = my_doc.root.comments + comments.frozen? # => true + comments.map {|c| c.class } # => [REXML::Comment, REXML::Comment] + comments.map {|c| c.to_s } # => ["foo", "bar"] + +[Processing Instruction Children] + + Use method REXML::Element#instructions to retrieve a frozen array + of processing instruction children: + + my_xml = <<-EOT + + + + + EOT + my_doc = REXML::Document.new(my_xml) + instrs = my_doc.root.instructions + instrs.frozen? # => true + instrs.map {|i| i.class } # => [REXML::Instruction, REXML::Instruction] + instrs.map {|i| i.to_s } # => ["", ""] + +[Text Children] + + Use method REXML::Element#has_text? to retrieve whether the element + has text children: + + doc.root.has_text? # => true + REXML::Element.new('foo').has_text? # => false + + Use method REXML::Element#texts to retrieve a frozen array of text children: + + my_xml = 'textmore' + my_doc = REXML::Document.new(my_xml) + texts = my_doc.root.texts + texts.frozen? # => true + texts.map {|t| t.class } # => [REXML::Text, REXML::Text] + texts.map {|t| t.to_s } # => ["text", "more"] + +[Parenthood] + + Use inherited method REXML::Parent.parent? to retrieve whether the element is a parent; + always returns +true+; only REXML::Child#parent returns +false+. + + doc.root.parent? # => true + +=== Element Attributes + +Use method REXML::Element#has_attributes? to return whether the element +has attributes: + + ele = doc.root # => ... + ele.has_attributes? # => false + ele = ele.elements.first # => ... + ele.has_attributes? # => true + +Use method REXML::Element#attributes to return the hash +containing the attributes for the element. +Each hash key is a string attribute name; +each hash value is an REXML::Attribute object. + + ele = doc.root # => ... + attrs = ele.attributes # => {} + + ele = ele.elements.first # => ... + attrs = ele.attributes # => {"category"=>category='cooking'} + attrs.size # => 1 + attr_name = attrs.keys.first # => "category" + attr_name.class # => String + attr_value = attrs.values.first # => category='cooking' + attr_value.class # => REXML::Attribute + +Use method REXML::Element#[] to retrieve the string value for a given attribute, +which may be given as either a string or a symbol: + + ele = doc.root.elements.first # => ... + attr_value = ele['category'] # => "cooking" + attr_value.class # => String + ele['nosuch'] # => nil + +Use method REXML::Element#attribute to retrieve the value of a named attribute: + + my_xml = "" + my_doc = REXML::Document.new(my_xml) + my_doc.root.attribute("x") # => x='x' + my_doc.root.attribute("x", "a") # => a:x='a:x' + +== Whitespace + +Use method REXML::Element#ignore_whitespace_nodes to determine whether +whitespace nodes were ignored when the XML was parsed; +returns +true+ if so, +nil+ otherwise. + +Use method REXML::Element#whitespace to determine whether whitespace +is respected for the element; returns +true+ if so, +false+ otherwise. + +== Namespaces + +Use method REXML::Element#namespace to retrieve the string namespace URI +for the element, which may derive from one of its ancestors: + + xml_string = <<-EOT + + + + + + + EOT + d = Document.new(xml_string) + b = d.elements['//b'] + b.namespace # => "1" + b.namespace('y') # => "2" + b.namespace('nosuch') # => nil + +Use method REXML::Element#namespaces to retrieve a hash of all defined namespaces +in the element and its ancestors: + + xml_string = <<-EOT + + + + + + + EOT + d = Document.new(xml_string) + d.elements['//a'].namespaces # => {"x"=>"1", "y"=>"2"} + d.elements['//b'].namespaces # => {"x"=>"1", "y"=>"2"} + d.elements['//c'].namespaces # => {"x"=>"1", "y"=>"2", "z"=>"3"} + +Use method REXML::Element#prefixes to retrieve an array of the string prefixes (names) +of all defined namespaces in the element and its ancestors: + + xml_string = <<-EOT + + + + + + + EOT + d = Document.new(xml_string, {compress_whitespace: :all}) + d.elements['//a'].prefixes # => ["x", "y"] + d.elements['//b'].prefixes # => ["x", "y"] + d.elements['//c'].prefixes # => ["x", "y", "z"] + +== Traversing + +You can use certain methods to traverse children of the element. +Each child that meets given criteria is yielded to the given block. + +[Traverse All Children] + + Use inherited method REXML::Parent#each (or its alias #each_child) to traverse + all children of the element: + + doc.root.each {|child| p [child.class, child] } + + Output: + + [REXML::Text, "\n\n"] + [REXML::Element, ... ] + [REXML::Text, "\n\n"] + [REXML::Element, ... ] + [REXML::Text, "\n\n"] + [REXML::Element, ... ] + [REXML::Text, "\n\n"] + [REXML::Element, ... ] + [REXML::Text, "\n\n"] + +[Traverse Element Children] + + Use method REXML::Element#each_element to traverse only the element children + of the element: + + doc.root.each_element {|e| p [e.class, e] } + + Output: + + [REXML::Element, ... ] + [REXML::Element, ... ] + [REXML::Element, ... ] + [REXML::Element, ... ] + +[Traverse Element Children with Attribute] + + Use method REXML::Element#each_element_with_attribute with the single argument + +attr_name+ to traverse each element child that has the given attribute: + + my_doc = Document.new '' + my_doc.root.each_element_with_attribute('id') {|e| p [e.class, e] } + + Output: + + [REXML::Element, ] + [REXML::Element, ] + [REXML::Element, ] + + Use the same method with a second argument +value+ to traverse + each element child element that has the given attribute and value: + + my_doc.root.each_element_with_attribute('id', '1') {|e| p [e.class, e] } + + Output: + + [REXML::Element, ] + [REXML::Element, ] + + Use the same method with a third argument +max+ to traverse + no more than the given number of element children: + + my_doc.root.each_element_with_attribute('id', '1', 1) {|e| p [e.class, e] } + + Output: + + [REXML::Element, ] + + Use the same method with a fourth argument +xpath+ to traverse + only those element children that match the given xpath: + + my_doc.root.each_element_with_attribute('id', '1', 2, '//d') {|e| p [e.class, e] } + + Output: + + [REXML::Element, ] + +[Traverse Element Children with Text] + + Use method REXML::Element#each_element_with_text with no arguments + to traverse those element children that have text: + + my_doc = Document.new 'bbd' + my_doc.root.each_element_with_text {|e| p [e.class, e] } + + Output: + + [REXML::Element, ... ] + [REXML::Element, ... ] + [REXML::Element, ... ] + + Use the same method with the single argument +text+ to traverse + those element children that have exactly that text: + + my_doc.root.each_element_with_text('b') {|e| p [e.class, e] } + + Output: + + [REXML::Element, ... ] + [REXML::Element, ... ] + + Use the same method with additional second argument +max+ to traverse + no more than the given number of element children: + + my_doc.root.each_element_with_text('b', 1) {|e| p [e.class, e] } + + Output: + + [REXML::Element, ... ] + + Use the same method with additional third argument +xpath+ to traverse + only those element children that also match the given xpath: + + my_doc.root.each_element_with_text('b', 2, '//c') {|e| p [e.class, e] } + + Output: + + [REXML::Element, ... ] + +[Traverse Element Children's Indexes] + + Use inherited method REXML::Parent#each_index to traverse all children's indexes + (not just those of element children): + + doc.root.each_index {|i| print i } + + Output: + + 012345678 + +[Traverse Children Recursively] + + Use included method REXML::Node#each_recursive to traverse all children recursively: + + doc.root.each_recursive {|child| p [child.class, child] } + + Output: + + [REXML::Element, ... ] + [REXML::Element, ... </>] + [REXML::Element, <author> ... </>] + [REXML::Element, <year> ... </>] + [REXML::Element, <price> ... </>] + [REXML::Element, <book category='children'> ... </>] + [REXML::Element, <title lang='en'> ... </>] + [REXML::Element, <author> ... </>] + [REXML::Element, <year> ... </>] + [REXML::Element, <price> ... </>] + [REXML::Element, <book category='web'> ... </>] + [REXML::Element, <title lang='en'> ... </>] + [REXML::Element, <author> ... </>] + [REXML::Element, <author> ... </>] + [REXML::Element, <author> ... </>] + [REXML::Element, <author> ... </>] + [REXML::Element, <author> ... </>] + [REXML::Element, <year> ... </>] + [REXML::Element, <price> ... </>] + [REXML::Element, <book category='web' cover='paperback'> ... </>] + [REXML::Element, <title lang='en'> ... </>] + [REXML::Element, <author> ... </>] + [REXML::Element, <year> ... </>] + [REXML::Element, <price> ... </>] + +== Searching + +You can use certain methods to search among the descendants of an element. + +Use method REXML::Element#get_elements to retrieve all element children of the element +that match the given +xpath+: + + xml_string = <<-EOT + <root> + <a level='1'> + <a level='2'/> + </a> + </root> + EOT + d = Document.new(xml_string) + d.root.get_elements('//a') # => [<a level='1'> ... </>, <a level='2'/>] + +Use method REXML::Element#get_text with no argument to retrieve the first text node +in the first child: + + my_doc = Document.new "<p>some text <b>this is bold!</b> more text</p>" + text_node = my_doc.root.get_text + text_node.class # => REXML::Text + text_node.to_s # => "some text " + +Use the same method with argument +xpath+ to retrieve the first text node +in the first child that matches the xpath: + + my_doc.root.get_text(1) # => "this is bold!" + +Use method REXML::Element#text with no argument to retrieve the text +from the first text node in the first child: + + my_doc = Document.new "<p>some text <b>this is bold!</b> more text</p>" + text_node = my_doc.root.text + text_node.class # => String + text_node # => "some text " + +Use the same method with argument +xpath+ to retrieve the text from the first text node +in the first child that matches the xpath: + + my_doc.root.text(1) # => "this is bold!" + +Use included method REXML::Node#find_first_recursive +to retrieve the first descendant element +for which the given block returns a truthy value, or +nil+ if none: + + doc.root.find_first_recursive do |ele| + ele.name == 'price' + end # => <price> ... </> + doc.root.find_first_recursive do |ele| + ele.name == 'nosuch' + end # => nil + +== Editing + +=== Editing a Document + +[Creating a Document] + + Create a new document with method REXML::Document::new: + + doc = Document.new(source_string) + empty_doc = REXML::Document.new + +[Adding to the Document] + + Add an XML declaration with method REXML::Document#add + and an argument of type REXML::XMLDecl: + + my_doc = Document.new + my_doc.xml_decl.to_s # => "" + my_doc.add(XMLDecl.new('2.0')) + my_doc.xml_decl.to_s # => "<?xml version='2.0'?>" + + Add a document type with method REXML::Document#add + and an argument of type REXML::DocType: + + my_doc = Document.new + my_doc.doctype.to_s # => "" + my_doc.add(DocType.new('foo')) + my_doc.doctype.to_s # => "<!DOCTYPE foo>" + + Add a node of any other REXML type with method REXML::Document#add and an argument + that is not of type REXML::XMLDecl or REXML::DocType: + + my_doc = Document.new + my_doc.add(Element.new('foo')) + my_doc.to_s # => "<foo/>" + + Add an existing element as the root element with method REXML::Document#add_element: + + ele = Element.new('foo') + my_doc = Document.new + my_doc.add_element(ele) + my_doc.root # => <foo/> + + Create and add an element as the root element with method REXML::Document#add_element: + + my_doc = Document.new + my_doc.add_element('foo') + my_doc.root # => <foo/> + +=== Editing an Element + +==== Creating an Element + +Create a new element with method REXML::Element::new: + + ele = Element.new('foo') # => <foo/> + +==== Setting Element Properties + +Set the context for an element with method REXML::Element#context= +(see {Element Context}[../context_rdoc.html]): + + ele.context # => nil + ele.context = {ignore_whitespace_nodes: :all} + ele.context # => {:ignore_whitespace_nodes=>:all} + +Set the parent for an element with inherited method REXML::Child#parent= + + ele.parent # => nil + ele.parent = Element.new('bar') + ele.parent # => <bar/> + +Set the text for an element with method REXML::Element#text=: + + ele.text # => nil + ele.text = 'bar' + ele.text # => "bar" + +==== Adding to an Element + +Add a node as the last child with inherited method REXML::Parent#add (or its alias #push): + + ele = Element.new('foo') # => <foo/> + ele.push(Text.new('bar')) + ele.push(Element.new('baz')) + ele.children # => ["bar", <baz/>] + +Add a node as the first child with inherited method REXML::Parent#unshift: + + ele = Element.new('foo') # => <foo/> + ele.unshift(Element.new('bar')) + ele.unshift(Text.new('baz')) + ele.children # => ["bar", <baz/>] + +Add an element as the last child with method REXML::Element#add_element: + + ele = Element.new('foo') # => <foo/> + ele.add_element('bar') + ele.add_element(Element.new('baz')) + ele.children # => [<bar/>, <baz/>] + +Add a text node as the last child with method REXML::Element#add_text: + + ele = Element.new('foo') # => <foo/> + ele.add_text('bar') + ele.add_text(Text.new('baz')) + ele.children # => ["bar", "baz"] + +Insert a node before a given node with method REXML::Parent#insert_before: + + ele = Element.new('foo') # => <foo/> + ele.add_text('bar') + ele.add_text(Text.new('baz')) + ele.children # => ["bar", "baz"] + target = ele[1] # => "baz" + ele.insert_before(target, Text.new('bat')) + ele.children # => ["bar", "bat", "baz"] + +Insert a node after a given node with method REXML::Parent#insert_after: + + ele = Element.new('foo') # => <foo/> + ele.add_text('bar') + ele.add_text(Text.new('baz')) + ele.children # => ["bar", "baz"] + target = ele[0] # => "bar" + ele.insert_after(target, Text.new('bat')) + ele.children # => ["bar", "bat", "baz"] + +Add an attribute with method REXML::Element#add_attribute: + + ele = Element.new('foo') # => <foo/> + ele.add_attribute('bar', 'baz') + ele.add_attribute(Attribute.new('bat', 'bam')) + ele.attributes # => {"bar"=>bar='baz', "bat"=>bat='bam'} + +Add multiple attributes with method REXML::Element#add_attributes: + + ele = Element.new('foo') # => <foo/> + ele.add_attributes({'bar' => 'baz', 'bat' => 'bam'}) + ele.add_attributes([['ban', 'bap'], ['bah', 'bad']]) + ele.attributes # => {"bar"=>bar='baz', "bat"=>bat='bam', "ban"=>ban='bap', "bah"=>bah='bad'} + +Add a namespace with method REXML::Element#add_namespace: + + ele = Element.new('foo') # => <foo/> + ele.add_namespace('bar') + ele.add_namespace('baz', 'bat') + ele.namespaces # => {"xmlns"=>"bar", "baz"=>"bat"} + +==== Deleting from an Element + +Delete a specific child object with inherited method REXML::Parent#delete: + + ele = Element.new('foo') # => <foo/> + ele.add_element('bar') + ele.add_text('baz') + ele.children # => [<bar/>, "baz"] + target = ele[1] # => "baz" + ele.delete(target) # => "baz" + ele.children # => [<bar/>] + target = ele[0] # => <baz/> + ele.delete(target) # => <baz/> + ele.children # => [] + +Delete a child at a specific index with inherited method REXML::Parent#delete_at: + + ele = Element.new('foo') # => <foo/> + ele.add_element('bar') + ele.add_text('baz') + ele.children # => [<bar/>, "baz"] + ele.delete_at(1) + ele.children # => [<bar/>] + ele.delete_at(0) + ele.children # => [] + +Delete all children meeting a specified criterion with inherited method +REXML::Parent#delete_if: + + ele = Element.new('foo') # => <foo/> + ele.add_element('bar') + ele.add_text('baz') + ele.add_element('bat') + ele.add_text('bam') + ele.children # => [<bar/>, "baz", <bat/>, "bam"] + ele.delete_if {|child| child.instance_of?(Text) } + ele.children # => [<bar/>, <bat/>] + +Delete an element at a specific 1-based index with method REXML::Element#delete_element: + + ele = Element.new('foo') # => <foo/> + ele.add_element('bar') + ele.add_text('baz') + ele.add_element('bat') + ele.add_text('bam') + ele.children # => [<bar/>, "baz", <bat/>, "bam"] + ele.delete_element(2) # => <bat/> + ele.children # => [<bar/>, "baz", "bam"] + ele.delete_element(1) # => <bar/> + ele.children # => ["baz", "bam"] + +Delete a specific element with the same method: + + ele = Element.new('foo') # => <foo/> + ele.add_element('bar') + ele.add_text('baz') + ele.add_element('bat') + ele.add_text('bam') + ele.children # => [<bar/>, "baz", <bat/>, "bam"] + target = ele.elements[2] # => <bat/> + ele.delete_element(target) # => <bat/> + ele.children # => [<bar/>, "baz", "bam"] + +Delete an element matching an xpath using the same method: + + ele = Element.new('foo') # => <foo/> + ele.add_element('bar') + ele.add_text('baz') + ele.add_element('bat') + ele.add_text('bam') + ele.children # => [<bar/>, "baz", <bat/>, "bam"] + ele.delete_element('./bat') # => <bat/> + ele.children # => [<bar/>, "baz", "bam"] + ele.delete_element('./bar') # => <bar/> + ele.children # => ["baz", "bam"] + +Delete an attribute by name with method REXML::Element#delete_attribute: + + ele = Element.new('foo') # => <foo/> + ele.add_attributes({'bar' => 'baz', 'bam' => 'bat'}) + ele.attributes # => {"bar"=>bar='baz', "bam"=>bam='bat'} + ele.delete_attribute('bam') + ele.attributes # => {"bar"=>bar='baz'} + +Delete a namespace with method REXML::Element#delete_namespace: + + ele = Element.new('foo') # => <foo/> + ele.add_namespace('bar') + ele.add_namespace('baz', 'bat') + ele.namespaces # => {"xmlns"=>"bar", "baz"=>"bat"} + ele.delete_namespace('xmlns') + ele.namespaces # => {} # => {"baz"=>"bat"} + ele.delete_namespace('baz') + ele.namespaces # => {} # => {} + +Remove an element from its parent with inherited method REXML::Child#remove: + + ele = Element.new('foo') # => <foo/> + parent = Element.new('bar') # => <bar/> + parent.add_element(ele) # => <foo/> + parent.children.size # => 1 + ele.remove # => <foo/> + parent.children.size # => 0 + +==== Replacing Nodes + +Replace the node at a given 0-based index with inherited method REXML::Parent#[]=: + + ele = Element.new('foo') # => <foo/> + ele.add_element('bar') + ele.add_text('baz') + ele.add_element('bat') + ele.add_text('bam') + ele.children # => [<bar/>, "baz", <bat/>, "bam"] + ele[2] = Text.new('bad') # => "bad" + ele.children # => [<bar/>, "baz", "bad", "bam"] + +Replace a given node with another node with inherited method REXML::Parent#replace_child: + + ele = Element.new('foo') # => <foo/> + ele.add_element('bar') + ele.add_text('baz') + ele.add_element('bat') + ele.add_text('bam') + ele.children # => [<bar/>, "baz", <bat/>, "bam"] + target = ele[2] # => <bat/> + ele.replace_child(target, Text.new('bah')) + ele.children # => [<bar/>, "baz", "bah", "bam"] + +Replace +self+ with a given node with inherited method REXML::Child#replace_with: + + ele = Element.new('foo') # => <foo/> + ele.add_element('bar') + ele.add_text('baz') + ele.add_element('bat') + ele.add_text('bam') + ele.children # => [<bar/>, "baz", <bat/>, "bam"] + target = ele[2] # => <bat/> + target.replace_with(Text.new('bah')) + ele.children # => [<bar/>, "baz", "bah", "bam"] + +=== Cloning + +Create a shallow clone of an element with method REXML::Element#clone. +The clone contains the name and attributes, but not the parent or children: + + ele = Element.new('foo') + ele.add_attributes({'bar' => 0, 'baz' => 1}) + ele.clone # => <foo bar='0' baz='1'/> + +Create a shallow clone of a document with method REXML::Document#clone. +The XML declaration is copied; the document type and root element are not cloned: + + my_xml = '<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE foo><root/>' + my_doc = Document.new(my_xml) + clone_doc = my_doc.clone + + my_doc.xml_decl # => <?xml ... ?> + clone_doc.xml_decl # => <?xml ... ?> + + my_doc.doctype.to_s # => "<?xml version='1.0' encoding='UTF-8'?>" + clone_doc.doctype.to_s # => "" + + my_doc.root # => <root/> + clone_doc.root # => nil + +Create a deep clone of an element with inherited method REXML::Parent#deep_clone. +All nodes and attributes are copied: + + doc.to_s.size # => 825 + clone = doc.deep_clone + clone.to_s.size # => 825 + +== Writing the Document + +Write a document to an \IO stream (defaults to <tt>$stdout</tt>) +with method REXML::Document#write: + + doc.write + +Output: + + <?xml version='1.0' encoding='UTF-8'?> + <bookstore> + + <book category='cooking'> + <title lang='en'>Everyday Italian + Giada De Laurentiis + 2005 + 30.00 + + + + Harry Potter + J K. Rowling + 2005 + 29.99 + + + + XQuery Kick Start + James McGovern + Per Bothner + Kurt Cagle + James Linn + Vaidyanathan Nagarajan + 2003 + 49.99 + + + + Learning XML + Erik T. Ray + 2003 + 39.95 + + + diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml.rb new file mode 100644 index 0000000..eee246e --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml.rb @@ -0,0 +1,3 @@ +# frozen_string_literal: true + +require_relative "rexml/document" diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/attlistdecl.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/attlistdecl.rb new file mode 100644 index 0000000..44a91d6 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/attlistdecl.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: false +#vim:ts=2 sw=2 noexpandtab: +require_relative 'child' +require_relative 'source' + +module REXML + # This class needs: + # * Documentation + # * Work! Not all types of attlists are intelligently parsed, so we just + # spew back out what we get in. This works, but it would be better if + # we formatted the output ourselves. + # + # AttlistDecls provide *just* enough support to allow namespace + # declarations. If you need some sort of generalized support, or have an + # interesting idea about how to map the hideous, terrible design of DTD + # AttlistDecls onto an intuitive Ruby interface, let me know. I'm desperate + # for anything to make DTDs more palateable. + class AttlistDecl < Child + include Enumerable + + # What is this? Got me. + attr_reader :element_name + + # Create an AttlistDecl, pulling the information from a Source. Notice + # that this isn't very convenient; to create an AttlistDecl, you basically + # have to format it yourself, and then have the initializer parse it. + # Sorry, but for the foreseeable future, DTD support in REXML is pretty + # weak on convenience. Have I mentioned how much I hate DTDs? + def initialize(source) + super() + if (source.kind_of? Array) + @element_name, @pairs, @contents = *source + end + end + + # Access the attlist attribute/value pairs. + # value = attlist_decl[ attribute_name ] + def [](key) + @pairs[key] + end + + # Whether an attlist declaration includes the given attribute definition + # if attlist_decl.include? "xmlns:foobar" + def include?(key) + @pairs.keys.include? key + end + + # Iterate over the key/value pairs: + # attlist_decl.each { |attribute_name, attribute_value| ... } + def each(&block) + @pairs.each(&block) + end + + # Write out exactly what we got in. + def write out, indent=-1 + out << @contents + end + + def node_type + :attlistdecl + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/attribute.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/attribute.rb new file mode 100644 index 0000000..c567324 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/attribute.rb @@ -0,0 +1,210 @@ +# frozen_string_literal: true +require_relative "namespace" +require_relative 'text' + +module REXML + # Defines an Element Attribute; IE, a attribute=value pair, as in: + # . Attributes can be in their own + # namespaces. General users of REXML will not interact with the + # Attribute class much. + class Attribute + include Node + include Namespace + + # The element to which this attribute belongs + attr_reader :element + PATTERN = /\s*(#{NAME_STR})\s*=\s*(["'])(.*?)\2/um + + NEEDS_A_SECOND_CHECK = /(<|&((#{Entity::NAME});|(#0*((?:\d+)|(?:x[a-fA-F0-9]+)));)?)/um + + # Constructor. + # FIXME: The parser doesn't catch illegal characters in attributes + # + # first:: + # Either: an Attribute, which this new attribute will become a + # clone of; or a String, which is the name of this attribute + # second:: + # If +first+ is an Attribute, then this may be an Element, or nil. + # If nil, then the Element parent of this attribute is the parent + # of the +first+ Attribute. If the first argument is a String, + # then this must also be a String, and is the content of the attribute. + # If this is the content, it must be fully normalized (contain no + # illegal characters). + # parent:: + # Ignored unless +first+ is a String; otherwise, may be the Element + # parent of this attribute, or nil. + # + # + # Attribute.new( attribute_to_clone ) + # Attribute.new( attribute_to_clone, parent_element ) + # Attribute.new( "attr", "attr_value" ) + # Attribute.new( "attr", "attr_value", parent_element ) + def initialize( first, second=nil, parent=nil ) + @normalized = @unnormalized = @element = nil + if first.kind_of? Attribute + self.name = first.expanded_name + @unnormalized = first.value + if second.kind_of? Element + @element = second + else + @element = first.element + end + elsif first.kind_of? String + @element = parent + self.name = first + @normalized = second.to_s + else + raise "illegal argument #{first.class.name} to Attribute constructor" + end + end + + # Returns the namespace of the attribute. + # + # e = Element.new( "elns:myelement" ) + # e.add_attribute( "nsa:a", "aval" ) + # e.add_attribute( "b", "bval" ) + # e.attributes.get_attribute( "a" ).prefix # -> "nsa" + # e.attributes.get_attribute( "b" ).prefix # -> "" + # a = Attribute.new( "x", "y" ) + # a.prefix # -> "" + def prefix + super + end + + # Returns the namespace URL, if defined, or nil otherwise + # + # e = Element.new("el") + # e.add_namespace("ns", "http://url") + # e.add_attribute("ns:a", "b") + # e.add_attribute("nsx:a", "c") + # e.attribute("ns:a").namespace # => "http://url" + # e.attribute("nsx:a").namespace # => nil + # + # This method always returns "" for no namespace attribute. Because + # the default namespace doesn't apply to attribute names. + # + # From https://www.w3.org/TR/xml-names/#uniqAttrs + # + # > the default namespace does not apply to attribute names + # + # e = REXML::Element.new("el") + # e.add_namespace("", "http://example.com/") + # e.namespace # => "http://example.com/" + # e.add_attribute("a", "b") + # e.attribute("a").namespace # => "" + def namespace arg=nil + arg = prefix if arg.nil? + if arg == "" + "" + else + @element.namespace(arg) + end + end + + # Returns true if other is an Attribute and has the same name and value, + # false otherwise. + def ==( other ) + other.kind_of?(Attribute) and other.name==name and other.value==value + end + + # Creates (and returns) a hash from both the name and value + def hash + name.hash + value.hash + end + + # Returns this attribute out as XML source, expanding the name + # + # a = Attribute.new( "x", "y" ) + # a.to_string # -> "x='y'" + # b = Attribute.new( "ns:x", "y" ) + # b.to_string # -> "ns:x='y'" + def to_string + value = to_s + if @element and @element.context and @element.context[:attribute_quote] == :quote + value = value.gsub('"', '"') if value.include?('"') + %Q^#@expanded_name="#{value}"^ + else + value = value.gsub("'", ''') if value.include?("'") + "#@expanded_name='#{value}'" + end + end + + def doctype + @element&.document&.doctype + end + + # Returns the attribute value, with entities replaced + def to_s + return @normalized if @normalized + + @normalized = Text::normalize( @unnormalized, doctype ) + @normalized + end + + # Returns the UNNORMALIZED value of this attribute. That is, entities + # have been expanded to their values + def value + return @unnormalized if @unnormalized + + @unnormalized = Text::unnormalize(@normalized, doctype, + entity_expansion_text_limit: @element&.document&.entity_expansion_text_limit) + end + + # The normalized value of this attribute. That is, the attribute with + # entities intact. + def normalized=(new_normalized) + @normalized = new_normalized + @unnormalized = nil + end + + # Returns a copy of this attribute + def clone + Attribute.new self + end + + # Sets the element of which this object is an attribute. Normally, this + # is not directly called. + # + # Returns this attribute + def element=( element ) + @element = element + + if @normalized + Text.check( @normalized, NEEDS_A_SECOND_CHECK ) + end + + self + end + + # Removes this Attribute from the tree, and returns true if successful + # + # This method is usually not called directly. + def remove + @element.attributes.delete self.name unless @element.nil? + end + + # Writes this attribute (EG, puts 'key="value"' to the output) + def write( output, indent=-1 ) + output << to_string + end + + def node_type + :attribute + end + + def inspect + rv = +"" + write( rv ) + rv + end + + def xpath + @element.xpath + "/@#{self.expanded_name}" + end + + def document + @element&.document + end + end +end +#vim:ts=2 sw=2 noexpandtab: diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/cdata.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/cdata.rb new file mode 100644 index 0000000..264ad64 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/cdata.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: false +require_relative "text" + +module REXML + class CData < Text + START = '' + ILLEGAL = /(\]\]>)/ + + # Constructor. CData is data between + # + # _Examples_ + # CData.new( source ) + # CData.new( "Here is some CDATA" ) + # CData.new( "Some unprocessed data", respect_whitespace_TF, parent_element ) + def initialize( first, whitespace=true, parent=nil ) + super( first, whitespace, parent, false, true, ILLEGAL ) + end + + # Make a copy of this object + # + # _Examples_ + # c = CData.new( "Some text" ) + # d = c.clone + # d.to_s # -> "Some text" + def clone + CData.new self + end + + # Returns the content of this CData object + # + # _Examples_ + # c = CData.new( "Some text" ) + # c.to_s # -> "Some text" + def to_s + @string + end + + def value + @string + end + + # == DEPRECATED + # See the rexml/formatters package + # + # Generates XML output of this object + # + # output:: + # Where to write the string. Defaults to $stdout + # indent:: + # The amount to indent this node by + # transitive:: + # Ignored + # ie_hack:: + # Ignored + # + # _Examples_ + # c = CData.new( " Some text " ) + # c.write( $stdout ) #-> + def write( output=$stdout, indent=-1, transitive=false, ie_hack=false ) + Kernel.warn( "#{self.class.name}#write is deprecated", uplevel: 1) + indent( output, indent ) + output << START + output << @string + output << STOP + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/child.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/child.rb new file mode 100644 index 0000000..2718040 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/child.rb @@ -0,0 +1,96 @@ +# frozen_string_literal: false +require_relative "node" + +module REXML + ## + # A Child object is something contained by a parent, and this class + # contains methods to support that. Most user code will not use this + # class directly. + class Child + include Node + attr_reader :parent # The Parent of this object + + # Constructor. Any inheritors of this class should call super to make + # sure this method is called. + # parent:: + # if supplied, the parent of this child will be set to the + # supplied value, and self will be added to the parent + def initialize( parent = nil ) + @parent = nil + # Declare @parent, but don't define it. The next line sets the + # parent. + parent.add( self ) if parent + end + + # Replaces this object with another object. Basically, calls + # Parent.replace_child + # + # Returns:: self + def replace_with( child ) + @parent.replace_child( self, child ) + self + end + + # Removes this child from the parent. + # + # Returns:: self + def remove + unless @parent.nil? + @parent.delete self + end + self + end + + # Sets the parent of this child to the supplied argument. + # + # other:: + # Must be a Parent object. If this object is the same object as the + # existing parent of this child, no action is taken. Otherwise, this + # child is removed from the current parent (if one exists), and is added + # to the new parent. + # Returns:: The parent added + def parent=( other ) + return @parent if @parent == other + @parent.delete self if defined? @parent and @parent + @parent = other + end + + alias :next_sibling :next_sibling_node + alias :previous_sibling :previous_sibling_node + + # Sets the next sibling of this child. This can be used to insert a child + # after some other child. + # a = Element.new("a") + # b = a.add_element("b") + # c = Element.new("c") + # b.next_sibling = c + # # => + def next_sibling=( other ) + parent.insert_after self, other + end + + # Sets the previous sibling of this child. This can be used to insert a + # child before some other child. + # a = Element.new("a") + # b = a.add_element("b") + # c = Element.new("c") + # b.previous_sibling = c + # # => + def previous_sibling=(other) + parent.insert_before self, other + end + + # Returns:: the document this child belongs to, or nil if this child + # belongs to no document + def document + parent&.document + end + + # This doesn't yet handle encodings + def bytes + document&.encoding + + to_s + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/comment.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/comment.rb new file mode 100644 index 0000000..e7e104d --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/comment.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: false +require_relative "child" + +module REXML + ## + # Represents an XML comment; that is, text between \ + class Comment < Child + include Comparable + START = "" + + # The content text + + attr_accessor :string + + ## + # Constructor. The first argument can be one of three types: + # @param first If String, the contents of this comment are set to the + # argument. If Comment, the argument is duplicated. If + # Source, the argument is scanned for a comment. + # @param second If the first argument is a Source, this argument + # should be nil, not supplied, or a Parent to be set as the parent + # of this object + def initialize( first, second = nil ) + super(second) + if first.kind_of? String + @string = first + elsif first.kind_of? Comment + @string = first.string + end + end + + def clone + Comment.new self + end + + # == DEPRECATED + # See REXML::Formatters + # + # output:: + # Where to write the string + # indent:: + # An integer. If -1, no indenting will be used; otherwise, the + # indentation will be this number of spaces, and children will be + # indented an additional amount. + # transitive:: + # Ignored by this class. The contents of comments are never modified. + # ie_hack:: + # Needed for conformity to the child API, but not used by this class. + def write( output, indent=-1, transitive=false, ie_hack=false ) + Kernel.warn("#{self.class.name}#write is deprecated. See REXML::Formatters", uplevel: 1) + indent( output, indent ) + output << START + output << @string + output << STOP + end + + alias :to_s :string + + ## + # Compares this Comment to another; the contents of the comment are used + # in the comparison. + def <=>(other) + other.to_s <=> @string + end + + ## + # Compares this Comment to another; the contents of the comment are used + # in the comparison. + def ==( other ) + other.kind_of? Comment and + (other <=> self) == 0 + end + + def node_type + :comment + end + end +end +#vim:ts=2 sw=2 noexpandtab: diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/doctype.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/doctype.rb new file mode 100644 index 0000000..a9cf9f7 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/doctype.rb @@ -0,0 +1,306 @@ +# frozen_string_literal: false +require_relative "parent" +require_relative "parseexception" +require_relative "namespace" +require_relative 'entity' +require_relative 'attlistdecl' +require_relative 'xmltokens' + +module REXML + class ReferenceWriter + def initialize(id_type, + public_id_literal, + system_literal, + context=nil) + @id_type = id_type + @public_id_literal = public_id_literal + @system_literal = system_literal + if context and context[:prologue_quote] == :apostrophe + @default_quote = "'" + else + @default_quote = "\"" + end + end + + def write(output) + output << " #{@id_type}" + if @public_id_literal + if @public_id_literal.include?("'") + quote = "\"" + else + quote = @default_quote + end + output << " #{quote}#{@public_id_literal}#{quote}" + end + if @system_literal + if @system_literal.include?("'") + quote = "\"" + elsif @system_literal.include?("\"") + quote = "'" + else + quote = @default_quote + end + output << " #{quote}#{@system_literal}#{quote}" + end + end + end + + # Represents an XML DOCTYPE declaration; that is, the contents of . DOCTYPES can be used to declare the DTD of a document, as well as + # being used to declare entities used in the document. + class DocType < Parent + include XMLTokens + START = "" + SYSTEM = "SYSTEM" + PUBLIC = "PUBLIC" + DEFAULT_ENTITIES = { + 'gt'=>EntityConst::GT, + 'lt'=>EntityConst::LT, + 'quot'=>EntityConst::QUOT, + "apos"=>EntityConst::APOS + } + + # name is the name of the doctype + # external_id is the referenced DTD, if given + attr_reader :name, :external_id, :entities, :namespaces + + # Constructor + # + # dt = DocType.new( 'foo', '-//I/Hate/External/IDs' ) + # # + # dt = DocType.new( doctype_to_clone ) + # # Incomplete. Shallow clone of doctype + # + # +Note+ that the constructor: + # + # Doctype.new( Source.new( "" ) ) + # + # is _deprecated_. Do not use it. It will probably disappear. + def initialize( first, parent=nil ) + @entities = DEFAULT_ENTITIES + @long_name = @uri = nil + if first.kind_of? String + super() + @name = first + @external_id = parent + elsif first.kind_of? DocType + super( parent ) + @name = first.name + @external_id = first.external_id + @long_name = first.instance_variable_get(:@long_name) + @uri = first.instance_variable_get(:@uri) + elsif first.kind_of? Array + super( parent ) + @name = first[0] + @external_id = first[1] + @long_name = first[2] + @uri = first[3] + elsif first.kind_of? Source + super( parent ) + parser = Parsers::BaseParser.new( first ) + event = parser.pull + if event[0] == :start_doctype + @name, @external_id, @long_name, @uri, = event[1..-1] + end + else + super() + end + end + + def node_type + :doctype + end + + def attributes_of element + rv = [] + each do |child| + child.each do |key,val| + rv << Attribute.new(key,val) + end if child.kind_of? AttlistDecl and child.element_name == element + end + rv + end + + def attribute_of element, attribute + att_decl = find do |child| + child.kind_of? AttlistDecl and + child.element_name == element and + child.include? attribute + end + return nil unless att_decl + att_decl[attribute] + end + + def clone + DocType.new self + end + + # output:: + # Where to write the string + # indent:: + # An integer. If -1, no indentation will be used; otherwise, the + # indentation will be this number of spaces, and children will be + # indented an additional amount. + # transitive:: + # Ignored + # ie_hack:: + # Ignored + def write( output, indent=0, transitive=false, ie_hack=false ) + f = REXML::Formatters::Default.new + indent( output, indent ) + output << START + output << ' ' + output << @name + if @external_id + reference_writer = ReferenceWriter.new(@external_id, + @long_name, + @uri, + context) + reference_writer.write(output) + end + unless @children.empty? + output << ' [' + @children.each { |child| + output << "\n" + f.write( child, output ) + } + output << "\n]" + end + output << STOP + end + + def context + @parent&.context + end + + def entity( name ) + @entities[name]&.unnormalized + end + + def add child + super(child) + @entities = DEFAULT_ENTITIES.clone if @entities == DEFAULT_ENTITIES + @entities[ child.name ] = child if child.kind_of? Entity + end + + # This method retrieves the public identifier identifying the document's + # DTD. + # + # Method contributed by Henrik Martensson + def public + case @external_id + when "SYSTEM" + nil + when "PUBLIC" + @long_name + end + end + + # This method retrieves the system identifier identifying the document's DTD + # + # Method contributed by Henrik Martensson + def system + case @external_id + when "SYSTEM" + @long_name + when "PUBLIC" + @uri.kind_of?(String) ? @uri : nil + end + end + + # This method returns a list of notations that have been declared in the + # _internal_ DTD subset. Notations in the external DTD subset are not + # listed. + # + # Method contributed by Henrik Martensson + def notations + children().select {|node| node.kind_of?(REXML::NotationDecl)} + end + + # Retrieves a named notation. Only notations declared in the internal + # DTD subset can be retrieved. + # + # Method contributed by Henrik Martensson + def notation(name) + notations.find { |notation_decl| + notation_decl.name == name + } + end + end + + # We don't really handle any of these since we're not a validating + # parser, so we can be pretty dumb about them. All we need to be able + # to do is spew them back out on a write() + + # This is an abstract class. You never use this directly; it serves as a + # parent class for the specific declarations. + class Declaration < Child + def initialize src + super() + @string = src + end + + def to_s + @string+'>' + end + + # == DEPRECATED + # See REXML::Formatters + # + def write( output, indent ) + output << to_s + end + end + + public + class ElementDecl < Declaration + def initialize( src ) + super + end + end + + class ExternalEntity < Child + def initialize( src ) + super() + @entity = src + end + def to_s + @entity + end + def write( output, indent ) + output << @entity + end + end + + class NotationDecl < Child + attr_accessor :public, :system + def initialize name, middle, pub, sys + super(nil) + @name = name + @middle = middle + @public = pub + @system = sys + end + + def to_s + context = parent&.context + notation = "" + notation + end + + def write( output, indent=-1 ) + output << to_s + end + + # This method retrieves the name of the notation. + # + # Method contributed by Henrik Martensson + def name + @name + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/document.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/document.rb new file mode 100644 index 0000000..bd3a004 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/document.rb @@ -0,0 +1,471 @@ +# frozen_string_literal: false +require_relative "security" +require_relative "element" +require_relative "xmldecl" +require_relative "source" +require_relative "comment" +require_relative "doctype" +require_relative "instruction" +require_relative "rexml" +require_relative "parseexception" +require_relative "output" +require_relative "parsers/baseparser" +require_relative "parsers/streamparser" +require_relative "parsers/treeparser" + +module REXML + # Represents an XML document. + # + # A document may have: + # + # - A single child that may be accessed via method #root. + # - An XML declaration. + # - A document type. + # - Processing instructions. + # + # == In a Hurry? + # + # If you're somewhat familiar with XML + # and have a particular task in mind, + # you may want to see the + # {tasks pages}[../doc/rexml/tasks/tocs/master_toc_rdoc.html], + # and in particular, the + # {tasks page for documents}[../doc/rexml/tasks/tocs/document_toc_rdoc.html]. + # + class Document < Element + # A convenient default XML declaration. Use: + # + # mydoc << XMLDecl.default + # + DECLARATION = XMLDecl.default + + # :call-seq: + # new(string = nil, context = {}) -> new_document + # new(io_stream = nil, context = {}) -> new_document + # new(document = nil, context = {}) -> new_document + # + # Returns a new \REXML::Document object. + # + # When no arguments are given, + # returns an empty document: + # + # d = REXML::Document.new + # d.to_s # => "" + # + # When argument +string+ is given, it must be a string + # containing a valid XML document: + # + # xml_string = 'FooBar' + # d = REXML::Document.new(xml_string) + # d.to_s # => "FooBar" + # + # When argument +io_stream+ is given, it must be an \IO object + # that is opened for reading, and when read must return a valid XML document: + # + # File.write('t.xml', xml_string) + # d = File.open('t.xml', 'r') do |io| + # REXML::Document.new(io) + # end + # d.to_s # => "FooBar" + # + # When argument +document+ is given, it must be an existing + # document object, whose context and attributes (but not children) + # are cloned into the new document: + # + # d = REXML::Document.new(xml_string) + # d.children # => [ ... ] + # d.context = {raw: :all, compress_whitespace: :all} + # d.add_attributes({'bar' => 0, 'baz' => 1}) + # d1 = REXML::Document.new(d) + # d1.children # => [] + # d1.context # => {:raw=>:all, :compress_whitespace=>:all} + # d1.attributes # => {"bar"=>bar='0', "baz"=>baz='1'} + # + # When argument +context+ is given, it must be a hash + # containing context entries for the document; + # see {Element Context}[../doc/rexml/context_rdoc.html]: + # + # context = {raw: :all, compress_whitespace: :all} + # d = REXML::Document.new(xml_string, context) + # d.context # => {:raw=>:all, :compress_whitespace=>:all} + # + def initialize( source = nil, context = {} ) + @entity_expansion_count = 0 + @entity_expansion_limit = Security.entity_expansion_limit + @entity_expansion_text_limit = Security.entity_expansion_text_limit + super() + @context = context + # `source = ""` is an invalid usage because no root element XML is an invalid XML. + # But we accept `""` for backward compatibility. + return if source.nil? or source == "" + if source.kind_of? Document + @context = source.context + super source + else + build( source ) + end + end + + # :call-seq: + # node_type -> :document + # + # Returns the symbol +:document+. + # + def node_type + :document + end + + # :call-seq: + # clone -> new_document + # + # Returns the new document resulting from executing + # Document.new(self). See Document.new. + # + def clone + Document.new self + end + + # :call-seq: + # expanded_name -> empty_string + # + # Returns an empty string. + # + def expanded_name + '' + #d = doc_type + #d ? d.name : "UNDEFINED" + end + alias :name :expanded_name + + # :call-seq: + # add(xml_decl) -> self + # add(doc_type) -> self + # add(object) -> self + # + # Adds an object to the document; returns +self+. + # + # When argument +xml_decl+ is given, + # it must be an REXML::XMLDecl object, + # which becomes the XML declaration for the document, + # replacing the previous XML declaration if any: + # + # d = REXML::Document.new + # d.xml_decl.to_s # => "" + # d.add(REXML::XMLDecl.new('2.0')) + # d.xml_decl.to_s # => "" + # + # When argument +doc_type+ is given, + # it must be an REXML::DocType object, + # which becomes the document type for the document, + # replacing the previous document type, if any: + # + # d = REXML::Document.new + # d.doctype.to_s # => "" + # d.add(REXML::DocType.new('foo')) + # d.doctype.to_s # => "" + # + # When argument +object+ (not an REXML::XMLDecl or REXML::DocType object) + # is given it is added as the last child: + # + # d = REXML::Document.new + # d.add(REXML::Element.new('foo')) + # d.to_s # => "" + # + def add( child ) + if child.kind_of? XMLDecl + if @children[0].kind_of? XMLDecl + @children[0] = child + else + @children.unshift child + end + child.parent = self + elsif child.kind_of? DocType + # Find first Element or DocType node and insert the decl right + # before it. If there is no such node, just insert the child at the + # end. If there is a child and it is an DocType, then replace it. + insert_before_index = @children.find_index { |x| + x.kind_of?(Element) || x.kind_of?(DocType) + } + if insert_before_index # Not null = not end of list + if @children[ insert_before_index ].kind_of? DocType + @children[ insert_before_index ] = child + else + @children[ insert_before_index-1, 0 ] = child + end + else # Insert at end of list + @children << child + end + child.parent = self + else + rv = super + raise "attempted adding second root element to document" if @elements.size > 1 + rv + end + end + alias :<< :add + + # :call-seq: + # add_element(name_or_element = nil, attributes = nil) -> new_element + # + # Adds an element to the document by calling REXML::Element.add_element: + # + # REXML::Element.add_element(name_or_element, attributes) + def add_element(arg=nil, arg2=nil) + rv = super + raise "attempted adding second root element to document" if @elements.size > 1 + rv + end + + # :call-seq: + # root -> root_element or nil + # + # Returns the root element of the document, if it exists, otherwise +nil+: + # + # d = REXML::Document.new('') + # d.root # => + # d = REXML::Document.new('') + # d.root # => nil + # + def root + elements[1] + #self + #@children.find { |item| item.kind_of? Element } + end + + # :call-seq: + # doctype -> doc_type or nil + # + # Returns the DocType object for the document, if it exists, otherwise +nil+: + # + # d = REXML::Document.new('') + # d.doctype.class # => REXML::DocType + # d = REXML::Document.new('') + # d.doctype.class # => nil + # + def doctype + @children.find { |item| item.kind_of? DocType } + end + + # :call-seq: + # xml_decl -> xml_decl + # + # Returns the XMLDecl object for the document, if it exists, + # otherwise the default XMLDecl object: + # + # d = REXML::Document.new('') + # d.xml_decl.class # => REXML::XMLDecl + # d.xml_decl.to_s # => "" + # d = REXML::Document.new('') + # d.xml_decl.class # => REXML::XMLDecl + # d.xml_decl.to_s # => "" + # + def xml_decl + rv = @children[0] + return rv if rv.kind_of? XMLDecl + @children.unshift(XMLDecl.default)[0] + end + + # :call-seq: + # version -> version_string + # + # Returns the XMLDecl version of this document as a string, + # if it has been set, otherwise the default version: + # + # d = REXML::Document.new('') + # d.version # => "2.0" + # d = REXML::Document.new('') + # d.version # => "1.0" + # + def version + xml_decl().version + end + + # :call-seq: + # encoding -> encoding_string + # + # Returns the XMLDecl encoding of the document, + # if it has been set, otherwise the default encoding: + # + # d = REXML::Document.new('') + # d.encoding # => "UTF-16" + # d = REXML::Document.new('') + # d.encoding # => "UTF-8" + # + def encoding + xml_decl().encoding + end + + # :call-seq: + # stand_alone? + # + # Returns the XMLDecl standalone value of the document as a string, + # if it has been set, otherwise the default standalone value: + # + # d = REXML::Document.new('') + # d.stand_alone? # => "yes" + # d = REXML::Document.new('') + # d.stand_alone? # => nil + # + def stand_alone? + xml_decl().stand_alone? + end + + # :call-seq: + # doc.write(output=$stdout, indent=-1, transitive=false, ie_hack=false, encoding=nil) + # doc.write(options={:output => $stdout, :indent => -1, :transitive => false, :ie_hack => false, :encoding => nil}) + # + # Write the XML tree out, optionally with indent. This writes out the + # entire XML document, including XML declarations, doctype declarations, + # and processing instructions (if any are given). + # + # A controversial point is whether Document should always write the XML + # declaration () whether or not one is given by the + # user (or source document). REXML does not write one if one was not + # specified, because it adds unnecessary bandwidth to applications such + # as XML-RPC. + # + # Accept Nth argument style and options Hash style as argument. + # The recommended style is options Hash style for one or more + # arguments case. + # + # _Examples_ + # Document.new("").write + # + # output = "" + # Document.new("").write(output) + # + # output = "" + # Document.new("").write(:output => output, :indent => 2) + # + # See also the classes in the rexml/formatters package for the proper way + # to change the default formatting of XML output. + # + # _Examples_ + # + # output = "" + # tr = Transitive.new + # tr.write(Document.new(""), output) + # + # output:: + # output an object which supports '<< string'; this is where the + # document will be written. + # indent:: + # An integer. If -1, no indenting will be used; otherwise, the + # indentation will be twice this number of spaces, and children will be + # indented an additional amount. For a value of 3, every item will be + # indented 3 more levels, or 6 more spaces (2 * 3). Defaults to -1 + # transitive:: + # If transitive is true and indent is >= 0, then the output will be + # pretty-printed in such a way that the added whitespace does not affect + # the absolute *value* of the document -- that is, it leaves the value + # and number of Text nodes in the document unchanged. + # ie_hack:: + # This hack inserts a space before the /> on empty tags to address + # a limitation of Internet Explorer. Defaults to false + # encoding:: + # Encoding name as String. Change output encoding to specified encoding + # instead of encoding in XML declaration. + # Defaults to nil. It means encoding in XML declaration is used. + def write(*arguments) + if arguments.size == 1 and arguments[0].class == Hash + options = arguments[0] + + output = options[:output] + indent = options[:indent] + transitive = options[:transitive] + ie_hack = options[:ie_hack] + encoding = options[:encoding] + else + output, indent, transitive, ie_hack, encoding, = *arguments + end + + output ||= $stdout + indent ||= -1 + transitive = false if transitive.nil? + ie_hack = false if ie_hack.nil? + encoding ||= xml_decl.encoding + + if encoding != 'UTF-8' && !output.kind_of?(Output) + output = Output.new( output, encoding ) + end + formatter = if indent > -1 + if transitive + require_relative "formatters/transitive" + REXML::Formatters::Transitive.new( indent, ie_hack ) + else + REXML::Formatters::Pretty.new( indent, ie_hack ) + end + else + REXML::Formatters::Default.new( ie_hack ) + end + formatter.write( self, output ) + end + + + def Document::parse_stream( source, listener ) + Parsers::StreamParser.new( source, listener ).parse + end + + # Set the entity expansion limit. By default the limit is set to 10000. + # + # Deprecated. Use REXML::Security.entity_expansion_limit= instead. + def Document::entity_expansion_limit=( val ) + Security.entity_expansion_limit = val + end + + # Get the entity expansion limit. By default the limit is set to 10000. + # + # Deprecated. Use REXML::Security.entity_expansion_limit= instead. + def Document::entity_expansion_limit + Security.entity_expansion_limit + end + + # Set the entity expansion limit. By default the limit is set to 10240. + # + # Deprecated. Use REXML::Security.entity_expansion_text_limit= instead. + def Document::entity_expansion_text_limit=( val ) + Security.entity_expansion_text_limit = val + end + + # Get the entity expansion limit. By default the limit is set to 10240. + # + # Deprecated. Use REXML::Security.entity_expansion_text_limit instead. + def Document::entity_expansion_text_limit + Security.entity_expansion_text_limit + end + + attr_reader :entity_expansion_count + attr_writer :entity_expansion_limit + attr_accessor :entity_expansion_text_limit + + def record_entity_expansion + @entity_expansion_count += 1 + if @entity_expansion_count > @entity_expansion_limit + raise "number of entity expansions exceeded, processing aborted." + end + end + + def document + self + end + + private + + attr_accessor :namespaces_cache + + # New document level cache is created and available in this block. + # This API is thread unsafe. Users can't change this document in this block. + def enable_cache + @namespaces_cache = {} + begin + yield + ensure + @namespaces_cache = nil + end + end + + def build( source ) + Parsers::TreeParser.new( source, self ).parse + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/dtd/attlistdecl.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/dtd/attlistdecl.rb new file mode 100644 index 0000000..1326cb2 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/dtd/attlistdecl.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: false +require_relative "../child" +module REXML + module DTD + class AttlistDecl < Child + START = ")/um + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/dtd/dtd.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/dtd/dtd.rb new file mode 100644 index 0000000..8b0f2d7 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/dtd/dtd.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: false +require_relative "elementdecl" +require_relative "entitydecl" +require_relative "../comment" +require_relative "notationdecl" +require_relative "attlistdecl" +require_relative "../parent" + +module REXML + module DTD + class Parser + def Parser.parse( input ) + case input + when String + parse_helper input + when File + parse_helper input.read + end + end + + # Takes a String and parses it out + def Parser.parse_helper( input ) + contents = Parent.new + while input.size > 0 + case input + when ElementDecl.PATTERN_RE + match = $& + contents << ElementDecl.new( match ) + when AttlistDecl.PATTERN_RE + matchdata = $~ + contents << AttlistDecl.new( matchdata ) + when EntityDecl.PATTERN_RE + matchdata = $~ + contents << EntityDecl.new( matchdata ) + when Comment.PATTERN_RE + matchdata = $~ + contents << Comment.new( matchdata ) + when NotationDecl.PATTERN_RE + matchdata = $~ + contents << NotationDecl.new( matchdata ) + end + end + contents + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/dtd/elementdecl.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/dtd/elementdecl.rb new file mode 100644 index 0000000..20ed023 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/dtd/elementdecl.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: false +require_relative "../child" +module REXML + module DTD + class ElementDecl < Child + START = "/um + PATTERN_RE = /^\s*#{START}\s+((?:[:\w][-\.\w]*:)?[-!\*\.\w]*)(.*?)>/ + #\s*((((["']).*?\5)|[^\/'">]*)*?)(\/)?>/um, true) + + def initialize match + @name = match[1] + @rest = match[2] + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/dtd/entitydecl.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/dtd/entitydecl.rb new file mode 100644 index 0000000..312df65 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/dtd/entitydecl.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: false +require_relative "../child" +module REXML + module DTD + class EntityDecl < Child + START = "/um + SYSTEM = /^\s*#{START}\s+(?:%\s+)?(\w+)\s+SYSTEM\s+((["']).*?\3)(?:\s+NDATA\s+\w+)?\s*>/um + PLAIN = /^\s*#{START}\s+(\w+)\s+((["']).*?\3)\s*>/um + PERCENT = /^\s*#{START}\s+%\s+(\w+)\s+((["']).*?\3)\s*>/um + # + # + def initialize src + super() + md = nil + if src.match( PUBLIC ) + md = src.match( PUBLIC, true ) + @middle = "PUBLIC" + @content = "#{md[2]} #{md[4]}" + elsif src.match( SYSTEM ) + md = src.match( SYSTEM, true ) + @middle = "SYSTEM" + @content = md[2] + elsif src.match( PLAIN ) + md = src.match( PLAIN, true ) + @middle = "" + @content = md[2] + elsif src.match( PERCENT ) + md = src.match( PERCENT, true ) + @middle = "" + @content = md[2] + end + raise ParseException.new("failed Entity match", src) if md.nil? + @name = md[1] + end + + def to_s + rv = " 0 + rv << @content + rv + end + + def write( output, indent ) + indent( output, indent ) + output << to_s + end + + def EntityDecl.parse_source source, listener + md = source.match( PATTERN_RE, true ) + thing = md[0].squeeze(" \t\n\r") + listener.send inspect.downcase, thing + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/dtd/notationdecl.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/dtd/notationdecl.rb new file mode 100644 index 0000000..04a9b08 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/dtd/notationdecl.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: false +require_relative "../child" +module REXML + module DTD + class NotationDecl < Child + START = "/um + SYSTEM = /^\s*#{START}\s+(\w[\w-]*)\s+(SYSTEM)\s+((["']).*?\4)\s*>/um + def initialize src + super() + if src.match( PUBLIC ) + md = src.match( PUBLIC, true ) + elsif src.match( SYSTEM ) + md = src.match( SYSTEM, true ) + else + raise ParseException.new( "error parsing notation: no matching pattern", src ) + end + @name = md[1] + @middle = md[2] + @rest = md[3] + end + + def to_s + "" + end + + def write( output, indent ) + indent( output, indent ) + output << to_s + end + + def NotationDecl.parse_source source, listener + md = source.match( PATTERN_RE, true ) + thing = md[0].squeeze(" \t\n\r") + listener.send inspect.downcase, thing + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/element.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/element.rb new file mode 100644 index 0000000..0d74811 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/element.rb @@ -0,0 +1,2578 @@ +# frozen_string_literal: false +require_relative "parent" +require_relative "namespace" +require_relative "attribute" +require_relative "cdata" +require_relative "xpath" +require_relative "parseexception" + +module REXML + # An \REXML::Element object represents an XML element. + # + # An element: + # + # - Has a name (string). + # - May have a parent (another element). + # - Has zero or more children + # (other elements, text, CDATA, processing instructions, and comments). + # - Has zero or more siblings + # (other elements, text, CDATA, processing instructions, and comments). + # - Has zero or more named attributes. + # + # == In a Hurry? + # + # If you're somewhat familiar with XML + # and have a particular task in mind, + # you may want to see the + # {tasks pages}[../doc/rexml/tasks/tocs/master_toc_rdoc.html], + # and in particular, the + # {tasks page for elements}[../doc/rexml/tasks/tocs/element_toc_rdoc.html]. + # + # === Name + # + # An element has a name, which is initially set when the element is created: + # + # e = REXML::Element.new('foo') + # e.name # => "foo" + # + # The name may be changed: + # + # e.name = 'bar' + # e.name # => "bar" + # + # + # === \Parent + # + # An element may have a parent. + # + # Its parent may be assigned explicitly when the element is created: + # + # e0 = REXML::Element.new('foo') + # e1 = REXML::Element.new('bar', e0) + # e1.parent # => ... + # + # Note: the representation of an element always shows the element's name. + # If the element has children, the representation indicates that + # by including an ellipsis (...). + # + # The parent may be assigned explicitly at any time: + # + # e2 = REXML::Element.new('baz') + # e1.parent = e2 + # e1.parent # => + # + # When an element is added as a child, its parent is set automatically: + # + # e1.add_element(e0) + # e0.parent # => ... + # + # For an element that has no parent, method +parent+ returns +nil+. + # + # === Children + # + # An element has zero or more children. + # The children are an ordered collection + # of all objects whose parent is the element itself. + # + # The children may include any combination of elements, text, comments, + # processing instructions, and CDATA. + # (This example keeps things clean by controlling whitespace + # via a +context+ setting.) + # + # xml_string = <<-EOT + # + # + # text 0 + # + # + # + # + # text 1 + # + # + # + # + # EOT + # context = {ignore_whitespace_nodes: :all, compress_whitespace: :all} + # d = REXML::Document.new(xml_string, context) + # root = d.root + # root.children.size # => 10 + # root.each {|child| p "#{child.class}: #{child}" } + # + # Output: + # + # "REXML::Element: " + # "REXML::Text: \n text 0\n " + # "REXML::Comment: comment 0" + # "REXML::Instruction: " + # "REXML::CData: cdata 0" + # "REXML::Element: " + # "REXML::Text: \n text 1\n " + # "REXML::Comment: comment 1" + # "REXML::Instruction: " + # "REXML::CData: cdata 1" + # + # A child may be added using inherited methods + # Parent#insert_before or Parent#insert_after: + # + # xml_string = '' + # d = REXML::Document.new(xml_string) + # root = d.root + # c = d.root[1] # => + # root.insert_before(c, REXML::Element.new('b')) + # root.to_a # => [, , , ] + # + # A child may be replaced using Parent#replace_child: + # + # root.replace_child(c, REXML::Element.new('x')) + # root.to_a # => [, , , ] + # + # A child may be removed using Parent#delete: + # + # x = root[2] # => + # root.delete(x) + # root.to_a # => [, , ] + # + # === Siblings + # + # An element has zero or more siblings, + # which are the other children of the element's parent. + # + # In the example above, element +ele_1+ is between a CDATA sibling + # and a text sibling: + # + # ele_1 = root[5] # => + # ele_1.previous_sibling # => "cdata 0" + # ele_1.next_sibling # => "\n text 1\n " + # + # === \Attributes + # + # An element has zero or more named attributes. + # + # A new element has no attributes: + # + # e = REXML::Element.new('foo') + # e.attributes # => {} + # + # Attributes may be added: + # + # e.add_attribute('bar', 'baz') + # e.add_attribute('bat', 'bam') + # e.attributes.size # => 2 + # e['bar'] # => "baz" + # e['bat'] # => "bam" + # + # An existing attribute may be modified: + # + # e.add_attribute('bar', 'bad') + # e.attributes.size # => 2 + # e['bar'] # => "bad" + # + # An existing attribute may be deleted: + # + # e.delete_attribute('bar') + # e.attributes.size # => 1 + # e['bar'] # => nil + # + # == What's Here + # + # To begin with, what's elsewhere? + # + # \Class \REXML::Element inherits from its ancestor classes: + # + # - REXML::Child + # - REXML::Parent + # + # \REXML::Element itself and its ancestors also include modules: + # + # - {Enumerable}[https://docs.ruby-lang.org/en/master/Enumerable.html] + # - REXML::Namespace + # - REXML::Node + # - REXML::XMLTokens + # + # === Methods for Creating an \Element + # + # ::new:: Returns a new empty element. + # #clone:: Returns a clone of another element. + # + # === Methods for Attributes + # + # {[attribute_name]}[#method-i-5B-5D]:: Returns an attribute value. + # #add_attribute:: Adds a new attribute. + # #add_attributes:: Adds multiple new attributes. + # #attribute:: Returns the attribute value for a given name and optional namespace. + # #delete_attribute:: Removes an attribute. + # + # === Methods for Children + # + # {[index]}[#method-i-5B-5D]:: Returns the child at the given offset. + # #add_element:: Adds an element as the last child. + # #delete_element:: Deletes a child element. + # #each_element:: Calls the given block with each child element. + # #each_element_with_attribute:: Calls the given block with each child element + # that meets given criteria, + # which can include the attribute name. + # #each_element_with_text:: Calls the given block with each child element + # that meets given criteria, + # which can include text. + # #get_elements:: Returns an array of element children that match a given xpath. + # + # === Methods for \Text Children + # + # #add_text:: Adds a text node to the element. + # #get_text:: Returns a text node that meets specified criteria. + # #text:: Returns the text string from the first node that meets specified criteria. + # #texts:: Returns an array of the text children of the element. + # #text=:: Adds, removes, or replaces the first text child of the element + # + # === Methods for Other Children + # + # #cdatas:: Returns an array of the cdata children of the element. + # #comments:: Returns an array of the comment children of the element. + # #instructions:: Returns an array of the instruction children of the element. + # + # === Methods for Namespaces + # + # #add_namespace:: Adds a namespace to the element. + # #delete_namespace:: Removes a namespace from the element. + # #namespace:: Returns the string namespace URI for the element. + # #namespaces:: Returns a hash of all defined namespaces in the element. + # #prefixes:: Returns an array of the string prefixes (names) + # of all defined namespaces in the element + # + # === Methods for Querying + # + # #document:: Returns the document, if any, that the element belongs to. + # #root:: Returns the most distant element (not document) ancestor of the element. + # #root_node:: Returns the most distant ancestor of the element. + # #xpath:: Returns the string xpath to the element + # relative to the most distant parent + # #has_attributes?:: Returns whether the element has attributes. + # #has_elements?:: Returns whether the element has elements. + # #has_text?:: Returns whether the element has text. + # #next_element:: Returns the next sibling that is an element. + # #previous_element:: Returns the previous sibling that is an element. + # #raw:: Returns whether raw mode is set for the element. + # #whitespace:: Returns whether whitespace is respected for the element. + # #ignore_whitespace_nodes:: Returns whether whitespace nodes + # are to be ignored for the element. + # #node_type:: Returns symbol :element. + # + # === One More Method + # + # #inspect:: Returns a string representation of the element. + # + # === Accessors + # + # #elements:: Returns the REXML::Elements object for the element. + # #attributes:: Returns the REXML::Attributes object for the element. + # #context:: Returns or sets the context hash for the element. + # + class Element < Parent + include Namespace + + UNDEFINED = "UNDEFINED"; # The default name + + # Mechanisms for accessing attributes and child elements of this + # element. + attr_reader :attributes, :elements + # The context holds information about the processing environment, such as + # whitespace handling. + attr_accessor :context + + # :call-seq: + # Element.new(name = 'UNDEFINED', parent = nil, context = nil) -> new_element + # Element.new(element, parent = nil, context = nil) -> new_element + # + # Returns a new \REXML::Element object. + # + # When no arguments are given, + # returns an element with name 'UNDEFINED': + # + # e = REXML::Element.new # => + # e.class # => REXML::Element + # e.name # => "UNDEFINED" + # + # When only argument +name+ is given, + # returns an element of the given name: + # + # REXML::Element.new('foo') # => + # + # When only argument +element+ is given, it must be an \REXML::Element object; + # returns a shallow copy of the given element: + # + # e0 = REXML::Element.new('foo') + # e1 = REXML::Element.new(e0) # => + # + # When argument +parent+ is also given, it must be an REXML::Parent object: + # + # e = REXML::Element.new('foo', REXML::Parent.new) + # e.parent # => #]> + # + # When argument +context+ is also given, it must be a hash + # representing the context for the element; + # see {Element Context}[../doc/rexml/context_rdoc.html]: + # + # e = REXML::Element.new('foo', nil, {raw: :all}) + # e.context # => {:raw=>:all} + # + def initialize( arg = UNDEFINED, parent=nil, context=nil ) + super(parent) + + @elements = Elements.new(self) + @attributes = Attributes.new(self) + @context = context + + if arg.kind_of? String + self.name = arg + elsif arg.kind_of? Element + self.name = arg.expanded_name + arg.attributes.each_attribute{ |attribute| + @attributes << Attribute.new( attribute ) + } + @context = arg.context + end + end + + # :call-seq: + # inspect -> string + # + # Returns a string representation of the element. + # + # For an element with no attributes and no children, shows the element name: + # + # REXML::Element.new.inspect # => "" + # + # Shows attributes, if any: + # + # e = REXML::Element.new('foo') + # e.add_attributes({'bar' => 0, 'baz' => 1}) + # e.inspect # => "" + # + # Shows an ellipsis (...), if there are child elements: + # + # e.add_element(REXML::Element.new('bar')) + # e.add_element(REXML::Element.new('baz')) + # e.inspect # => " ... " + # + def inspect + rv = "<#@expanded_name" + + @attributes.each_attribute do |attr| + rv << " " + attr.write( rv, 0 ) + end + + if children.size > 0 + rv << "> ... " + else + rv << "/>" + end + end + + # :call-seq: + # clone -> new_element + # + # Returns a shallow copy of the element, containing the name and attributes, + # but not the parent or children: + # + # e = REXML::Element.new('foo') + # e.add_attributes({'bar' => 0, 'baz' => 1}) + # e.clone # => + # + def clone + self.class.new self + end + + # :call-seq: + # root_node -> document or element + # + # Returns the most distant ancestor of +self+. + # + # When the element is part of a document, + # returns the root node of the document. + # Note that the root node is different from the document element; + # in this example +a+ is document element and the root node is its parent: + # + # d = REXML::Document.new('') + # top_element = d.first # => ... + # child = top_element.first # => ... + # d.root_node == d # => true + # top_element.root_node == d # => true + # child.root_node == d # => true + # + # When the element is not part of a document, but does have ancestor elements, + # returns the most distant ancestor element: + # + # e0 = REXML::Element.new('foo') + # e1 = REXML::Element.new('bar') + # e1.parent = e0 + # e2 = REXML::Element.new('baz') + # e2.parent = e1 + # e2.root_node == e0 # => true + # + # When the element has no ancestor elements, + # returns +self+: + # + # e = REXML::Element.new('foo') + # e.root_node == e # => true + # + # Related: #root, #document. + # + def root_node + parent.nil? ? self : parent.root_node + end + + # :call-seq: + # root -> element + # + # Returns the most distant _element_ (not document) ancestor of the element: + # + # d = REXML::Document.new('') + # top_element = d.first + # child = top_element.first + # top_element.root == top_element # => true + # child.root == top_element # => true + # + # For a document, returns the topmost element: + # + # d.root == top_element # => true + # + # Related: #root_node, #document. + # + def root + target = self + while target + return target.elements[1] if target.kind_of? Document + parent = target.parent + return target if parent.kind_of? Document or parent.nil? + target = parent + end + nil + end + + # :call-seq: + # document -> document or nil + # + # If the element is part of a document, returns that document: + # + # d = REXML::Document.new('') + # top_element = d.first + # child = top_element.first + # top_element.document == d # => true + # child.document == d # => true + # + # If the element is not part of a document, returns +nil+: + # + # REXML::Element.new.document # => nil + # + # For a document, returns +self+: + # + # d.document == d # => true + # + # Related: #root, #root_node. + # + def document + root&.parent + end + + # :call-seq: + # whitespace + # + # Returns +true+ if whitespace is respected for this element, + # +false+ otherwise. + # + # See {Element Context}[../doc/rexml/context_rdoc.html]. + # + # The evaluation is tested against the element's +expanded_name+, + # and so is namespace-sensitive. + def whitespace + @whitespace = nil + if @context + if @context[:respect_whitespace] + @whitespace = (@context[:respect_whitespace] == :all or + @context[:respect_whitespace].include? expanded_name) + end + @whitespace = false if (@context[:compress_whitespace] and + (@context[:compress_whitespace] == :all or + @context[:compress_whitespace].include? expanded_name) + ) + end + @whitespace = true unless @whitespace == false + @whitespace + end + + # :call-seq: + # ignore_whitespace_nodes + # + # Returns +true+ if whitespace nodes are ignored for the element. + # + # See {Element Context}[../doc/rexml/context_rdoc.html]. + # + def ignore_whitespace_nodes + @ignore_whitespace_nodes = false + if @context + if @context[:ignore_whitespace_nodes] + @ignore_whitespace_nodes = + (@context[:ignore_whitespace_nodes] == :all or + @context[:ignore_whitespace_nodes].include? expanded_name) + end + end + end + + # :call-seq: + # raw + # + # Returns +true+ if raw mode is set for the element. + # + # See {Element Context}[../doc/rexml/context_rdoc.html]. + # + # The evaluation is tested against +expanded_name+, and so is namespace + # sensitive. + def raw + @raw = (@context and @context[:raw] and + (@context[:raw] == :all or + @context[:raw].include? expanded_name)) + @raw + end + + #once :whitespace, :raw, :ignore_whitespace_nodes + + ################################################# + # Namespaces # + ################################################# + + # :call-seq: + # prefixes -> array_of_namespace_prefixes + # + # Returns an array of the string prefixes (names) of all defined namespaces + # in the element and its ancestors: + # + # xml_string = <<-EOT + # + # + # + # + # + # + # EOT + # d = REXML::Document.new(xml_string, {compress_whitespace: :all}) + # d.elements['//a'].prefixes # => ["x", "y"] + # d.elements['//b'].prefixes # => ["x", "y"] + # d.elements['//c'].prefixes # => ["x", "y", "z"] + # + def prefixes + prefixes = [] + prefixes = parent.prefixes if parent + prefixes |= attributes.prefixes + prefixes + end + + # :call-seq: + # namespaces -> array_of_namespace_names + # + # Returns a hash of all defined namespaces + # in the element and its ancestors: + # + # xml_string = <<-EOT + # + # + # + # + # + # + # EOT + # d = REXML::Document.new(xml_string) + # d.elements['//a'].namespaces # => {"x"=>"1", "y"=>"2"} + # d.elements['//b'].namespaces # => {"x"=>"1", "y"=>"2"} + # d.elements['//c'].namespaces # => {"x"=>"1", "y"=>"2", "z"=>"3"} + # + def namespaces + namespaces_cache = document&.__send__(:namespaces_cache) + if namespaces_cache + namespaces_cache[self] ||= calculate_namespaces + else + calculate_namespaces + end + end + + # :call-seq: + # namespace(prefix = nil) -> string_uri or nil + # + # Returns the string namespace URI for the element, + # possibly deriving from one of its ancestors. + # + # xml_string = <<-EOT + # + # + # + # + # + # + # EOT + # d = REXML::Document.new(xml_string) + # b = d.elements['//b'] + # b.namespace # => "1" + # b.namespace('y') # => "2" + # b.namespace('nosuch') # => nil + # + def namespace(prefix=nil) + if prefix.nil? + prefix = prefix() + end + prefix = (prefix == '') ? 'xmlns' : prefix.delete_prefix("xmlns:") + ns = namespaces[prefix] + + ns = '' if ns.nil? and prefix == 'xmlns' + ns + end + + # :call-seq: + # add_namespace(prefix, uri = nil) -> self + # + # Adds a namespace to the element; returns +self+. + # + # With the single argument +prefix+, + # adds a namespace using the given +prefix+ and the namespace URI: + # + # e = REXML::Element.new('foo') + # e.add_namespace('bar') + # e.namespaces # => {"xmlns"=>"bar"} + # + # With both arguments +prefix+ and +uri+ given, + # adds a namespace using both arguments: + # + # e.add_namespace('baz', 'bat') + # e.namespaces # => {"xmlns"=>"bar", "baz"=>"bat"} + # + def add_namespace( prefix, uri=nil ) + unless uri + @attributes["xmlns"] = prefix + else + prefix = "xmlns:#{prefix}" unless prefix =~ /^xmlns:/ + @attributes[ prefix ] = uri + end + self + end + + # :call-seq: + # delete_namespace(namespace = 'xmlns') -> self + # + # Removes a namespace from the element. + # + # With no argument, removes the default namespace: + # + # d = REXML::Document.new "" + # d.to_s # => "" + # d.root.delete_namespace # => + # d.to_s # => "" + # + # With argument +namespace+, removes the specified namespace: + # + # d.root.delete_namespace('foo') + # d.to_s # => "" + # + # Does nothing if no such namespace is found: + # + # d.root.delete_namespace('nosuch') + # d.to_s # => "" + # + def delete_namespace namespace="xmlns" + namespace = "xmlns:#{namespace}" unless namespace == 'xmlns' + attribute = attributes.get_attribute(namespace) + attribute.remove unless attribute.nil? + self + end + + ################################################# + # Elements # + ################################################# + + # :call-seq: + # add_element(name, attributes = nil) -> new_element + # add_element(element, attributes = nil) -> element + # + # Adds a child element, optionally setting attributes + # on the added element; returns the added element. + # + # With string argument +name+, creates a new element with that name + # and adds the new element as a child: + # + # e0 = REXML::Element.new('foo') + # e0.add_element('bar') + # e0[0] # => + # + # + # With argument +name+ and hash argument +attributes+, + # sets attributes on the new element: + # + # e0.add_element('baz', {'bat' => '0', 'bam' => '1'}) + # e0[1] # => + # + # With element argument +element+, adds that element as a child: + # + # e0 = REXML::Element.new('foo') + # e1 = REXML::Element.new('bar') + # e0.add_element(e1) + # e0[0] # => + # + # With argument +element+ and hash argument +attributes+, + # sets attributes on the added element: + # + # e0.add_element(e1, {'bat' => '0', 'bam' => '1'}) + # e0[1] # => + # + def add_element element, attrs=nil + raise "First argument must be either an element name, or an Element object" if element.nil? + el = @elements.add(element) + attrs.each do |key, value| + el.attributes[key]=value + end if attrs.kind_of? Hash + el + end + + # :call-seq: + # delete_element(index) -> removed_element or nil + # delete_element(element) -> removed_element or nil + # delete_element(xpath) -> removed_element or nil + # + # Deletes a child element. + # + # When 1-based integer argument +index+ is given, + # removes and returns the child element at that offset if it exists; + # indexing does not include text nodes; + # returns +nil+ if the element does not exist: + # + # d = REXML::Document.new 'text' + # a = d.root # => ... + # a.delete_element(1) # => + # a.delete_element(1) # => + # a.delete_element(1) # => nil + # + # When element argument +element+ is given, + # removes and returns that child element if it exists, + # otherwise returns +nil+: + # + # d = REXML::Document.new 'text' + # a = d.root # => ... + # c = a[2] # => + # a.delete_element(c) # => + # a.delete_element(c) # => nil + # + # When xpath argument +xpath+ is given, + # removes and returns the element at xpath if it exists, + # otherwise returns +nil+: + # + # d = REXML::Document.new 'text' + # a = d.root # => ... + # a.delete_element('//c') # => + # a.delete_element('//c') # => nil + # + def delete_element element + @elements.delete element + end + + # :call-seq: + # has_elements? + # + # Returns +true+ if the element has one or more element children, + # +false+ otherwise: + # + # d = REXML::Document.new 'text' + # a = d.root # => ... + # a.has_elements? # => true + # b = a[0] # => + # b.has_elements? # => false + # + def has_elements? + !@elements.empty? + end + + # :call-seq: + # each_element_with_attribute(attr_name, value = nil, max = 0, xpath = nil) {|e| ... } + # + # Calls the given block with each child element that meets given criteria. + # + # When only string argument +attr_name+ is given, + # calls the block with each child element that has that attribute: + # + # d = REXML::Document.new '' + # a = d.root + # a.each_element_with_attribute('id') {|e| p e } + # + # Output: + # + # + # + # + # + # With argument +attr_name+ and string argument +value+ given, + # calls the block with each child element that has that attribute + # with that value: + # + # a.each_element_with_attribute('id', '1') {|e| p e } + # + # Output: + # + # + # + # + # With arguments +attr_name+, +value+, and integer argument +max+ given, + # calls the block with at most +max+ child elements: + # + # a.each_element_with_attribute('id', '1', 1) {|e| p e } + # + # Output: + # + # + # + # With all arguments given, including +xpath+, + # calls the block with only those child elements + # that meet the first three criteria, + # and also match the given +xpath+: + # + # a.each_element_with_attribute('id', '1', 2, '//d') {|e| p e } + # + # Output: + # + # + # + def each_element_with_attribute( key, value=nil, max=0, name=nil, &block ) # :yields: Element + each_with_something( proc {|child| + if value.nil? + child.attributes[key] != nil + else + child.attributes[key]==value + end + }, max, name, &block ) + end + + # :call-seq: + # each_element_with_text(text = nil, max = 0, xpath = nil) {|e| ... } + # + # Calls the given block with each child element that meets given criteria. + # + # With no arguments, calls the block with each child element that has text: + # + # d = REXML::Document.new 'bbd' + # a = d.root + # a.each_element_with_text {|e| p e } + # + # Output: + # + # ... + # ... + # ... + # + # With the single string argument +text+, + # calls the block with each element that has exactly that text: + # + # a.each_element_with_text('b') {|e| p e } + # + # Output: + # + # ... + # ... + # + # With argument +text+ and integer argument +max+, + # calls the block with at most +max+ elements: + # + # a.each_element_with_text('b', 1) {|e| p e } + # + # Output: + # + # ... + # + # With all arguments given, including +xpath+, + # calls the block with only those child elements + # that meet the first two criteria, + # and also match the given +xpath+: + # + # a.each_element_with_text('b', 2, '//c') {|e| p e } + # + # Output: + # + # ... + # + def each_element_with_text( text=nil, max=0, name=nil, &block ) # :yields: Element + each_with_something( proc {|child| + if text.nil? + child.has_text? + else + child.text == text + end + }, max, name, &block ) + end + + # :call-seq: + # each_element {|e| ... } + # + # Calls the given block with each child element: + # + # d = REXML::Document.new 'bbd' + # a = d.root + # a.each_element {|e| p e } + # + # Output: + # + # ... + # ... + # ... + # + # + def each_element( xpath=nil, &block ) # :yields: Element + @elements.each( xpath, &block ) + end + + # :call-seq: + # get_elements(xpath) + # + # Returns an array of the elements that match the given +xpath+: + # + # xml_string = <<-EOT + # + # + # + # + # + # EOT + # d = REXML::Document.new(xml_string) + # d.root.get_elements('//a') # => [ ... , ] + # + def get_elements( xpath ) + @elements.to_a( xpath ) + end + + # :call-seq: + # next_element + # + # Returns the next sibling that is an element if it exists, + # +niL+ otherwise: + # + # d = REXML::Document.new 'text' + # d.root.elements['b'].next_element #-> + # d.root.elements['c'].next_element #-> nil + # + def next_element + element = next_sibling + element = element.next_sibling until element.nil? or element.kind_of? Element + element + end + + # :call-seq: + # previous_element + # + # Returns the previous sibling that is an element if it exists, + # +niL+ otherwise: + # + # d = REXML::Document.new 'text' + # d.root.elements['c'].previous_element #-> + # d.root.elements['b'].previous_element #-> nil + # + def previous_element + element = previous_sibling + element = element.previous_sibling until element.nil? or element.kind_of? Element + element + end + + + ################################################# + # Text # + ################################################# + + # :call-seq: + # has_text? -> true or false + # + # Returns +true+ if the element has one or more text noded, + # +false+ otherwise: + # + # d = REXML::Document.new 'text' + # a = d.root + # a.has_text? # => true + # b = a[0] + # b.has_text? # => false + # + def has_text? + not text().nil? + end + + # :call-seq: + # text(xpath = nil) -> text_string or nil + # + # Returns the text string from the first text node child + # in a specified element, if it exists, +nil+ otherwise. + # + # With no argument, returns the text from the first text node in +self+: + # + # d = REXML::Document.new "

some text this is bold! more text

" + # d.root.text.class # => String + # d.root.text # => "some text " + # + # With argument +xpath+, returns text from the first text node + # in the element that matches +xpath+: + # + # d.root.text(1) # => "this is bold!" + # + # Note that an element may have multiple text nodes, + # possibly separated by other non-text children, as above. + # Even so, the returned value is the string text from the first such node. + # + # Note also that the text note is retrieved by method get_text, + # and so is always normalized text. + # + def text( path = nil ) + rv = get_text(path) + rv&.value + end + + # :call-seq: + # get_text(xpath = nil) -> text_node or nil + # + # Returns the first text node child in a specified element, if it exists, + # +nil+ otherwise. + # + # With no argument, returns the first text node from +self+: + # + # d = REXML::Document.new "

some text this is bold! more text

" + # d.root.get_text.class # => REXML::Text + # d.root.get_text # => "some text " + # + # With argument +xpath+, returns the first text node from the element + # that matches +xpath+: + # + # d.root.get_text(1) # => "this is bold!" + # + def get_text path = nil + rv = nil + if path + element = @elements[ path ] + rv = element.get_text unless element.nil? + else + rv = @children.find { |node| node.kind_of? Text } + end + rv + end + + # :call-seq: + # text = string -> string + # text = nil -> nil + # + # Adds, replaces, or removes the first text node child in the element. + # + # With string argument +string+, + # creates a new \REXML::Text node containing that string, + # honoring the current settings for whitespace and row, + # then places the node as the first text child in the element; + # returns +string+. + # + # If the element has no text child, the text node is added: + # + # d = REXML::Document.new '' + # d.root.text = 'foo' #-> 'foo' + # + # If the element has a text child, it is replaced: + # + # d.root.text = 'bar' #-> 'bar' + # + # With argument +nil+, removes the first text child: + # + # d.root.text = nil #-> '' + # + def text=( text ) + if text.kind_of? String + text = Text.new( text, whitespace(), nil, raw() ) + elsif !text.nil? and !text.kind_of? Text + text = Text.new( text.to_s, whitespace(), nil, raw() ) + end + old_text = get_text + if text.nil? + old_text.remove unless old_text.nil? + else + if old_text.nil? + self << text + else + old_text.replace_with( text ) + end + end + self + end + + # :call-seq: + # add_text(string) -> nil + # add_text(text_node) -> self + # + # Adds text to the element. + # + # When string argument +string+ is given, returns +nil+. + # + # If the element has no child text node, + # creates a \REXML::Text object using the string, + # honoring the current settings for whitespace and raw, + # then adds that node to the element: + # + # d = REXML::Document.new('') + # a = d.root + # a.add_text('foo') + # a.to_a # => [, "foo"] + # + # If the element has child text nodes, + # appends the string to the _last_ text node: + # + # d = REXML::Document.new('foobar') + # a = d.root + # a.add_text('baz') + # a.to_a # => ["foo", , "barbaz"] + # a.add_text('baz') + # a.to_a # => ["foo", , "barbazbaz"] + # + # When text node argument +text_node+ is given, + # appends the node as the last text node in the element; + # returns +self+: + # + # d = REXML::Document.new('foobar') + # a = d.root + # a.add_text(REXML::Text.new('baz')) + # a.to_a # => ["foo", , "bar", "baz"] + # a.add_text(REXML::Text.new('baz')) + # a.to_a # => ["foo", , "bar", "baz", "baz"] + # + def add_text( text ) + if text.kind_of? String + if @children[-1].kind_of? Text + @children[-1] << text + return + end + text = Text.new( text, whitespace(), nil, raw() ) + end + self << text unless text.nil? + self + end + + # :call-seq: + # node_type -> :element + # + # Returns symbol :element: + # + # d = REXML::Document.new('') + # a = d.root # => + # a.node_type # => :element + # + def node_type + :element + end + + # :call-seq: + # xpath -> string_xpath + # + # Returns the string xpath to the element + # relative to the most distant parent: + # + # d = REXML::Document.new('') + # a = d.root # => ... + # b = a[0] # => ... + # c = b[0] # => + # d.xpath # => "" + # a.xpath # => "/a" + # b.xpath # => "/a/b" + # c.xpath # => "/a/b/c" + # + # If there is no parent, returns the expanded name of the element: + # + # e = REXML::Element.new('foo') + # e.xpath # => "foo" + # + def xpath + path_elements = [] + cur = self + path_elements << __to_xpath_helper( self ) + while cur.parent + cur = cur.parent + path_elements << __to_xpath_helper( cur ) + end + path_elements.reverse.join( "/" ) + end + + ################################################# + # Attributes # + ################################################# + + # :call-seq: + # [index] -> object + # [attr_name] -> attr_value + # [attr_sym] -> attr_value + # + # With integer argument +index+ given, + # returns the child at offset +index+, or +nil+ if none: + # + # d = REXML::Document.new '>textmore
' + # root = d.root + # (0..root.size).each do |index| + # node = root[index] + # p "#{index}: #{node} (#{node.class})" + # end + # + # Output: + # + # "0: (REXML::Element)" + # "1: text (REXML::Text)" + # "2: (REXML::Element)" + # "3: more (REXML::Text)" + # "4: (REXML::Element)" + # "5: (NilClass)" + # + # With string argument +attr_name+ given, + # returns the string value for the given attribute name if it exists, + # otherwise +nil+: + # + # d = REXML::Document.new('') + # root = d.root + # root['attr'] # => "value" + # root['nosuch'] # => nil + # + # With symbol argument +attr_sym+ given, + # returns [attr_sym.to_s]: + # + # root[:attr] # => "value" + # root[:nosuch] # => nil + # + def [](name_or_index) + case name_or_index + when String + attributes[name_or_index] + when Symbol + attributes[name_or_index.to_s] + else + super + end + end + + + # :call-seq: + # attribute(name, namespace = nil) + # + # Returns the string value for the given attribute name. + # + # With only argument +name+ given, + # returns the value of the named attribute if it exists, otherwise +nil+: + # + # xml_string = <<-EOT + # + # + # + # + #
+ # EOT + # d = REXML::Document.new(xml_string) + # root = d.root + # a = root[1] # => + # a.attribute('attr') # => attr='value' + # a.attribute('nope') # => nil + # + # With arguments +name+ and +namespace+ given, + # returns the value of the named attribute if it exists, otherwise +nil+: + # + # xml_string = "" + # document = REXML::Document.new(xml_string) + # document.root.attribute("x") # => x='x' + # document.root.attribute("x", "a") # => a:x='a:x' + # + def attribute( name, namespace=nil ) + prefix = namespaces.key(namespace) if namespace + prefix = nil if prefix == 'xmlns' + + ret_val = + attributes.get_attribute( prefix ? "#{prefix}:#{name}" : name ) + + return ret_val unless ret_val.nil? + return nil if prefix.nil? + + # now check that prefix'es namespace is not the same as the + # default namespace + return nil unless ( namespaces[ prefix ] == namespaces[ 'xmlns' ] ) + + attributes.get_attribute( name ) + end + + # :call-seq: + # has_attributes? -> true or false + # + # Returns +true+ if the element has attributes, +false+ otherwise: + # + # d = REXML::Document.new('') + # a, b = *d.root + # a.has_attributes? # => true + # b.has_attributes? # => false + # + def has_attributes? + !@attributes.empty? + end + + # :call-seq: + # add_attribute(name, value) -> value + # add_attribute(attribute) -> attribute + # + # Adds an attribute to this element, overwriting any existing attribute + # by the same name. + # + # With string argument +name+ and object +value+ are given, + # adds the attribute created with that name and value: + # + # e = REXML::Element.new + # e.add_attribute('attr', 'value') # => "value" + # e['attr'] # => "value" + # e.add_attribute('attr', 'VALUE') # => "VALUE" + # e['attr'] # => "VALUE" + # + # With only attribute object +attribute+ given, + # adds the given attribute: + # + # a = REXML::Attribute.new('attr', 'value') + # e.add_attribute(a) # => attr='value' + # e['attr'] # => "value" + # a = REXML::Attribute.new('attr', 'VALUE') + # e.add_attribute(a) # => attr='VALUE' + # e['attr'] # => "VALUE" + # + def add_attribute( key, value=nil ) + if key.kind_of? Attribute + @attributes << key + else + @attributes[key] = value + end + end + + # :call-seq: + # add_attributes(hash) -> hash + # add_attributes(array) + # + # Adds zero or more attributes to the element; + # returns the argument. + # + # If hash argument +hash+ is given, + # each key must be a string; + # adds each attribute created with the key/value pair: + # + # e = REXML::Element.new + # h = {'foo' => 'bar', 'baz' => 'bat'} + # e.add_attributes(h) + # + # If argument +array+ is given, + # each array member must be a 2-element array [name, value]; + # each name must be a string: + # + # e = REXML::Element.new + # a = [['foo' => 'bar'], ['baz' => 'bat']] + # e.add_attributes(a) + # + def add_attributes hash + if hash.kind_of? Hash + hash.each_pair {|key, value| @attributes[key] = value } + elsif hash.kind_of? Array + hash.each { |value| @attributes[ value[0] ] = value[1] } + end + end + + # :call-seq: + # delete_attribute(name) -> removed_attribute or nil + # + # Removes a named attribute if it exists; + # returns the removed attribute if found, otherwise +nil+: + # + # e = REXML::Element.new('foo') + # e.add_attribute('bar', 'baz') + # e.delete_attribute('bar') # => + # e.delete_attribute('bar') # => nil + # + def delete_attribute(key) + attr = @attributes.get_attribute(key) + attr.remove unless attr.nil? + end + + ################################################# + # Other Utilities # + ################################################# + + # :call-seq: + # cdatas -> array_of_cdata_children + # + # Returns a frozen array of the REXML::CData children of the element: + # + # xml_string = <<-EOT + # + # + # + # + # EOT + # d = REXML::Document.new(xml_string) + # cds = d.root.cdatas # => ["foo", "bar"] + # cds.frozen? # => true + # cds.map {|cd| cd.class } # => [REXML::CData, REXML::CData] + # + def cdatas + find_all { |child| child.kind_of? CData }.freeze + end + + # :call-seq: + # comments -> array_of_comment_children + # + # Returns a frozen array of the REXML::Comment children of the element: + # + # xml_string = <<-EOT + # + # + # + # + # EOT + # d = REXML::Document.new(xml_string) + # cs = d.root.comments + # cs.frozen? # => true + # cs.map {|c| c.class } # => [REXML::Comment, REXML::Comment] + # cs.map {|c| c.to_s } # => ["foo", "bar"] + # + def comments + find_all { |child| child.kind_of? Comment }.freeze + end + + # :call-seq: + # instructions -> array_of_instruction_children + # + # Returns a frozen array of the REXML::Instruction children of the element: + # + # xml_string = <<-EOT + # + # + # + # + # EOT + # d = REXML::Document.new(xml_string) + # is = d.root.instructions + # is.frozen? # => true + # is.map {|i| i.class } # => [REXML::Instruction, REXML::Instruction] + # is.map {|i| i.to_s } # => ["", ""] + # + def instructions + find_all { |child| child.kind_of? Instruction }.freeze + end + + # :call-seq: + # texts -> array_of_text_children + # + # Returns a frozen array of the REXML::Text children of the element: + # + # xml_string = 'textmore' + # d = REXML::Document.new(xml_string) + # ts = d.root.texts + # ts.frozen? # => true + # ts.map {|t| t.class } # => [REXML::Text, REXML::Text] + # ts.map {|t| t.to_s } # => ["text", "more"] + # + def texts + find_all { |child| child.kind_of? Text }.freeze + end + + # == DEPRECATED + # See REXML::Formatters + # + # Writes out this element, and recursively, all children. + # output:: + # output an object which supports '<< string'; this is where the + # document will be written. + # indent:: + # An integer. If -1, no indenting will be used; otherwise, the + # indentation will be this number of spaces, and children will be + # indented an additional amount. Defaults to -1 + # transitive:: + # If transitive is true and indent is >= 0, then the output will be + # pretty-printed in such a way that the added whitespace does not affect + # the parse tree of the document + # ie_hack:: + # This hack inserts a space before the /> on empty tags to address + # a limitation of Internet Explorer. Defaults to false + # + # out = '' + # doc.write( out ) #-> doc is written to the string 'out' + # doc.write( $stdout ) #-> doc written to the console + def write(output=$stdout, indent=-1, transitive=false, ie_hack=false) + Kernel.warn("#{self.class.name}#write is deprecated. See REXML::Formatters", uplevel: 1) + formatter = if indent > -1 + if transitive + require_relative "formatters/transitive" + REXML::Formatters::Transitive.new( indent, ie_hack ) + else + REXML::Formatters::Pretty.new( indent, ie_hack ) + end + else + REXML::Formatters::Default.new( ie_hack ) + end + formatter.write( self, output ) + end + + private + def calculate_namespaces + if parent + parent.namespaces.merge(attributes.namespaces) + else + attributes.namespaces + end + end + + def __to_xpath_helper node + rv = node.expanded_name.clone + if node.parent + results = node.parent.find_all {|n| + n.kind_of?(REXML::Element) and n.expanded_name == node.expanded_name + } + if results.length > 1 + idx = results.index( node ) + rv << "[#{idx+1}]" + end + end + rv + end + + # A private helper method + def each_with_something( test, max=0, name=nil ) + num = 0 + @elements.each( name ){ |child| + yield child if test.call(child) and num += 1 + return if max>0 and num == max + } + end + end + + ######################################################################## + # ELEMENTS # + ######################################################################## + + # A class which provides filtering of children for Elements, and + # XPath search support. You are expected to only encounter this class as + # the element.elements object. Therefore, you are + # _not_ expected to instantiate this yourself. + # + # xml_string = <<-EOT + # + # + # + # Everyday Italian + # Giada De Laurentiis + # 2005 + # 30.00 + # + # + # Harry Potter + # J K. Rowling + # 2005 + # 29.99 + # + # + # XQuery Kick Start + # James McGovern + # Per Bothner + # Kurt Cagle + # James Linn + # Vaidyanathan Nagarajan + # 2003 + # 49.99 + # + # + # Learning XML + # Erik T. Ray + # 2003 + # 39.95 + # + # + # EOT + # d = REXML::Document.new(xml_string) + # elements = d.root.elements + # elements # => # ... > + # + class Elements + include Enumerable + # :call-seq: + # new(parent) -> new_elements_object + # + # Returns a new \Elements object with the given +parent+. + # Does _not_ assign parent.elements = self: + # + # d = REXML::Document.new(xml_string) + # eles = REXML::Elements.new(d.root) + # eles # => # ... > + # eles == d.root.elements # => false + # + def initialize parent + @element = parent + end + + # :call-seq: + # parent + # + # Returns the parent element cited in creating the \Elements object. + # This element is also the default starting point for searching + # in the \Elements object. + # + # d = REXML::Document.new(xml_string) + # elements = REXML::Elements.new(d.root) + # elements.parent == d.root # => true + # + def parent + @element + end + + # :call-seq: + # elements[index] -> element or nil + # elements[xpath] -> element or nil + # elements[n, name] -> element or nil + # + # Returns the first \Element object selected by the arguments, + # if any found, or +nil+ if none found. + # + # Notes: + # - The +index+ is 1-based, not 0-based, so that: + # - The first element has index 1 + # - The _nth_ element has index +n+. + # - The selection ignores non-\Element nodes. + # + # When the single argument +index+ is given, + # returns the element given by the index, if any; otherwise, +nil+: + # + # d = REXML::Document.new(xml_string) + # eles = d.root.elements + # eles # => # ... > + # eles[1] # => ... + # eles.size # => 4 + # eles[4] # => ... + # eles[5] # => nil + # + # The node at this index is not an \Element, and so is not returned: + # + # eles = d.root.first.first # => ... </> + # eles.to_a # => ["Everyday Italian"] + # eles[1] # => nil + # + # When the single argument +xpath+ is given, + # returns the first element found via that +xpath+, if any; otherwise, +nil+: + # + # eles = d.root.elements # => #<REXML::Elements @element=<bookstore> ... </>> + # eles['/bookstore'] # => <bookstore> ... </> + # eles['//book'] # => <book category='cooking'> ... </> + # eles['//book [@category="children"]'] # => <book category='children'> ... </> + # eles['/nosuch'] # => nil + # eles['//nosuch'] # => nil + # eles['//book [@category="nosuch"]'] # => nil + # eles['.'] # => <bookstore> ... </> + # eles['..'].class # => REXML::Document + # + # With arguments +n+ and +name+ given, + # returns the _nth_ found element that has the given +name+, + # or +nil+ if there is no such _nth_ element: + # + # eles = d.root.elements # => #<REXML::Elements @element=<bookstore> ... </>> + # eles[1, 'book'] # => <book category='cooking'> ... </> + # eles[4, 'book'] # => <book category='web' cover='paperback'> ... </> + # eles[5, 'book'] # => nil + # + def []( index, name=nil) + if index.kind_of? Integer + raise "index (#{index}) must be >= 1" if index < 1 + name = literalize(name) if name + num = 0 + @element.find { |child| + child.kind_of? Element and + (name.nil? ? true : child.has_name?( name )) and + (num += 1) == index + } + else + XPath::first( @element, index ) + end + end + + # :call-seq: + # elements[] = index, replacement_element -> replacement_element or nil + # + # Replaces or adds an element. + # + # When <tt>eles[index]</tt> exists, replaces it with +replacement_element+ + # and returns +replacement_element+: + # + # d = REXML::Document.new(xml_string) + # eles = d.root.elements # => #<REXML::Elements @element=<bookstore> ... </>> + # eles[1] # => <book category='cooking'> ... </> + # eles[1] = REXML::Element.new('foo') + # eles[1] # => <foo/> + # + # Does nothing (or raises an exception) + # if +replacement_element+ is not an \Element: + # eles[2] # => <book category='web' cover='paperback'> ... </> + # eles[2] = REXML::Text.new('bar') + # eles[2] # => <book category='web' cover='paperback'> ... </> + # + # When <tt>eles[index]</tt> does not exist, + # adds +replacement_element+ to the element and returns + # + # d = REXML::Document.new(xml_string) + # eles = d.root.elements # => #<REXML::Elements @element=<bookstore> ... </>> + # eles.size # => 4 + # eles[50] = REXML::Element.new('foo') # => <foo/> + # eles.size # => 5 + # eles[5] # => <foo/> + # + # Does nothing (or raises an exception) + # if +replacement_element+ is not an \Element: + # + # eles[50] = REXML::Text.new('bar') # => "bar" + # eles.size # => 5 + # + def []=( index, element ) + previous = self[index] + if previous.nil? + @element.add element + else + previous.replace_with element + end + previous + end + + # :call-seq: + # empty? -> true or false + # + # Returns +true+ if there are no children, +false+ otherwise. + # + # d = REXML::Document.new('') + # d.elements.empty? # => true + # d = REXML::Document.new(xml_string) + # d.elements.empty? # => false + # + def empty? + @element.find{ |child| child.kind_of? Element}.nil? + end + + # :call-seq: + # index(element) + # + # Returns the 1-based index of the given +element+, if found; + # otherwise, returns -1: + # + # d = REXML::Document.new(xml_string) + # elements = d.root.elements + # ele_1, ele_2, ele_3, ele_4 = *elements + # elements.index(ele_4) # => 4 + # elements.delete(ele_3) + # elements.index(ele_4) # => 3 + # elements.index(ele_3) # => -1 + # + def index element + rv = 0 + found = @element.find do |child| + child.kind_of? Element and + (rv += 1) and + child == element + end + return rv if found == element + -1 + end + + # :call-seq: + # delete(index) -> removed_element or nil + # delete(element) -> removed_element or nil + # delete(xpath) -> removed_element or nil + # + # Removes an element; returns the removed element, or +nil+ if none removed. + # + # With integer argument +index+ given, + # removes the child element at that offset: + # + # d = REXML::Document.new(xml_string) + # elements = d.root.elements + # elements.size # => 4 + # elements[2] # => <book category='children'> ... </> + # elements.delete(2) # => <book category='children'> ... </> + # elements.size # => 3 + # elements[2] # => <book category='web'> ... </> + # elements.delete(50) # => nil + # + # With element argument +element+ given, + # removes that child element: + # + # d = REXML::Document.new(xml_string) + # elements = d.root.elements + # ele_1, ele_2, ele_3, ele_4 = *elements + # elements.size # => 4 + # elements[2] # => <book category='children'> ... </> + # elements.delete(ele_2) # => <book category='children'> ... </> + # elements.size # => 3 + # elements[2] # => <book category='web'> ... </> + # elements.delete(ele_2) # => nil + # + # With string argument +xpath+ given, + # removes the first element found via that xpath: + # + # d = REXML::Document.new(xml_string) + # elements = d.root.elements + # elements.delete('//book') # => <book category='cooking'> ... </> + # elements.delete('//book [@category="children"]') # => <book category='children'> ... </> + # elements.delete('//nosuch') # => nil + # + def delete element + if element.kind_of? Element + @element.delete element + else + el = self[element] + el.remove if el + end + end + + # :call-seq: + # delete_all(xpath) + # + # Removes all elements found via the given +xpath+; + # returns the array of removed elements, if any, else +nil+. + # + # d = REXML::Document.new(xml_string) + # elements = d.root.elements + # elements.size # => 4 + # deleted_elements = elements.delete_all('//book [@category="web"]') + # deleted_elements.size # => 2 + # elements.size # => 2 + # deleted_elements = elements.delete_all('//book') + # deleted_elements.size # => 2 + # elements.size # => 0 + # elements.delete_all('//book') # => [] + # + def delete_all( xpath ) + rv = [] + XPath::each( @element, xpath) {|element| + rv << element if element.kind_of? Element + } + rv.each do |element| + @element.delete element + element.remove + end + rv + end + + # :call-seq: + # add -> new_element + # add(name) -> new_element + # add(element) -> element + # + # Adds an element; returns the element added. + # + # With no argument, creates and adds a new element. + # The new element has: + # + # - No name. + # - \Parent from the \Elements object. + # - Context from the that parent. + # + # Example: + # + # d = REXML::Document.new(xml_string) + # elements = d.root.elements + # parent = elements.parent # => <bookstore> ... </> + # parent.context = {raw: :all} + # elements.size # => 4 + # new_element = elements.add # => </> + # elements.size # => 5 + # new_element.name # => nil + # new_element.parent # => <bookstore> ... </> + # new_element.context # => {:raw=>:all} + # + # With string argument +name+, creates and adds a new element. + # The new element has: + # + # - Name +name+. + # - \Parent from the \Elements object. + # - Context from the that parent. + # + # Example: + # + # d = REXML::Document.new(xml_string) + # elements = d.root.elements + # parent = elements.parent # => <bookstore> ... </> + # parent.context = {raw: :all} + # elements.size # => 4 + # new_element = elements.add('foo') # => <foo/> + # elements.size # => 5 + # new_element.name # => "foo" + # new_element.parent # => <bookstore> ... </> + # new_element.context # => {:raw=>:all} + # + # With argument +element+, + # creates and adds a clone of the given +element+. + # The new element has name, parent, and context from the given +element+. + # + # d = REXML::Document.new(xml_string) + # elements = d.root.elements + # elements.size # => 4 + # e0 = REXML::Element.new('foo') + # e1 = REXML::Element.new('bar', e0, {raw: :all}) + # element = elements.add(e1) # => <bar/> + # elements.size # => 5 + # element.name # => "bar" + # element.parent # => <bookstore> ... </> + # element.context # => {:raw=>:all} + # + def add element=nil + if element.nil? + Element.new("", self, @element.context) + elsif not element.kind_of?(Element) + Element.new(element, self, @element.context) + else + @element << element + element.context = @element.context + element + end + end + + alias :<< :add + + # :call-seq: + # each(xpath = nil) {|element| ... } -> self + # + # Iterates over the elements. + # + # With no argument, calls the block with each element: + # + # d = REXML::Document.new(xml_string) + # elements = d.root.elements + # elements.each {|element| p element } + # + # Output: + # + # <book category='cooking'> ... </> + # <book category='children'> ... </> + # <book category='web'> ... </> + # <book category='web' cover='paperback'> ... </> + # + # With argument +xpath+, calls the block with each element + # that matches the given +xpath+: + # + # elements.each('//book [@category="web"]') {|element| p element } + # + # Output: + # + # <book category='web'> ... </> + # <book category='web' cover='paperback'> ... </> + # + def each( xpath=nil ) + XPath::each( @element, xpath ) {|e| yield e if e.kind_of? Element } + end + + # :call-seq: + # collect(xpath = nil) {|element| ... } -> array + # + # Iterates over the elements; returns the array of block return values. + # + # With no argument, iterates over all elements: + # + # d = REXML::Document.new(xml_string) + # elements = d.root.elements + # elements.collect {|element| element.size } # => [9, 9, 17, 9] + # + # With argument +xpath+, iterates over elements that match + # the given +xpath+: + # + # xpath = '//book [@category="web"]' + # elements.collect(xpath) {|element| element.size } # => [17, 9] + # + def collect( xpath=nil ) + collection = [] + XPath::each( @element, xpath ) {|e| + collection << yield(e) if e.kind_of?(Element) + } + collection + end + + # :call-seq: + # inject(xpath = nil, initial = nil) -> object + # + # Calls the block with elements; returns the last block return value. + # + # With no argument, iterates over the elements, calling the block + # <tt>elements.size - 1</tt> times. + # + # - The first call passes the first and second elements. + # - The second call passes the first block return value and the third element. + # - The third call passes the second block return value and the fourth element. + # - And so on. + # + # In this example, the block returns the passed element, + # which is then the object argument to the next call: + # + # d = REXML::Document.new(xml_string) + # elements = d.root.elements + # elements.inject do |object, element| + # p [elements.index(object), elements.index(element)] + # element + # end + # + # Output: + # + # [1, 2] + # [2, 3] + # [3, 4] + # + # With the single argument +xpath+, calls the block only with + # elements matching that xpath: + # + # elements.inject('//book [@category="web"]') do |object, element| + # p [elements.index(object), elements.index(element)] + # element + # end + # + # Output: + # + # [3, 4] + # + # With argument +xpath+ given as +nil+ + # and argument +initial+ also given, + # calls the block once for each element. + # + # - The first call passes the +initial+ and the first element. + # - The second call passes the first block return value and the second element. + # - The third call passes the second block return value and the third element. + # - And so on. + # + # In this example, the first object index is <tt>-1</tt> + # + # elements.inject(nil, 'Initial') do |object, element| + # p [elements.index(object), elements.index(element)] + # element + # end + # + # Output: + # + # [-1, 1] + # [1, 2] + # [2, 3] + # [3, 4] + # + # In this form the passed object can be used as an accumulator: + # + # elements.inject(nil, 0) do |total, element| + # total += element.size + # end # => 44 + # + # With both arguments +xpath+ and +initial+ are given, + # calls the block only with elements matching that xpath: + # + # elements.inject('//book [@category="web"]', 0) do |total, element| + # total += element.size + # end # => 26 + # + def inject( xpath=nil, initial=nil ) + first = true + XPath::each( @element, xpath ) {|e| + if (e.kind_of? Element) + if (first and initial == nil) + initial = e + first = false + else + initial = yield( initial, e ) if e.kind_of? Element + end + end + } + initial + end + + # :call-seq: + # size -> integer + # + # Returns the count of \Element children: + # + # d = REXML::Document.new '<a>sean<b/>elliott<b/>russell<b/></a>' + # d.root.elements.size # => 3 # Three elements. + # d.root.size # => 6 # Three elements plus three text nodes.. + # + def size + count = 0 + @element.each {|child| count+=1 if child.kind_of? Element } + count + end + + # :call-seq: + # to_a(xpath = nil) -> array_of_elements + # + # Returns an array of element children (not including non-element children). + # + # With no argument, returns an array of all element children: + # + # d = REXML::Document.new '<a>sean<b/>elliott<c/></a>' + # elements = d.root.elements + # elements.to_a # => [<b/>, <c/>] # Omits non-element children. + # children = d.root.children + # children # => ["sean", <b/>, "elliott", <c/>] # Includes non-element children. + # + # With argument +xpath+, returns an array of element children + # that match the xpath: + # + # elements.to_a('//c') # => [<c/>] + # + def to_a( xpath=nil ) + rv = XPath.match( @element, xpath ) + return rv.find_all{|e| e.kind_of? Element} if xpath + rv + end + + private + # Private helper class. Removes quotes from quoted strings + def literalize name + name = name[1..-2] if name[0] == ?' or name[0] == ?" #' + name + end + end + + ######################################################################## + # ATTRIBUTES # + ######################################################################## + + # A class that defines the set of Attributes of an Element and provides + # operations for accessing elements in that set. + class Attributes < Hash + + # :call-seq: + # new(element) + # + # Creates and returns a new \REXML::Attributes object. + # The element given by argument +element+ is stored, + # but its own attributes are not modified: + # + # ele = REXML::Element.new('foo') + # attrs = REXML::Attributes.new(ele) + # attrs.object_id == ele.attributes.object_id # => false + # + # Other instance methods in class \REXML::Attributes may refer to: + # + # - +element.document+. + # - +element.prefix+. + # - +element.expanded_name+. + # + def initialize element + @element = element + end + + # :call-seq: + # [name] -> attribute_value or nil + # + # Returns the value for the attribute given by +name+, + # if it exists; otherwise +nil+. + # The value returned is the unnormalized attribute value, + # with entities expanded: + # + # xml_string = <<-EOT + # <root xmlns:foo="http://foo" xmlns:bar="http://bar"> + # <ele foo:att='1' bar:att='2' att='<'/> + # </root> + # EOT + # d = REXML::Document.new(xml_string) + # ele = d.elements['//ele'] # => <a foo:att='1' bar:att='2' att='<'/> + # ele.attributes['att'] # => "<" + # ele.attributes['bar:att'] # => "2" + # ele.attributes['nosuch'] # => nil + # + # Related: get_attribute (returns an \Attribute object). + # + def [](name) + attr = get_attribute(name) + attr&.value + end + + # :call-seq: + # to_a -> array_of_attribute_objects + # + # Returns an array of \REXML::Attribute objects representing + # the attributes: + # + # xml_string = <<-EOT + # <root xmlns:foo="http://foo" xmlns:bar="http://bar"> + # <ele foo:att='1' bar:att='2' att='<'/> + # </root> + # EOT + # d = REXML::Document.new(xml_string) + # ele = d.root.elements['//ele'] # => <a foo:att='1' bar:att='2' att='<'/> + # attrs = ele.attributes.to_a # => [foo:att='1', bar:att='2', att='<'] + # attrs.first.class # => REXML::Attribute + # + def to_a + enum_for(:each_attribute).to_a + end + + # :call-seq: + # length + # + # Returns the count of attributes: + # + # xml_string = <<-EOT + # <root xmlns:foo="http://foo" xmlns:bar="http://bar"> + # <ele foo:att='1' bar:att='2' att='<'/> + # </root> + # EOT + # d = REXML::Document.new(xml_string) + # ele = d.root.elements['//ele'] # => <a foo:att='1' bar:att='2' att='<'/> + # ele.attributes.length # => 3 + # + def length + c = 0 + each_attribute { c+=1 } + c + end + alias :size :length + + # :call-seq: + # each_attribute {|attr| ... } + # + # Calls the given block with each \REXML::Attribute object: + # + # xml_string = <<-EOT + # <root xmlns:foo="http://foo" xmlns:bar="http://bar"> + # <ele foo:att='1' bar:att='2' att='<'/> + # </root> + # EOT + # d = REXML::Document.new(xml_string) + # ele = d.root.elements['//ele'] # => <a foo:att='1' bar:att='2' att='<'/> + # ele.attributes.each_attribute do |attr| + # p [attr.class, attr] + # end + # + # Output: + # + # [REXML::Attribute, foo:att='1'] + # [REXML::Attribute, bar:att='2'] + # [REXML::Attribute, att='<'] + # + def each_attribute # :yields: attribute + return to_enum(__method__) unless block_given? + each_value do |val| + if val.kind_of? Attribute + yield val + else + val.each_value { |atr| yield atr } + end + end + end + + # :call-seq: + # each {|expanded_name, value| ... } + # + # Calls the given block with each expanded-name/value pair: + # + # xml_string = <<-EOT + # <root xmlns:foo="http://foo" xmlns:bar="http://bar"> + # <ele foo:att='1' bar:att='2' att='<'/> + # </root> + # EOT + # d = REXML::Document.new(xml_string) + # ele = d.root.elements['//ele'] # => <a foo:att='1' bar:att='2' att='<'/> + # ele.attributes.each do |expanded_name, value| + # p [expanded_name, value] + # end + # + # Output: + # + # ["foo:att", "1"] + # ["bar:att", "2"] + # ["att", "<"] + # + def each + return to_enum(__method__) unless block_given? + each_attribute do |attr| + yield [attr.expanded_name, attr.value] + end + end + + # :call-seq: + # get_attribute(name) -> attribute_object or nil + # + # Returns the \REXML::Attribute object for the given +name+: + # + # xml_string = <<-EOT + # <root xmlns:foo="http://foo" xmlns:bar="http://bar"> + # <ele foo:att='1' bar:att='2' att='<'/> + # </root> + # EOT + # d = REXML::Document.new(xml_string) + # ele = d.root.elements['//ele'] # => <a foo:att='1' bar:att='2' att='<'/> + # attrs = ele.attributes + # attrs.get_attribute('foo:att') # => foo:att='1' + # attrs.get_attribute('foo:att').class # => REXML::Attribute + # attrs.get_attribute('bar:att') # => bar:att='2' + # attrs.get_attribute('att') # => att='<' + # attrs.get_attribute('nosuch') # => nil + # + def get_attribute( name ) + attr = fetch( name, nil ) + if attr.nil? + return nil if name.nil? + # Look for prefix + name =~ Namespace::NAMESPLIT + prefix, n = $1, $2 + if prefix + attr = fetch( n, nil ) + # check prefix + if attr == nil + elsif attr.kind_of? Attribute + return attr if prefix == attr.prefix + else + attr = attr[ prefix ] + return attr + end + end + doctype = @element.document&.doctype + if doctype + expn = @element.expanded_name + expn = doctype.name if expn.size == 0 + attr_val = doctype.attribute_of(expn, name) + return Attribute.new( name, attr_val ) if attr_val + end + return nil + end + if attr.kind_of? Hash + attr = attr[ @element.prefix ] + end + attr + end + + # :call-seq: + # [name] = value -> value + # + # When +value+ is non-+nil+, + # assigns that to the attribute for the given +name+, + # overwriting the previous value if it exists: + # + # xml_string = <<-EOT + # <root xmlns:foo="http://foo" xmlns:bar="http://bar"> + # <ele foo:att='1' bar:att='2' att='<'/> + # </root> + # EOT + # d = REXML::Document.new(xml_string) + # ele = d.root.elements['//ele'] # => <a foo:att='1' bar:att='2' att='<'/> + # attrs = ele.attributes + # attrs['foo:att'] = '2' # => "2" + # attrs['baz:att'] = '3' # => "3" + # + # When +value+ is +nil+, deletes the attribute if it exists: + # + # attrs['baz:att'] = nil + # attrs.include?('baz:att') # => false + # + def []=( name, value ) + if value.nil? # Delete the named attribute + attr = get_attribute(name) + delete attr + return + end + + unless value.kind_of? Attribute + doctype = @element.document&.doctype + if doctype + value = Text::normalize( value, doctype ) + else + value = Text::normalize( value, nil ) + end + value = Attribute.new(name, value) + end + value.element = @element + old_attr = fetch(value.name, nil) + if old_attr.nil? + store(value.name, value) + elsif old_attr.kind_of? Hash + old_attr[value.prefix] = value + elsif old_attr.prefix != value.prefix + store value.name, {old_attr.prefix => old_attr, + value.prefix => value} + else + store value.name, value + end + @element + end + + # :call-seq: + # prefixes -> array_of_prefix_strings + # + # Returns an array of prefix strings in the attributes. + # The array does not include the default + # namespace declaration, if one exists. + # + # xml_string = '<a xmlns="foo" xmlns:x="bar" xmlns:y="twee" z="glorp"/>' + # d = REXML::Document.new(xml_string) + # d.root.attributes.prefixes # => ["x", "y"] + # + def prefixes + ns = [] + each_attribute do |attribute| + ns << attribute.name if attribute.prefix == 'xmlns' + end + doctype = @element.document&.doctype + if doctype + expn = @element.expanded_name + expn = doctype.name if expn.size == 0 + doctype.attributes_of(expn).each { + |attribute| + ns << attribute.name if attribute.prefix == 'xmlns' + } + end + ns + end + + # :call-seq: + # namespaces + # + # Returns a hash of name/value pairs for the namespaces: + # + # xml_string = '<a xmlns="foo" xmlns:x="bar" xmlns:y="twee" z="glorp"/>' + # d = REXML::Document.new(xml_string) + # d.root.attributes.namespaces # => {"xmlns"=>"foo", "x"=>"bar", "y"=>"twee"} + # + def namespaces + namespaces = {} + each_attribute do |attribute| + namespaces[attribute.name] = attribute.value if attribute.prefix == 'xmlns' or attribute.name == 'xmlns' + end + doctype = @element.document&.doctype + if doctype + expn = @element.expanded_name + expn = doctype.name if expn.size == 0 + doctype.attributes_of(expn).each { + |attribute| + namespaces[attribute.name] = attribute.value if attribute.prefix == 'xmlns' or attribute.name == 'xmlns' + } + end + namespaces + end + + # :call-seq: + # delete(name) -> element + # delete(attribute) -> element + # + # Removes a specified attribute if it exists; + # returns the attributes' element. + # + # When string argument +name+ is given, + # removes the attribute of that name if it exists: + # + # xml_string = <<-EOT + # <root xmlns:foo="http://foo" xmlns:bar="http://bar"> + # <ele foo:att='1' bar:att='2' att='<'/> + # </root> + # EOT + # d = REXML::Document.new(xml_string) + # ele = d.root.elements['//ele'] # => <a foo:att='1' bar:att='2' att='<'/> + # attrs = ele.attributes + # attrs.delete('foo:att') # => <ele bar:att='2' att='<'/> + # attrs.delete('foo:att') # => <ele bar:att='2' att='<'/> + # + # When attribute argument +attribute+ is given, + # removes that attribute if it exists: + # + # attr = REXML::Attribute.new('bar:att', '2') + # attrs.delete(attr) # => <ele att='<'/> # => <ele att='<'/> + # attrs.delete(attr) # => <ele att='<'/> # => <ele/> + # + def delete( attribute ) + name = nil + prefix = nil + if attribute.kind_of? Attribute + name = attribute.name + prefix = attribute.prefix + else + attribute =~ Namespace::NAMESPLIT + prefix, name = $1, $2 + prefix = '' unless prefix + end + old = fetch(name, nil) + if old.kind_of? Hash # the supplied attribute is one of many + old.delete(prefix) + if old.size == 1 + repl = nil + old.each_value{|v| repl = v} + store name, repl + end + elsif old # the supplied attribute is a top-level one + super(name) + end + @element + end + + # :call-seq: + # add(attribute) -> attribute + # + # Adds attribute +attribute+, replacing the previous + # attribute of the same name if it exists; + # returns +attribute+: + # + # xml_string = <<-EOT + # <root xmlns:foo="http://foo" xmlns:bar="http://bar"> + # <ele foo:att='1' bar:att='2' att='<'/> + # </root> + # EOT + # d = REXML::Document.new(xml_string) + # ele = d.root.elements['//ele'] # => <a foo:att='1' bar:att='2' att='<'/> + # attrs = ele.attributes + # attrs # => {"att"=>{"foo"=>foo:att='1', "bar"=>bar:att='2', ""=>att='<'}} + # attrs.add(REXML::Attribute.new('foo:att', '2')) # => foo:att='2' + # attrs.add(REXML::Attribute.new('baz', '3')) # => baz='3' + # attrs.include?('baz') # => true + # + def add( attribute ) + self[attribute.name] = attribute + end + + alias :<< :add + + # :call-seq: + # delete_all(name) -> array_of_removed_attributes + # + # Removes all attributes matching the given +name+; + # returns an array of the removed attributes: + # + # xml_string = <<-EOT + # <root xmlns:foo="http://foo" xmlns:bar="http://bar"> + # <ele foo:att='1' bar:att='2' att='<'/> + # </root> + # EOT + # d = REXML::Document.new(xml_string) + # ele = d.root.elements['//ele'] # => <a foo:att='1' bar:att='2' att='<'/> + # attrs = ele.attributes + # attrs.delete_all('att') # => [att='<'] + # + def delete_all( name ) + rv = [] + each_attribute { |attribute| + rv << attribute if attribute.expanded_name == name + } + rv.each{ |attr| attr.remove } + rv + end + + # :call-seq: + # get_attribute_ns(namespace, name) + # + # Returns the \REXML::Attribute object among the attributes + # that matches the given +namespace+ and +name+: + # + # xml_string = <<-EOT + # <root xmlns:foo="http://foo" xmlns:bar="http://bar"> + # <ele foo:att='1' bar:att='2' att='<'/> + # </root> + # EOT + # d = REXML::Document.new(xml_string) + # ele = d.root.elements['//ele'] # => <a foo:att='1' bar:att='2' att='<'/> + # attrs = ele.attributes + # attrs.get_attribute_ns('http://foo', 'att') # => foo:att='1' + # attrs.get_attribute_ns('http://foo', 'nosuch') # => nil + # + def get_attribute_ns(namespace, name) + result = nil + each_attribute() { |attribute| + if name == attribute.name && + namespace == attribute.namespace() && + ( !namespace.empty? || !attribute.fully_expanded_name.index(':') ) + # foo will match xmlns:foo, but only if foo isn't also an attribute + result = attribute if !result or !namespace.empty? or + !attribute.fully_expanded_name.index(':') + end + } + result + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/encoding.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/encoding.rb new file mode 100644 index 0000000..7eb05f4 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/encoding.rb @@ -0,0 +1,48 @@ +# coding: US-ASCII +# frozen_string_literal: false +module REXML + module Encoding + # ID ---> Encoding name + attr_reader :encoding + def encoding=(encoding) + encoding = encoding.name if encoding.is_a?(::Encoding) + if encoding.is_a?(String) + original_encoding = encoding + encoding = find_encoding(encoding) + unless encoding + raise ArgumentError, "Bad encoding name #{original_encoding}" + end + end + encoding = encoding.upcase if encoding + return false if defined?(@encoding) and encoding == @encoding + @encoding = encoding || "UTF-8" + true + end + + def encode(string) + string.encode(@encoding) + end + + def decode(string) + string.encode(::Encoding::UTF_8, @encoding) + end + + private + def find_encoding(name) + case name + when /\Ashift-jis\z/i + return "SHIFT_JIS" + when /\ACP-(\d+)\z/ + name = "CP#{$1}" + when /\AUTF-8\z/i + return name + end + begin + ::Encoding::Converter.search_convpath(name, 'UTF-8') + rescue ::Encoding::ConverterNotFoundError + return nil + end + name + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/entity.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/entity.rb new file mode 100644 index 0000000..1ba5a7b --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/entity.rb @@ -0,0 +1,142 @@ +# frozen_string_literal: false +require_relative 'child' +require_relative 'source' +require_relative 'xmltokens' + +module REXML + class Entity < Child + include XMLTokens + PUBIDCHAR = "\x20\x0D\x0Aa-zA-Z0-9\\-()+,./:=?;!*@$_%#" + SYSTEMLITERAL = %Q{((?:"[^"]*")|(?:'[^']*'))} + PUBIDLITERAL = %Q{("[#{PUBIDCHAR}']*"|'[#{PUBIDCHAR}]*')} + EXTERNALID = "(?:(?:(SYSTEM)\\s+#{SYSTEMLITERAL})|(?:(PUBLIC)\\s+#{PUBIDLITERAL}\\s+#{SYSTEMLITERAL}))" + NDATADECL = "\\s+NDATA\\s+#{NAME}" + PEREFERENCE = "%#{NAME};" + PEREFERENCE_RE = /#{PEREFERENCE}/um + ENTITYVALUE = %Q{((?:"(?:[^%&"]|#{PEREFERENCE}|#{REFERENCE})*")|(?:'([^%&']|#{PEREFERENCE}|#{REFERENCE})*'))} + PEDEF = "(?:#{ENTITYVALUE}|#{EXTERNALID})" + ENTITYDEF = "(?:#{ENTITYVALUE}|(?:#{EXTERNALID}(#{NDATADECL})?))" + PEDECL = "<!ENTITY\\s+(%)\\s+#{NAME}\\s+#{PEDEF}\\s*>" + GEDECL = "<!ENTITY\\s+#{NAME}\\s+#{ENTITYDEF}\\s*>" + ENTITYDECL = /\s*(?:#{GEDECL})|(?:#{PEDECL})/um + + attr_reader :name, :external, :ref, :ndata, :pubid, :value + + # Create a new entity. Simple entities can be constructed by passing a + # name, value to the constructor; this creates a generic, plain entity + # reference. For anything more complicated, you have to pass a Source to + # the constructor with the entity definition, or use the accessor methods. + # +WARNING+: There is no validation of entity state except when the entity + # is read from a stream. If you start poking around with the accessors, + # you can easily create a non-conformant Entity. + # + # e = Entity.new( 'amp', '&' ) + def initialize stream, value=nil, parent=nil, reference=false + super(parent) + @ndata = @pubid = @value = @external = nil + if stream.kind_of? Array + @name = stream[1] + if stream[-1] == '%' + @reference = true + stream.pop + else + @reference = false + end + if stream[2] =~ /SYSTEM|PUBLIC/ + @external = stream[2] + if @external == 'SYSTEM' + @ref = stream[3] + @ndata = stream[4] if stream.size == 5 + else + @pubid = stream[3] + @ref = stream[4] + end + else + @value = stream[2] + end + else + @reference = reference + @external = nil + @name = stream + @value = value + end + end + + # Evaluates whether the given string matches an entity definition, + # returning true if so, and false otherwise. + def Entity::matches? string + (ENTITYDECL =~ string) == 0 + end + + # Evaluates to the unnormalized value of this entity; that is, replacing + # &ent; entities. + def unnormalized + document&.record_entity_expansion + + return nil if @value.nil? + + @unnormalized = Text::unnormalize(@value, parent, + entity_expansion_text_limit: document&.entity_expansion_text_limit) + end + + #once :unnormalized + + # Returns the value of this entity unprocessed -- raw. This is the + # normalized value; that is, with all %ent; and &ent; entities intact + def normalized + @value + end + + # Write out a fully formed, correct entity definition (assuming the Entity + # object itself is valid.) + # + # out:: + # An object implementing <TT><<</TT> to which the entity will be + # output + # indent:: + # *DEPRECATED* and ignored + def write out, indent=-1 + out << '<!ENTITY ' + out << '% ' if @reference + out << @name + out << ' ' + if @external + out << @external << ' ' + if @pubid + q = @pubid.include?('"')?"'":'"' + out << q << @pubid << q << ' ' + end + q = @ref.include?('"')?"'":'"' + out << q << @ref << q + out << ' NDATA ' << @ndata if @ndata + else + q = @value.include?('"')?"'":'"' + out << q << @value << q + end + out << '>' + end + + # Returns this entity as a string. See write(). + def to_s + rv = '' + write rv + rv + end + end + + # This is a set of entity constants -- the ones defined in the XML + # specification. These are +gt+, +lt+, +amp+, +quot+ and +apos+. + # CAUTION: these entities does not have parent and document + module EntityConst + # +>+ + GT = Entity.new( 'gt', '>' ) + # +<+ + LT = Entity.new( 'lt', '<' ) + # +&+ + AMP = Entity.new( 'amp', '&' ) + # +"+ + QUOT = Entity.new( 'quot', '"' ) + # +'+ + APOS = Entity.new( 'apos', "'" ) + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/formatters/default.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/formatters/default.rb new file mode 100644 index 0000000..811b2ff --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/formatters/default.rb @@ -0,0 +1,116 @@ +# frozen_string_literal: false + +module REXML + module Formatters + class Default + # Prints out the XML document with no formatting -- except if ie_hack is + # set. + # + # ie_hack:: + # If set to true, then inserts whitespace before the close of an empty + # tag, so that IE's bad XML parser doesn't choke. + def initialize( ie_hack=false ) + @ie_hack = ie_hack + end + + # Writes the node to some output. + # + # node:: + # The node to write + # output:: + # A class implementing <TT><<</TT>. Pass in an Output object to + # change the output encoding. + def write( node, output ) + case node + + when Document + if node.xml_decl.encoding != 'UTF-8' && !output.kind_of?(Output) + output = Output.new( output, node.xml_decl.encoding ) + end + write_document( node, output ) + + when Element + write_element( node, output ) + + when Declaration, ElementDecl, NotationDecl, ExternalEntity, Entity, + Attribute, AttlistDecl + node.write( output,-1 ) + + when Instruction + write_instruction( node, output ) + + when DocType, XMLDecl + node.write( output ) + + when Comment + write_comment( node, output ) + + when CData + write_cdata( node, output ) + + when Text + write_text( node, output ) + + else + raise Exception.new("XML FORMATTING ERROR") + + end + end + + protected + def write_document( node, output ) + node.children.each { |child| write( child, output ) } + end + + def write_element( node, output ) + output << "<#{node.expanded_name}" + + node.attributes.to_a.map { |a| + Hash === a ? a.values : a + }.flatten.sort_by {|attr| attr.name}.each do |attr| + output << " " + attr.write( output ) + end unless node.attributes.empty? + + if node.children.empty? + output << " " if @ie_hack + output << "/" + else + output << ">" + node.children.each { |child| + write( child, output ) + } + output << "</#{node.expanded_name}" + end + output << ">" + end + + def write_text( node, output ) + output << node.to_s() + end + + def write_comment( node, output ) + output << Comment::START + output << node.to_s + output << Comment::STOP + end + + def write_cdata( node, output ) + output << CData::START + output << node.to_s + output << CData::STOP + end + + def write_instruction( node, output ) + output << Instruction::START + output << node.target + content = node.content + if content + output << ' ' + output << content + end + output << Instruction::STOP + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/formatters/pretty.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/formatters/pretty.rb new file mode 100644 index 0000000..a838d83 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/formatters/pretty.rb @@ -0,0 +1,142 @@ +# frozen_string_literal: true +require_relative 'default' + +module REXML + module Formatters + # Pretty-prints an XML document. This destroys whitespace in text nodes + # and will insert carriage returns and indentations. + # + # TODO: Add an option to print attributes on new lines + class Pretty < Default + + # If compact is set to true, then the formatter will attempt to use as + # little space as possible + attr_accessor :compact + # The width of a page. Used for formatting text + attr_accessor :width + + # Create a new pretty printer. + # + # output:: + # An object implementing '<<(String)', to which the output will be written. + # indentation:: + # An integer greater than 0. The indentation of each level will be + # this number of spaces. If this is < 1, the behavior of this object + # is undefined. Defaults to 2. + # ie_hack:: + # If true, the printer will insert whitespace before closing empty + # tags, thereby allowing Internet Explorer's XML parser to + # function. Defaults to false. + def initialize( indentation=2, ie_hack=false ) + @indentation = indentation + @level = 0 + @ie_hack = ie_hack + @width = 80 + @compact = false + end + + protected + def write_element(node, output) + output << ' '*@level + output << "<#{node.expanded_name}" + + node.attributes.each_attribute do |attr| + output << " " + attr.write( output ) + end unless node.attributes.empty? + + if node.children.empty? + if @ie_hack + output << " " + end + output << "/" + else + output << ">" + # If compact and all children are text, and if the formatted output + # is less than the specified width, then try to print everything on + # one line + skip = false + if compact + if node.children.inject(true) {|s,c| s & c.kind_of?(Text)} + string = +"" + old_level = @level + @level = 0 + node.children.each { |child| write( child, string ) } + @level = old_level + if string.length < @width + output << string + skip = true + end + end + end + unless skip + output << "\n" + @level += @indentation + node.children.each { |child| + next if child.kind_of?(Text) and child.to_s.strip.length == 0 + write( child, output ) + output << "\n" + } + @level -= @indentation + output << ' '*@level + end + output << "</#{node.expanded_name}" + end + output << ">" + end + + def write_text( node, output ) + s = node.to_s() + s.gsub!(/\s/,' ') + s.squeeze!(" ") + s = wrap(s, @width - @level) + s = indent_text(s, @level, " ", true) + output << (' '*@level + s) + end + + def write_comment( node, output) + output << ' ' * @level + super + end + + def write_cdata( node, output) + output << ' ' * @level + super + end + + def write_document( node, output ) + # Ok, this is a bit odd. All XML documents have an XML declaration, + # but it may not write itself if the user didn't specifically add it, + # either through the API or in the input document. If it doesn't write + # itself, then we don't need a carriage return... which makes this + # logic more complex. + node.children.each { |child| + next if child.instance_of?(Text) + unless child == node.children[0] or child.instance_of?(Text) or + (child == node.children[1] and !node.children[0].writethis) + output << "\n" + end + write( child, output ) + } + end + + private + def indent_text(string, level=1, style="\t", indentfirstline=true) + return string if level < 0 + string.gsub(/\n/, "\n#{style*level}") + end + + def wrap(string, width) + parts = [] + while string.length > width and place = string.rindex(' ', width) + parts << string[0...place] + string = string[place+1..-1] + end + parts << string + parts.join("\n") + end + + end + end +end + diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/formatters/transitive.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/formatters/transitive.rb new file mode 100644 index 0000000..5ff51e1 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/formatters/transitive.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: false +require_relative 'pretty' + +module REXML + module Formatters + # The Transitive formatter writes an XML document that parses to an + # identical document as the source document. This means that no extra + # whitespace nodes are inserted, and whitespace within text nodes is + # preserved. Within these constraints, the document is pretty-printed, + # with whitespace inserted into the metadata to introduce formatting. + # + # Note that this is only useful if the original XML is not already + # formatted. Since this formatter does not alter whitespace nodes, the + # results of formatting already formatted XML will be odd. + class Transitive < Default + def initialize( indentation=2, ie_hack=false ) + @indentation = indentation + @level = 0 + @ie_hack = ie_hack + end + + protected + def write_element( node, output ) + output << "<#{node.expanded_name}" + + node.attributes.each_attribute do |attr| + output << " " + attr.write( output ) + end unless node.attributes.empty? + + output << "\n" + output << ' '*@level + if node.children.empty? + output << " " if @ie_hack + output << "/" + else + output << ">" + # If compact and all children are text, and if the formatted output + # is less than the specified width, then try to print everything on + # one line + @level += @indentation + node.children.each { |child| + write( child, output ) + } + @level -= @indentation + output << "</#{node.expanded_name}" + output << "\n" + output << ' '*@level + end + output << ">" + end + + def write_text( node, output ) + output << node.to_s() + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/functions.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/functions.rb new file mode 100644 index 0000000..60ae34e --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/functions.rb @@ -0,0 +1,446 @@ +# frozen_string_literal: false +module REXML + # If you add a method, keep in mind two things: + # (1) the first argument will always be a list of nodes from which to + # filter. In the case of context methods (such as position), the function + # should return an array with a value for each child in the array. + # (2) all method calls from XML will have "-" replaced with "_". + # Therefore, in XML, "local-name()" is identical (and actually becomes) + # "local_name()" + module Functions + @@available_functions = {} + @@context = nil + @@namespace_context = {} + @@variables = {} + + INTERNAL_METHODS = [ + :namespace_context, + :namespace_context=, + :variables, + :variables=, + :context=, + :get_namespace, + :send, + ] + class << self + def singleton_method_added(name) + unless INTERNAL_METHODS.include?(name) + @@available_functions[name] = true + end + end + end + + def Functions::namespace_context=(x) ; @@namespace_context=x ; end + def Functions::variables=(x) ; @@variables=x ; end + def Functions::namespace_context ; @@namespace_context ; end + def Functions::variables ; @@variables ; end + + def Functions::context=(value); @@context = value; end + + def Functions::text( ) + if @@context[:node].node_type == :element + @@context[:node].find_all{|n| n.node_type == :text}.collect{|n| n.value} + elsif @@context[:node].node_type == :text + @@context[:node].value + else + false + end + end + + # Returns the last node of the given list of nodes. + def Functions::last( ) + @@context[:size] + end + + def Functions::position( ) + @@context[:index] + end + + # Returns the size of the given list of nodes. + def Functions::count( node_set ) + node_set.size + end + + # Since REXML is non-validating, this method is not implemented as it + # requires a DTD + def Functions::id( object ) + end + + def Functions::local_name(node_set=nil) + get_namespace(node_set) do |node| + return node.local_name + end + "" + end + + def Functions::namespace_uri( node_set=nil ) + get_namespace( node_set ) {|node| node.namespace} + end + + def Functions::name( node_set=nil ) + get_namespace( node_set ) do |node| + node.expanded_name + end + end + + # Helper method. + def Functions::get_namespace( node_set = nil ) + if node_set == nil + yield @@context[:node] if @@context[:node].respond_to?(:namespace) + else + if node_set.respond_to? :each + result = [] + node_set.each do |node| + result << yield(node) if node.respond_to?(:namespace) + end + result + elsif node_set.respond_to? :namespace + yield node_set + end + end + end + + # A node-set is converted to a string by returning the string-value of the + # node in the node-set that is first in document order. If the node-set is + # empty, an empty string is returned. + # + # A number is converted to a string as follows + # + # NaN is converted to the string NaN + # + # positive zero is converted to the string 0 + # + # negative zero is converted to the string 0 + # + # positive infinity is converted to the string Infinity + # + # negative infinity is converted to the string -Infinity + # + # if the number is an integer, the number is represented in decimal form + # as a Number with no decimal point and no leading zeros, preceded by a + # minus sign (-) if the number is negative + # + # otherwise, the number is represented in decimal form as a Number + # including a decimal point with at least one digit before the decimal + # point and at least one digit after the decimal point, preceded by a + # minus sign (-) if the number is negative; there must be no leading zeros + # before the decimal point apart possibly from the one required digit + # immediately before the decimal point; beyond the one required digit + # after the decimal point there must be as many, but only as many, more + # digits as are needed to uniquely distinguish the number from all other + # IEEE 754 numeric values. + # + # The boolean false value is converted to the string false. The boolean + # true value is converted to the string true. + # + # An object of a type other than the four basic types is converted to a + # string in a way that is dependent on that type. + def Functions::string( object=@@context[:node] ) + if object.respond_to?(:node_type) + case object.node_type + when :attribute + object.value + when :element + string_value(object) + when :document + string_value(object.root) + when :processing_instruction + object.content + else + object.to_s + end + else + case object + when Array + string(object[0]) + when Float + if object.nan? + "NaN" + else + integer = object.to_i + if object == integer + "%d" % integer + else + object.to_s + end + end + else + object.to_s + end + end + end + + # A node-set is converted to a string by + # returning the concatenation of the string-value + # of each of the children of the node in the + # node-set that is first in document order. + # If the node-set is empty, an empty string is returned. + def Functions::string_value( o ) + rv = "" + o.children.each { |e| + if e.node_type == :text + rv << e.to_s + elsif e.node_type == :element + rv << string_value( e ) + end + } + rv + end + + def Functions::concat( *objects ) + concatenated = "" + objects.each do |object| + concatenated << string(object) + end + concatenated + end + + # Fixed by Mike Stok + def Functions::starts_with( string, test ) + string(string).index(string(test)) == 0 + end + + # Fixed by Mike Stok + def Functions::contains( string, test ) + string(string).include?(string(test)) + end + + # Kouhei fixed this + def Functions::substring_before( string, test ) + ruby_string = string(string) + ruby_index = ruby_string.index(string(test)) + if ruby_index.nil? + "" + else + ruby_string[ 0...ruby_index ] + end + end + + # Kouhei fixed this too + def Functions::substring_after( string, test ) + ruby_string = string(string) + return $1 if ruby_string =~ /#{test}(.*)/ + "" + end + + # Take equal portions of Mike Stok and Sean Russell; mix + # vigorously, and pour into a tall, chilled glass. Serves 10,000. + def Functions::substring( string, start, length=nil ) + ruby_string = string(string) + ruby_length = if length.nil? + ruby_string.length.to_f + else + number(length) + end + ruby_start = number(start) + + # Handle the special cases + return '' if ( + ruby_length.nan? or + ruby_start.nan? or + ruby_start.infinite? + ) + + infinite_length = ruby_length.infinite? == 1 + ruby_length = ruby_string.length if infinite_length + + # Now, get the bounds. The XPath bounds are 1..length; the ruby bounds + # are 0..length. Therefore, we have to offset the bounds by one. + ruby_start = round(ruby_start) - 1 + ruby_length = round(ruby_length) + + if ruby_start < 0 + ruby_length += ruby_start unless infinite_length + ruby_start = 0 + end + return '' if ruby_length <= 0 + ruby_string[ruby_start,ruby_length] + end + + # UNTESTED + def Functions::string_length( string ) + string(string).length + end + + def Functions::normalize_space( string=nil ) + string = string(@@context[:node]) if string.nil? + if string.kind_of? Array + string.collect{|x| x.to_s.strip.gsub(/\s+/um, ' ') if x} + else + string.to_s.strip.gsub(/\s+/um, ' ') + end + end + + # This is entirely Mike Stok's beast + def Functions::translate( string, tr1, tr2 ) + from = string(tr1) + to = string(tr2) + + # the map is our translation table. + # + # if a character occurs more than once in the + # from string then we ignore the second & + # subsequent mappings + # + # if a character maps to nil then we delete it + # in the output. This happens if the from + # string is longer than the to string + # + # there's nothing about - or ^ being special in + # http://www.w3.org/TR/xpath#function-translate + # so we don't build ranges or negated classes + + map = Hash.new + 0.upto(from.length - 1) { |pos| + from_char = from[pos] + unless map.has_key? from_char + map[from_char] = + if pos < to.length + to[pos] + else + nil + end + end + } + + if ''.respond_to? :chars + string(string).chars.collect { |c| + if map.has_key? c then map[c] else c end + }.compact.join + else + string(string).unpack('U*').collect { |c| + if map.has_key? c then map[c] else c end + }.compact.pack('U*') + end + end + + def Functions::boolean(object=@@context[:node]) + case object + when true, false + object + when Float + return false if object.zero? + return false if object.nan? + true + when Numeric + not object.zero? + when String + not object.empty? + when Array + not object.empty? + else + object ? true : false + end + end + + # UNTESTED + def Functions::not( object ) + not boolean( object ) + end + + # UNTESTED + def Functions::true( ) + true + end + + # UNTESTED + def Functions::false( ) + false + end + + # UNTESTED + def Functions::lang( language ) + lang = false + node = @@context[:node] + attr = nil + until node.nil? + if node.node_type == :element + attr = node.attributes["xml:lang"] + unless attr.nil? + lang = compare_language(string(language), attr) + break + else + end + end + node = node.parent + end + lang + end + + def Functions::compare_language lang1, lang2 + lang2.downcase.index(lang1.downcase) == 0 + end + + # a string that consists of optional whitespace followed by an optional + # minus sign followed by a Number followed by whitespace is converted to + # the IEEE 754 number that is nearest (according to the IEEE 754 + # round-to-nearest rule) to the mathematical value represented by the + # string; any other string is converted to NaN + # + # boolean true is converted to 1; boolean false is converted to 0 + # + # a node-set is first converted to a string as if by a call to the string + # function and then converted in the same way as a string argument + # + # an object of a type other than the four basic types is converted to a + # number in a way that is dependent on that type + def Functions::number(object=@@context[:node]) + case object + when true + Float(1) + when false + Float(0) + when Array + number(string(object)) + when Numeric + object.to_f + else + str = string(object) + case str.strip + when /\A\s*(-?(?:\d+(?:\.\d*)?|\.\d+))\s*\z/ + $1.to_f + else + Float::NAN + end + end + end + + def Functions::sum( nodes ) + nodes = [nodes] unless nodes.kind_of? Array + nodes.inject(0) { |r,n| r + number(string(n)) } + end + + def Functions::floor( number ) + number(number).floor + end + + def Functions::ceiling( number ) + number(number).ceil + end + + def Functions::round( number ) + number = number(number) + begin + neg = number.negative? + number = number.abs.round + neg ? -number : number + rescue FloatDomainError + number + end + end + + def Functions::processing_instruction( node ) + node.node_type == :processing_instruction + end + + def Functions::send(name, *args) + if @@available_functions[name.to_sym] + super + else + # TODO: Maybe, this is not XPath spec behavior. + # This behavior must be reconsidered. + XPath.match(@@context[:node], name.to_s) + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/instruction.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/instruction.rb new file mode 100644 index 0000000..a3dfbbe --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/instruction.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: false + +require_relative "child" +require_relative "source" + +module REXML + # Represents an XML Instruction; IE, <? ... ?> + # TODO: Add parent arg (3rd arg) to constructor + class Instruction < Child + START = "<?" + STOP = "?>" + + # target is the "name" of the Instruction; IE, the "tag" in <?tag ...?> + # content is everything else. + attr_accessor :target, :content + + # Constructs a new Instruction + # @param target can be one of a number of things. If String, then + # the target of this instruction is set to this. If an Instruction, + # then the Instruction is shallowly cloned (target and content are + # copied). + # @param content Must be either a String, or a Parent. Can only + # be a Parent if the target argument is a Source. Otherwise, this + # String is set as the content of this instruction. + def initialize(target, content=nil) + case target + when String + super() + @target = target + @content = content + when Instruction + super(content) + @target = target.target + @content = target.content + else + message = + "processing instruction target must be String or REXML::Instruction: " + message << "<#{target.inspect}>" + raise ArgumentError, message + end + @content.strip! if @content + end + + def clone + Instruction.new self + end + + # == DEPRECATED + # See the rexml/formatters package + # + def write writer, indent=-1, transitive=false, ie_hack=false + Kernel.warn( "#{self.class.name}#write is deprecated", uplevel: 1) + indent(writer, indent) + writer << START + writer << @target + if @content + writer << ' ' + writer << @content + end + writer << STOP + end + + # @return true if other is an Instruction, and the content and target + # of the other matches the target and content of this object. + def ==( other ) + other.kind_of? Instruction and + other.target == @target and + other.content == @content + end + + def node_type + :processing_instruction + end + + def inspect + "<?p-i #{target} ...?>" + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/light/node.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/light/node.rb new file mode 100644 index 0000000..3dab885 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/light/node.rb @@ -0,0 +1,188 @@ +# frozen_string_literal: false +require_relative '../xmltokens' + +module REXML + module Light + # Represents a tagged XML element. Elements are characterized by + # having children, attributes, and names, and can themselves be + # children. + class Node + NAMESPLIT = /^(?:(#{XMLTokens::NCNAME_STR}):)?(#{XMLTokens::NCNAME_STR})/u + PARENTS = [ :element, :document, :doctype ] + # Create a new element. + def initialize node=nil + @node = node + if node.kind_of? String + node = [ :text, node ] + elsif node.nil? + node = [ :document, nil, nil ] + elsif node[0] == :start_element + node[0] = :element + elsif node[0] == :start_doctype + node[0] = :doctype + elsif node[0] == :start_document + node[0] = :document + end + end + + def size + if PARENTS.include? @node[0] + @node[-1].size + else + 0 + end + end + + def each + size.times { |x| yield( at(x+4) ) } + end + + def name + at(2) + end + + def name=( name_str, ns=nil ) + pfx = '' + pfx = "#{prefix(ns)}:" if ns + _old_put(2, "#{pfx}#{name_str}") + end + + def parent=( node ) + _old_put(1,node) + end + + def local_name + namesplit + @name + end + + def local_name=( name_str ) + _old_put( 1, "#@prefix:#{name_str}" ) + end + + def prefix( namespace=nil ) + prefix_of( self, namespace ) + end + + def namespace( prefix=prefix() ) + namespace_of( self, prefix ) + end + + def namespace=( namespace ) + @prefix = prefix( namespace ) + pfx = '' + pfx = "#@prefix:" if @prefix.size > 0 + _old_put(1, "#{pfx}#@name") + end + + def []( reference, ns=nil ) + if reference.kind_of? String + pfx = '' + pfx = "#{prefix(ns)}:" if ns + at(3)["#{pfx}#{reference}"] + elsif reference.kind_of? Range + _old_get( Range.new(4+reference.begin, reference.end, reference.exclude_end?) ) + else + _old_get( 4+reference ) + end + end + + def =~( path ) + XPath.match( self, path ) + end + + # Doesn't handle namespaces yet + def []=( reference, ns, value=nil ) + if reference.kind_of? String + value = ns unless value + at( 3 )[reference] = value + elsif reference.kind_of? Range + _old_put( Range.new(3+reference.begin, reference.end, reference.exclude_end?), ns ) + else + if value + _old_put( 4+reference, ns, value ) + else + _old_put( 4+reference, ns ) + end + end + end + + # Append a child to this element, optionally under a provided namespace. + # The namespace argument is ignored if the element argument is an Element + # object. Otherwise, the element argument is a string, the namespace (if + # provided) is the namespace the element is created in. + def << element + if node_type() == :text + at(-1) << element + else + newnode = Node.new( element ) + newnode.parent = self + self.push( newnode ) + end + at(-1) + end + + def node_type + _old_get(0) + end + + def text=( foo ) + replace = at(4).kind_of?(String)? 1 : 0 + self._old_put(4,replace, normalizefoo) + end + + def root + context = self + context = context.at(1) while context.at(1) + end + + def has_name?( name, namespace = '' ) + at(3) == name and namespace() == namespace + end + + def children + self + end + + def parent + at(1) + end + + def to_s + + end + + private + + def namesplit + return if @name.defined? + at(2) =~ NAMESPLIT + @prefix = '' || $1 + @name = $2 + end + + def namespace_of( node, prefix=nil ) + if not prefix + name = at(2) + name =~ NAMESPLIT + prefix = $1 + end + to_find = 'xmlns' + to_find = "xmlns:#{prefix}" if not prefix.nil? + ns = at(3)[ to_find ] + ns ? ns : namespace_of( @node[0], prefix ) + end + + def prefix_of( node, namespace=nil ) + if not namespace + name = node.name + name =~ NAMESPLIT + $1 + else + ns = at(3).find { |k,v| v == namespace } + ns ? ns : prefix_of( node.parent, namespace ) + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/namespace.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/namespace.rb new file mode 100644 index 0000000..232b7ca --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/namespace.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +require_relative 'xmltokens' + +module REXML + # Adds named attributes to an object. + module Namespace + # The name of the object, valid if set + attr_reader :name, :expanded_name + # The expanded name of the object, valid if name is set + attr_accessor :prefix + include XMLTokens + NAME_WITHOUT_NAMESPACE = /\A#{NCNAME_STR}\z/ + NAMESPLIT = /^(?:(#{NCNAME_STR}):)?(#{NCNAME_STR})/u + + # Sets the name and the expanded name + def name=( name ) + @expanded_name = name + if name.match?(NAME_WITHOUT_NAMESPACE) + @prefix = "" + @namespace = "" + @name = name + elsif name =~ NAMESPLIT + if $1 + @prefix = $1 + else + @prefix = "" + @namespace = "" + end + @name = $2 + elsif name == "" + @prefix = nil + @namespace = nil + @name = nil + else + message = "name must be \#{PREFIX}:\#{LOCAL_NAME} or \#{LOCAL_NAME}: " + message += "<#{name.inspect}>" + raise ArgumentError, message + end + end + + # Compares names optionally WITH namespaces + def has_name?( other, ns=nil ) + if ns + namespace() == ns and name() == other + elsif other.include? ":" + fully_expanded_name == other + else + name == other + end + end + + alias :local_name :name + + # Fully expand the name, even if the prefix wasn't specified in the + # source file. + def fully_expanded_name + ns = prefix + return "#{ns}:#@name" if ns.size > 0 + @name + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/node.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/node.rb new file mode 100644 index 0000000..bccacc5 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/node.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: false +require_relative "parseexception" +require_relative "formatters/pretty" +require_relative "formatters/default" + +module REXML + # Represents a node in the tree. Nodes are never encountered except as + # superclasses of other objects. Nodes have siblings. + module Node + # @return the next sibling (nil if unset) + def next_sibling_node + return nil if @parent.nil? + @parent[ @parent.index(self) + 1 ] + end + + # @return the previous sibling (nil if unset) + def previous_sibling_node + return nil if @parent.nil? + ind = @parent.index(self) + return nil if ind == 0 + @parent[ ind - 1 ] + end + + # indent:: + # *DEPRECATED* This parameter is now ignored. See the formatters in the + # REXML::Formatters package for changing the output style. + def to_s indent=nil + unless indent.nil? + Kernel.warn( "#{self.class.name}#to_s(indent) parameter is deprecated", uplevel: 1) + f = REXML::Formatters::Pretty.new( indent ) + f.write( self, rv = "" ) + else + f = REXML::Formatters::Default.new + f.write( self, rv = "" ) + end + return rv + end + + def indent to, ind + if @parent and @parent.context and not @parent.context[:indentstyle].nil? then + indentstyle = @parent.context[:indentstyle] + else + indentstyle = ' ' + end + to << indentstyle*ind unless ind<1 + end + + def parent? + false; + end + + + # Visit all subnodes of +self+ recursively + def each_recursive(&block) # :yields: node + stack = [] + each { |child| stack.unshift child if child.node_type == :element } + until stack.empty? + child = stack.pop + yield child + n = stack.size + child.each { |grandchild| stack.insert n, grandchild if grandchild.node_type == :element } + end + end + + # Find (and return) first subnode (recursively) for which the block + # evaluates to true. Returns +nil+ if none was found. + def find_first_recursive(&block) # :yields: node + each_recursive {|node| + return node if block.call(node) + } + nil + end + + # Returns the position that +self+ holds in its parent's array, indexed + # from 1. + def index_in_parent + parent.index(self)+1 + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/output.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/output.rb new file mode 100644 index 0000000..88a5fb3 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/output.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: false +require_relative 'encoding' + +module REXML + class Output + include Encoding + + attr_reader :encoding + + def initialize real_IO, encd="iso-8859-1" + @output = real_IO + self.encoding = encd + + @to_utf = encoding != 'UTF-8' + + if encoding == "UTF-16" + @output << "\ufeff".encode("UTF-16BE") + self.encoding = "UTF-16BE" + end + end + + def <<( content ) + @output << (@to_utf ? self.encode(content) : content) + end + + def to_s + "Output[#{encoding}]" + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/parent.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/parent.rb new file mode 100644 index 0000000..6a53b37 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/parent.rb @@ -0,0 +1,166 @@ +# frozen_string_literal: false +require_relative "child" + +module REXML + # A parent has children, and has methods for accessing them. The Parent + # class is never encountered except as the superclass for some other + # object. + class Parent < Child + include Enumerable + + # Constructor + # @param parent if supplied, will be set as the parent of this object + def initialize parent=nil + super(parent) + @children = [] + end + + def add( object ) + object.parent = self + @children << object + object + end + + alias :push :add + alias :<< :push + + def unshift( object ) + object.parent = self + @children.unshift object + end + + def delete( object ) + found = false + @children.delete_if {|c| c.equal?(object) and found = true } + object.parent = nil if found + found ? object : nil + end + + def each(&block) + @children.each(&block) + end + + def delete_if( &block ) + @children.delete_if(&block) + end + + def delete_at( index ) + @children.delete_at index + end + + def each_index( &block ) + @children.each_index(&block) + end + + # Fetches a child at a given index + # @param index the Integer index of the child to fetch + def []( index ) + @children[index] + end + + alias :each_child :each + + + + # Set an index entry. See Array.[]= + # @param index the index of the element to set + # @param opt either the object to set, or an Integer length + # @param child if opt is an Integer, this is the child to set + # @return the parent (self) + def []=( *args ) + args[-1].parent = self + @children[*args[0..-2]] = args[-1] + end + + # Inserts an child before another child + # @param child1 this is either an xpath or an Element. If an Element, + # child2 will be inserted before child1 in the child list of the parent. + # If an xpath, child2 will be inserted before the first child to match + # the xpath. + # @param child2 the child to insert + # @return the parent (self) + def insert_before( child1, child2 ) + if child1.kind_of? String + child1 = XPath.first( self, child1 ) + child1.parent.insert_before child1, child2 + else + ind = index(child1) + child2.parent.delete(child2) if child2.parent + @children[ind,0] = child2 + child2.parent = self + end + self + end + + # Inserts an child after another child + # @param child1 this is either an xpath or an Element. If an Element, + # child2 will be inserted after child1 in the child list of the parent. + # If an xpath, child2 will be inserted after the first child to match + # the xpath. + # @param child2 the child to insert + # @return the parent (self) + def insert_after( child1, child2 ) + if child1.kind_of? String + child1 = XPath.first( self, child1 ) + child1.parent.insert_after child1, child2 + else + ind = index(child1)+1 + child2.parent.delete(child2) if child2.parent + @children[ind,0] = child2 + child2.parent = self + end + self + end + + def to_a + @children.dup + end + + # Fetches the index of a given child + # @param child the child to get the index of + # @return the index of the child, or nil if the object is not a child + # of this parent. + def index( child ) + count = -1 + @children.find { |i| count += 1 ; i.hash == child.hash } + count + end + + # @return the number of children of this parent + def size + @children.size + end + + alias :length :size + + # Replaces one child with another, making sure the nodelist is correct + # @param to_replace the child to replace (must be a Child) + # @param replacement the child to insert into the nodelist (must be a + # Child) + def replace_child( to_replace, replacement ) + @children.map! {|c| c.equal?( to_replace ) ? replacement : c } + to_replace.parent = nil + replacement.parent = self + end + + # Deeply clones this object. This creates a complete duplicate of this + # Parent, including all descendants. + def deep_clone + cl = clone() + each do |child| + if child.kind_of? Parent + cl << child.deep_clone + else + cl << child.clone + end + end + cl + end + + alias :children :to_a + + def parent? + true + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/parseexception.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/parseexception.rb new file mode 100644 index 0000000..e57d05f --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/parseexception.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: false +module REXML + class ParseException < RuntimeError + attr_accessor :source, :parser, :continued_exception + + def initialize( message, source=nil, parser=nil, exception=nil ) + super(message) + @source = source + @parser = parser + @continued_exception = exception + end + + def to_s + # Quote the original exception, if there was one + if @continued_exception + err = @continued_exception.inspect + err << "\n" + err << @continued_exception.backtrace.join("\n") + err << "\n...\n" + else + err = "" + end + + # Get the stack trace and error message + err << super + + # Add contextual information + if @source + err << "\nLine: #{line}\n" + err << "Position: #{position}\n" + err << "Last 80 unconsumed characters:\n" + err.force_encoding("ASCII-8BIT") + err << @source.buffer[0..80].force_encoding("ASCII-8BIT").gsub(/\n/, ' ') + end + + err + end + + def position + @source.current_line[0] if @source and defined? @source.current_line and + @source.current_line + end + + def line + @source.current_line[2] if @source and defined? @source.current_line and + @source.current_line + end + + def context + @source.current_line + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/parsers/baseparser.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/parsers/baseparser.rb new file mode 100644 index 0000000..8fe287a --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/parsers/baseparser.rb @@ -0,0 +1,949 @@ +# frozen_string_literal: true +require_relative '../parseexception' +require_relative '../undefinednamespaceexception' +require_relative '../security' +require_relative '../source' +require 'set' +require "strscan" + +module REXML + module Parsers + unless [].respond_to?(:tally) + module EnumerableTally + refine Enumerable do + def tally + counts = {} + each do |item| + counts[item] ||= 0 + counts[item] += 1 + end + counts + end + end + end + using EnumerableTally + end + + if StringScanner::Version < "3.0.8" + module StringScannerCaptures + refine StringScanner do + def captures + values_at(*(1...size)) + end + end + end + using StringScannerCaptures + end + + # = Using the Pull Parser + # <em>This API is experimental, and subject to change.</em> + # parser = PullParser.new( "<a>text<b att='val'/>txet</a>" ) + # while parser.has_next? + # res = parser.next + # puts res[1]['att'] if res.start_tag? and res[0] == 'b' + # end + # See the PullEvent class for information on the content of the results. + # The data is identical to the arguments passed for the various events to + # the StreamListener API. + # + # Notice that: + # parser = PullParser.new( "<a>BAD DOCUMENT" ) + # while parser.has_next? + # res = parser.next + # raise res[1] if res.error? + # end + # + # Nat Price gave me some good ideas for the API. + class BaseParser + LETTER = '[:alpha:]' + DIGIT = '[:digit:]' + + COMBININGCHAR = '' # TODO + EXTENDER = '' # TODO + + NCNAME_STR= "[#{LETTER}_][-[:alnum:]._#{COMBININGCHAR}#{EXTENDER}]*" + QNAME_STR= "(?:(#{NCNAME_STR}):)?(#{NCNAME_STR})" + QNAME = /(#{QNAME_STR})/ + + # Just for backward compatibility. For example, kramdown uses this. + # It's not used in REXML. + UNAME_STR= "(?:#{NCNAME_STR}:)?#{NCNAME_STR}" + + NAMECHAR = '[\-\w\.:]' + NAME = "([\\w:]#{NAMECHAR}*)" + NMTOKEN = "(?:#{NAMECHAR})+" + NMTOKENS = "#{NMTOKEN}(\\s+#{NMTOKEN})*" + REFERENCE = "&(?:#{NAME};|#\\d+;|#x[0-9a-fA-F]+;)" + REFERENCE_RE = /#{REFERENCE}/ + + DOCTYPE_START = /\A\s*<!DOCTYPE\s/um + DOCTYPE_END = /\A\s*\]\s*>/um + ATTRIBUTE_PATTERN = /\s*(#{QNAME_STR})\s*=\s*(["'])(.*?)\4/um + COMMENT_START = /\A<!--/u + COMMENT_PATTERN = /<!--(.*?)-->/um + CDATA_START = /\A<!\[CDATA\[/u + CDATA_END = /\A\s*\]\s*>/um + CDATA_PATTERN = /<!\[CDATA\[(.*?)\]\]>/um + XMLDECL_START = /\A<\?xml\s/u; + XMLDECL_PATTERN = /<\?xml\s+(.*?)\?>/um + INSTRUCTION_START = /\A<\?/u + INSTRUCTION_PATTERN = /<\?#{NAME}(\s+.*?)?\?>/um + TAG_MATCH = /\A<((?>#{QNAME_STR}))/um + CLOSE_MATCH = /\A\s*<\/(#{QNAME_STR})\s*>/um + + VERSION = /\bversion\s*=\s*["'](.*?)['"]/um + ENCODING = /\bencoding\s*=\s*["'](.*?)['"]/um + STANDALONE = /\bstandalone\s*=\s*["'](.*?)['"]/um + + ENTITY_START = /\A\s*<!ENTITY/ + ELEMENTDECL_START = /\A\s*<!ELEMENT/um + ELEMENTDECL_PATTERN = /\A\s*(<!ELEMENT.*?)>/um + SYSTEMENTITY = /\A\s*(%.*?;)\s*$/um + ENUMERATION = "\\(\\s*#{NMTOKEN}(?:\\s*\\|\\s*#{NMTOKEN})*\\s*\\)" + NOTATIONTYPE = "NOTATION\\s+\\(\\s*#{NAME}(?:\\s*\\|\\s*#{NAME})*\\s*\\)" + ENUMERATEDTYPE = "(?:(?:#{NOTATIONTYPE})|(?:#{ENUMERATION}))" + ATTTYPE = "(CDATA|ID|IDREF|IDREFS|ENTITY|ENTITIES|NMTOKEN|NMTOKENS|#{ENUMERATEDTYPE})" + ATTVALUE = "(?:\"((?:[^<&\"]|#{REFERENCE})*)\")|(?:'((?:[^<&']|#{REFERENCE})*)')" + DEFAULTDECL = "(#REQUIRED|#IMPLIED|(?:(#FIXED\\s+)?#{ATTVALUE}))" + ATTDEF = "\\s+#{NAME}\\s+#{ATTTYPE}\\s+#{DEFAULTDECL}" + ATTDEF_RE = /#{ATTDEF}/ + ATTLISTDECL_START = /\A\s*<!ATTLIST/um + ATTLISTDECL_PATTERN = /\A\s*<!ATTLIST\s+#{NAME}(?:#{ATTDEF})*\s*>/um + + TEXT_PATTERN = /\A([^<]*)/um + + # Entity constants + PUBIDCHAR = "\x20\x0D\x0Aa-zA-Z0-9\\-()+,./:=?;!*@$_%#" + SYSTEMLITERAL = %Q{((?:"[^"]*")|(?:'[^']*'))} + PUBIDLITERAL = %Q{("[#{PUBIDCHAR}']*"|'[#{PUBIDCHAR}]*')} + EXTERNALID = "(?:(?:(SYSTEM)\\s+#{SYSTEMLITERAL})|(?:(PUBLIC)\\s+#{PUBIDLITERAL}\\s+#{SYSTEMLITERAL}))" + NDATADECL = "\\s+NDATA\\s+#{NAME}" + PEREFERENCE = "%#{NAME};" + ENTITYVALUE = %Q{((?:"(?:[^%&"]|#{PEREFERENCE}|#{REFERENCE})*")|(?:'([^%&']|#{PEREFERENCE}|#{REFERENCE})*'))} + PEDEF = "(?:#{ENTITYVALUE}|#{EXTERNALID})" + ENTITYDEF = "(?:#{ENTITYVALUE}|(?:#{EXTERNALID}(#{NDATADECL})?))" + PEDECL = "<!ENTITY\\s+(%)\\s+#{NAME}\\s+#{PEDEF}\\s*>" + GEDECL = "<!ENTITY\\s+#{NAME}\\s+#{ENTITYDEF}\\s*>" + ENTITYDECL = /\s*(?:#{GEDECL})|\s*(?:#{PEDECL})/um + + NOTATIONDECL_START = /\A\s*<!NOTATION/um + EXTERNAL_ID_PUBLIC = /\A\s*PUBLIC\s+#{PUBIDLITERAL}\s+#{SYSTEMLITERAL}\s*/um + EXTERNAL_ID_SYSTEM = /\A\s*SYSTEM\s+#{SYSTEMLITERAL}\s*/um + PUBLIC_ID = /\A\s*PUBLIC\s+#{PUBIDLITERAL}\s*/um + + EREFERENCE = /&(?!#{NAME};)/ + + DEFAULT_ENTITIES = { + 'gt' => [/>/, '>', '>', />/], + 'lt' => [/</, '<', '<', /</], + 'quot' => [/"/, '"', '"', /"/], + "apos" => [/'/, "'", "'", /'/] + } + + module Private + PEREFERENCE_PATTERN = /#{PEREFERENCE}/um + TAG_PATTERN = /((?>#{QNAME_STR}))\s*/um + CLOSE_PATTERN = /(#{QNAME_STR})\s*>/um + EQUAL_PATTERN = /\s*=\s*/um + ATTLISTDECL_END = /\s+#{NAME}(?:#{ATTDEF})*\s*>/um + NAME_PATTERN = /#{NAME}/um + GEDECL_PATTERN = "\\s+#{NAME}\\s+#{ENTITYDEF}\\s*>" + PEDECL_PATTERN = "\\s+(%)\\s+#{NAME}\\s+#{PEDEF}\\s*>" + ENTITYDECL_PATTERN = /(?:#{GEDECL_PATTERN})|(?:#{PEDECL_PATTERN})/um + CARRIAGE_RETURN_NEWLINE_PATTERN = /\r\n?/ + CHARACTER_REFERENCES = /&#((?:\d+)|(?:x[a-fA-F0-9]+));/ + DEFAULT_ENTITIES_PATTERNS = {} + default_entities = ['gt', 'lt', 'quot', 'apos', 'amp'] + default_entities.each do |term| + DEFAULT_ENTITIES_PATTERNS[term] = /&#{term};/ + end + XML_PREFIXED_NAMESPACE = "http://www.w3.org/XML/1998/namespace" + end + private_constant :Private + + def initialize( source ) + self.stream = source + @listeners = [] + @prefixes = Set.new + @entity_expansion_count = 0 + @entity_expansion_limit = Security.entity_expansion_limit + @entity_expansion_text_limit = Security.entity_expansion_text_limit + @source.ensure_buffer + @version = nil + end + + def add_listener( listener ) + @listeners << listener + end + + attr_reader :source + attr_reader :entity_expansion_count + attr_writer :entity_expansion_limit + attr_writer :entity_expansion_text_limit + + def stream=( source ) + @source = SourceFactory.create_from( source ) + reset + end + + def reset + @closed = nil + @have_root = false + @document_status = nil + @tags = [] + @stack = [] + @entities = [] + @namespaces = {"xml" => Private::XML_PREFIXED_NAMESPACE} + @namespaces_restore_stack = [] + end + + def position + if @source.respond_to? :position + @source.position + else + # FIXME + 0 + end + end + + # Returns true if there are no more events + def empty? + (@source.empty? and @stack.empty?) + end + + # Returns true if there are more events. Synonymous with !empty? + def has_next? + !(@source.empty? and @stack.empty?) + end + + # Push an event back on the head of the stream. This method + # has (theoretically) infinite depth. + def unshift token + @stack.unshift(token) + end + + # Peek at the +depth+ event in the stack. The first element on the stack + # is at depth 0. If +depth+ is -1, will parse to the end of the input + # stream and return the last event, which is always :end_document. + # Be aware that this causes the stream to be parsed up to the +depth+ + # event, so you can effectively pre-parse the entire document (pull the + # entire thing into memory) using this method. + def peek depth=0 + raise %Q[Illegal argument "#{depth}"] if depth < -1 + temp = [] + if depth == -1 + temp.push(pull()) until empty? + else + while @stack.size+temp.size < depth+1 + temp.push(pull()) + end + end + @stack += temp if temp.size > 0 + @stack[depth] + end + + # Returns the next event. This is a +PullEvent+ object. + def pull + @source.drop_parsed_content + + pull_event.tap do |event| + @listeners.each do |listener| + listener.receive event + end + end + end + + def pull_event + if @closed + x, @closed = @closed, nil + return [ :end_element, x ] + end + if empty? + if @document_status == :in_doctype + raise ParseException.new("Malformed DOCTYPE: unclosed", @source) + end + unless @tags.empty? + path = "/" + @tags.join("/") + raise ParseException.new("Missing end tag for '#{path}'", @source) + end + + unless @document_status == :in_element + raise ParseException.new("Malformed XML: No root element", @source) + end + + return [ :end_document ] + end + return @stack.shift if @stack.size > 0 + #STDERR.puts @source.encoding + #STDERR.puts "BUFFER = #{@source.buffer.inspect}" + + @source.ensure_buffer + if @document_status == nil + start_position = @source.position + if @source.match?("<?", true) + return process_instruction + elsif @source.match?("<!", true) + if @source.match?("--", true) + return [ :comment, process_comment ] + elsif @source.match?("DOCTYPE", true) + base_error_message = "Malformed DOCTYPE" + unless @source.skip_spaces + if @source.match?(">") + message = "#{base_error_message}: name is missing" + else + message = "#{base_error_message}: invalid name" + end + @source.position = start_position + raise REXML::ParseException.new(message, @source) + end + name = parse_name(base_error_message) + @source.skip_spaces + if @source.match?("[", true) + id = [nil, nil, nil] + @document_status = :in_doctype + elsif @source.match?(">", true) + id = [nil, nil, nil] + @document_status = :after_doctype + @source.ensure_buffer + else + id = parse_id(base_error_message, + accept_external_id: true, + accept_public_id: false) + if id[0] == "SYSTEM" + # For backward compatibility + id[1], id[2] = id[2], nil + end + @source.skip_spaces + if @source.match?("[", true) + @document_status = :in_doctype + elsif @source.match?(">", true) + @document_status = :after_doctype + @source.ensure_buffer + else + message = "#{base_error_message}: garbage after external ID" + raise REXML::ParseException.new(message, @source) + end + end + args = [:start_doctype, name, *id] + if @document_status == :after_doctype + @source.skip_spaces + @stack << [ :end_doctype ] + end + return args + else + message = "Invalid XML" + raise REXML::ParseException.new(message, @source) + end + end + end + if @document_status == :in_doctype + @source.skip_spaces + start_position = @source.position + if @source.match?("<!", true) + if @source.match?("ELEMENT", true) + md = @source.match(/(.*?)>/um, true) + raise REXML::ParseException.new( "Bad ELEMENT declaration!", @source ) if md.nil? + return [ :elementdecl, "<!ELEMENT" + md[1] ] + elsif @source.match?("ENTITY", true) + match_data = @source.match(Private::ENTITYDECL_PATTERN, true) + unless match_data + raise REXML::ParseException.new("Malformed entity declaration", @source) + end + match = [:entitydecl, *match_data.captures.compact] + ref = false + if match[1] == '%' + ref = true + match.delete_at 1 + end + # Now we have to sort out what kind of entity reference this is + if match[2] == 'SYSTEM' + # External reference + match[3] = match[3][1..-2] # PUBID + match.delete_at(4) if match.size > 4 # Chop out NDATA decl + # match is [ :entity, name, SYSTEM, pubid(, ndata)? ] + elsif match[2] == 'PUBLIC' + # External reference + match[3] = match[3][1..-2] # PUBID + match[4] = match[4][1..-2] # HREF + match.delete_at(5) if match.size > 5 # Chop out NDATA decl + # match is [ :entity, name, PUBLIC, pubid, href(, ndata)? ] + elsif Private::PEREFERENCE_PATTERN.match?(match[2]) + raise REXML::ParseException.new("Parameter entity references forbidden in internal subset: #{match[2]}", @source) + else + match[2] = match[2][1..-2] + match.pop if match.size == 4 + # match is [ :entity, name, value ] + end + match << '%' if ref + return match + elsif @source.match?("ATTLIST", true) + md = @source.match(Private::ATTLISTDECL_END, true) + raise REXML::ParseException.new( "Bad ATTLIST declaration!", @source ) if md.nil? + element = md[1] + contents = "<!ATTLIST" + md[0] + + pairs = {} + values = md[0].strip.scan( ATTDEF_RE ) + values.each do |attdef| + unless attdef[3] == "#IMPLIED" + attdef.compact! + val = attdef[3] + val = attdef[4] if val == "#FIXED " + pairs[attdef[0]] = val + if attdef[0] =~ /^xmlns:(.*)/ + @namespaces[$1] = val + end + end + end + return [ :attlistdecl, element, pairs, contents ] + elsif @source.match?("NOTATION", true) + base_error_message = "Malformed notation declaration" + unless @source.skip_spaces + if @source.match?(">") + message = "#{base_error_message}: name is missing" + else + message = "#{base_error_message}: invalid name" + end + @source.position = start_position + raise REXML::ParseException.new(message, @source) + end + name = parse_name(base_error_message) + id = parse_id(base_error_message, + accept_external_id: true, + accept_public_id: true) + @source.skip_spaces + unless @source.match?(">", true) + message = "#{base_error_message}: garbage before end >" + raise REXML::ParseException.new(message, @source) + end + return [:notationdecl, name, *id] + elsif @source.match?("--", true) + return [ :comment, process_comment ] + else + raise REXML::ParseException.new("Malformed node: Started with '<!' but not a comment nor ELEMENT,ENTITY,ATTLIST,NOTATION", @source) + end + elsif match = @source.match(/(%.*?;)\s*/um, true) + return [ :externalentity, match[1] ] + elsif @source.match?(/\]\s*>/um, true) + @document_status = :after_doctype + return [ :end_doctype ] + else + raise ParseException.new("Malformed DOCTYPE: invalid declaration", @source) + end + end + if @document_status == :after_doctype + @source.skip_spaces + end + begin + start_position = @source.position + if @source.match?("<", true) + # :text's read_until may remain only "<" in buffer. In the + # case, buffer is empty here. So we need to fill buffer + # here explicitly. + @source.ensure_buffer + if @source.match?("/", true) + @namespaces_restore_stack.pop + last_tag = @tags.pop + md = @source.match(Private::CLOSE_PATTERN, true) + if md and !last_tag + message = "Unexpected top-level end tag (got '#{md[1]}')" + raise REXML::ParseException.new(message, @source) + end + if md.nil? or last_tag != md[1] + message = "Missing end tag for '#{last_tag}'" + message += " (got '#{md[1]}')" if md + @source.position = start_position if md.nil? + raise REXML::ParseException.new(message, @source) + end + return [ :end_element, last_tag ] + elsif @source.match?("!", true) + #STDERR.puts "SOURCE BUFFER = #{source.buffer}, #{source.buffer.size}" + if @source.match?("--", true) + return [ :comment, process_comment ] + elsif @source.match?("[CDATA[", true) + text = @source.read_until("]]>") + if text.chomp!("]]>") + return [ :cdata, text ] + else + raise REXML::ParseException.new("Malformed CDATA: Missing end ']]>'", @source) + end + else + raise REXML::ParseException.new("Malformed node: Started with '<!' but not a comment nor CDATA", @source) + end + elsif @source.match?("?", true) + return process_instruction + else + # Get the next tag + md = @source.match(Private::TAG_PATTERN, true) + unless md + @source.position = start_position + raise REXML::ParseException.new("malformed XML: missing tag start", @source) + end + tag = md[1] + @document_status = :in_element + @prefixes.clear + @prefixes << md[2] if md[2] + push_namespaces_restore + attributes, closed = parse_attributes(@prefixes) + # Verify that all of the prefixes have been defined + for prefix in @prefixes + unless @namespaces.key?(prefix) + raise UndefinedNamespaceException.new(prefix,@source,self) + end + end + + if closed + @closed = tag + pop_namespaces_restore + else + if @tags.empty? and @have_root + raise ParseException.new("Malformed XML: Extra tag at the end of the document (got '<#{tag}')", @source) + end + @tags.push( tag ) + end + @have_root = true + return [ :start_element, tag, attributes ] + end + else + text = @source.read_until("<") + if text.chomp!("<") + @source.position -= "<".bytesize + end + if @tags.empty? + unless /\A\s*\z/.match?(text) + if @have_root + raise ParseException.new("Malformed XML: Extra content at the end of the document (got '#{text}')", @source) + else + raise ParseException.new("Malformed XML: Content at the start of the document (got '#{text}')", @source) + end + end + return pull_event if @have_root + end + return [ :text, text ] + end + rescue REXML::UndefinedNamespaceException + raise + rescue REXML::ParseException + raise + rescue => error + raise REXML::ParseException.new( "Exception parsing", + @source, self, (error ? error : $!) ) + end + # NOTE: The end of the method never runs, because it is unreachable. + # All branches of code above have explicit unconditional return or raise statements. + end + private :pull_event + + def entity( reference, entities ) + return unless entities + + value = entities[ reference ] + return if value.nil? + + record_entity_expansion + unnormalize( value, entities ) + end + + # Escapes all possible entities + def normalize( input, entities=nil, entity_filter=nil ) + copy = input.clone + # Doing it like this rather than in a loop improves the speed + copy.gsub!( EREFERENCE, '&' ) + entities.each do |key, value| + copy.gsub!( value, "&#{key};" ) unless entity_filter and + entity_filter.include?(entity) + end if entities + copy.gsub!( EREFERENCE, '&' ) + DEFAULT_ENTITIES.each do |key, value| + copy.gsub!( value[3], value[1] ) + end + copy + end + + # Unescapes all possible entities + def unnormalize( string, entities=nil, filter=nil ) + if string.include?("\r") + rv = string.gsub( Private::CARRIAGE_RETURN_NEWLINE_PATTERN, "\n" ) + else + rv = string.dup + end + matches = rv.scan( REFERENCE_RE ) + return rv if matches.size == 0 + rv.gsub!( Private::CHARACTER_REFERENCES ) { + m=$1 + if m.start_with?("x") + code_point = Integer(m[1..-1], 16) + else + code_point = Integer(m, 10) + end + [code_point].pack('U*') + } + matches.collect!{|x|x[0]}.compact! + if filter + matches.reject! do |entity_reference| + filter.include?(entity_reference) + end + end + if matches.size > 0 + matches.tally.each do |entity_reference, n| + entity_expansion_count_before = @entity_expansion_count + entity_value = entity( entity_reference, entities ) + if entity_value + if n > 1 + entity_expansion_count_delta = + @entity_expansion_count - entity_expansion_count_before + record_entity_expansion(entity_expansion_count_delta * (n - 1)) + end + re = Private::DEFAULT_ENTITIES_PATTERNS[entity_reference] || /&#{entity_reference};/ + rv.gsub!( re, entity_value ) + if rv.bytesize > @entity_expansion_text_limit + raise "entity expansion has grown too large" + end + else + er = DEFAULT_ENTITIES[entity_reference] + rv.gsub!( er[0], er[2] ) if er + end + end + rv.gsub!( Private::DEFAULT_ENTITIES_PATTERNS['amp'], '&' ) + end + rv + end + + private + def add_namespace(prefix, uri) + @namespaces_restore_stack.last[prefix] = @namespaces[prefix] + if uri.nil? + @namespaces.delete(prefix) + else + @namespaces[prefix] = uri + end + end + + def push_namespaces_restore + namespaces_restore = {} + @namespaces_restore_stack.push(namespaces_restore) + namespaces_restore + end + + def pop_namespaces_restore + namespaces_restore = @namespaces_restore_stack.pop + namespaces_restore.each do |prefix, uri| + if uri.nil? + @namespaces.delete(prefix) + else + @namespaces[prefix] = uri + end + end + end + + def record_entity_expansion(delta=1) + @entity_expansion_count += delta + if @entity_expansion_count > @entity_expansion_limit + raise "number of entity expansions exceeded, processing aborted." + end + end + + def need_source_encoding_update?(xml_declaration_encoding) + return false if xml_declaration_encoding.nil? + return false if /\AUTF-16\z/i =~ xml_declaration_encoding + true + end + + def normalize_xml_declaration_encoding(xml_declaration_encoding) + /\AUTF-16(?:BE|LE)\z/i.match?(xml_declaration_encoding) ? "UTF-16" : nil + end + + def parse_name(base_error_message) + md = @source.match(Private::NAME_PATTERN, true) + unless md + if @source.match?(/\S/um) + message = "#{base_error_message}: invalid name" + else + message = "#{base_error_message}: name is missing" + end + raise REXML::ParseException.new(message, @source) + end + md[0] + end + + def parse_id(base_error_message, + accept_external_id:, + accept_public_id:) + if accept_external_id and (md = @source.match(EXTERNAL_ID_PUBLIC, true)) + pubid = system = nil + pubid_literal = md[1] + pubid = pubid_literal[1..-2] if pubid_literal # Remove quote + system_literal = md[2] + system = system_literal[1..-2] if system_literal # Remove quote + ["PUBLIC", pubid, system] + elsif accept_public_id and (md = @source.match(PUBLIC_ID, true)) + pubid = system = nil + pubid_literal = md[1] + pubid = pubid_literal[1..-2] if pubid_literal # Remove quote + ["PUBLIC", pubid, nil] + elsif accept_external_id and (md = @source.match(EXTERNAL_ID_SYSTEM, true)) + system = nil + system_literal = md[1] + system = system_literal[1..-2] if system_literal # Remove quote + ["SYSTEM", nil, system] + else + details = parse_id_invalid_details(accept_external_id: accept_external_id, + accept_public_id: accept_public_id) + message = "#{base_error_message}: #{details}" + raise REXML::ParseException.new(message, @source) + end + end + + def parse_id_invalid_details(accept_external_id:, + accept_public_id:) + public = /\A\s*PUBLIC/um + system = /\A\s*SYSTEM/um + if (accept_external_id or accept_public_id) and @source.match?(/#{public}/um) + if @source.match?(/#{public}(?:\s+[^'"]|\s*[\[>])/um) + return "public ID literal is missing" + end + unless @source.match?(/#{public}\s+#{PUBIDLITERAL}/um) + return "invalid public ID literal" + end + if accept_public_id + if @source.match?(/#{public}\s+#{PUBIDLITERAL}\s+[^'"]/um) + return "system ID literal is missing" + end + unless @source.match?(/#{public}\s+#{PUBIDLITERAL}\s+#{SYSTEMLITERAL}/um) + return "invalid system literal" + end + "garbage after system literal" + else + "garbage after public ID literal" + end + elsif accept_external_id and @source.match?(/#{system}/um) + if @source.match?(/#{system}(?:\s+[^'"]|\s*[\[>])/um) + return "system literal is missing" + end + unless @source.match?(/#{system}\s+#{SYSTEMLITERAL}/um) + return "invalid system literal" + end + "garbage after system literal" + else + unless @source.match?(/\A\s*(?:PUBLIC|SYSTEM)\s/um) + return "invalid ID type" + end + "ID type is missing" + end + end + + def process_comment + text = @source.read_until("-->") + unless text.chomp!("-->") + raise REXML::ParseException.new("Unclosed comment: Missing end '-->'", @source) + end + + if text.include? "--" or text.end_with?("-") + raise REXML::ParseException.new("Malformed comment", @source) + end + text + end + + def process_instruction + name = parse_name("Malformed XML: Invalid processing instruction node") + if name == "xml" + xml_declaration + else # PITarget + if @source.skip_spaces # e.g. <?name content?> + start_position = @source.position + content = @source.read_until("?>") + unless content.chomp!("?>") + @source.position = start_position + raise ParseException.new("Malformed XML: Unclosed processing instruction: <#{name}>", @source) + end + else # e.g. <?name?> + content = nil + unless @source.match?("?>", true) + raise ParseException.new("Malformed XML: Unclosed processing instruction: <#{name}>", @source) + end + end + [:processing_instruction, name, content] + end + end + + def xml_declaration + unless @version.nil? + raise ParseException.new("Malformed XML: XML declaration is duplicated", @source) + end + if @document_status + raise ParseException.new("Malformed XML: XML declaration is not at the start", @source) + end + unless @source.skip_spaces + raise ParseException.new("Malformed XML: XML declaration misses spaces before version", @source) + end + unless @source.match?("version", true) + raise ParseException.new("Malformed XML: XML declaration misses version", @source) + end + @version = parse_attribute_value_with_equal("xml") + unless @source.skip_spaces + unless @source.match?("?>", true) + raise ParseException.new("Malformed XML: Unclosed XML declaration", @source) + end + encoding = normalize_xml_declaration_encoding(@source.encoding) + return [ :xmldecl, @version, encoding, nil ] # e.g. <?xml version="1.0"?> + end + + if @source.match?("encoding", true) + encoding = parse_attribute_value_with_equal("xml") + unless @source.skip_spaces + unless @source.match?("?>", true) + raise ParseException.new("Malformed XML: Unclosed XML declaration", @source) + end + if need_source_encoding_update?(encoding) + @source.encoding = encoding + end + encoding ||= normalize_xml_declaration_encoding(@source.encoding) + return [ :xmldecl, @version, encoding, nil ] # e.g. <?xml version="1.1" encoding="UTF-8"?> + end + end + + if @source.match?("standalone", true) + standalone = parse_attribute_value_with_equal("xml") + case standalone + when "yes", "no" + else + raise ParseException.new("Malformed XML: XML declaration standalone is not yes or no : <#{standalone}>", @source) + end + end + @source.skip_spaces + unless @source.match?("?>", true) + raise ParseException.new("Malformed XML: Unclosed XML declaration", @source) + end + + if need_source_encoding_update?(encoding) + @source.encoding = encoding + end + encoding ||= normalize_xml_declaration_encoding(@source.encoding) + + # e.g. <?xml version="1.0" ?> + # <?xml version="1.1" encoding="UTF-8" ?> + # <?xml version="1.1" standalone="yes"?> + # <?xml version="1.1" encoding="UTF-8" standalone="yes" ?> + [ :xmldecl, @version, encoding, standalone ] + end + + if StringScanner::Version < "3.1.1" + def scan_quote + @source.match(/(['"])/, true)&.[](1) + end + else + def scan_quote + case @source.peek_byte + when 34 # '"'.ord + @source.scan_byte + '"' + when 39 # "'".ord + @source.scan_byte + "'" + else + nil + end + end + end + + def parse_attribute_value_with_equal(name) + unless @source.match?(Private::EQUAL_PATTERN, true) + message = "Missing attribute equal: <#{name}>" + raise REXML::ParseException.new(message, @source) + end + unless quote = scan_quote + message = "Missing attribute value start quote: <#{name}>" + raise REXML::ParseException.new(message, @source) + end + start_position = @source.position + value = @source.read_until(quote) + unless value.chomp!(quote) + @source.position = start_position + message = "Missing attribute value end quote: <#{name}>: <#{quote}>" + raise REXML::ParseException.new(message, @source) + end + value + end + + def parse_attributes(prefixes) + attributes = {} + expanded_names = {} + closed = false + while true + if @source.match?(">", true) + return attributes, closed + elsif @source.match?("/>", true) + closed = true + return attributes, closed + elsif match = @source.match(QNAME, true) + name = match[1] + prefix = match[2] + local_part = match[3] + value = parse_attribute_value_with_equal(name) + @source.skip_spaces + if prefix == "xmlns" + if local_part == "xml" + if value != Private::XML_PREFIXED_NAMESPACE + msg = "The 'xml' prefix must not be bound to any other namespace "+ + "(http://www.w3.org/TR/REC-xml-names/#ns-decl)" + raise REXML::ParseException.new( msg, @source, self ) + end + elsif local_part == "xmlns" + msg = "The 'xmlns' prefix must not be declared "+ + "(http://www.w3.org/TR/REC-xml-names/#ns-decl)" + raise REXML::ParseException.new( msg, @source, self) + end + add_namespace(local_part, value) + elsif prefix + prefixes << prefix unless prefix == "xml" + end + + if attributes[name] + msg = "Duplicate attribute #{name.inspect}" + raise REXML::ParseException.new(msg, @source, self) + end + + unless prefix == "xmlns" + uri = @namespaces[prefix] + expanded_name = [uri, local_part] + existing_prefix = expanded_names[expanded_name] + if existing_prefix + message = "Namespace conflict in adding attribute " + + "\"#{local_part}\": " + + "Prefix \"#{existing_prefix}\" = \"#{uri}\" and " + + "prefix \"#{prefix}\" = \"#{uri}\"" + raise REXML::ParseException.new(message, @source, self) + end + expanded_names[expanded_name] = prefix + end + + attributes[name] = value + else + message = "Invalid attribute name: <#{@source.buffer.split(%r{[/>\s]}).first}>" + raise REXML::ParseException.new(message, @source) + end + end + end + end + end +end + +=begin + case event[0] + when :start_element + when :text + when :end_element + when :processing_instruction + when :cdata + when :comment + when :xmldecl + when :start_doctype + when :end_doctype + when :externalentity + when :elementdecl + when :entity + when :attlistdecl + when :notationdecl + when :end_doctype + end +=end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/parsers/lightparser.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/parsers/lightparser.rb new file mode 100644 index 0000000..bdc0827 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/parsers/lightparser.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: false +require_relative 'streamparser' +require_relative 'baseparser' +require_relative '../light/node' + +module REXML + module Parsers + class LightParser + def initialize stream + @stream = stream + @parser = REXML::Parsers::BaseParser.new( stream ) + end + + def add_listener( listener ) + @parser.add_listener( listener ) + end + + def rewind + @stream.rewind + @parser.stream = @stream + end + + def parse + root = context = [ :document ] + while true + event = @parser.pull + case event[0] + when :end_document + break + when :start_element, :start_doctype + new_node = event + context << new_node + new_node[1,0] = [context] + context = new_node + when :end_element, :end_doctype + context = context[1] + else + new_node = event + context << new_node + new_node[1,0] = [context] + end + end + root + end + end + + # An element is an array. The array contains: + # 0 The parent element + # 1 The tag name + # 2 A hash of attributes + # 3..-1 The child elements + # An element is an array of size > 3 + # Text is a String + # PIs are [ :processing_instruction, target, data ] + # Comments are [ :comment, data ] + # DocTypes are DocType structs + # The root is an array with XMLDecls, Text, DocType, Array, Text + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/parsers/pullparser.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/parsers/pullparser.rb new file mode 100644 index 0000000..e0b1e94 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/parsers/pullparser.rb @@ -0,0 +1,213 @@ +# frozen_string_literal: false +require 'forwardable' + +require_relative '../parseexception' +require_relative 'baseparser' +require_relative '../xmltokens' + +module REXML + module Parsers + # = Using the Pull Parser + # <em>This API is experimental, and subject to change.</em> + # parser = PullParser.new( "<a>text<b att='val'/>txet</a>" ) + # while parser.has_next? + # res = parser.next + # puts res[1]['att'] if res.start_tag? and res[0] == 'b' + # end + # See the PullEvent class for information on the content of the results. + # The data is identical to the arguments passed for the various events to + # the StreamListener API. + # + # Notice that: + # parser = PullParser.new( "<a>BAD DOCUMENT" ) + # while parser.has_next? + # res = parser.next + # raise res[1] if res.error? + # end + # + # Nat Price gave me some good ideas for the API. + class PullParser + include XMLTokens + extend Forwardable + + def_delegators( :@parser, :has_next? ) + def_delegators( :@parser, :entity ) + def_delegators( :@parser, :empty? ) + def_delegators( :@parser, :source ) + + def initialize stream + @entities = {} + @listeners = nil + @parser = BaseParser.new( stream ) + @my_stack = [] + end + + def add_listener( listener ) + @listeners = [] unless @listeners + @listeners << listener + end + + def entity_expansion_count + @parser.entity_expansion_count + end + + def entity_expansion_limit=( limit ) + @parser.entity_expansion_limit = limit + end + + def entity_expansion_text_limit=( limit ) + @parser.entity_expansion_text_limit = limit + end + + def each + while has_next? + yield self.pull + end + end + + def peek depth=0 + if @my_stack.length <= depth + (depth - @my_stack.length + 1).times { + e = PullEvent.new(@parser.pull) + @my_stack.push(e) + } + end + @my_stack[depth] + end + + def pull + return @my_stack.shift if @my_stack.length > 0 + + event = @parser.pull + case event[0] + when :entitydecl + @entities[ event[1] ] = + event[2] unless event[2] =~ /PUBLIC|SYSTEM/ + when :text + unnormalized = @parser.unnormalize( event[1], @entities ) + event << unnormalized + end + PullEvent.new( event ) + end + + def unshift token + @my_stack.unshift token + end + + def reset + @parser.reset + end + end + + # A parsing event. The contents of the event are accessed as an +Array?, + # and the type is given either by the ...? methods, or by accessing the + # +type+ accessor. The contents of this object vary from event to event, + # but are identical to the arguments passed to +StreamListener+s for each + # event. + class PullEvent + # The type of this event. Will be one of :tag_start, :tag_end, :text, + # :processing_instruction, :comment, :doctype, :attlistdecl, :entitydecl, + # :notationdecl, :entity, :cdata, :xmldecl, or :error. + def initialize(arg) + @contents = arg + end + + def []( start, endd=nil) + if start.kind_of? Range + @contents.slice( start.begin+1 .. start.end ) + elsif start.kind_of? Numeric + if endd.nil? + @contents.slice( start+1 ) + else + @contents.slice( start+1, endd ) + end + else + raise "Illegal argument #{start.inspect} (#{start.class})" + end + end + + def event_type + @contents[0] + end + + # Content: [ String tag_name, Hash attributes ] + def start_element? + @contents[0] == :start_element + end + + # Content: [ String tag_name ] + def end_element? + @contents[0] == :end_element + end + + # Content: [ String raw_text, String unnormalized_text ] + def text? + @contents[0] == :text + end + + # Content: [ String text ] + def instruction? + @contents[0] == :processing_instruction + end + + # Content: [ String text ] + def comment? + @contents[0] == :comment + end + + # Content: [ String name, String pub_sys, String long_name, String uri ] + def doctype? + @contents[0] == :start_doctype + end + + # Content: [ String text ] + def attlistdecl? + @contents[0] == :attlistdecl + end + + # Content: [ String text ] + def elementdecl? + @contents[0] == :elementdecl + end + + # Due to the wonders of DTDs, an entity declaration can be just about + # anything. There's no way to normalize it; you'll have to interpret the + # content yourself. However, the following is true: + # + # * If the entity declaration is an internal entity: + # [ String name, String value ] + # Content: [ String text ] + def entitydecl? + @contents[0] == :entitydecl + end + + # Content: [ String text ] + def notationdecl? + @contents[0] == :notationdecl + end + + # Content: [ String text ] + def entity? + @contents[0] == :entity + end + + # Content: [ String text ] + def cdata? + @contents[0] == :cdata + end + + # Content: [ String version, String encoding, String standalone ] + def xmldecl? + @contents[0] == :xmldecl + end + + def error? + @contents[0] == :error + end + + def inspect + @contents[0].to_s + ": " + @contents[1..-1].inspect + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/parsers/sax2parser.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/parsers/sax2parser.rb new file mode 100644 index 0000000..a51477d --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/parsers/sax2parser.rb @@ -0,0 +1,270 @@ +# frozen_string_literal: false +require_relative 'baseparser' +require_relative '../parseexception' +require_relative '../namespace' +require_relative '../text' + +module REXML + module Parsers + # SAX2Parser + class SAX2Parser + def initialize source + @parser = BaseParser.new(source) + @listeners = [] + @procs = [] + @namespace_stack = [] + @has_listeners = false + @tag_stack = [] + @entities = {} + end + + def source + @parser.source + end + + def entity_expansion_count + @parser.entity_expansion_count + end + + def entity_expansion_limit=( limit ) + @parser.entity_expansion_limit = limit + end + + def entity_expansion_text_limit=( limit ) + @parser.entity_expansion_text_limit = limit + end + + def add_listener( listener ) + @parser.add_listener( listener ) + end + + # Listen arguments: + # + # Symbol, Array, Block + # Listen to Symbol events on Array elements + # Symbol, Block + # Listen to Symbol events + # Array, Listener + # Listen to all events on Array elements + # Array, Block + # Listen to :start_element events on Array elements + # Listener + # Listen to All events + # + # Symbol can be one of: :start_element, :end_element, + # :start_prefix_mapping, :end_prefix_mapping, :characters, + # :processing_instruction, :doctype, :attlistdecl, :elementdecl, + # :entitydecl, :notationdecl, :cdata, :xmldecl, :comment + # + # There is an additional symbol that can be listened for: :progress. + # This will be called for every event generated, passing in the current + # stream position. + # + # Array contains regular expressions or strings which will be matched + # against fully qualified element names. + # + # Listener must implement the methods in SAX2Listener + # + # Block will be passed the same arguments as a SAX2Listener method would + # be, where the method name is the same as the matched Symbol. + # See the SAX2Listener for more information. + def listen( *args, &blok ) + if args[0].kind_of? Symbol + if args.size == 2 + args[1].each { |match| @procs << [args[0], match, blok] } + else + add( [args[0], nil, blok] ) + end + elsif args[0].kind_of? Array + if args.size == 2 + args[0].each { |match| add( [nil, match, args[1]] ) } + else + args[0].each { |match| add( [ :start_element, match, blok ] ) } + end + else + add([nil, nil, args[0]]) + end + end + + def deafen( listener=nil, &blok ) + if listener + @listeners.delete_if {|item| item[-1] == listener } + @has_listeners = false if @listeners.size == 0 + else + @procs.delete_if {|item| item[-1] == blok } + end + end + + def parse + @procs.each { |sym,match,block| block.call if sym == :start_document } + @listeners.each { |sym,match,block| + block.start_document if sym == :start_document or sym.nil? + } + context = [] + while true + event = @parser.pull + case event[0] + when :end_document + handle( :end_document ) + break + when :start_doctype + handle( :doctype, *event[1..-1]) + when :end_doctype + context = context[1] + when :start_element + @tag_stack.push(event[1]) + # find the observers for namespaces + procs = get_procs( :start_prefix_mapping, event[1] ) + listeners = get_listeners( :start_prefix_mapping, event[1] ) + if procs or listeners + # break out the namespace declarations + # The attributes live in event[2] + event[2].each {|n, v| event[2][n] = @parser.normalize(v)} + nsdecl = event[2].find_all { |n, value| n =~ /^xmlns(:|$)/ } + nsdecl.collect! { |n, value| [ n[6..-1], value ] } + @namespace_stack.push({}) + nsdecl.each do |n,v| + @namespace_stack[-1][n] = v + # notify observers of namespaces + procs.each { |ob| ob.call( n, v ) } if procs + listeners.each { |ob| ob.start_prefix_mapping(n, v) } if listeners + end + end + event[1] =~ Namespace::NAMESPLIT + prefix = $1 + local = $2 + uri = get_namespace(prefix) + # find the observers for start_element + procs = get_procs( :start_element, event[1] ) + listeners = get_listeners( :start_element, event[1] ) + # notify observers + procs.each { |ob| ob.call( uri, local, event[1], event[2] ) } if procs + listeners.each { |ob| + ob.start_element( uri, local, event[1], event[2] ) + } if listeners + when :end_element + @tag_stack.pop + event[1] =~ Namespace::NAMESPLIT + prefix = $1 + local = $2 + uri = get_namespace(prefix) + # find the observers for start_element + procs = get_procs( :end_element, event[1] ) + listeners = get_listeners( :end_element, event[1] ) + # notify observers + procs.each { |ob| ob.call( uri, local, event[1] ) } if procs + listeners.each { |ob| + ob.end_element( uri, local, event[1] ) + } if listeners + + namespace_mapping = @namespace_stack.pop + # find the observers for namespaces + procs = get_procs( :end_prefix_mapping, event[1] ) + listeners = get_listeners( :end_prefix_mapping, event[1] ) + if procs or listeners + namespace_mapping.each do |ns_prefix, ns_uri| + # notify observers of namespaces + procs.each { |ob| ob.call( ns_prefix ) } if procs + listeners.each { |ob| ob.end_prefix_mapping(ns_prefix) } if listeners + end + end + when :text + unnormalized = @parser.unnormalize( event[1], @entities ) + handle( :characters, unnormalized ) + when :entitydecl + handle_entitydecl( event ) + when :processing_instruction, :comment, :attlistdecl, + :elementdecl, :cdata, :notationdecl, :xmldecl + handle( *event ) + end + handle( :progress, @parser.position ) + end + end + + private + def handle( symbol, *arguments ) + tag = @tag_stack[-1] + procs = get_procs( symbol, tag ) + listeners = get_listeners( symbol, tag ) + # notify observers + procs.each { |ob| ob.call( *arguments ) } if procs + listeners.each { |l| + l.send( symbol.to_s, *arguments ) + } if listeners + end + + def handle_entitydecl( event ) + @entities[ event[1] ] = event[2] if event.size == 3 + parameter_reference_p = false + case event[2] + when "SYSTEM" + if event.size == 5 + if event.last == "%" + parameter_reference_p = true + else + event[4, 0] = "NDATA" + end + end + when "PUBLIC" + if event.size == 6 + if event.last == "%" + parameter_reference_p = true + else + event[5, 0] = "NDATA" + end + end + else + parameter_reference_p = (event.size == 4) + end + event[1, 0] = event.pop if parameter_reference_p + handle( event[0], event[1..-1] ) + end + + # The following methods are duplicates, but it is faster than using + # a helper + def get_procs( symbol, name ) + return nil if @procs.size == 0 + @procs.find_all do |sym, match, block| + ( + (sym.nil? or symbol == sym) and + ((name.nil? and match.nil?) or match.nil? or ( + (name == match) or + (match.kind_of? Regexp and name =~ match) + ) + ) + ) + end.collect{|x| x[-1]} + end + def get_listeners( symbol, name ) + return nil if @listeners.size == 0 + @listeners.find_all do |sym, match, block| + ( + (sym.nil? or symbol == sym) and + ((name.nil? and match.nil?) or match.nil? or ( + (name == match) or + (match.kind_of? Regexp and name =~ match) + ) + ) + ) + end.collect{|x| x[-1]} + end + + def add( pair ) + if pair[-1].respond_to? :call + @procs << pair unless @procs.include? pair + else + @listeners << pair unless @listeners.include? pair + @has_listeners = true + end + end + + def get_namespace( prefix ) + return nil if @namespace_stack.empty? + + uris = (@namespace_stack.find_all { |ns| not ns[prefix].nil? }) || + (@namespace_stack.find { |ns| not ns[nil].nil? }) + uris[-1][prefix] unless uris.nil? or 0 == uris.size + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/parsers/streamparser.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/parsers/streamparser.rb new file mode 100644 index 0000000..6c64d97 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/parsers/streamparser.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: false +require_relative "baseparser" + +module REXML + module Parsers + class StreamParser + def initialize source, listener + @listener = listener + @parser = BaseParser.new( source ) + @entities = {} + end + + def add_listener( listener ) + @parser.add_listener( listener ) + end + + def entity_expansion_count + @parser.entity_expansion_count + end + + def entity_expansion_limit=( limit ) + @parser.entity_expansion_limit = limit + end + + def entity_expansion_text_limit=( limit ) + @parser.entity_expansion_text_limit = limit + end + + def parse + # entity string + while true + event = @parser.pull + case event[0] + when :end_document + return + when :start_element + attrs = event[2].each do |n, v| + event[2][n] = @parser.unnormalize( v ) + end + @listener.tag_start( event[1], attrs ) + when :end_element + @listener.tag_end( event[1] ) + when :text + unnormalized = @parser.unnormalize( event[1], @entities ) + @listener.text( unnormalized ) + when :processing_instruction + @listener.instruction( *event[1,2] ) + when :start_doctype + @listener.doctype( *event[1..-1] ) + when :end_doctype + # FIXME: remove this condition for milestone:3.2 + @listener.doctype_end if @listener.respond_to? :doctype_end + when :comment, :attlistdecl, :cdata, :xmldecl, :elementdecl + @listener.send( event[0].to_s, *event[1..-1] ) + when :entitydecl, :notationdecl + @entities[ event[1] ] = event[2] if event.size == 3 + @listener.send( event[0].to_s, event[1..-1] ) + when :externalentity + entity_reference = event[1] + content = entity_reference.gsub(/\A%|;\z/, "") + @listener.entity(content) + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/parsers/treeparser.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/parsers/treeparser.rb new file mode 100644 index 0000000..4565a40 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/parsers/treeparser.rb @@ -0,0 +1,89 @@ +# frozen_string_literal: false +require_relative '../validation/validationexception' +require_relative '../undefinednamespaceexception' + +module REXML + module Parsers + class TreeParser + def initialize( source, build_context = Document.new ) + @build_context = build_context + @parser = Parsers::BaseParser.new( source ) + end + + def add_listener( listener ) + @parser.add_listener( listener ) + end + + def parse + entities = nil + begin + while true + event = @parser.pull + #STDERR.puts "TREEPARSER GOT #{event.inspect}" + case event[0] + when :end_document + return + when :start_element + el = @build_context = @build_context.add_element( event[1] ) + event[2].each do |key, value| + el.attributes[key]=Attribute.new(key,value,self) + end + when :end_element + @build_context = @build_context.parent + when :text + if @build_context[-1].instance_of? Text + @build_context[-1] << event[1] + else + @build_context.add( + Text.new(event[1], @build_context.whitespace, nil, true) + ) unless ( + @build_context.ignore_whitespace_nodes and + event[1].strip.size==0 + ) + end + when :comment + c = Comment.new( event[1] ) + @build_context.add( c ) + when :cdata + c = CData.new( event[1] ) + @build_context.add( c ) + when :processing_instruction + @build_context.add( Instruction.new( event[1], event[2] ) ) + when :end_doctype + entities.each { |k,v| entities[k] = @build_context.entities[k].value } + @build_context = @build_context.parent + when :start_doctype + doctype = DocType.new( event[1..-1], @build_context ) + @build_context = doctype + entities = {} + when :attlistdecl + n = AttlistDecl.new( event[1..-1] ) + @build_context.add( n ) + when :externalentity + n = ExternalEntity.new( event[1] ) + @build_context.add( n ) + when :elementdecl + n = ElementDecl.new( event[1] ) + @build_context.add(n) + when :entitydecl + entities[ event[1] ] = event[2] unless event[2] =~ /PUBLIC|SYSTEM/ + @build_context.add(Entity.new(event)) + when :notationdecl + n = NotationDecl.new( *event[1..-1] ) + @build_context.add( n ) + when :xmldecl + x = XMLDecl.new( event[1], event[2], event[3] ) + @build_context.add( x ) + end + end + rescue REXML::Validation::ValidationException + raise + rescue REXML::ParseException + raise + rescue + raise ParseException.new( $!.message, @parser.source, @parser, $! ) + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/parsers/ultralightparser.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/parsers/ultralightparser.rb new file mode 100644 index 0000000..e0029f4 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/parsers/ultralightparser.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: false +require_relative 'streamparser' +require_relative 'baseparser' + +module REXML + module Parsers + class UltraLightParser + def initialize stream + @stream = stream + @parser = REXML::Parsers::BaseParser.new( stream ) + end + + def add_listener( listener ) + @parser.add_listener( listener ) + end + + def rewind + @stream.rewind + @parser.stream = @stream + end + + def parse + root = context = [] + while true + event = @parser.pull + case event[0] + when :end_document + break + when :end_doctype + context = context[1] + when :start_element, :start_doctype + context << event + event[1,0] = [context] + context = event + when :end_element + context = context[1] + else + context << event + end + end + root + end + end + + # An element is an array. The array contains: + # 0 The parent element + # 1 The tag name + # 2 A hash of attributes + # 3..-1 The child elements + # An element is an array of size > 3 + # Text is a String + # PIs are [ :processing_instruction, target, data ] + # Comments are [ :comment, data ] + # DocTypes are DocType structs + # The root is an array with XMLDecls, Text, DocType, Array, Text + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/parsers/xpathparser.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/parsers/xpathparser.rb new file mode 100644 index 0000000..a6d76fd --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/parsers/xpathparser.rb @@ -0,0 +1,739 @@ +# frozen_string_literal: false + +require_relative '../namespace' +require_relative '../xmltokens' + +module REXML + module Parsers + # You don't want to use this class. Really. Use XPath, which is a wrapper + # for this class. Believe me. You don't want to poke around in here. + # There is strange, dark magic at work in this code. Beware. Go back! Go + # back while you still can! + class XPathParser + include XMLTokens + LITERAL = /^'([^']*)'|^"([^"]*)"/u + + def namespaces=( namespaces ) + Functions::namespace_context = namespaces + @namespaces = namespaces + end + + def parse path + path = path.dup + path.gsub!(/([\(\[])\s+/, '\1') # Strip ignorable spaces + path.gsub!( /\s+([\]\)])/, '\1') + parsed = [] + rest = OrExpr(path, parsed) + if rest + unless rest.strip.empty? + raise ParseException.new("Garbage component exists at the end: " + + "<#{rest}>: <#{path}>") + end + end + parsed + end + + def predicate path + parsed = [] + Predicate( "[#{path}]", parsed ) + parsed + end + + def abbreviate(path_or_parsed) + if path_or_parsed.kind_of?(String) + parsed = parse(path_or_parsed) + else + parsed = path_or_parsed + end + components = [] + component = nil + while parsed.size > 0 + op = parsed.shift + case op + when :node + component << "node()" + when :attribute + component = "@" + components << component + when :child + component = "" + components << component + when :descendant_or_self + next_op = parsed[0] + if next_op == :node + parsed.shift + component = "" + components << component + else + component = "descendant-or-self::" + components << component + end + when :self + next_op = parsed[0] + if next_op == :node + parsed.shift + components << "." + else + component = "self::" + components << component + end + when :parent + next_op = parsed[0] + if next_op == :node + parsed.shift + components << ".." + else + component = "parent::" + components << component + end + when :any + component << "*" + when :text + component << "text()" + when :following, :following_sibling, + :ancestor, :ancestor_or_self, :descendant, + :namespace, :preceding, :preceding_sibling + component = op.to_s.tr("_", "-") << "::" + components << component + when :qname + prefix = parsed.shift + name = parsed.shift + component << prefix+":" if prefix.size > 0 + component << name + when :predicate + component << '[' + component << predicate_to_path(parsed.shift) {|x| abbreviate(x)} + component << ']' + when :document + components << "" + when :function + component << parsed.shift + component << "( " + component << predicate_to_path(parsed.shift[0]) {|x| abbreviate(x)} + component << " )" + when :literal + component << quote_literal(parsed.shift) + else + component << "UNKNOWN(" + component << op.inspect + component << ")" + end + end + case components + when [""] + "/" + when ["", ""] + "//" + else + components.join("/") + end + end + + def expand(path_or_parsed) + if path_or_parsed.kind_of?(String) + parsed = parse(path_or_parsed) + else + parsed = path_or_parsed + end + path = "" + document = false + while parsed.size > 0 + op = parsed.shift + case op + when :node + path << "node()" + when :attribute, :child, :following, :following_sibling, + :ancestor, :ancestor_or_self, :descendant, :descendant_or_self, + :namespace, :preceding, :preceding_sibling, :self, :parent + path << "/" unless path.size == 0 + path << op.to_s.tr("_", "-") + path << "::" + when :any + path << "*" + when :qname + prefix = parsed.shift + name = parsed.shift + path << prefix+":" if prefix.size > 0 + path << name + when :predicate + path << '[' + path << predicate_to_path( parsed.shift ) { |x| expand(x) } + path << ']' + when :document + document = true + else + path << "UNKNOWN(" + path << op.inspect + path << ")" + end + end + path = "/"+path if document + path + end + + def predicate_to_path(parsed, &block) + path = "" + case parsed[0] + when :and, :or, :mult, :plus, :minus, :neq, :eq, :lt, :gt, :lteq, :gteq, :div, :mod, :union + op = parsed.shift + case op + when :eq + op = "=" + when :lt + op = "<" + when :gt + op = ">" + when :lteq + op = "<=" + when :gteq + op = ">=" + when :neq + op = "!=" + when :union + op = "|" + end + left = predicate_to_path( parsed.shift, &block ) + right = predicate_to_path( parsed.shift, &block ) + path << left + path << " " + path << op.to_s + path << " " + path << right + when :function + parsed.shift + name = parsed.shift + path << name + path << "(" + parsed.shift.each_with_index do |argument, i| + path << ", " if i > 0 + path << predicate_to_path(argument, &block) + end + path << ")" + when :literal + parsed.shift + path << quote_literal(parsed.shift) + else + path << yield( parsed ) + end + path.squeeze(" ") + end + # For backward compatibility + alias_method :preciate_to_string, :predicate_to_path + + private + def quote_literal( literal ) + case literal + when String + # XPath 1.0 does not support escape characters. + # Assumes literal does not contain both single and double quotes. + if literal.include?("'") + "\"#{literal}\"" + else + "'#{literal}'" + end + else + literal.inspect + end + end + + #LocationPath + # | RelativeLocationPath + # | '/' RelativeLocationPath? + # | '//' RelativeLocationPath + def LocationPath path, parsed + path = path.lstrip + if path[0] == ?/ + parsed << :document + if path[1] == ?/ + parsed << :descendant_or_self + parsed << :node + path = path[2..-1] + else + path = path[1..-1] + end + end + RelativeLocationPath( path, parsed ) if path.size > 0 + end + + #RelativeLocationPath + # | Step + # | (AXIS_NAME '::' | '@' | '') AxisSpecifier + # NodeTest + # Predicate + # | '.' | '..' AbbreviatedStep + # | RelativeLocationPath '/' Step + # | RelativeLocationPath '//' Step + AXIS = /^(ancestor|ancestor-or-self|attribute|child|descendant|descendant-or-self|following|following-sibling|namespace|parent|preceding|preceding-sibling|self)::/ + def RelativeLocationPath path, parsed + loop do + original_path = path + path = path.lstrip + + return original_path if path.empty? + + # (axis or @ or <child::>) nodetest predicate > + # OR > / Step + # (. or ..) > + if path[0] == ?. + if path[1] == ?. + parsed << :parent + parsed << :node + path = path[2..-1] + else + parsed << :self + parsed << :node + path = path[1..-1] + end + else + path_before_axis_specifier = path + parsed_not_abberviated = [] + if path[0] == ?@ + parsed_not_abberviated << :attribute + path = path[1..-1] + # Goto Nodetest + elsif path =~ AXIS + parsed_not_abberviated << $1.tr('-','_').intern + path = $' + # Goto Nodetest + else + parsed_not_abberviated << :child + end + + path_before_node_test = path + path = NodeTest(path, parsed_not_abberviated) + if path == path_before_node_test + return path_before_axis_specifier + end + path = Predicate(path, parsed_not_abberviated) + + parsed.concat(parsed_not_abberviated) + end + + original_path = path + path = path.lstrip + return original_path if path.empty? + + return original_path if path[0] != ?/ + + if path[1] == ?/ + parsed << :descendant_or_self + parsed << :node + path = path[2..-1] + else + path = path[1..-1] + end + end + end + + # Returns a 1-1 map of the nodeset + # The contents of the resulting array are either: + # true/false, if a positive match + # String, if a name match + #NodeTest + # | ('*' | NCNAME ':' '*' | QNAME) NameTest + # | '*' ':' NCNAME NameTest since XPath 2.0 + # | NODE_TYPE '(' ')' NodeType + # | PI '(' LITERAL ')' PI + # | '[' expr ']' Predicate + PREFIX_WILDCARD = /^\*:(#{NCNAME_STR})/u + LOCAL_NAME_WILDCARD = /^(#{NCNAME_STR}):\*/u + QNAME = Namespace::NAMESPLIT + NODE_TYPE = /^(comment|text|node)\(\s*\)/m + PI = /^processing-instruction\(/ + def NodeTest path, parsed + original_path = path + path = path.lstrip + case path + when PREFIX_WILDCARD + prefix = nil + name = $1 + path = $' + parsed << :qname + parsed << prefix + parsed << name + when /^\*/ + path = $' + parsed << :any + when NODE_TYPE + type = $1 + path = $' + parsed << type.tr('-', '_').intern + when PI + path = $' + literal = nil + if path =~ /^\s*\)/ + path = $' + else + path =~ LITERAL + literal = $1 + path = $' + raise ParseException.new("Missing ')' after processing instruction") if path[0] != ?) + path = path[1..-1] + end + parsed << :processing_instruction + parsed << (literal || '') + when LOCAL_NAME_WILDCARD + prefix = $1 + path = $' + parsed << :namespace + parsed << prefix + when QNAME + prefix = $1 + name = $2 + path = $' + prefix = "" unless prefix + parsed << :qname + parsed << prefix + parsed << name + else + path = original_path + end + path + end + + # Filters the supplied nodeset on the predicate(s) + def Predicate path, parsed + original_path = path + path = path.lstrip + return original_path unless path[0] == ?[ + predicates = [] + while path[0] == ?[ + path, expr = get_group(path) + predicates << expr[1..-2] if expr + end + predicates.each{ |pred| + preds = [] + parsed << :predicate + parsed << preds + OrExpr(pred, preds) + } + path + end + + # The following return arrays of true/false, a 1-1 mapping of the + # supplied nodeset, except for axe(), which returns a filtered + # nodeset + + #| OrExpr S 'or' S AndExpr + #| AndExpr + def OrExpr path, parsed + n = [] + rest = AndExpr( path, n ) + if rest != path + while rest =~ /^\s*( or )/ + n = [ :or, n, [] ] + rest = AndExpr( $', n[-1] ) + end + end + if parsed.size == 0 and n.size != 0 + parsed.replace(n) + elsif n.size > 0 + parsed << n + end + rest + end + + #| AndExpr S 'and' S EqualityExpr + #| EqualityExpr + def AndExpr path, parsed + n = [] + rest = EqualityExpr( path, n ) + if rest != path + while rest =~ /^\s*( and )/ + n = [ :and, n, [] ] + rest = EqualityExpr( $', n[-1] ) + end + end + if parsed.size == 0 and n.size != 0 + parsed.replace(n) + elsif n.size > 0 + parsed << n + end + rest + end + + #| EqualityExpr ('=' | '!=') RelationalExpr + #| RelationalExpr + def EqualityExpr path, parsed + n = [] + rest = RelationalExpr( path, n ) + if rest != path + while rest =~ /^\s*(!?=)\s*/ + if $1[0] == ?! + n = [ :neq, n, [] ] + else + n = [ :eq, n, [] ] + end + rest = RelationalExpr( $', n[-1] ) + end + end + if parsed.size == 0 and n.size != 0 + parsed.replace(n) + elsif n.size > 0 + parsed << n + end + rest + end + + #| RelationalExpr ('<' | '>' | '<=' | '>=') AdditiveExpr + #| AdditiveExpr + def RelationalExpr path, parsed + n = [] + rest = AdditiveExpr( path, n ) + if rest != path + while rest =~ /^\s*([<>]=?)\s*/ + if $1[0] == ?< + sym = "lt" + else + sym = "gt" + end + sym << "eq" if $1[-1] == ?= + n = [ sym.intern, n, [] ] + rest = AdditiveExpr( $', n[-1] ) + end + end + if parsed.size == 0 and n.size != 0 + parsed.replace(n) + elsif n.size > 0 + parsed << n + end + rest + end + + #| AdditiveExpr ('+' | '-') MultiplicativeExpr + #| MultiplicativeExpr + def AdditiveExpr path, parsed + n = [] + rest = MultiplicativeExpr( path, n ) + if rest != path + while rest =~ /^\s*(\+|-)\s*/ + if $1[0] == ?+ + n = [ :plus, n, [] ] + else + n = [ :minus, n, [] ] + end + rest = MultiplicativeExpr( $', n[-1] ) + end + end + if parsed.size == 0 and n.size != 0 + parsed.replace(n) + elsif n.size > 0 + parsed << n + end + rest + end + + #| MultiplicativeExpr ('*' | S ('div' | 'mod') S) UnaryExpr + #| UnaryExpr + def MultiplicativeExpr path, parsed + n = [] + rest = UnaryExpr( path, n ) + if rest != path + while rest =~ /^\s*(\*| div | mod )\s*/ + if $1[0] == ?* + n = [ :mult, n, [] ] + elsif $1.include?( "div" ) + n = [ :div, n, [] ] + else + n = [ :mod, n, [] ] + end + rest = UnaryExpr( $', n[-1] ) + end + end + if parsed.size == 0 and n.size != 0 + parsed.replace(n) + elsif n.size > 0 + parsed << n + end + rest + end + + #| '-' UnaryExpr + #| UnionExpr + def UnaryExpr path, parsed + path =~ /^(\-*)/ + path = $' + if $1 and (($1.size % 2) != 0) + mult = -1 + else + mult = 1 + end + parsed << :neg if mult < 0 + + n = [] + path = UnionExpr( path, n ) + parsed.concat( n ) + path + end + + #| UnionExpr '|' PathExpr + #| PathExpr + def UnionExpr path, parsed + n = [] + rest = PathExpr( path, n ) + if rest != path + while rest =~ /^\s*(\|)\s*/ + n = [ :union, n, [] ] + rest = PathExpr( $', n[-1] ) + end + end + if parsed.size == 0 and n.size != 0 + parsed.replace( n ) + elsif n.size > 0 + parsed << n + end + rest + end + + #| LocationPath + #| FilterExpr ('/' | '//') RelativeLocationPath + def PathExpr path, parsed + path = path.lstrip + n = [] + rest = FilterExpr( path, n ) + if rest != path + if rest and rest[0] == ?/ + rest = RelativeLocationPath(rest, n) + parsed.concat(n) + return rest + end + end + rest = LocationPath(rest, n) if rest =~ /\A[\/\.\@\[\w*]/ + parsed.concat(n) + rest + end + + #| FilterExpr Predicate + #| PrimaryExpr + def FilterExpr path, parsed + n = [] + path_before_primary_expr = path + path = PrimaryExpr(path, n) + return path_before_primary_expr if path == path_before_primary_expr + path = Predicate(path, n) + parsed.concat(n) + path + end + + #| VARIABLE_REFERENCE + #| '(' expr ')' + #| LITERAL + #| NUMBER + #| FunctionCall + VARIABLE_REFERENCE = /^\$(#{NAME_STR})/u + NUMBER = /^(\d*\.?\d+)/ + NT = /^comment|text|processing-instruction|node$/ + def PrimaryExpr path, parsed + case path + when VARIABLE_REFERENCE + varname = $1 + path = $' + parsed << :variable + parsed << varname + #arry << @variables[ varname ] + when /^(\w[-\w]*)(?:\()/ + fname = $1 + tmp = $' + return path if fname =~ NT + path = tmp + parsed << :function + parsed << fname + path = FunctionCall(path, parsed) + when NUMBER + varname = $1.nil? ? $2 : $1 + path = $' + parsed << :literal + parsed << (varname.include?('.') ? varname.to_f : varname.to_i) + when LITERAL + varname = $1.nil? ? $2 : $1 + path = $' + parsed << :literal + parsed << varname + when /^\(/ #/ + path, contents = get_group(path) + contents = contents[1..-2] + n = [] + OrExpr( contents, n ) + parsed.concat(n) + end + path + end + + #| FUNCTION_NAME '(' ( expr ( ',' expr )* )? ')' + def FunctionCall rest, parsed + path, arguments = parse_args(rest) + argset = [] + for argument in arguments + args = [] + OrExpr( argument, args ) + argset << args + end + parsed << argset + path + end + + # get_group( '[foo]bar' ) -> ['bar', '[foo]'] + def get_group string + ind = 0 + depth = 0 + st = string[0,1] + en = (st == "(" ? ")" : "]") + begin + case string[ind,1] + when st + depth += 1 + when en + depth -= 1 + end + ind += 1 + end while depth > 0 and ind < string.length + return nil unless depth==0 + [string[ind..-1], string[0..ind-1]] + end + + def parse_args( string ) + arguments = [] + ind = 0 + inquot = false + inapos = false + depth = 1 + begin + case string[ind] + when ?" + inquot = !inquot unless inapos + when ?' + inapos = !inapos unless inquot + else + unless inquot or inapos + case string[ind] + when ?( + depth += 1 + if depth == 1 + string = string[1..-1] + ind -= 1 + end + when ?) + depth -= 1 + if depth == 0 + s = string[0,ind].strip + arguments << s unless s == "" + string = string[ind+1..-1] + end + when ?, + if depth == 1 + s = string[0,ind].strip + arguments << s unless s == "" + string = string[ind+1..-1] + ind = -1 + end + end + end + end + ind += 1 + end while depth > 0 and ind < string.length + return nil unless depth==0 + [string,arguments] + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/quickpath.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/quickpath.rb new file mode 100644 index 0000000..cded06f --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/quickpath.rb @@ -0,0 +1,267 @@ +# frozen_string_literal: false +require_relative 'functions' +require_relative 'xmltokens' + +module REXML + class QuickPath + include Functions + include XMLTokens + + # A base Hash object to be used when initializing a + # default empty namespaces set. + EMPTY_HASH = {} + + def QuickPath::first element, path, namespaces=EMPTY_HASH + match(element, path, namespaces)[0] + end + + def QuickPath::each element, path, namespaces=EMPTY_HASH, &block + path = "*" unless path + match(element, path, namespaces).each( &block ) + end + + def QuickPath::match element, path, namespaces=EMPTY_HASH + raise "nil is not a valid xpath" unless path + results = nil + Functions::namespace_context = namespaces + case path + when /^\/([^\/]|$)/u + # match on root + path = path[1..-1] + return [element.root.parent] if path == '' + results = filter([element.root], path) + when /^[-\w]*::/u + results = filter([element], path) + when /^\*/u + results = filter(element.to_a, path) + when /^[\[!\w:]/u + # match on child + children = element.to_a + results = filter(children, path) + else + results = filter([element], path) + end + results + end + + # Given an array of nodes it filters the array based on the path. The + # result is that when this method returns, the array will contain elements + # which match the path + def QuickPath::filter elements, path + return elements if path.nil? or path == '' or elements.size == 0 + case path + when /^\/\//u # Descendant + axe( elements, "descendant-or-self", $' ) + when /^\/?\b(\w[-\w]*)\b::/u # Axe + axe( elements, $1, $' ) + when /^\/(?=\b([:!\w][-\.\w]*:)?[-!\*\.\w]*\b([^:(]|$)|\*)/u # Child + rest = $' + results = [] + elements.each do |element| + results |= filter( element.to_a, rest ) + end + results + when /^\/?(\w[-\w]*)\(/u # / Function + function( elements, $1, $' ) + when Namespace::NAMESPLIT # Element name + name = $2 + ns = $1 + rest = $' + elements.delete_if do |element| + !(element.kind_of? Element and + (element.expanded_name == name or + (element.name == name and + element.namespace == Functions.namespace_context[ns]))) + end + filter( elements, rest ) + when /^\/\[/u + matches = [] + elements.each do |element| + matches |= predicate( element.to_a, path[1..-1] ) if element.kind_of? Element + end + matches + when /^\[/u # Predicate + predicate( elements, path ) + when /^\/?\.\.\./u # Ancestor + axe( elements, "ancestor", $' ) + when /^\/?\.\./u # Parent + filter( elements.collect{|e|e.parent}, $' ) + when /^\/?\./u # Self + filter( elements, $' ) + when /^\*/u # Any + results = [] + elements.each do |element| + results |= filter( [element], $' ) if element.kind_of? Element + #if element.kind_of? Element + # children = element.to_a + # children.delete_if { |child| !child.kind_of?(Element) } + # results |= filter( children, $' ) + #end + end + results + else + [] + end + end + + def QuickPath::axe( elements, axe_name, rest ) + matches = [] + matches = filter( elements.dup, rest ) if axe_name =~ /-or-self$/u + case axe_name + when /^descendant/u + elements.each do |element| + matches |= filter( element.to_a, "descendant-or-self::#{rest}" ) if element.kind_of? Element + end + when /^ancestor/u + elements.each do |element| + while element.parent + matches << element.parent + element = element.parent + end + end + matches = filter( matches, rest ) + when "self" + matches = filter( elements, rest ) + when "child" + elements.each do |element| + matches |= filter( element.to_a, rest ) if element.kind_of? Element + end + when "attribute" + elements.each do |element| + matches << element.attributes[ rest ] if element.kind_of? Element + end + when "parent" + matches = filter(elements.collect{|element| element.parent}.uniq, rest) + when "following-sibling" + matches = filter(elements.collect{|element| element.next_sibling}.uniq, + rest) + when "previous-sibling" + matches = filter(elements.collect{|element| + element.previous_sibling}.uniq, rest ) + end + matches.uniq + end + + OPERAND_ = '((?=(?:(?!and|or).)*[^\s<>=])[^\s<>=]+)' + # A predicate filters a node-set with respect to an axis to produce a + # new node-set. For each node in the node-set to be filtered, the + # PredicateExpr is evaluated with that node as the context node, with + # the number of nodes in the node-set as the context size, and with the + # proximity position of the node in the node-set with respect to the + # axis as the context position; if PredicateExpr evaluates to true for + # that node, the node is included in the new node-set; otherwise, it is + # not included. + # + # A PredicateExpr is evaluated by evaluating the Expr and converting + # the result to a boolean. If the result is a number, the result will + # be converted to true if the number is equal to the context position + # and will be converted to false otherwise; if the result is not a + # number, then the result will be converted as if by a call to the + # boolean function. Thus a location path para[3] is equivalent to + # para[position()=3]. + def QuickPath::predicate( elements, path ) + ind = 1 + bcount = 1 + while bcount > 0 + bcount += 1 if path[ind] == ?[ + bcount -= 1 if path[ind] == ?] + ind += 1 + end + ind -= 1 + predicate = path[1..ind-1] + rest = path[ind+1..-1] + + # have to change 'a [=<>] b [=<>] c' into 'a [=<>] b and b [=<>] c' + # + predicate.gsub!( + /#{OPERAND_}\s*([<>=])\s*#{OPERAND_}\s*([<>=])\s*#{OPERAND_}/u, + '\1 \2 \3 and \3 \4 \5' ) + # Let's do some Ruby trickery to avoid some work: + predicate.gsub!( /&/u, "&&" ) + predicate.gsub!( /=/u, "==" ) + predicate.gsub!( /@(\w[-\w.]*)/u, 'attribute("\1")' ) + predicate.gsub!( /\bmod\b/u, "%" ) + predicate.gsub!( /\b(\w[-\w.]*\()/u ) { + fname = $1 + fname.gsub( /-/u, "_" ) + } + + Functions.pair = [ 0, elements.size ] + results = [] + elements.each do |element| + Functions.pair[0] += 1 + Functions.node = element + res = eval( predicate ) + case res + when true + results << element + when Integer + results << element if Functions.pair[0] == res + when String + results << element + end + end + filter( results, rest ) + end + + def QuickPath::attribute( name ) + Functions.node.attributes[name] if Functions.node.kind_of? Element + end + + def QuickPath::name() + Functions.node.name if Functions.node.kind_of? Element + end + + def QuickPath::method_missing( id, *args ) + begin + Functions.send( id.id2name, *args ) + rescue Exception + raise "METHOD: #{id.id2name}(#{args.join ', '})\n#{$!.message}" + end + end + + def QuickPath::function( elements, fname, rest ) + args = parse_args( elements, rest ) + Functions.pair = [0, elements.size] + results = [] + elements.each do |element| + Functions.pair[0] += 1 + Functions.node = element + res = Functions.send( fname, *args ) + case res + when true + results << element + when Integer + results << element if Functions.pair[0] == res + end + end + results + end + + def QuickPath::parse_args( element, string ) + # /.*?(?:\)|,)/ + arguments = [] + buffer = "" + while string and string != "" + c = string[0] + string.sub!(/^./u, "") + case c + when ?, + # if depth = 1, then we start a new argument + arguments << evaluate( buffer ) + #arguments << evaluate( string[0..count] ) + when ?( + # start a new method call + function( element, buffer, string ) + buffer = "" + when ?) + # close the method call and return arguments + return arguments + else + buffer << c + end + end + "" + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/rexml.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/rexml.rb new file mode 100644 index 0000000..226e198 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/rexml.rb @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +# frozen_string_literal: false +# +# \Module \REXML provides classes and methods for parsing, +# editing, and generating XML. +# +# == Implementation +# +# \REXML: +# - Is pure Ruby. +# - Provides tree, stream, SAX2, pull, and lightweight APIs. +# - Conforms to {XML version 1.0}[https://www.w3.org/TR/REC-xml/]. +# - Fully implements {XPath version 1.0}[http://www.w3c.org/tr/xpath]. +# - Is {non-validating}[https://www.w3.org/TR/xml/]. +# - Passes 100% of the non-validating {Oasis tests}[http://www.oasis-open.org/committees/xml-conformance/xml-test-suite.shtml]. +# +# == In a Hurry? +# +# If you're somewhat familiar with XML +# and have a particular task in mind, +# you may want to see {the tasks pages}[doc/rexml/tasks/tocs/master_toc_rdoc.html]. +# +# == API +# +# Among the most important classes for using \REXML are: +# - REXML::Document. +# - REXML::Element. +# +# There's also an {REXML tutorial}[doc/rexml/tutorial_rdoc.html]. +# +module REXML + COPYRIGHT = "Copyright © 2001-2008 Sean Russell <ser@germane-software.com>" + DATE = "2008/019" + VERSION = "3.4.4" + REVISION = "" + + Copyright = COPYRIGHT + Version = VERSION +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/sax2listener.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/sax2listener.rb new file mode 100644 index 0000000..5afdc80 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/sax2listener.rb @@ -0,0 +1,98 @@ +# frozen_string_literal: false +module REXML + # A template for stream parser listeners. + # Note that the declarations (attlistdecl, elementdecl, etc) are trivially + # processed; REXML doesn't yet handle doctype entity declarations, so you + # have to parse them out yourself. + # === Missing methods from SAX2 + # ignorable_whitespace + # === Methods extending SAX2 + # +WARNING+ + # These methods are certainly going to change, until DTDs are fully + # supported. Be aware of this. + # start_document + # end_document + # doctype + # elementdecl + # attlistdecl + # entitydecl + # notationdecl + # cdata + # xmldecl + # comment + module SAX2Listener + def start_document + end + def end_document + end + def start_prefix_mapping prefix, uri + end + def end_prefix_mapping prefix + end + def start_element uri, localname, qname, attributes + end + def end_element uri, localname, qname + end + def characters text + end + def processing_instruction target, data + end + # Handles a doctype declaration. Any attributes of the doctype which are + # not supplied will be nil. # EG, <!DOCTYPE me PUBLIC "foo" "bar"> + # @p name the name of the doctype; EG, "me" + # @p pub_sys "PUBLIC", "SYSTEM", or nil. EG, "PUBLIC" + # @p long_name the supplied long name, or nil. EG, "foo" + # @p uri the uri of the doctype, or nil. EG, "bar" + def doctype name, pub_sys, long_name, uri + end + # If a doctype includes an ATTLIST declaration, it will cause this + # method to be called. The content is the declaration itself, unparsed. + # EG, <!ATTLIST el attr CDATA #REQUIRED> will come to this method as "el + # attr CDATA #REQUIRED". This is the same for all of the .*decl + # methods. + def attlistdecl(element, pairs, contents) + end + # <!ELEMENT ...> + def elementdecl content + end + # <!ENTITY ...> + # The argument passed to this method is an array of the entity + # declaration. It can be in a number of formats, but in general it + # returns (example, result): + # <!ENTITY % YN '"Yes"'> + # ["%", "YN", "\"Yes\""] + # <!ENTITY % YN 'Yes'> + # ["%", "YN", "Yes"] + # <!ENTITY WhatHeSaid "He said %YN;"> + # ["WhatHeSaid", "He said %YN;"] + # <!ENTITY open-hatch SYSTEM "http://www.textuality.com/boilerplate/OpenHatch.xml"> + # ["open-hatch", "SYSTEM", "http://www.textuality.com/boilerplate/OpenHatch.xml"] + # <!ENTITY open-hatch PUBLIC "-//Textuality//TEXT Standard open-hatch boilerplate//EN" "http://www.textuality.com/boilerplate/OpenHatch.xml"> + # ["open-hatch", "PUBLIC", "-//Textuality//TEXT Standard open-hatch boilerplate//EN", "http://www.textuality.com/boilerplate/OpenHatch.xml"] + # <!ENTITY hatch-pic SYSTEM "../grafix/OpenHatch.gif" NDATA gif> + # ["hatch-pic", "SYSTEM", "../grafix/OpenHatch.gif", "NDATA", "gif"] + def entitydecl declaration + end + # <!NOTATION ...> + def notationdecl name, public_or_system, public_id, system_id + end + # Called when <![CDATA[ ... ]]> is encountered in a document. + # @p content "..." + def cdata content + end + # Called when an XML PI is encountered in the document. + # EG: <?xml version="1.0" encoding="utf"?> + # @p version the version attribute value. EG, "1.0" + # @p encoding the encoding attribute value, or nil. EG, "utf" + # @p standalone the standalone attribute value, or nil. EG, nil + # @p spaced the declaration is followed by a line break + def xmldecl version, encoding, standalone + end + # Called when a comment is encountered. + # @p comment The content of the comment + def comment comment + end + def progress position + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/security.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/security.rb new file mode 100644 index 0000000..e8e8c6b --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/security.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: false +module REXML + module Security + @@entity_expansion_limit = 10_000 + + # Set the entity expansion limit. By default the limit is set to 10000. + def self.entity_expansion_limit=( val ) + @@entity_expansion_limit = val + end + + # Get the entity expansion limit. By default the limit is set to 10000. + def self.entity_expansion_limit + @@entity_expansion_limit + end + + @@entity_expansion_text_limit = 10_240 + + # Set the entity expansion limit. By default the limit is set to 10240. + def self.entity_expansion_text_limit=( val ) + @@entity_expansion_text_limit = val + end + + # Get the entity expansion limit. By default the limit is set to 10240. + def self.entity_expansion_text_limit + @@entity_expansion_text_limit + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/source.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/source.rb new file mode 100644 index 0000000..8b8ba0d --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/source.rb @@ -0,0 +1,388 @@ +# coding: US-ASCII +# frozen_string_literal: false + +require "stringio" +require "strscan" + +require_relative 'encoding' + +module REXML + if StringScanner::Version < "1.0.0" + module StringScannerCheckScanString + refine StringScanner do + def check(pattern) + pattern = /#{Regexp.escape(pattern)}/ if pattern.is_a?(String) + super(pattern) + end + + def scan(pattern) + pattern = /#{Regexp.escape(pattern)}/ if pattern.is_a?(String) + super(pattern) + end + + def match?(pattern) + pattern = /#{Regexp.escape(pattern)}/ if pattern.is_a?(String) + super(pattern) + end + + def skip(pattern) + pattern = /#{Regexp.escape(pattern)}/ if pattern.is_a?(String) + super(pattern) + end + end + end + using StringScannerCheckScanString + end + + # Generates Source-s. USE THIS CLASS. + class SourceFactory + # Generates a Source object + # @param arg Either a String, or an IO + # @return a Source, or nil if a bad argument was given + def SourceFactory::create_from(arg) + if arg.respond_to? :read and + arg.respond_to? :readline and + arg.respond_to? :nil? and + arg.respond_to? :eof? + IOSource.new(arg) + elsif arg.respond_to? :to_str + IOSource.new(StringIO.new(arg)) + elsif arg.kind_of? Source + arg + else + raise "#{arg.class} is not a valid input stream. It must walk \n"+ + "like either a String, an IO, or a Source." + end + end + end + + # A Source can be searched for patterns, and wraps buffers and other + # objects and provides consumption of text + class Source + include Encoding + # The line number of the last consumed text + attr_reader :line + attr_reader :encoding + + module Private + SPACES_PATTERN = /\s+/um + SCANNER_RESET_SIZE = 100000 + PRE_DEFINED_TERM_PATTERNS = {} + pre_defined_terms = ["'", '"', "<", "]]>", "?>"] + if StringScanner::Version < "3.1.1" + pre_defined_terms.each do |term| + PRE_DEFINED_TERM_PATTERNS[term] = /#{Regexp.escape(term)}/ + end + else + pre_defined_terms.each do |term| + PRE_DEFINED_TERM_PATTERNS[term] = term + end + end + end + private_constant :Private + + # Constructor + # @param arg must be a String, and should be a valid XML document + # @param encoding if non-null, sets the encoding of the source to this + # value, overriding all encoding detection + def initialize(arg, encoding=nil) + @orig = arg + @scanner = StringScanner.new(@orig) + if encoding + self.encoding = encoding + else + detect_encoding + end + @line = 0 + @encoded_terms = {} + end + + # The current buffer (what we're going to read next) + def buffer + @scanner.rest + end + + def drop_parsed_content + if @scanner.pos > Private::SCANNER_RESET_SIZE + @scanner.string = @scanner.rest + end + end + + def buffer_encoding=(encoding) + @scanner.string.force_encoding(encoding) + end + + # Inherited from Encoding + # Overridden to support optimized en/decoding + def encoding=(enc) + return unless super + encoding_updated + end + + def read(term = nil) + end + + def read_until(term) + pattern = Private::PRE_DEFINED_TERM_PATTERNS[term] || /#{Regexp.escape(term)}/ + data = @scanner.scan_until(pattern) + unless data + data = @scanner.rest + @scanner.pos = @scanner.string.bytesize + end + data + end + + def ensure_buffer + end + + def match(pattern, cons=false) + if cons + @scanner.scan(pattern).nil? ? nil : @scanner + else + @scanner.check(pattern).nil? ? nil : @scanner + end + end + + def match?(pattern, cons=false) + if cons + !@scanner.skip(pattern).nil? + else + !@scanner.match?(pattern).nil? + end + end + + def skip_spaces + @scanner.skip(Private::SPACES_PATTERN) ? true : false + end + + def position + @scanner.pos + end + + def position=(pos) + @scanner.pos = pos + end + + def peek_byte + @scanner.peek_byte + end + + def scan_byte + @scanner.scan_byte + end + + # @return true if the Source is exhausted + def empty? + @scanner.eos? + end + + # @return the current line in the source + def current_line + lines = @orig.split + res = lines.grep @scanner.rest[0..30] + res = res[-1] if res.kind_of? Array + lines.index( res ) if res + end + + private + + def detect_encoding + scanner_encoding = @scanner.rest.encoding + detected_encoding = "UTF-8" + begin + @scanner.string.force_encoding("ASCII-8BIT") + if @scanner.scan(/\xfe\xff/n) + detected_encoding = "UTF-16BE" + elsif @scanner.scan(/\xff\xfe/n) + detected_encoding = "UTF-16LE" + elsif @scanner.scan(/\xef\xbb\xbf/n) + detected_encoding = "UTF-8" + end + ensure + @scanner.string.force_encoding(scanner_encoding) + end + self.encoding = detected_encoding + end + + def encoding_updated + if @encoding != 'UTF-8' + @scanner.string = decode(@scanner.rest) + @to_utf = true + else + @to_utf = false + @scanner.string.force_encoding(::Encoding::UTF_8) + end + end + end + + # A Source that wraps an IO. See the Source class for method + # documentation + class IOSource < Source + #attr_reader :block_size + + # block_size has been deprecated + def initialize(arg, block_size=500, encoding=nil) + @er_source = @source = arg + @to_utf = false + @pending_buffer = nil + + if encoding + super("", encoding) + else + super(@source.read(3) || "") + end + + if !@to_utf and + @orig.respond_to?(:force_encoding) and + @source.respond_to?(:external_encoding) and + @source.external_encoding != ::Encoding::UTF_8 + @force_utf8 = true + else + @force_utf8 = false + end + end + + def read(term = nil, min_bytes = 1) + term = encode(term) if term + begin + str = readline(term) + @scanner << str + read_bytes = str.bytesize + begin + while read_bytes < min_bytes + str = readline(term) + @scanner << str + read_bytes += str.bytesize + end + rescue IOError + end + true + rescue Exception, NameError + @source = nil + false + end + end + + def read_until(term) + pattern = Private::PRE_DEFINED_TERM_PATTERNS[term] || /#{Regexp.escape(term)}/ + term = @encoded_terms[term] ||= encode(term) + until str = @scanner.scan_until(pattern) + break if @source.nil? + break if @source.eof? + @scanner << readline(term) + end + if str + read if @scanner.eos? and @source and !@source.eof? + str + else + rest = @scanner.rest + @scanner.pos = @scanner.string.bytesize + rest + end + end + + def ensure_buffer + read if @scanner.eos? && @source + end + + def match( pattern, cons=false ) + # To avoid performance issue, we need to increase bytes to read per scan + min_bytes = 1 + while true + if cons + md = @scanner.scan(pattern) + else + md = @scanner.check(pattern) + end + break if md + return nil if pattern.is_a?(String) + return nil if @source.nil? + return nil unless read(nil, min_bytes) + min_bytes *= 2 + end + + md.nil? ? nil : @scanner + end + + def match?( pattern, cons=false ) + # To avoid performance issue, we need to increase bytes to read per scan + min_bytes = 1 + while true + if cons + n_matched_bytes = @scanner.skip(pattern) + else + n_matched_bytes = @scanner.match?(pattern) + end + return true if n_matched_bytes + return false if pattern.is_a?(String) + return false if @source.nil? + return false unless read(nil, min_bytes) + min_bytes *= 2 + end + end + + def empty? + super and ( @source.nil? || @source.eof? ) + end + + # @return the current line in the source + def current_line + begin + pos = @er_source.pos # The byte position in the source + lineno = @er_source.lineno # The XML < position in the source + @er_source.rewind + line = 0 # The \r\n position in the source + begin + while @er_source.pos < pos + @er_source.readline + line += 1 + end + rescue + end + @er_source.seek(pos) + rescue IOError, SystemCallError + pos = -1 + line = -1 + end + [pos, lineno, line] + end + + private + def readline(term = nil) + if @pending_buffer + begin + str = @source.readline(term || @line_break) + rescue IOError + end + if str.nil? + str = @pending_buffer + else + str = @pending_buffer + str + end + @pending_buffer = nil + else + str = @source.readline(term || @line_break) + end + return nil if str.nil? + + if @to_utf + decode(str) + else + str.force_encoding(::Encoding::UTF_8) if @force_utf8 + str + end + end + + def encoding_updated + case @encoding + when "UTF-16BE", "UTF-16LE" + @source.binmode + @source.set_encoding(@encoding, @encoding) + end + @line_break = encode(">") + @pending_buffer, @scanner.string = @scanner.rest, "" + @pending_buffer.force_encoding(@encoding) + super + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/streamlistener.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/streamlistener.rb new file mode 100644 index 0000000..30c8945 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/streamlistener.rb @@ -0,0 +1,93 @@ +# frozen_string_literal: false +module REXML + # A template for stream parser listeners. + # Note that the declarations (attlistdecl, elementdecl, etc) are trivially + # processed; REXML doesn't yet handle doctype entity declarations, so you + # have to parse them out yourself. + module StreamListener + # Called when a tag is encountered. + # @p name the tag name + # @p attrs an array of arrays of attribute/value pairs, suitable for + # use with assoc or rassoc. IE, <tag attr1="value1" attr2="value2"> + # will result in + # tag_start( "tag", # [["attr1","value1"],["attr2","value2"]]) + def tag_start name, attrs + end + # Called when the end tag is reached. In the case of <tag/>, tag_end + # will be called immediately after tag_start + # @p the name of the tag + def tag_end name + end + # Called when text is encountered in the document + # @p text the text content. + def text text + end + # Called when an instruction is encountered. EG: <?xsl sheet='foo'?> + # @p name the instruction name; in the example, "xsl" + # @p instruction the rest of the instruction. In the example, + # "sheet='foo'" + def instruction name, instruction + end + # Called when a comment is encountered. + # @p comment The content of the comment + def comment comment + end + # Handles a doctype declaration. Any attributes of the doctype which are + # not supplied will be nil. # EG, <!DOCTYPE me PUBLIC "foo" "bar"> + # @p name the name of the doctype; EG, "me" + # @p pub_sys "PUBLIC", "SYSTEM", or nil. EG, "PUBLIC" + # @p long_name the supplied long name, or nil. EG, "foo" + # @p uri the uri of the doctype, or nil. EG, "bar" + def doctype name, pub_sys, long_name, uri + end + # Called when the doctype is done + def doctype_end + end + # If a doctype includes an ATTLIST declaration, it will cause this + # method to be called. The content is the declaration itself, unparsed. + # EG, <!ATTLIST el attr CDATA #REQUIRED> will come to this method as "el + # attr CDATA #REQUIRED". This is the same for all of the .*decl + # methods. + def attlistdecl element_name, attributes, raw_content + end + # <!ELEMENT ...> + def elementdecl content + end + # <!ENTITY ...> + # The argument passed to this method is an array of the entity + # declaration. It can be in a number of formats, but in general it + # returns (example, result): + # <!ENTITY % YN '"Yes"'> + # ["YN", "\"Yes\"", "%"] + # <!ENTITY % YN 'Yes'> + # ["YN", "Yes", "%"] + # <!ENTITY WhatHeSaid "He said %YN;"> + # ["WhatHeSaid", "He said %YN;"] + # <!ENTITY open-hatch SYSTEM "http://www.textuality.com/boilerplate/OpenHatch.xml"> + # ["open-hatch", "SYSTEM", "http://www.textuality.com/boilerplate/OpenHatch.xml"] + # <!ENTITY open-hatch PUBLIC "-//Textuality//TEXT Standard open-hatch boilerplate//EN" "http://www.textuality.com/boilerplate/OpenHatch.xml"> + # ["open-hatch", "PUBLIC", "-//Textuality//TEXT Standard open-hatch boilerplate//EN", "http://www.textuality.com/boilerplate/OpenHatch.xml"] + # <!ENTITY hatch-pic SYSTEM "../grafix/OpenHatch.gif" NDATA gif> + # ["hatch-pic", "SYSTEM", "../grafix/OpenHatch.gif", "gif"] + def entitydecl content + end + # <!NOTATION ...> + def notationdecl content + end + # Called when %foo; is encountered in a doctype declaration. + # @p content "foo" + def entity content + end + # Called when <![CDATA[ ... ]]> is encountered in a document. + # @p content "..." + def cdata content + end + # Called when an XML PI is encountered in the document. + # EG: <?xml version="1.0" encoding="utf"?> + # @p version the version attribute value. EG, "1.0" + # @p encoding the encoding attribute value, or nil. EG, "utf" + # @p standalone the standalone attribute value, or nil. EG, nil + def xmldecl version, encoding, standalone + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/text.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/text.rb new file mode 100644 index 0000000..8d5281c --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/text.rb @@ -0,0 +1,420 @@ +# frozen_string_literal: true +require_relative 'security' +require_relative 'entity' +require_relative 'doctype' +require_relative 'child' +require_relative 'doctype' +require_relative 'parseexception' + +module REXML + # Represents text nodes in an XML document + class Text < Child + include Comparable + # The order in which the substitutions occur + SPECIALS = [ /&(?!#?[\w-]+;)/u, /</u, />/u, /"/u, /'/u, /\r/u ] + SUBSTITUTES = ['&', '<', '>', '"', ''', ' '] + # Characters which are substituted in written strings + SLAICEPS = [ '<', '>', '"', "'", '&' ] + SETUTITSBUS = [ /</u, />/u, /"/u, /'/u, /&/u ] + + # If +raw+ is true, then REXML leaves the value alone + attr_accessor :raw + + NEEDS_A_SECOND_CHECK = /(<|&((#{Entity::NAME});|(#0*((?:\d+)|(?:x[a-fA-F0-9]+)));)?)/um + NUMERICENTITY = /�*((?:\d+)|(?:x[a-fA-F0-9]+));/ + VALID_CHAR = [ + 0x9, 0xA, 0xD, + (0x20..0xD7FF), + (0xE000..0xFFFD), + (0x10000..0x10FFFF) + ] + + VALID_XML_CHARS = Regexp.new('^['+ + VALID_CHAR.map { |item| + case item + when Integer + [item].pack('U').force_encoding('utf-8') + when Range + [item.first, '-'.ord, item.last].pack('UUU').force_encoding('utf-8') + end + }.join + + ']*$') + + # Constructor + # +arg+ if a String, the content is set to the String. If a Text, + # the object is shallowly cloned. + # + # +respect_whitespace+ (boolean, false) if true, whitespace is + # respected + # + # +parent+ (nil) if this is a Parent object, the parent + # will be set to this. + # + # +raw+ (nil) This argument can be given three values. + # If true, then the value of used to construct this object is expected to + # contain no unescaped XML markup, and REXML will not change the text. If + # this value is false, the string may contain any characters, and REXML will + # escape any and all defined entities whose values are contained in the + # text. If this value is nil (the default), then the raw value of the + # parent will be used as the raw value for this node. If there is no raw + # value for the parent, and no value is supplied, the default is false. + # Use this field if you have entities defined for some text, and you don't + # want REXML to escape that text in output. + # Text.new( "<&", false, nil, false ) #-> "<&" + # Text.new( "<&", false, nil, false ) #-> "&lt;&amp;" + # Text.new( "<&", false, nil, true ) #-> Parse exception + # Text.new( "<&", false, nil, true ) #-> "<&" + # # Assume that the entity "s" is defined to be "sean" + # # and that the entity "r" is defined to be "russell" + # Text.new( "sean russell" ) #-> "&s; &r;" + # Text.new( "sean russell", false, nil, true ) #-> "sean russell" + # + # +entity_filter+ (nil) This can be an array of entities to match in the + # supplied text. This argument is only useful if +raw+ is set to false. + # Text.new( "sean russell", false, nil, false, ["s"] ) #-> "&s; russell" + # Text.new( "sean russell", false, nil, true, ["s"] ) #-> "sean russell" + # In the last example, the +entity_filter+ argument is ignored. + # + # +illegal+ INTERNAL USE ONLY + def initialize(arg, respect_whitespace=false, parent=nil, raw=nil, + entity_filter=nil, illegal=NEEDS_A_SECOND_CHECK ) + + @raw = false + @parent = nil + @entity_filter = nil + + if parent + super( parent ) + @raw = parent.raw + end + + if arg.kind_of? String + @string = arg.dup + elsif arg.kind_of? Text + @string = arg.instance_variable_get(:@string).dup + @raw = arg.raw + @entity_filter = arg.instance_variable_get(:@entity_filter) + else + raise "Illegal argument of type #{arg.type} for Text constructor (#{arg})" + end + + @string.squeeze!(" \n\t") unless respect_whitespace + @string.gsub!(/\r\n?/, "\n") + @raw = raw unless raw.nil? + @entity_filter = entity_filter if entity_filter + clear_cache + + Text.check(@string, illegal) if @raw + end + + def parent= parent + super(parent) + Text.check(@string, NEEDS_A_SECOND_CHECK) if @raw and @parent + end + + # check for illegal characters + def Text.check string, pattern, doctype = nil + + # illegal anywhere + if !string.match?(VALID_XML_CHARS) + string.chars.each do |c| + case c.ord + when *VALID_CHAR + else + raise "Illegal character #{c.inspect} in raw string #{string.inspect}" + end + end + end + + pos = 0 + while (index = string.index(/<|&/, pos)) + if string[index] == "<" + raise "Illegal character \"#{string[index]}\" in raw string #{string.inspect}" + end + + unless (end_index = string.index(/[^\s];/, index + 1)) + raise "Illegal character \"#{string[index]}\" in raw string #{string.inspect}" + end + + value = string[(index + 1)..end_index] + if /\s/.match?(value) + raise "Illegal character \"#{string[index]}\" in raw string #{string.inspect}" + end + + if value[0] == "#" + character_reference = value[1..-1] + + unless (/\A(\d+|x[0-9a-fA-F]+)\z/.match?(character_reference)) + if character_reference[0] == "x" || character_reference[-1] == "x" + raise "Illegal character \"#{string[index]}\" in raw string #{string.inspect}" + else + raise "Illegal character #{string.inspect} in raw string #{string.inspect}" + end + end + + case (character_reference[0] == "x" ? character_reference[1..-1].to_i(16) : character_reference[0..-1].to_i) + when *VALID_CHAR + else + raise "Illegal character #{string.inspect} in raw string #{string.inspect}" + end + elsif !(/\A#{Entity::NAME}\z/um.match?(value)) + raise "Illegal character \"#{string[index]}\" in raw string #{string.inspect}" + end + + pos = end_index + 1 + end + + string + end + + def node_type + :text + end + + def empty? + @string.size==0 + end + + + def clone + Text.new(self, true) + end + + + # Appends text to this text node. The text is appended in the +raw+ mode + # of this text node. + # + # +returns+ the text itself to enable method chain like + # 'text << "XXX" << "YYY"'. + def <<( to_append ) + @string << to_append.gsub( /\r\n?/, "\n" ) + clear_cache + self + end + + + # +other+ a String or a Text + # +returns+ the result of (to_s <=> arg.to_s) + def <=>( other ) + to_s() <=> other.to_s + end + + def doctype + @parent&.document&.doctype + end + + REFERENCE = /#{Entity::REFERENCE}/ + # Returns the string value of this text node. This string is always + # escaped, meaning that it is a valid XML text node string, and all + # entities that can be escaped, have been inserted. This method respects + # the entity filter set in the constructor. + # + # # Assume that the entity "s" is defined to be "sean", and that the + # # entity "r" is defined to be "russell" + # t = Text.new( "< & sean russell", false, nil, false, ['s'] ) + # t.to_s #-> "< & &s; russell" + # t = Text.new( "< & &s; russell", false, nil, false ) + # t.to_s #-> "< & &s; russell" + # u = Text.new( "sean russell", false, nil, true ) + # u.to_s #-> "sean russell" + def to_s + return @string if @raw + @normalized ||= Text::normalize( @string, doctype, @entity_filter ) + end + + def inspect + @string.inspect + end + + # Returns the string value of this text. This is the text without + # entities, as it might be used programmatically, or printed to the + # console. This ignores the 'raw' attribute setting, and any + # entity_filter. + # + # # Assume that the entity "s" is defined to be "sean", and that the + # # entity "r" is defined to be "russell" + # t = Text.new( "< & sean russell", false, nil, false, ['s'] ) + # t.value #-> "< & sean russell" + # t = Text.new( "< & &s; russell", false, nil, false ) + # t.value #-> "< & sean russell" + # u = Text.new( "sean russell", false, nil, true ) + # u.value #-> "sean russell" + def value + @unnormalized ||= Text::unnormalize(@string, doctype, + entity_expansion_text_limit: document&.entity_expansion_text_limit) + end + + # Sets the contents of this text node. This expects the text to be + # unnormalized. It returns self. + # + # e = Element.new( "a" ) + # e.add_text( "foo" ) # <a>foo</a> + # e[0].value = "bar" # <a>bar</a> + # e[0].value = "<a>" # <a><a></a> + def value=( val ) + @string = val.gsub( /\r\n?/, "\n" ) + clear_cache + @raw = false + end + + def wrap(string, width, addnewline=false) + # Recursively wrap string at width. + return string if string.length <= width + place = string.rindex(' ', width) # Position in string with last ' ' before cutoff + if addnewline + "\n" + string[0,place] + "\n" + wrap(string[place+1..-1], width) + else + string[0,place] + "\n" + wrap(string[place+1..-1], width) + end + end + + def indent_text(string, level=1, style="\t", indentfirstline=true) + Kernel.warn("#{self.class.name}#indent_text is deprecated. See REXML::Formatters", uplevel: 1) + return string if level < 0 + + new_string = +'' + string.each_line { |line| + indent_string = style * level + new_line = (indent_string + line).sub(/[\s]+$/,'') + new_string << new_line + } + new_string.strip! unless indentfirstline + new_string + end + + # == DEPRECATED + # See REXML::Formatters + # + def write( writer, indent=-1, transitive=false, ie_hack=false ) + Kernel.warn("#{self.class.name}#write is deprecated. See REXML::Formatters", uplevel: 1) + formatter = if indent > -1 + REXML::Formatters::Pretty.new( indent ) + else + REXML::Formatters::Default.new + end + formatter.write( self, writer ) + end + + # FIXME + # This probably won't work properly + def xpath + @parent.xpath + "/text()" + end + + # Writes out text, substituting special characters beforehand. + # +out+ A String, IO, or any other object supporting <<( String ) + # +input+ the text to substitute and the write out + # + # z=utf8.unpack("U*") + # ascOut="" + # z.each{|r| + # if r < 0x100 + # ascOut.concat(r.chr) + # else + # ascOut.concat(sprintf("&#x%x;", r)) + # end + # } + # puts ascOut + def write_with_substitution out, input + copy = input.clone + # Doing it like this rather than in a loop improves the speed + copy.gsub!( SPECIALS[0], SUBSTITUTES[0] ) + copy.gsub!( SPECIALS[1], SUBSTITUTES[1] ) + copy.gsub!( SPECIALS[2], SUBSTITUTES[2] ) + copy.gsub!( SPECIALS[3], SUBSTITUTES[3] ) + copy.gsub!( SPECIALS[4], SUBSTITUTES[4] ) + copy.gsub!( SPECIALS[5], SUBSTITUTES[5] ) + out << copy + end + + private + def clear_cache + @normalized = nil + @unnormalized = nil + end + + # Reads text, substituting entities + def Text::read_with_substitution( input, illegal=nil ) + copy = input.clone + + if copy =~ illegal + raise ParseException.new( "malformed text: Illegal character #$& in \"#{copy}\"" ) + end if illegal + + copy.gsub!( /\r\n?/, "\n" ) + if copy.include? ?& + copy.gsub!( SETUTITSBUS[0], SLAICEPS[0] ) + copy.gsub!( SETUTITSBUS[1], SLAICEPS[1] ) + copy.gsub!( SETUTITSBUS[2], SLAICEPS[2] ) + copy.gsub!( SETUTITSBUS[3], SLAICEPS[3] ) + copy.gsub!( SETUTITSBUS[4], SLAICEPS[4] ) + copy.gsub!( /�*((?:\d+)|(?:x[a-f0-9]+));/ ) { + m=$1 + #m='0' if m=='' + m = "0#{m}" if m[0] == ?x + [Integer(m)].pack('U*') + } + end + copy + end + + EREFERENCE = /&(?!#{Entity::NAME};)/ + # Escapes all possible entities + def Text::normalize( input, doctype=nil, entity_filter=nil ) + copy = input.to_s + # Doing it like this rather than in a loop improves the speed + #copy = copy.gsub( EREFERENCE, '&' ) + copy = copy.gsub( "&", "&" ) if copy.include?("&") + if doctype + # Replace all ampersands that aren't part of an entity + doctype.entities.each_value do |entity| + copy = copy.gsub( entity.value, + "&#{entity.name};" ) if entity.value and + not( entity_filter and entity_filter.include?(entity.name) ) + end + else + # Replace all ampersands that aren't part of an entity + DocType::DEFAULT_ENTITIES.each_value do |entity| + if copy.include?(entity.value) + copy = copy.gsub(entity.value, "&#{entity.name};" ) + end + end + end + copy + end + + # Unescapes all possible entities + def Text::unnormalize( string, doctype=nil, filter=nil, illegal=nil, entity_expansion_text_limit: nil ) + entity_expansion_text_limit ||= Security.entity_expansion_text_limit + sum = 0 + string.gsub( /\r\n?/, "\n" ).gsub( REFERENCE ) { + s = Text.expand($&, doctype, filter) + if sum + s.bytesize > entity_expansion_text_limit + raise "entity expansion has grown too large" + else + sum += s.bytesize + end + s + } + end + + def Text.expand(ref, doctype, filter) + if ref[1] == ?# + if ref[2] == ?x + [ref[3...-1].to_i(16)].pack('U*') + else + [ref[2...-1].to_i].pack('U*') + end + elsif ref == '&' + '&' + elsif filter and filter.include?( ref[1...-1] ) + ref + elsif doctype + doctype.entity( ref[1...-1] ) or ref + else + entity_value = DocType::DEFAULT_ENTITIES[ ref[1...-1] ] + entity_value ? entity_value.value : ref + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/undefinednamespaceexception.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/undefinednamespaceexception.rb new file mode 100644 index 0000000..492a098 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/undefinednamespaceexception.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: false +require_relative 'parseexception' +module REXML + class UndefinedNamespaceException < ParseException + def initialize( prefix, source, parser ) + super( "Undefined prefix #{prefix} found" ) + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/validation/relaxng.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/validation/relaxng.rb new file mode 100644 index 0000000..c6894dc --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/validation/relaxng.rb @@ -0,0 +1,540 @@ +# frozen_string_literal: false +require_relative "validation" +require_relative "../parsers/baseparser" + +module REXML + module Validation + # Implemented: + # * empty + # * element + # * attribute + # * text + # * optional + # * choice + # * oneOrMore + # * zeroOrMore + # * group + # * value + # * interleave + # * mixed + # * ref + # * grammar + # * start + # * define + # + # Not implemented: + # * data + # * param + # * include + # * externalRef + # * notAllowed + # * anyName + # * nsName + # * except + # * name + class RelaxNG + include Validator + + INFINITY = 1.0 / 0.0 + EMPTY = Event.new( nil ) + TEXT = [:start_element, "text"] + attr_accessor :current + attr_accessor :count + attr_reader :references + + # FIXME: Namespaces + def initialize source + parser = REXML::Parsers::BaseParser.new( source ) + + @count = 0 + @references = {} + @root = @current = Sequence.new(self) + @root.previous = true + states = [ @current ] + begin + event = parser.pull + case event[0] + when :start_element + case event[1] + when "empty" + when "element", "attribute", "text", "value" + states[-1] << event + when "optional" + states << Optional.new( self ) + states[-2] << states[-1] + when "choice" + states << Choice.new( self ) + states[-2] << states[-1] + when "oneOrMore" + states << OneOrMore.new( self ) + states[-2] << states[-1] + when "zeroOrMore" + states << ZeroOrMore.new( self ) + states[-2] << states[-1] + when "group" + states << Sequence.new( self ) + states[-2] << states[-1] + when "interleave" + states << Interleave.new( self ) + states[-2] << states[-1] + when "mixed" + states << Interleave.new( self ) + states[-2] << states[-1] + states[-1] << TEXT + when "define" + states << [ event[2]["name"] ] + when "ref" + states[-1] << Ref.new( event[2]["name"] ) + when "anyName" + states << AnyName.new( self ) + states[-2] << states[-1] + when "nsName" + when "except" + when "name" + when "data" + when "param" + when "include" + when "grammar" + when "start" + when "externalRef" + when "notAllowed" + end + when :end_element + case event[1] + when "element", "attribute" + states[-1] << event + when "zeroOrMore", "oneOrMore", "choice", "optional", + "interleave", "group", "mixed" + states.pop + when "define" + ref = states.pop + @references[ ref.shift ] = ref + #when "empty" + end + when :end_document + states[-1] << event + when :text + states[-1] << event + end + end while event[0] != :end_document + end + + def receive event + validate( event ) + end + end + + class State + def initialize( context ) + @previous = [] + @events = [] + @current = 0 + @count = context.count += 1 + @references = context.references + @value = false + end + + def reset + return if @current == 0 + @current = 0 + @events.each {|s| s.reset if s.kind_of? State } + end + + def previous=( previous ) + @previous << previous + end + + def next( event ) + #print "In next with #{event.inspect}. " + #p @previous + return @previous.pop.next( event ) if @events[@current].nil? + expand_ref_in( @events, @current ) if @events[@current].class == Ref + if ( @events[@current].kind_of? State ) + @current += 1 + @events[@current-1].previous = self + return @events[@current-1].next( event ) + end + if ( @events[@current].matches?(event) ) + @current += 1 + if @events[@current].nil? + @previous.pop + elsif @events[@current].kind_of? State + @current += 1 + @events[@current-1].previous = self + @events[@current-1] + else + self + end + else + nil + end + end + + def to_s + # Abbreviated: + self.class.name =~ /(?:::)(\w)\w+$/ + # Full: + #self.class.name =~ /(?:::)(\w+)$/ + "#$1.#@count" + end + + def inspect + "< #{to_s} #{@events.collect{|e| + pre = e == @events[@current] ? '#' : '' + pre + e.inspect unless self == e + }.join(', ')} >" + end + + def expected + [@events[@current]] + end + + def <<( event ) + add_event_to_arry( @events, event ) + end + + + protected + def expand_ref_in( arry, ind ) + new_events = [] + @references[ arry[ind].to_s ].each{ |evt| + add_event_to_arry(new_events,evt) + } + arry[ind,1] = new_events + end + + def add_event_to_arry( arry, evt ) + evt = generate_event( evt ) + if evt.kind_of? String + arry[-1].event_arg = evt if arry[-1].kind_of? Event and @value + @value = false + else + arry << evt + end + end + + def generate_event( event ) + return event if event.kind_of? State or event.class == Ref + evt = nil + arg = nil + case event[0] + when :start_element + case event[1] + when "element" + evt = :start_element + arg = event[2]["name"] + when "attribute" + evt = :start_attribute + arg = event[2]["name"] + when "text" + evt = :text + when "value" + evt = :text + @value = true + end + when :text + return event[1] + when :end_document + return Event.new( event[0] ) + else # then :end_element + case event[1] + when "element" + evt = :end_element + when "attribute" + evt = :end_attribute + end + end + Event.new( evt, arg ) + end + end + + + class Sequence < State + def matches?(event) + @events[@current].matches?( event ) + end + end + + + class Optional < State + def next( event ) + if @current == 0 + rv = super + return rv if rv + @prior = @previous.pop + @prior.next( event ) + else + super + end + end + + def matches?(event) + @events[@current].matches?(event) || + (@current == 0 and @previous[-1].matches?(event)) + end + + def expected + return [ @prior.expected, @events[0] ].flatten if @current == 0 + [@events[@current]] + end + end + + + class ZeroOrMore < Optional + def next( event ) + expand_ref_in( @events, @current ) if @events[@current].class == Ref + if ( @events[@current].matches?(event) ) + @current += 1 + if @events[@current].nil? + @current = 0 + self + elsif @events[@current].kind_of? State + @current += 1 + @events[@current-1].previous = self + @events[@current-1] + else + self + end + else + @prior = @previous.pop + return @prior.next( event ) if @current == 0 + nil + end + end + + def expected + return [ @prior.expected, @events[0] ].flatten if @current == 0 + [@events[@current]] + end + end + + + class OneOrMore < State + def initialize context + super + @ord = 0 + end + + def reset + super + @ord = 0 + end + + def next( event ) + expand_ref_in( @events, @current ) if @events[@current].class == Ref + if ( @events[@current].matches?(event) ) + @current += 1 + @ord += 1 + if @events[@current].nil? + @current = 0 + self + elsif @events[@current].kind_of? State + @current += 1 + @events[@current-1].previous = self + @events[@current-1] + else + self + end + else + return @previous.pop.next( event ) if @current == 0 and @ord > 0 + nil + end + end + + def matches?( event ) + @events[@current].matches?(event) || + (@current == 0 and @ord > 0 and @previous[-1].matches?(event)) + end + + def expected + if @current == 0 and @ord > 0 + [@previous[-1].expected, @events[0]].flatten + else + [@events[@current]] + end + end + end + + + class Choice < State + def initialize context + super + @choices = [] + end + + def reset + super + @events = [] + @choices.each { |c| c.each { |s| s.reset if s.kind_of? State } } + end + + def <<( event ) + add_event_to_arry( @choices, event ) + end + + def next( event ) + # Make the choice if we haven't + if @events.size == 0 + c = 0 ; max = @choices.size + while c < max + if @choices[c][0].class == Ref + expand_ref_in( @choices[c], 0 ) + @choices += @choices[c] + @choices.delete( @choices[c] ) + max -= 1 + else + c += 1 + end + end + @events = @choices.find { |evt| evt[0].matches? event } + # Remove the references + # Find the events + end + unless @events + @events = [] + return nil + end + super + end + + def matches?( event ) + return @events[@current].matches?( event ) if @events.size > 0 + !@choices.find{|evt| evt[0].matches?(event)}.nil? + end + + def expected + return [@events[@current]] if @events.size > 0 + @choices.collect do |x| + if x[0].kind_of? State + x[0].expected + else + x[0] + end + end.flatten + end + + def inspect + "< #{to_s} #{@choices.collect{|e| e.collect{|f|f.to_s}.join(', ')}.join(' or ')} >" + end + + protected + def add_event_to_arry( arry, evt ) + if evt.kind_of? State or evt.class == Ref + arry << [evt] + elsif evt[0] == :text + if arry[-1] and + arry[-1][-1].kind_of?( Event ) and + arry[-1][-1].event_type == :text and @value + + arry[-1][-1].event_arg = evt[1] + @value = false + end + else + arry << [] if evt[0] == :start_element + arry[-1] << generate_event( evt ) + end + end + end + + + class Interleave < Choice + def initialize context + super + @choice = 0 + end + + def reset + @choice = 0 + end + + def next_current( event ) + # Expand references + c = 0 ; max = @choices.size + while c < max + if @choices[c][0].class == Ref + expand_ref_in( @choices[c], 0 ) + @choices += @choices[c] + @choices.delete( @choices[c] ) + max -= 1 + else + c += 1 + end + end + @events = @choices[@choice..-1].find { |evt| evt[0].matches? event } + @current = 0 + if @events + # reorder the choices + old = @choices[@choice] + idx = @choices.index( @events ) + @choices[@choice] = @events + @choices[idx] = old + @choice += 1 + end + + @events = [] unless @events + end + + + def next( event ) + # Find the next series + next_current(event) unless @events[@current] + return nil unless @events[@current] + + expand_ref_in( @events, @current ) if @events[@current].class == Ref + if ( @events[@current].kind_of? State ) + @current += 1 + @events[@current-1].previous = self + return @events[@current-1].next( event ) + end + return @previous.pop.next( event ) if @events[@current].nil? + if ( @events[@current].matches?(event) ) + @current += 1 + if @events[@current].nil? + return self unless @choices[@choice].nil? + @previous.pop + elsif @events[@current].kind_of? State + @current += 1 + @events[@current-1].previous = self + @events[@current-1] + else + self + end + else + nil + end + end + + def matches?( event ) + return @events[@current].matches?( event ) if @events[@current] + !@choices[@choice..-1].find{|evt| evt[0].matches?(event)}.nil? + end + + def expected + return [@events[@current]] if @events[@current] + @choices[@choice..-1].collect do |x| + if x[0].kind_of? State + x[0].expected + else + x[0] + end + end.flatten + end + + def inspect + "< #{to_s} #{@choices.collect{|e| e.collect{|f|f.to_s}.join(', ')}.join(' and ')} >" + end + end + + class Ref + def initialize value + @value = value + end + def to_s + @value + end + def inspect + "{#{to_s}}" + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/validation/validation.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/validation/validation.rb new file mode 100644 index 0000000..6475c62 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/validation/validation.rb @@ -0,0 +1,144 @@ +# frozen_string_literal: false +require_relative 'validationexception' + +module REXML + module Validation + module Validator + NILEVENT = [ nil ] + def reset + @current = @root + @root.reset + @root.previous = true + @attr_stack = [] + self + end + def dump + puts @root.inspect + end + def validate( event ) + @attr_stack = [] unless defined? @attr_stack + match = @current.next(event) + raise ValidationException.new( "Validation error. Expected: "+ + @current.expected.join( " or " )+" from #{@current.inspect} "+ + " but got #{Event.new( event[0], event[1] ).inspect}" ) unless match + @current = match + + # Check for attributes + case event[0] + when :start_element + @attr_stack << event[2] + begin + sattr = [:start_attribute, nil] + eattr = [:end_attribute] + text = [:text, nil] + k, = event[2].find { |key,value| + sattr[1] = key + m = @current.next( sattr ) + if m + # If the state has text children... + if m.matches?( eattr ) + @current = m + else + text[1] = value + m = m.next( text ) + text[1] = nil + return false unless m + @current = m if m + end + m = @current.next( eattr ) + if m + @current = m + true + else + false + end + else + false + end + } + event[2].delete(k) if k + end while k + when :end_element + attrs = @attr_stack.pop + raise ValidationException.new( "Validation error. Illegal "+ + " attributes: #{attrs.inspect}") if attrs.length > 0 + end + end + end + + class Event + def initialize(event_type, event_arg=nil ) + @event_type = event_type + @event_arg = event_arg + end + + attr_reader :event_type + attr_accessor :event_arg + + def done? + @done + end + + def single? + (@event_type != :start_element and @event_type != :start_attribute) + end + + def matches?( event ) + return false unless event[0] == @event_type + case event[0] + when nil + true + when :start_element + event[1] == @event_arg + when :end_element + true + when :start_attribute + event[1] == @event_arg + when :end_attribute + true + when :end_document + true + when :text + @event_arg.nil? || @event_arg == event[1] +=begin + when :processing_instruction + false + when :xmldecl + false + when :start_doctype + false + when :end_doctype + false + when :externalentity + false + when :elementdecl + false + when :entity + false + when :attlistdecl + false + when :notationdecl + false + when :end_doctype + false +=end + else + false + end + end + + def ==( other ) + return false unless other.kind_of? Event + @event_type == other.event_type and @event_arg == other.event_arg + end + + def to_s + inspect + end + + def inspect + "#{@event_type.inspect}( #@event_arg )" + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/validation/validationexception.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/validation/validationexception.rb new file mode 100644 index 0000000..78cd63f --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/validation/validationexception.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: false +module REXML + module Validation + class ValidationException < RuntimeError + def initialize msg + super + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/xmldecl.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/xmldecl.rb new file mode 100644 index 0000000..d19407c --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/xmldecl.rb @@ -0,0 +1,130 @@ +# frozen_string_literal: false + +require_relative 'encoding' +require_relative 'source' + +module REXML + # NEEDS DOCUMENTATION + class XMLDecl < Child + include Encoding + + DEFAULT_VERSION = "1.0" + DEFAULT_ENCODING = "UTF-8" + DEFAULT_STANDALONE = "no" + START = "<?xml" + STOP = "?>" + + attr_accessor :version, :standalone + attr_reader :writeencoding, :writethis + + def initialize(version=DEFAULT_VERSION, encoding=nil, standalone=nil) + @writethis = true + @writeencoding = !encoding.nil? + if version.kind_of? XMLDecl + super() + @version = version.version + self.encoding = version.encoding + @writeencoding = version.writeencoding + @standalone = version.standalone + @writethis = version.writethis + else + super() + @version = version + self.encoding = encoding + @standalone = standalone + end + @version = DEFAULT_VERSION if @version.nil? + end + + def clone + XMLDecl.new(self) + end + + # indent:: + # Ignored. There must be no whitespace before an XML declaration + # transitive:: + # Ignored + # ie_hack:: + # Ignored + def write(writer, indent=-1, transitive=false, ie_hack=false) + return nil unless @writethis or writer.kind_of? Output + writer << START + writer << " #{content encoding}" + writer << STOP + end + + def ==( other ) + other.kind_of?(XMLDecl) and + other.version == @version and + other.encoding == self.encoding and + other.standalone == @standalone + end + + def xmldecl version, encoding, standalone + @version = version + self.encoding = encoding + @standalone = standalone + end + + def node_type + :xmldecl + end + + alias :stand_alone? :standalone + alias :old_enc= :encoding= + + def encoding=( enc ) + if enc.nil? + self.old_enc = "UTF-8" + @writeencoding = false + else + self.old_enc = enc + @writeencoding = true + end + self.dowrite + end + + # Only use this if you do not want the XML declaration to be written; + # this object is ignored by the XML writer. Otherwise, instantiate your + # own XMLDecl and add it to the document. + # + # Note that XML 1.1 documents *must* include an XML declaration + def XMLDecl.default + rv = XMLDecl.new( "1.0" ) + rv.nowrite + rv + end + + def nowrite + @writethis = false + end + + def dowrite + @writethis = true + end + + def inspect + "#{START} ... #{STOP}" + end + + private + def content(enc) + context = nil + context = parent.context if parent + if context and context[:prologue_quote] == :quote + quote = "\"" + else + quote = "'" + end + + rv = "version=#{quote}#{@version}#{quote}" + if @writeencoding or enc !~ /\Autf-8\z/i + rv << " encoding=#{quote}#{enc}#{quote}" + end + if @standalone + rv << " standalone=#{quote}#{@standalone}#{quote}" + end + rv + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/xmltokens.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/xmltokens.rb new file mode 100644 index 0000000..392b47b --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/xmltokens.rb @@ -0,0 +1,85 @@ +# frozen_string_literal: false +module REXML + # Defines a number of tokens used for parsing XML. Not for general + # consumption. + module XMLTokens + # From http://www.w3.org/TR/REC-xml/#sec-common-syn + # + # [4] NameStartChar ::= + # ":" | + # [A-Z] | + # "_" | + # [a-z] | + # [#xC0-#xD6] | + # [#xD8-#xF6] | + # [#xF8-#x2FF] | + # [#x370-#x37D] | + # [#x37F-#x1FFF] | + # [#x200C-#x200D] | + # [#x2070-#x218F] | + # [#x2C00-#x2FEF] | + # [#x3001-#xD7FF] | + # [#xF900-#xFDCF] | + # [#xFDF0-#xFFFD] | + # [#x10000-#xEFFFF] + name_start_chars = [ + ":", + "A-Z", + "_", + "a-z", + "\\u00C0-\\u00D6", + "\\u00D8-\\u00F6", + "\\u00F8-\\u02FF", + "\\u0370-\\u037D", + "\\u037F-\\u1FFF", + "\\u200C-\\u200D", + "\\u2070-\\u218F", + "\\u2C00-\\u2FEF", + "\\u3001-\\uD7FF", + "\\uF900-\\uFDCF", + "\\uFDF0-\\uFFFD", + "\\u{10000}-\\u{EFFFF}", + ] + # From http://www.w3.org/TR/REC-xml/#sec-common-syn + # + # [4a] NameChar ::= + # NameStartChar | + # "-" | + # "." | + # [0-9] | + # #xB7 | + # [#x0300-#x036F] | + # [#x203F-#x2040] + name_chars = name_start_chars + [ + "\\-", + "\\.", + "0-9", + "\\u00B7", + "\\u0300-\\u036F", + "\\u203F-\\u2040", + ] + NAME_START_CHAR = "[#{name_start_chars.join('')}]" + NAME_CHAR = "[#{name_chars.join('')}]" + NAMECHAR = NAME_CHAR # deprecated. Use NAME_CHAR instead. + + # From http://www.w3.org/TR/xml-names11/#NT-NCName + # + # [6] NCNameStartChar ::= NameStartChar - ':' + ncname_start_chars = name_start_chars - [":"] + # From http://www.w3.org/TR/xml-names11/#NT-NCName + # + # [5] NCNameChar ::= NameChar - ':' + ncname_chars = name_chars - [":"] + NCNAME_STR = "[#{ncname_start_chars.join('')}][#{ncname_chars.join('')}]*" + NAME_STR = "(?:#{NCNAME_STR}:)?#{NCNAME_STR}" + + NAME = "(#{NAME_START_CHAR}#{NAME_CHAR}*)" + NMTOKEN = "(?:#{NAME_CHAR})+" + NMTOKENS = "#{NMTOKEN}(\\s+#{NMTOKEN})*" + REFERENCE = "(?:&#{NAME};|&#\\d+;|&#x[0-9a-fA-F]+;)" + + #REFERENCE = "(?:#{ENTITYREF}|#{CHARREF})" + #ENTITYREF = "&#{NAME};" + #CHARREF = "&#\\d+;|&#x[0-9a-fA-F]+;" + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/xpath.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/xpath.rb new file mode 100644 index 0000000..eed0300 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/xpath.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: false +require_relative 'functions' +require_relative 'xpath_parser' + +module REXML + # Wrapper class. Use this class to access the XPath functions. + class XPath + include Functions + # A base Hash object, supposing to be used when initializing a + # default empty namespaces set, but is currently unused. + # TODO: either set the namespaces=EMPTY_HASH, or deprecate this. + EMPTY_HASH = {} + + # Finds and returns the first node that matches the supplied xpath. + # element:: + # The context element + # path:: + # The xpath to search for. If not supplied or nil, returns the first + # node matching '*'. + # namespaces:: + # If supplied, a Hash which defines a namespace mapping. + # variables:: + # If supplied, a Hash which maps $variables in the query + # to values. This can be used to avoid XPath injection attacks + # or to automatically handle escaping string values. + # + # XPath.first( node ) + # XPath.first( doc, "//b"} ) + # XPath.first( node, "a/x:b", { "x"=>"http://doofus" } ) + # XPath.first( node, '/book/publisher/text()=$publisher', {}, {"publisher"=>"O'Reilly"}) + def XPath::first(element, path=nil, namespaces=nil, variables={}, options={}) + raise "The namespaces argument, if supplied, must be a hash object." unless namespaces.nil? or namespaces.kind_of?(Hash) + raise "The variables argument, if supplied, must be a hash object." unless variables.kind_of?(Hash) + match(element, path, namespaces, variables, options).flatten[0] + end + + # Iterates over nodes that match the given path, calling the supplied + # block with the match. + # element:: + # The context element + # path:: + # The xpath to search for. If not supplied or nil, defaults to '*' + # namespaces:: + # If supplied, a Hash which defines a namespace mapping + # variables:: + # If supplied, a Hash which maps $variables in the query + # to values. This can be used to avoid XPath injection attacks + # or to automatically handle escaping string values. + # + # XPath.each( node ) { |el| ... } + # XPath.each( node, '/*[@attr='v']' ) { |el| ... } + # XPath.each( node, 'ancestor::x' ) { |el| ... } + # XPath.each( node, '/book/publisher/text()=$publisher', {}, {"publisher"=>"O'Reilly"}) \ + # {|el| ... } + def XPath::each(element, path=nil, namespaces=nil, variables={}, options={}, &block) + raise "The namespaces argument, if supplied, must be a hash object." unless namespaces.nil? or namespaces.kind_of?(Hash) + raise "The variables argument, if supplied, must be a hash object." unless variables.kind_of?(Hash) + match(element, path, namespaces, variables, options).each( &block ) + end + + # Returns an array of nodes matching a given XPath. + def XPath::match(element, path=nil, namespaces=nil, variables={}, options={}) + parser = XPathParser.new(**options) + parser.namespaces = namespaces + parser.variables = variables + path = "*" unless path + parser.parse(path,element) + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/xpath_parser.rb b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/xpath_parser.rb new file mode 100644 index 0000000..64c8846 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/rexml-3.4.4/lib/rexml/xpath_parser.rb @@ -0,0 +1,980 @@ +# frozen_string_literal: false + +require "pp" + +require_relative 'namespace' +require_relative 'xmltokens' +require_relative 'attribute' +require_relative 'parsers/xpathparser' + +module REXML + module DClonable + refine Object do + # provides a unified +clone+ operation, for REXML::XPathParser + # to use across multiple Object types + def dclone + clone + end + end + refine Symbol do + # provides a unified +clone+ operation, for REXML::XPathParser + # to use across multiple Object types + def dclone ; self ; end + end + refine Integer do + # provides a unified +clone+ operation, for REXML::XPathParser + # to use across multiple Object types + def dclone ; self ; end + end + refine Float do + # provides a unified +clone+ operation, for REXML::XPathParser + # to use across multiple Object types + def dclone ; self ; end + end + refine Array do + # provides a unified +clone+ operation, for REXML::XPathParser + # to use across multiple Object+ types + def dclone + klone = self.clone + klone.clear + self.each{|v| klone << v.dclone} + klone + end + end + end +end + +using REXML::DClonable + +module REXML + # You don't want to use this class. Really. Use XPath, which is a wrapper + # for this class. Believe me. You don't want to poke around in here. + # There is strange, dark magic at work in this code. Beware. Go back! Go + # back while you still can! + class XPathParser + include XMLTokens + LITERAL = /^'([^']*)'|^"([^"]*)"/u + + DEBUG = (ENV["REXML_XPATH_PARSER_DEBUG"] == "true") + + def initialize(strict: false) + @debug = DEBUG + @parser = REXML::Parsers::XPathParser.new + @namespaces = nil + @variables = {} + @nest = 0 + @strict = strict + end + + def namespaces=( namespaces={} ) + Functions::namespace_context = namespaces + @namespaces = namespaces + end + + def variables=( vars={} ) + Functions::variables = vars + @variables = vars + end + + def parse path, node + path_stack = @parser.parse( path ) + if node.is_a?(Array) + Kernel.warn("REXML::XPath.each, REXML::XPath.first, REXML::XPath.match dropped support for nodeset...", uplevel: 1) + return [] if node.empty? + node = node.first + end + + document = node.document + if document + document.__send__(:enable_cache) do + match( path_stack, node ) + end + else + match( path_stack, node ) + end + end + + def get_first path, node + path_stack = @parser.parse( path ) + first( path_stack, node ) + end + + def predicate path, node + path_stack = @parser.parse( path ) + match( path_stack, node ) + end + + def []=( variable_name, value ) + @variables[ variable_name ] = value + end + + + # Performs a depth-first (document order) XPath search, and returns the + # first match. This is the fastest, lightest way to return a single result. + # + # FIXME: This method is incomplete! + def first( path_stack, node ) + return nil if path.size == 0 + + case path[0] + when :document + # do nothing + first( path[1..-1], node ) + when :child + for c in node.children + r = first( path[1..-1], c ) + return r if r + end + when :qname + name = path[2] + if node.name == name + return node if path.size == 3 + first( path[3..-1], node ) + else + nil + end + when :descendant_or_self + r = first( path[1..-1], node ) + return r if r + for c in node.children + r = first( path, c ) + return r if r + end + when :node + first( path[1..-1], node ) + when :any + first( path[1..-1], node ) + else + nil + end + end + + + def match(path_stack, node) + nodeset = [XPathNode.new(node, position: 1)] + result = expr(path_stack, nodeset) + case result + when Array # nodeset + unnode(result).uniq + else + [result] + end + end + + private + def strict? + @strict + end + + # Returns a String namespace for a node, given a prefix + # The rules are: + # + # 1. Use the supplied namespace mapping first. + # 2. If no mapping was supplied, use the context node to look up the namespace + def get_namespace( node, prefix ) + if @namespaces + @namespaces[prefix] || '' + else + return node.namespace( prefix ) if node.node_type == :element + '' + end + end + + + # Expr takes a stack of path elements and a set of nodes (either a Parent + # or an Array and returns an Array of matching nodes + def expr( path_stack, nodeset, context=nil ) + enter(:expr, path_stack, nodeset) if @debug + return nodeset if path_stack.length == 0 || nodeset.length == 0 + while path_stack.length > 0 + trace(:while, path_stack, nodeset) if @debug + if nodeset.length == 0 + path_stack.clear + return [] + end + op = path_stack.shift + case op + when :document + first_raw_node = nodeset.first.raw_node + nodeset = [XPathNode.new(first_raw_node.root_node, position: 1)] + when :self + nodeset = step(path_stack) do + [nodeset] + end + when :child + nodeset = step(path_stack) do + child(nodeset) + end + when :literal + trace(:literal, path_stack, nodeset) if @debug + return path_stack.shift + when :attribute + nodeset = step(path_stack, any_type: :attribute) do + nodesets = [] + nodeset.each do |node| + raw_node = node.raw_node + next unless raw_node.node_type == :element + attributes = raw_node.attributes + next if attributes.empty? + nodesets << attributes.each_attribute.collect.with_index do |attribute, i| + XPathNode.new(attribute, position: i + 1) + end + end + nodesets + end + when :namespace + pre_defined_namespaces = { + "xml" => "http://www.w3.org/XML/1998/namespace", + } + nodeset = step(path_stack, any_type: :namespace) do + nodesets = [] + nodeset.each do |node| + raw_node = node.raw_node + case raw_node.node_type + when :element + if @namespaces + nodesets << pre_defined_namespaces.merge(@namespaces) + else + nodesets << pre_defined_namespaces.merge(raw_node.namespaces) + end + when :attribute + if @namespaces + nodesets << pre_defined_namespaces.merge(@namespaces) + else + nodesets << pre_defined_namespaces.merge(raw_node.element.namespaces) + end + end + end + nodesets + end + when :parent + nodeset = step(path_stack) do + nodesets = [] + nodeset.each do |node| + raw_node = node.raw_node + if raw_node.node_type == :attribute + parent = raw_node.element + else + parent = raw_node.parent + end + nodesets << [XPathNode.new(parent, position: 1)] if parent + end + nodesets + end + when :ancestor + nodeset = step(path_stack) do + nodesets = [] + # new_nodes = {} + nodeset.each do |node| + raw_node = node.raw_node + new_nodeset = [] + while raw_node.parent + raw_node = raw_node.parent + # next if new_nodes.key?(node) + new_nodeset << XPathNode.new(raw_node, + position: new_nodeset.size + 1) + # new_nodes[node] = true + end + nodesets << new_nodeset unless new_nodeset.empty? + end + nodesets + end + when :ancestor_or_self + nodeset = step(path_stack) do + nodesets = [] + # new_nodes = {} + nodeset.each do |node| + raw_node = node.raw_node + next unless raw_node.node_type == :element + new_nodeset = [XPathNode.new(raw_node, position: 1)] + # new_nodes[node] = true + while raw_node.parent + raw_node = raw_node.parent + # next if new_nodes.key?(node) + new_nodeset << XPathNode.new(raw_node, + position: new_nodeset.size + 1) + # new_nodes[node] = true + end + nodesets << new_nodeset unless new_nodeset.empty? + end + nodesets + end + when :descendant_or_self + nodeset = step(path_stack) do + descendant(nodeset, true) + end + when :descendant + nodeset = step(path_stack) do + descendant(nodeset, false) + end + when :following_sibling + nodeset = step(path_stack) do + nodesets = [] + nodeset.each do |node| + raw_node = node.raw_node + next unless raw_node.respond_to?(:parent) + next if raw_node.parent.nil? + all_siblings = raw_node.parent.children + current_index = all_siblings.index(raw_node) + following_siblings = all_siblings[(current_index + 1)..-1] + next if following_siblings.empty? + nodesets << following_siblings.collect.with_index do |sibling, i| + XPathNode.new(sibling, position: i + 1) + end + end + nodesets + end + when :preceding_sibling + nodeset = step(path_stack, order: :reverse) do + nodesets = [] + nodeset.each do |node| + raw_node = node.raw_node + next unless raw_node.respond_to?(:parent) + next if raw_node.parent.nil? + all_siblings = raw_node.parent.children + current_index = all_siblings.index(raw_node) + preceding_siblings = all_siblings[0, current_index].reverse + next if preceding_siblings.empty? + nodesets << preceding_siblings.collect.with_index do |sibling, i| + XPathNode.new(sibling, position: i + 1) + end + end + nodesets + end + when :preceding + nodeset = step(path_stack, order: :reverse) do + unnode(nodeset) do |node| + preceding(node) + end + end + when :following + nodeset = step(path_stack) do + unnode(nodeset) do |node| + following(node) + end + end + when :variable + var_name = path_stack.shift + return [@variables[var_name]] + + when :eq, :neq, :lt, :lteq, :gt, :gteq + left = expr( path_stack.shift, nodeset.dup, context ) + right = expr( path_stack.shift, nodeset.dup, context ) + res = equality_relational_compare( left, op, right ) + trace(op, left, right, res) if @debug + return res + + when :or + left = expr(path_stack.shift, nodeset.dup, context) + return true if Functions.boolean(left) + right = expr(path_stack.shift, nodeset.dup, context) + return Functions.boolean(right) + + when :and + left = expr(path_stack.shift, nodeset.dup, context) + return false unless Functions.boolean(left) + right = expr(path_stack.shift, nodeset.dup, context) + return Functions.boolean(right) + + when :div, :mod, :mult, :plus, :minus + left = expr(path_stack.shift, nodeset, context) + right = expr(path_stack.shift, nodeset, context) + left = unnode(left) if left.is_a?(Array) + right = unnode(right) if right.is_a?(Array) + left = Functions::number(left) + right = Functions::number(right) + case op + when :div + return left / right + when :mod + return left % right + when :mult + return left * right + when :plus + return left + right + when :minus + return left - right + else + raise "[BUG] Unexpected operator: <#{op.inspect}>" + end + when :union + left = expr( path_stack.shift, nodeset, context ) + right = expr( path_stack.shift, nodeset, context ) + left = unnode(left) if left.is_a?(Array) + right = unnode(right) if right.is_a?(Array) + return (left | right) + when :neg + res = expr( path_stack, nodeset, context ) + res = unnode(res) if res.is_a?(Array) + return -Functions.number(res) + when :not + when :function + func_name = path_stack.shift.tr('-','_') + arguments = path_stack.shift + + if nodeset.size != 1 + message = "[BUG] Node set size must be 1 for function call: " + message += "<#{func_name}>: <#{nodeset.inspect}>: " + message += "<#{arguments.inspect}>" + raise message + end + + node = nodeset.first + if context + target_context = context + else + target_context = {:size => nodeset.size} + if node.is_a?(XPathNode) + target_context[:node] = node.raw_node + target_context[:index] = node.position + else + target_context[:node] = node + target_context[:index] = 1 + end + end + args = arguments.dclone.collect do |arg| + result = expr(arg, nodeset, target_context) + result = unnode(result) if result.is_a?(Array) + result + end + Functions.context = target_context + return Functions.send(func_name, *args) + + else + raise "[BUG] Unexpected path: <#{op.inspect}>: <#{path_stack.inspect}>" + end + end # while + return nodeset + ensure + leave(:expr, path_stack, nodeset) if @debug + end + + def step(path_stack, any_type: :element, order: :forward) + nodesets = yield + begin + enter(:step, path_stack, nodesets) if @debug + nodesets = node_test(path_stack, nodesets, any_type: any_type) + while path_stack[0] == :predicate + path_stack.shift # :predicate + predicate_expression = path_stack.shift.dclone + nodesets = evaluate_predicate(predicate_expression, nodesets) + end + if nodesets.size == 1 + ordered_nodeset = nodesets[0] + else + raw_nodes = [] + nodesets.each do |nodeset| + nodeset.each do |node| + if node.respond_to?(:raw_node) + raw_nodes << node.raw_node + else + raw_nodes << node + end + end + end + ordered_nodeset = sort(raw_nodes, order) + end + new_nodeset = [] + ordered_nodeset.each do |node| + # TODO: Remove duplicated + new_nodeset << XPathNode.new(node, position: new_nodeset.size + 1) + end + new_nodeset + ensure + leave(:step, path_stack, new_nodeset) if @debug + end + end + + def node_test(path_stack, nodesets, any_type: :element) + enter(:node_test, path_stack, nodesets) if @debug + operator = path_stack.shift + case operator + when :qname + prefix = path_stack.shift + name = path_stack.shift + new_nodesets = nodesets.collect do |nodeset| + filter_nodeset(nodeset) do |node| + raw_node = node.raw_node + case raw_node.node_type + when :element + if prefix.nil? + raw_node.name == name + elsif prefix.empty? + if strict? + raw_node.name == name and raw_node.namespace == "" + else + raw_node.name == name and raw_node.namespace == get_namespace(raw_node, prefix) + end + else + raw_node.name == name and raw_node.namespace == get_namespace(raw_node, prefix) + end + when :attribute + if prefix.nil? + raw_node.name == name + elsif prefix.empty? + raw_node.name == name and raw_node.namespace == "" + else + raw_node.name == name and raw_node.namespace == get_namespace(raw_node.element, prefix) + end + else + false + end + end + end + when :namespace + prefix = path_stack.shift + new_nodesets = nodesets.collect do |nodeset| + filter_nodeset(nodeset) do |node| + raw_node = node.raw_node + case raw_node.node_type + when :element + namespaces = @namespaces || raw_node.namespaces + raw_node.namespace == namespaces[prefix] + when :attribute + namespaces = @namespaces || raw_node.element.namespaces + raw_node.namespace == namespaces[prefix] + else + false + end + end + end + when :any + new_nodesets = nodesets.collect do |nodeset| + filter_nodeset(nodeset) do |node| + raw_node = node.raw_node + raw_node.node_type == any_type + end + end + when :comment + new_nodesets = nodesets.collect do |nodeset| + filter_nodeset(nodeset) do |node| + raw_node = node.raw_node + raw_node.node_type == :comment + end + end + when :text + new_nodesets = nodesets.collect do |nodeset| + filter_nodeset(nodeset) do |node| + raw_node = node.raw_node + raw_node.node_type == :text + end + end + when :processing_instruction + target = path_stack.shift + new_nodesets = nodesets.collect do |nodeset| + filter_nodeset(nodeset) do |node| + raw_node = node.raw_node + (raw_node.node_type == :processing_instruction) and + (target.empty? or (raw_node.target == target)) + end + end + when :node + new_nodesets = nodesets.collect do |nodeset| + filter_nodeset(nodeset) do |node| + true + end + end + else + message = "[BUG] Unexpected node test: " + + "<#{operator.inspect}>: <#{path_stack.inspect}>" + raise message + end + new_nodesets + ensure + leave(:node_test, path_stack, new_nodesets) if @debug + end + + def filter_nodeset(nodeset) + new_nodeset = [] + nodeset.each do |node| + next unless yield(node) + new_nodeset << XPathNode.new(node, position: new_nodeset.size + 1) + end + new_nodeset + end + + def evaluate_predicate(expression, nodesets) + enter(:predicate, expression, nodesets) if @debug + new_nodeset_count = 0 + new_nodesets = nodesets.collect do |nodeset| + new_nodeset = [] + subcontext = { :size => nodeset.size } + nodeset.each_with_index do |node, index| + if node.is_a?(XPathNode) + subcontext[:node] = node.raw_node + subcontext[:index] = node.position + else + subcontext[:node] = node + subcontext[:index] = index + 1 + end + result = expr(expression.dclone, [node], subcontext) + trace(:predicate_evaluate, expression, node, subcontext, result) if @debug + result = result[0] if result.kind_of? Array and result.length == 1 + if result.kind_of? Numeric + if result == node.position + new_nodeset_count += 1 + new_nodeset << XPathNode.new(node, position: new_nodeset_count) + end + elsif result.instance_of? Array + if result.size > 0 and result.inject(false) {|k,s| s or k} + if result.size > 0 + new_nodeset_count += 1 + new_nodeset << XPathNode.new(node, position: new_nodeset_count) + end + end + else + if result + new_nodeset_count += 1 + new_nodeset << XPathNode.new(node, position: new_nodeset_count) + end + end + end + new_nodeset + end + new_nodesets + ensure + leave(:predicate, new_nodesets) if @debug + end + + def trace(*args) + indent = " " * @nest + PP.pp(args, "").each_line do |line| + puts("#{indent}#{line}") + end + end + + def enter(tag, *args) + trace(:enter, tag, *args) + @nest += 1 + end + + def leave(tag, *args) + @nest -= 1 + trace(:leave, tag, *args) + end + + # Reorders an array of nodes so that they are in document order + # It tries to do this efficiently. + # + # FIXME: I need to get rid of this, but the issue is that most of the XPath + # interpreter functions as a filter, which means that we lose context going + # in and out of function calls. If I knew what the index of the nodes was, + # I wouldn't have to do this. Maybe add a document IDX for each node? + # Problems with mutable documents. Or, rewrite everything. + def sort(array_of_nodes, order) + new_arry = [] + array_of_nodes.each { |node| + node_idx = [] + np = node.node_type == :attribute ? node.element : node + while np.parent and np.parent.node_type == :element + node_idx << np.parent.index( np ) + np = np.parent + end + new_arry << [ node_idx.reverse, node ] + } + ordered = new_arry.sort_by do |index, node| + if order == :forward + index + else + index.map(&:-@) + end + end + ordered.collect do |_index, node| + node + end + end + + def descendant(nodeset, include_self) + nodesets = [] + nodeset.each do |node| + new_nodeset = [] + new_nodes = {} + descendant_recursive(node.raw_node, new_nodeset, new_nodes, include_self) + nodesets << new_nodeset unless new_nodeset.empty? + end + nodesets + end + + def descendant_recursive(raw_node, new_nodeset, new_nodes, include_self) + if include_self + return if new_nodes.key?(raw_node) + new_nodeset << XPathNode.new(raw_node, position: new_nodeset.size + 1) + new_nodes[raw_node] = true + end + + node_type = raw_node.node_type + if node_type == :element or node_type == :document + raw_node.children.each do |child| + descendant_recursive(child, new_nodeset, new_nodes, true) + end + end + end + + # Builds a nodeset of all of the preceding nodes of the supplied node, + # in reverse document order + # preceding:: includes every element in the document that precedes this node, + # except for ancestors + def preceding(node) + ancestors = [] + parent = node.parent + while parent + ancestors << parent + parent = parent.parent + end + + precedings = [] + preceding_node = preceding_node_of(node) + while preceding_node + if ancestors.include?(preceding_node) + ancestors.delete(preceding_node) + else + precedings << XPathNode.new(preceding_node, + position: precedings.size + 1) + end + preceding_node = preceding_node_of(preceding_node) + end + precedings + end + + def preceding_node_of( node ) + psn = node.previous_sibling_node + if psn.nil? + if node.parent.nil? or node.parent.class == Document + return nil + end + return node.parent + #psn = preceding_node_of( node.parent ) + end + while psn and psn.kind_of? Element and psn.children.size > 0 + psn = psn.children[-1] + end + psn + end + + def following(node) + followings = [] + following_node = next_sibling_node(node) + while following_node + followings << XPathNode.new(following_node, + position: followings.size + 1) + following_node = following_node_of(following_node) + end + followings + end + + def following_node_of( node ) + return node.children[0] if node.kind_of?(Element) and node.children.size > 0 + + next_sibling_node(node) + end + + def next_sibling_node(node) + psn = node.next_sibling_node + while psn.nil? + return nil if node.parent.nil? or node.parent.class == Document + node = node.parent + psn = node.next_sibling_node + end + psn + end + + def child(nodeset) + nodesets = [] + nodeset.each do |node| + raw_node = node.raw_node + node_type = raw_node.node_type + # trace(:child, node_type, node) + case node_type + when :element + nodesets << raw_node.children.collect.with_index do |child_node, i| + XPathNode.new(child_node, position: i + 1) + end + when :document + new_nodeset = [] + raw_node.children.each do |child| + case child + when XMLDecl, Text + # Ignore + else + new_nodeset << XPathNode.new(child, position: new_nodeset.size + 1) + end + end + nodesets << new_nodeset unless new_nodeset.empty? + end + end + nodesets + end + + def norm b + case b + when true, false + b + when 'true', 'false' + Functions::boolean( b ) + when /^\d+(\.\d+)?$/, Numeric + Functions::number( b ) + else + Functions::string( b ) + end + end + + def equality_relational_compare(set1, op, set2) + set1 = unnode(set1) if set1.is_a?(Array) + set2 = unnode(set2) if set2.is_a?(Array) + + if set1.kind_of? Array and set2.kind_of? Array + # If both objects to be compared are node-sets, then the + # comparison will be true if and only if there is a node in the + # first node-set and a node in the second node-set such that the + # result of performing the comparison on the string-values of + # the two nodes is true. + set1.product(set2).any? do |node1, node2| + node_string1 = Functions.string(node1) + node_string2 = Functions.string(node2) + compare(node_string1, op, node_string2) + end + elsif set1.kind_of? Array or set2.kind_of? Array + # If one is nodeset and other is number, compare number to each item + # in nodeset s.t. number op number(string(item)) + # If one is nodeset and other is string, compare string to each item + # in nodeset s.t. string op string(item) + # If one is nodeset and other is boolean, compare boolean to each item + # in nodeset s.t. boolean op boolean(item) + if set1.kind_of? Array + a = set1 + b = set2 + else + a = set2 + b = set1 + end + + case b + when true, false + each_unnode(a).any? do |unnoded| + compare(Functions.boolean(unnoded), op, b) + end + when Numeric + each_unnode(a).any? do |unnoded| + compare(Functions.number(unnoded), op, b) + end + when /\A\d+(\.\d+)?\z/ + b = Functions.number(b) + each_unnode(a).any? do |unnoded| + compare(Functions.number(unnoded), op, b) + end + else + b = Functions::string(b) + each_unnode(a).any? do |unnoded| + compare(Functions::string(unnoded), op, b) + end + end + else + # If neither is nodeset, + # If op is = or != + # If either boolean, convert to boolean + # If either number, convert to number + # Else, convert to string + # Else + # Convert both to numbers and compare + compare(set1, op, set2) + end + end + + def value_type(value) + case value + when true, false + :boolean + when Numeric + :number + when String + :string + else + raise "[BUG] Unexpected value type: <#{value.inspect}>" + end + end + + def normalize_compare_values(a, operator, b) + a_type = value_type(a) + b_type = value_type(b) + case operator + when :eq, :neq + if a_type == :boolean or b_type == :boolean + a = Functions.boolean(a) unless a_type == :boolean + b = Functions.boolean(b) unless b_type == :boolean + elsif a_type == :number or b_type == :number + a = Functions.number(a) unless a_type == :number + b = Functions.number(b) unless b_type == :number + else + a = Functions.string(a) unless a_type == :string + b = Functions.string(b) unless b_type == :string + end + when :lt, :lteq, :gt, :gteq + a = Functions.number(a) unless a_type == :number + b = Functions.number(b) unless b_type == :number + else + message = "[BUG] Unexpected compare operator: " + + "<#{operator.inspect}>: <#{a.inspect}>: <#{b.inspect}>" + raise message + end + [a, b] + end + + def compare(a, operator, b) + a, b = normalize_compare_values(a, operator, b) + case operator + when :eq + a == b + when :neq + a != b + when :lt + a < b + when :lteq + a <= b + when :gt + a > b + when :gteq + a >= b + else + message = "[BUG] Unexpected compare operator: " + + "<#{operator.inspect}>: <#{a.inspect}>: <#{b.inspect}>" + raise message + end + end + + def each_unnode(nodeset) + return to_enum(__method__, nodeset) unless block_given? + nodeset.each do |node| + if node.is_a?(XPathNode) + unnoded = node.raw_node + else + unnoded = node + end + yield(unnoded) + end + end + + def unnode(nodeset) + each_unnode(nodeset).collect do |unnoded| + unnoded = yield(unnoded) if block_given? + unnoded + end + end + end + + # @private + class XPathNode + attr_reader :raw_node, :context + def initialize(node, context=nil) + if node.is_a?(XPathNode) + @raw_node = node.raw_node + else + @raw_node = node + end + @context = context || {} + end + + def position + @context[:position] + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/ruby2_keywords-0.0.5/ChangeLog b/vendor/bundle/ruby/3.3.0/gems/ruby2_keywords-0.0.5/ChangeLog new file mode 100644 index 0000000..848806c --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/ruby2_keywords-0.0.5/ChangeLog @@ -0,0 +1,214 @@ +-*- coding: utf-8 -*- + +commit 92ad9c5c3fff591b8383ada8b93c3da1279d24ad + Author: Benoit Daloze <eregontp@gmail.com> + AuthorDate: 2021-01-19 16:15:55 +0100 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2021-02-13 09:02:24 +0900 + + Add TruffleRuby in CI + +commit 07d7fa17e4c61102597280bd31a6b5972d8e5588 + Author: Nobuyoshi Nakada <nobu@ruby-lang.org> + AuthorDate: 2021-02-11 17:23:30 +0900 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2021-02-11 21:51:58 +0900 + + bundle-install only on ruby 2.1 + +commit 5f993b84a469cdc1995077dc0d8391928bb7ac1a + Author: Nobuyoshi Nakada <nobu@ruby-lang.org> + AuthorDate: 2021-02-11 12:18:26 +0900 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2021-02-11 21:39:44 +0900 + + Split Rakefile into rakelib + +commit 8e4d9a8de92e9f1f3690fbc224aac1e0d102c36e + Author: Nobuyoshi Nakada <nobu@ruby-lang.org> + AuthorDate: 2021-02-11 21:38:24 +0900 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2021-02-11 21:38:24 +0900 + + Ignore lock file and generated logs + +commit 03b864c09e657c130a66c7ab68d962a31df3b819 + Author: Nobuyoshi Nakada <nobu@ruby-lang.org> + AuthorDate: 2021-02-10 21:37:20 +0900 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2021-02-10 21:44:29 +0900 + + Do not use gemspec for gem dependecy + +commit 636c350c0a10ec75a9b01dd4db983abe6310136f + Author: Nobuyoshi Nakada <nobu@ruby-lang.org> + AuthorDate: 2021-02-10 12:26:30 +0900 + Commit: GitHub <noreply@github.com> + CommitDate: 2021-02-10 12:26:30 +0900 + + Reduced tests + + Target only the currently maintained versions and the oldest available version, omitting the in-betweens. + +commit 97b4de75c83c927eca773e689ecb49557a972024 + Author: Ivo Anjo <ivo.anjo@datadoghq.com> + AuthorDate: 2021-02-04 11:58:41 +0000 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2021-02-10 11:54:45 +0900 + + Add docker-compose.yml for easy testing of older rubies + +commit 6974495d294cd59b8c0dba78a26b391f25154050 + Author: Ivo Anjo <ivo.anjo@datadoghq.com> + AuthorDate: 2021-02-04 11:39:26 +0000 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2021-02-10 11:54:45 +0900 + + Explicitly declare support for Ruby >= 2.0.0 + + This can be used to clarify support, as well as in the future to drop + support for rubies, if so desired. + +commit 64aad913e16d7e6008aa6ca06cf3f1b6fa864c4a + Author: Nobuyoshi Nakada <nobu@ruby-lang.org> + AuthorDate: 2021-02-10 00:42:59 +0900 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2021-02-10 00:42:59 +0900 + + Separated install from test + +commit 74cb318db44a3851f724ac72624f1509bbf1bdd4 + Author: Ivo Anjo <ivo.anjo@datadoghq.com> + AuthorDate: 2021-02-04 12:09:11 +0000 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2021-02-10 00:39:31 +0900 + + Add older Rubies to CI as well + +commit 098295f4e9510a751097a6fc0e76c278ae9a1ff0 + Author: Ivo Anjo <ivo.anjo@datadoghq.com> + AuthorDate: 2021-02-04 11:20:19 +0000 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2021-02-10 00:25:20 +0900 + + Avoid built-in old `test-unit` + + In Ruby <= 2.1, `test-unit` was shipped with Ruby itself (unbundling + was done for 2.2 -- see <https://bugs.ruby-lang.org/issues/9711>). + + The `test-unit` version shipped with 2.1 breaks some of the tests. + To fix this, I've added the minimum needed version explicitly to the + `gemspec`, as well as added a `gems.rb` for allowing the use of + `bundler` to run the tests and ensure the correct `test-unit` is used. + +commit 1773502b1c445ae0ca1c31960a1b64b2f040f8c1 + Author: Ivo Anjo <ivo.anjo@datadoghq.com> + AuthorDate: 2021-02-04 10:43:18 +0000 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2021-02-10 00:22:22 +0900 + + Avoid using `Binding#receiver` + + This feature is only available on Ruby 2.2+ and breaks older rubies. + + See <https://docs.ruby-lang.org/en/2.2.0/NEWS.html> for more details. + +commit 0784ef08e280a5eb3c08fd9198b381af0ec027f6 + Author: Nobuyoshi Nakada <nobu@ruby-lang.org> + AuthorDate: 2021-02-09 23:46:24 +0900 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2021-02-10 00:22:22 +0900 + + Strip the source directory from globbed paths + +commit 7f5f4f8cd9c605741bec1cdabece0dd7e53afd9a + Author: Ivo Anjo <ivo.anjo@datadoghq.com> + AuthorDate: 2021-02-04 10:15:27 +0000 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2021-02-10 00:22:22 +0900 + + Avoid using `base:` option for `Dir.glob` + + This option is only available on Ruby 2.5+ and breaks older rubies. + + See <https://rubyreferences.github.io/rubychanges/2.5.html#dirglob-base-argument> + for more details. + +commit f40159f5a66fff7bed873d68e06439ec960bc3f9 + Author: Ivo Anjo <ivo.anjo@datadoghq.com> + AuthorDate: 2021-02-04 10:35:42 +0000 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2021-02-10 00:22:21 +0900 + + Avoid using numbered block parameters + + This feature is only available on Ruby 2.7+ and breaks older rubies. + + See <https://rubyreferences.github.io/rubychanges/2.7.html#numbered-block-parameters> + for more details. + +commit c898163464e896d63698f19a49bc0ab8cc593081 + Author: Nobuyoshi Nakada <nobu@ruby-lang.org> + AuthorDate: 2021-02-09 23:50:56 +0900 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2021-02-10 00:21:21 +0900 + + Revert "Add TruffleRuby in CI" + + This reverts commit 294d9e79171b1b954f223f08acc6144f0fc6efd4. + +commit 88867dc48b9f0ec139cd349af40ae9dbea677b93 + Author: Nobuyoshi Nakada <nobu@ruby-lang.org> + AuthorDate: 2021-02-09 23:37:17 +0900 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2021-02-09 23:37:17 +0900 + + Moved the mandatory argument first + +commit 294d9e79171b1b954f223f08acc6144f0fc6efd4 + Author: Benoit Daloze <eregontp@gmail.com> + AuthorDate: 2021-01-19 16:15:55 +0100 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2021-02-09 23:09:57 +0900 + + Add TruffleRuby in CI + +commit 2f7e9000b4a64240616b1cbfbcff5e9174fdf6b1 + Author: Nobuyoshi Nakada <nobu@ruby-lang.org> + AuthorDate: 2021-01-20 13:19:12 +0900 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2021-01-24 20:01:28 +0900 + + Include ChangeLogs for old versions + +commit 4c54e01675202ad0a69bbd39a790290b9870e125 + Author: Nobuyoshi Nakada <nobu@ruby-lang.org> + AuthorDate: 2021-01-20 10:52:47 +0900 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2021-01-24 19:58:45 +0900 + + Added ChangeLog rule + +commit 9e5b2a4ba56d61a2b59f9db52c98155c0c449152 + Author: Nobuyoshi Nakada <nobu@ruby-lang.org> + AuthorDate: 2021-01-20 10:24:47 +0900 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2021-01-20 10:54:31 +0900 + + Added extra_rdoc_files to make README.md the main page + +commit 75927b417a79377770cddfe219b34aa87280a5e7 + Author: Nobuyoshi Nakada <nobu@ruby-lang.org> + AuthorDate: 2021-01-20 10:21:52 +0900 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2021-01-20 10:54:25 +0900 + + Separate tagging from version bump + +commit c353a3fffc323982d829275c82ae09fdbad94816 + Author: Nobuyoshi Nakada <nobu@ruby-lang.org> + AuthorDate: 2021-01-20 10:20:25 +0900 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2021-01-20 10:20:45 +0900 + + bump up to 0.0.5 diff --git a/vendor/bundle/ruby/3.3.0/gems/ruby2_keywords-0.0.5/LICENSE b/vendor/bundle/ruby/3.3.0/gems/ruby2_keywords-0.0.5/LICENSE new file mode 100644 index 0000000..3b9d383 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/ruby2_keywords-0.0.5/LICENSE @@ -0,0 +1,22 @@ +Copyright 2019-2020 Nobuyoshi Nakada, Yusuke Endoh + +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. diff --git a/vendor/bundle/ruby/3.3.0/gems/ruby2_keywords-0.0.5/README.md b/vendor/bundle/ruby/3.3.0/gems/ruby2_keywords-0.0.5/README.md new file mode 100644 index 0000000..42e1157 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/ruby2_keywords-0.0.5/README.md @@ -0,0 +1,75 @@ +# ruby2_keywords + +Provides empty `Module#ruby2_keywords` method, for the forward +source-level compatibility against ruby2.7 and ruby3. + +## Installation + +Add this line to your application's Gemfile: + +```ruby +gem 'ruby2_keywords' +``` + +And then execute: + + $ bundle + +Or install it yourself as: + + $ gem install ruby2_keywords + +## Usage + +For class/module instance methods: + +```ruby +require 'ruby2_keywords' + +module YourModule + ruby2_keywords def delegating_method(*args) + other_method(*args) + end +end +``` + +For global methods: + +```ruby +require 'ruby2_keywords' + +ruby2_keywords def oldstyle_keywords(options = {}) +end +``` + +You can do the same for a method defined by `Module#define_method`: + +```ruby +define_method :delegating_method do |*args, &block| + other_method(*args, &block) +end +ruby2_keywords :delegating_method +``` + +## Contributing + +Bug reports and pull requests are welcome on [GitHub] or +[Ruby Issue Tracking System]. + +## Development + +After checking out the repo, run `bundle install` to install dependencies. +Then, run `bundle exec rake test` to run the tests. + +To test on older Ruby versions, you can use docker. E.g. to test on Ruby 2.0, +use `docker-compose run ruby-2.0`. + +## License + +The gem is available as open source under the terms of the +[Ruby License] or the [2-Clause BSD License]. + +[GitHub]: https://github.com/ruby/ruby2_keywords/ +[Ruby Issue Tracking System]: https://bugs.ruby-lang.org +[Ruby License]: https://www.ruby-lang.org/en/about/license.txt +[2-Clause BSD License]: https://opensource.org/licenses/BSD-2-Clause diff --git a/vendor/bundle/ruby/3.3.0/gems/ruby2_keywords-0.0.5/lib/ruby2_keywords.rb b/vendor/bundle/ruby/3.3.0/gems/ruby2_keywords-0.0.5/lib/ruby2_keywords.rb new file mode 100644 index 0000000..09827b5 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/ruby2_keywords-0.0.5/lib/ruby2_keywords.rb @@ -0,0 +1,57 @@ +class Module + unless private_method_defined?(:ruby2_keywords) + private + # call-seq: + # ruby2_keywords(method_name, ...) + # + # Does nothing. + def ruby2_keywords(name, *) + # nil + end + end +end + +main = TOPLEVEL_BINDING.eval('self') +unless main.respond_to?(:ruby2_keywords, true) + # call-seq: + # ruby2_keywords(method_name, ...) + # + # Does nothing. + def main.ruby2_keywords(name, *) + # nil + end +end + +class Proc + unless method_defined?(:ruby2_keywords) + # call-seq: + # proc.ruby2_keywords -> proc + # + # Does nothing and just returns the receiver. + def ruby2_keywords + self + end + end +end + +class << Hash + unless method_defined?(:ruby2_keywords_hash?) + # call-seq: + # Hash.ruby2_keywords_hash?(hash) -> false + # + # Returns false. + def ruby2_keywords_hash?(hash) + false + end + end + + unless method_defined?(:ruby2_keywords_hash) + # call-seq: + # Hash.ruby2_keywords_hash(hash) -> new_hash + # + # Duplicates a given hash and returns the new hash. + def ruby2_keywords_hash(hash) + hash.dup + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/ruby2_keywords-0.0.5/logs/ChangeLog-0.0.0 b/vendor/bundle/ruby/3.3.0/gems/ruby2_keywords-0.0.5/logs/ChangeLog-0.0.0 new file mode 100644 index 0000000..2da8f55 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/ruby2_keywords-0.0.5/logs/ChangeLog-0.0.0 @@ -0,0 +1,25 @@ +-*- coding: utf-8 -*- + +commit 33787f35f09e26f4c1ca716fafc81144d5d21333 + Author: Nobuyoshi Nakada <nobu@ruby-lang.org> + AuthorDate: 2019-10-22 23:57:24 +0900 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2019-10-22 23:57:24 +0900 + + Added readme to files in gemspec + +commit 53b25f66cffa09e2c2b6730fd49241bb359f33db + Author: Nobuyoshi Nakada <nobu@ruby-lang.org> + AuthorDate: 2019-10-22 23:55:26 +0900 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2019-10-22 23:55:26 +0900 + + authors may not be empty + +commit 6e6756bfa47bcf15ecc10ce07237886339edc415 + Author: Nobuyoshi Nakada <nobu@ruby-lang.org> + AuthorDate: 2019-10-17 00:18:01 +0900 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2019-10-17 00:18:01 +0900 + + Initial version diff --git a/vendor/bundle/ruby/3.3.0/gems/ruby2_keywords-0.0.5/logs/ChangeLog-0.0.1 b/vendor/bundle/ruby/3.3.0/gems/ruby2_keywords-0.0.5/logs/ChangeLog-0.0.1 new file mode 100644 index 0000000..3cc954f --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/ruby2_keywords-0.0.5/logs/ChangeLog-0.0.1 @@ -0,0 +1,9 @@ +-*- coding: utf-8 -*- + +commit b879d6e1d72651af6317e67eaa129d5c9be62e40 + Author: Nobuyoshi Nakada <nobu@ruby-lang.org> + AuthorDate: 2019-12-02 08:08:31 +0900 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2019-12-02 08:09:04 +0900 + + Added the toplevel method diff --git a/vendor/bundle/ruby/3.3.0/gems/ruby2_keywords-0.0.5/logs/ChangeLog-0.0.2 b/vendor/bundle/ruby/3.3.0/gems/ruby2_keywords-0.0.5/logs/ChangeLog-0.0.2 new file mode 100644 index 0000000..fc9fb81 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/ruby2_keywords-0.0.5/logs/ChangeLog-0.0.2 @@ -0,0 +1,55 @@ +-*- coding: utf-8 -*- + +commit a198860c7ceba43ccee428c20bdd082f2bdaba6e + Author: Nobuyoshi Nakada <nobu@ruby-lang.org> + AuthorDate: 2020-01-08 15:51:35 +0900 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2020-01-08 15:51:35 +0900 + + Achieve version numbers from tags + +commit 07e126eea667923b2d5f4a7584687cb1decd3a56 + Author: Yusuke Endoh <mame@ruby-lang.org> + AuthorDate: 2020-01-06 15:27:08 +0900 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2020-01-08 15:38:19 +0900 + + Add a guard for Proc#ruby2_keywords + +commit ff392be2fbea77872d801ed0051c2f166dd6eee9 + Author: Yusuke Endoh <mame@ruby-lang.org> + AuthorDate: 2020-01-03 23:51:21 +0900 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2020-01-08 15:38:19 +0900 + + Add a shim for Proc#ruby2_keywords + +commit d5d8c0c8f45102c512bb8015988116c5110b28db + Author: Nobuyoshi Nakada <nobu@ruby-lang.org> + AuthorDate: 2020-01-03 10:26:25 +0900 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2020-01-08 15:36:44 +0900 + + Check Module#ruby2_keywords arity + + It is considered a mistake, because calling this method with no + arguments has no effect. + +commit 9cf7c9791857db17afb235230059d6cbc2408e9e + Author: Jeremy Evans <code@jeremyevans.net> + AuthorDate: 2019-11-10 12:04:28 -0800 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2019-12-04 16:23:33 +0900 + + Fix usage example in README + + The examle warns in Ruby 2.7, and it isn't a case where you would + want to use ruby2_keywords. + +commit dcc6958efdf25045dce149bf4d0a327e8878c9dd + Author: Yusuke Endoh <mame@ruby-lang.org> + AuthorDate: 2019-12-03 18:08:39 +0900 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2019-12-03 18:54:26 +0900 + + Update homepage to the github repository diff --git a/vendor/bundle/ruby/3.3.0/gems/ruby2_keywords-0.0.5/logs/ChangeLog-0.0.3 b/vendor/bundle/ruby/3.3.0/gems/ruby2_keywords-0.0.5/logs/ChangeLog-0.0.3 new file mode 100644 index 0000000..70ff5b6 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/ruby2_keywords-0.0.5/logs/ChangeLog-0.0.3 @@ -0,0 +1,124 @@ +-*- coding: utf-8 -*- + +commit 396cc7991604632bc686e3c363504db42337cca3 + Author: Nobuyoshi Nakada <nobu@ruby-lang.org> + AuthorDate: 2021-01-19 20:57:52 +0900 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2021-01-19 21:10:28 +0900 + + Added tests + +commit aa06490df9efa905ef17c143e96edee547c4ffad + Author: Nobuyoshi Nakada <nobu@ruby-lang.org> + AuthorDate: 2021-01-19 20:20:31 +0900 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2021-01-19 20:20:31 +0900 + + Fixed RDoc location + +commit 9603fec096b257d382776c09ab1f5fe88d289307 + Author: Nobuyoshi Nakada <nobu@ruby-lang.org> + AuthorDate: 2021-01-19 20:19:09 +0900 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2021-01-19 20:19:09 +0900 + + Make README.md the main page + +commit 5093cd212b44d1fbd8ef1c6b3f2bfa8f3427de16 + Author: Nobuyoshi Nakada <nobu@ruby-lang.org> + AuthorDate: 2021-01-19 19:21:06 +0900 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2021-01-19 19:21:06 +0900 + + Added least documents + +commit 52b8acf6a89de00f44c8854f0e30c2be4a3d7cb3 + Author: Nobuyoshi Nakada <nobu@ruby-lang.org> + AuthorDate: 2021-01-19 19:19:59 +0900 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2021-01-19 19:19:59 +0900 + + Define Hash.ruby2_keywords_hash singleton method + +commit 51c47c060d9678ae2c28bcf415bc87346cba1860 + Author: Nobuyoshi Nakada <nobu@ruby-lang.org> + AuthorDate: 2021-01-19 19:19:09 +0900 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2021-01-19 19:19:09 +0900 + + Define Hash.ruby2_keywords_hash? singleton method + +commit 2ee450c041cb1a3b15580c3963b778b33926503c + Author: Nobuyoshi Nakada <nobu@ruby-lang.org> + AuthorDate: 2021-01-19 18:53:19 +0900 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2021-01-19 18:53:19 +0900 + + Package LICENSE file + + The source gemspec file is useless after building the gem file. + +commit a841a82a1ff485ab6dd5759f6f31dff17de45b65 + Author: Nobuyoshi Nakada <nobu@ruby-lang.org> + AuthorDate: 2021-01-19 14:41:53 +0900 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2021-01-19 14:51:06 +0900 + + README: fix Contributing and License + +commit cbecd4307612f6794962a701cb16ac620872c1f9 + Author: Nobuyoshi Nakada <nobu@ruby-lang.org> + AuthorDate: 2021-01-19 12:13:21 +0900 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2021-01-19 12:13:21 +0900 + + Added version guard against the default gem + +commit 52c15f0e55dfdcb8204e92c85a4dd5d524549533 + Author: Yusuke Endoh <mame@ruby-lang.org> + AuthorDate: 2021-01-07 17:39:52 +0900 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2021-01-07 19:56:19 +0900 + + Use private_method_defined? instead of respond_to? + + `Module.respond_to?(:ruby2_keywords, true)` does NOT check if + `Module#ruby2_keywords` is available. It worked well because there is + toplevel `ruby2_keywords` method, but using `private_method_defined?` is + better, I think. + + Also, this fixes a syntactic error. + +commit 23981c5296aec6c5dbe104b8adc7ca0e85cb4313 + Author: Yusuke Endoh <mame@ruby-lang.org> + AuthorDate: 2020-12-28 14:07:40 +0900 + Commit: GitHub <noreply@github.com> + CommitDate: 2020-12-28 14:07:40 +0900 + + Add an example for Module#define_method (#7) + +commit 92e74341dffc9a41d7671ea82709ba2e091ef4e8 + Author: Nobuyoshi Nakada <nobu@ruby-lang.org> + AuthorDate: 2020-12-27 17:43:35 +0900 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2020-12-27 17:43:35 +0900 + + Added BSD-2-Clause to the licenses of the gemspec + +commit 46ed72d40db163f9edbddbe6e5706794484ac5bb + Author: Antonio Terceiro <asa@terceiro.xyz> + AuthorDate: 2020-04-03 14:50:29 -0300 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2020-12-27 17:06:49 +0900 + + Add explicit license file + + Fixes #4 + +commit 53833c0f660239eeb572dd33d4a1fac503c4834a + Author: Nobuyoshi Nakada <nobu@ruby-lang.org> + AuthorDate: 2020-12-27 17:05:37 +0900 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2020-12-27 17:05:37 +0900 + + Support Hash.ruby2_keywords_hash? diff --git a/vendor/bundle/ruby/3.3.0/gems/ruby2_keywords-0.0.5/logs/ChangeLog-0.0.4 b/vendor/bundle/ruby/3.3.0/gems/ruby2_keywords-0.0.5/logs/ChangeLog-0.0.4 new file mode 100644 index 0000000..ba9b591 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/ruby2_keywords-0.0.5/logs/ChangeLog-0.0.4 @@ -0,0 +1,53 @@ +-*- coding: utf-8 -*- + +commit 31766f4327e6e4555543b44fc6a5dc252c8ff6d9 + Author: Nobuyoshi Nakada <nobu@ruby-lang.org> + AuthorDate: 2021-01-19 23:49:55 +0900 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2021-01-19 23:49:55 +0900 + + bump up to 0.0.4 + +commit 8bf4b5b4169545ef5be46dec8cd6502d902a3e4a + Author: Nobuyoshi Nakada <nobu@ruby-lang.org> + AuthorDate: 2021-01-19 23:49:51 +0900 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2021-01-19 23:49:51 +0900 + + Added bump target + +commit fba8eb45d6b2db2d0f829b0d20300e7d19268146 + Author: Nobuyoshi Nakada <nobu@ruby-lang.org> + AuthorDate: 2021-01-19 23:29:46 +0900 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2021-01-19 23:35:44 +0900 + + Build package + +commit 403ff84d12c9fe1f34397b3a164b0b2f73a560d1 + Author: Nobuyoshi Nakada <nobu@ruby-lang.org> + AuthorDate: 2021-01-19 23:25:17 +0900 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2021-01-19 23:33:35 +0900 + + Set SOURCE_DATE_EPOCH to make builds reproducible + +commit 956156ba793330928280c5301b093300a1a9f792 + Author: Nazar Matus <funkyloverone@gmail.com> + AuthorDate: 2021-01-19 16:07:37 +0200 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2021-01-19 23:33:11 +0900 + + Add Ruby 2.5 to the CI matrix + +commit d6d1775d793bcaf206af700120b0b4bd2dc3842d + Author: Nazar Matus <funkyloverone@gmail.com> + AuthorDate: 2021-01-19 15:47:38 +0200 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2021-01-19 23:33:11 +0900 + + Fix Ruby 2.5 incopatibility + + We don't really need that second optional argument, + as its default value is just what we need + https://ruby-doc.org/core-2.7.2/Module.html#method-i-private_method_defined-3F diff --git a/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/Gemfile b/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/Gemfile new file mode 100644 index 0000000..a111014 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/Gemfile @@ -0,0 +1,13 @@ +source "https://rubygems.org" + +gemspec + +gem "rake" + +group :test do + gem "minitest" +end + +install_if -> { ENV["FARADAY_VERSION"] } do + gem "faraday", ENV["FARADAY_VERSION"] +end diff --git a/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/LICENSE.md b/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/LICENSE.md new file mode 100644 index 0000000..f07bf4e --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/LICENSE.md @@ -0,0 +1,20 @@ +Copyright (c) 2011 rick olson + +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/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/README.md b/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/README.md new file mode 100644 index 0000000..139fcaf --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/README.md @@ -0,0 +1,62 @@ +# Sawyer + +Sawyer is an experimental hypermedia agent for Ruby built on top of [Faraday][faraday]. + +[faraday]: https://github.com/lostisland/faraday + +## Installation + +Add this line to your application's Gemfile: + +```ruby +gem 'sawyer' +``` + +And then execute: + +```sh +bundle +``` + +Or install it yourself as: + +```sh +gem install sawyer +``` + +## Usage + +```ruby +require "sawyer" + +# Create a Sawyer agent +agent = Sawyer::Agent.new("https://api.github.com", + links_parser: Sawyer::LinkParsers::Simple.new) + +# Fetch the root of the API +root = agent.root.data + +# Access a resource directly +contributors = agent.call(:get, "repos/lostisland/sawyer/contributors").data + +# Load a hypermedia relation +top_contributor = contributors.first +followers = top_contributor.rels[:followers].get.data +``` + +For more information, check out the [documentation](http://www.rubydoc.info/gems/sawyer/). + +## Development + +After checking out the repo, run `script/test` to bootstrap the project and run the tests. +You can also run `script/console` for an interactive prompt that will allow you to experiment. + +To package the gem, run `script/package`. To release a new version, update the version number in [`lib/sawyer.rb`](lib/sawyer.rb), and then run `script/release`, which will create a git tag for the version, push git commits and tags, and push the .gem file to [rubygems.org](https://rubygems.org). + +## Contributing + +Check out the [contributing guide](CONTRIBUTING.md) for more information on contributing. + +## License + +The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT). diff --git a/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/Rakefile b/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/Rakefile new file mode 100644 index 0000000..2f01626 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/Rakefile @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +require 'bundler' +Bundler::GemHelper.install_tasks + +task :default => :test + +require 'rake/testtask' +Rake::TestTask.new(:test) do |test| + test.libs << 'lib' << 'test' + test.pattern = 'test/**/*_test.rb' + test.verbose = true +end + diff --git a/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/lib/sawyer.rb b/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/lib/sawyer.rb new file mode 100644 index 0000000..4b531b7 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/lib/sawyer.rb @@ -0,0 +1,17 @@ +module Sawyer + VERSION = "0.9.3" + + class Error < StandardError; end +end + +require 'set' + +%w( + resource + relation + response + serializer + agent + link_parsers/hal + link_parsers/simple +).each { |f| require File.expand_path("../sawyer/#{f}", __FILE__) } diff --git a/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/lib/sawyer/agent.rb b/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/lib/sawyer/agent.rb new file mode 100644 index 0000000..61acac9 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/lib/sawyer/agent.rb @@ -0,0 +1,163 @@ +require 'faraday' +require 'addressable/template' + +module Sawyer + class Agent + NO_BODY = Set.new([:get, :head]) + + attr_accessor :links_parser + attr_accessor :allow_undefined_methods + + class << self + attr_writer :serializer + end + + def self.serializer + @serializer ||= Serializer.any_json + end + + def self.encode(data) + serializer.encode(data) + end + + def self.decode(data) + serializer.decode(data) + end + + # Agents handle making the requests, and passing responses to + # Sawyer::Response. + # + # endpoint - String URI of the API entry point. + # options - Hash of options. + # :allow_undefined_methods - Allow relations to call all the HTTP verbs, + # not just the ones defined. + # :faraday - Optional Faraday::Connection to use. + # :links_parser - Optional parser to parse link relations + # Defaults: Sawyer::LinkParsers::Hal.new + # :serializer - Optional serializer Class. Defaults to + # self.serializer_class. + # + # Yields the Faraday::Connection if a block is given. + def initialize(endpoint, options = nil) + @endpoint = endpoint + @conn = (options && options[:faraday]) || Faraday.new + @serializer = (options && options[:serializer]) || self.class.serializer + @links_parser = (options && options[:links_parser]) || Sawyer::LinkParsers::Hal.new + @allow_undefined_methods = (options && options[:allow_undefined_methods]) + @conn.url_prefix = @endpoint + yield @conn if block_given? + end + + # Public: Close the underlying connection. + def close + @conn.close if @conn.respond_to?(:close) + end + + # Public: Retains a reference to the root relations of the API. + # + # Returns a Sawyer::Relation::Map. + def rels + @rels ||= root.data._rels + end + + # Public: Retains a reference to the root response of the API. + # + # Returns a Sawyer::Response. + def root + @root ||= start + end + + # Public: Hits the root of the API to get the initial actions. + # + # Returns a Sawyer::Response. + def start + call :get, @endpoint + end + + # Makes a request through Faraday. + # + # method - The Symbol name of an HTTP method. + # url - The String URL to access. This can be relative to the Agent's + # endpoint. + # data - The Optional Hash or Resource body to be sent. :get or :head + # requests can have no body, so this can be the options Hash + # instead. + # options - Hash of option to configure the API request. + # :headers - Hash of API headers to set. + # :query - Hash of URL query params to set. + # + # Returns a Sawyer::Response. + def call(method, url, data = nil, options = nil) + if NO_BODY.include?(method) + options ||= data + data = nil + end + + options ||= {} + url = expand_url(url, options[:uri]) + started = nil + res = @conn.send method, url do |req| + if data + req.body = data.is_a?(String) ? data : encode_body(data) + end + if params = options[:query] + req.params.update params + end + if headers = options[:headers] + req.headers.update headers + end + started = Time.now + end + + Response.new self, res, :sawyer_started => started, :sawyer_ended => Time.now + end + + # Encodes an object to a string for the API request. + # + # data - The Hash or Resource that is being sent. + # + # Returns a String. + def encode_body(data) + @serializer.encode(data) + end + + # Decodes a String response body to a resource. + # + # str - The String body from the response. + # + # Returns an Object resource (Hash by default). + def decode_body(str) + @serializer.decode(str) + end + + def parse_links(data) + @links_parser.parse(data) + end + + def expand_url(url, options = nil) + tpl = url.respond_to?(:expand) ? url : Addressable::Template.new(url.to_s) + tpl.expand(options || {}).to_s + end + + def allow_undefined_methods? + !!@allow_undefined_methods + end + + def inspect + %(<#{self.class} #{@endpoint}>) + end + + # private + def to_yaml_properties + [:@endpoint] + end + + def marshal_dump + [@endpoint] + end + + def marshal_load(dumped) + @endpoint = *dumped.shift(1) + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/lib/sawyer/link_parsers/hal.rb b/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/lib/sawyer/link_parsers/hal.rb new file mode 100644 index 0000000..219e673 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/lib/sawyer/link_parsers/hal.rb @@ -0,0 +1,15 @@ +module Sawyer + module LinkParsers + + class Hal + + def parse(data) + links = data.delete(:_links) + + return data, links + end + + end + + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/lib/sawyer/link_parsers/simple.rb b/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/lib/sawyer/link_parsers/simple.rb new file mode 100644 index 0000000..4648128 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/lib/sawyer/link_parsers/simple.rb @@ -0,0 +1,29 @@ +module Sawyer + module LinkParsers + + class Simple + + LINK_REGEX = /_?url$/ + + + # Public: Parses simple *_url style links on resources + # + # data - Hash of resource data + # + # Returns a Hash of data with separate links Hash + def parse(data) + + links = {} + inline_links = data.keys.select {|k| k.to_s[LINK_REGEX] } + inline_links.each do |key| + rel_name = key.to_s == 'url' ? 'self' : key.to_s.gsub(LINK_REGEX, '') + links[rel_name.to_sym] = data[key] + end + + return data, links + end + + end + + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/lib/sawyer/relation.rb b/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/lib/sawyer/relation.rb new file mode 100644 index 0000000..e33e879 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/lib/sawyer/relation.rb @@ -0,0 +1,272 @@ +module Sawyer + class Relation + class Map + # Tracks the available next actions for a resource, and + # issues requests for them. + def initialize + @map = {} + end + + # Adds a Relation to the map. + # + # rel - A Relation. + # + # Returns nothing. + def <<(rel) + @map[rel.name] = rel if rel + end + + # Gets the raw Relation by its name. + # + # key - The Symbol name of the Relation. + # + # Returns a Relation. + def [](key) + @map[key.to_sym] + end + + # Gets the number of mapped Relations. + # + # Returns an Integer. + def size + @map.size + end + + # Gets a list of the Relation names. + # + # Returns an Array of Symbols in no specific order. + def keys + @map.keys + end + + def to_hash + pairs = @map.map do |k, v| + [(k.to_s + "_url").to_sym, v.href] + end + Hash[pairs] + end + alias :to_h :to_hash + + def inspect + hash = to_hash + hash.respond_to?(:pretty_inspect) ? hash.pretty_inspect : hash.inspect + end + end + + attr_reader :agent, + :name, + :href_template, + :method, + :available_methods + + # Public: Builds an index of Relations from the value of a `_links` + # property in a resource. :get is the default method. Any links with + # multiple specified methods will get multiple relations created. + # + # index - The Hash mapping Relation names to the Hash Relation + # options. + # rels - A Relation::Map to store the Relations. + # + # Returns a Relation::Map + def self.from_links(agent, index, rels = Map.new) + if index.is_a?(Array) + raise ArgumentError, "Links must be a hash of rel => {_href => '...'}: #{index.inspect}" + end + + index.each do |name, options| + rels << from_link(agent, name, options) + end if index + + rels + end + + # Public: Builds a single Relation from the given options. These are + # usually taken from a `_links` property in a resource. + # + # agent - The Sawyer::Agent that made the request. + # name - The Symbol name of the Relation. + # options - A Hash containing the other Relation properties. + # :href - The String URL of the next action's location. + # :method - The optional String HTTP method. + # + # Returns a Relation. + def self.from_link(agent, name, options) + case options + when Hash + new agent, name, options[:href], options[:method] + when String + new agent, name, options + end + end + + # A Relation represents an available next action for a resource. + # + # agent - The Sawyer::Agent that made the request. + # name - The Symbol name of the relation. + # href - The String URL of the location of the next action. + # method - The Symbol HTTP method. Default: :get + def initialize(agent, name, href, method = nil) + @agent = agent + @name = name.to_sym + @href = href + @href_template = Addressable::Template.new(href.to_s) + + methods = nil + + if method.is_a? String + if method.size.zero? + method = nil + else + method.downcase! + methods = method.split(',').map! do |m| + m.strip! + m.to_sym + end + method = methods.first + end + end + + @method = (method || :get).to_sym + @available_methods = Set.new methods || [@method] + end + + # Public: Makes an API request with the curent Relation using HEAD. + # + # data - The Optional Hash or Resource body to be sent. :get or :head + # requests can have no body, so this can be the options Hash + # instead. + # options - Hash of option to configure the API request. + # :headers - Hash of API headers to set. + # :query - Hash of URL query params to set. + # :method - Symbol HTTP method. + # + # Returns a Sawyer::Response. + def head(options = nil) + options ||= {} + options[:method] = :head + call options + end + + # Public: Makes an API request with the curent Relation using GET. + # + # data - The Optional Hash or Resource body to be sent. :get or :head + # requests can have no body, so this can be the options Hash + # instead. + # options - Hash of option to configure the API request. + # :headers - Hash of API headers to set. + # :query - Hash of URL query params to set. + # :method - Symbol HTTP method. + # + # Returns a Sawyer::Response. + def get(options = nil) + options ||= {} + options[:method] = :get + call options + end + + # Public: Makes an API request with the curent Relation using POST. + # + # data - The Optional Hash or Resource body to be sent. + # options - Hash of option to configure the API request. + # :headers - Hash of API headers to set. + # :query - Hash of URL query params to set. + # :method - Symbol HTTP method. + # + # Returns a Sawyer::Response. + def post(data = nil, options = nil) + options ||= {} + options[:method] = :post + call data, options + end + + # Public: Makes an API request with the curent Relation using PUT. + # + # data - The Optional Hash or Resource body to be sent. + # options - Hash of option to configure the API request. + # :headers - Hash of API headers to set. + # :query - Hash of URL query params to set. + # :method - Symbol HTTP method. + # + # Returns a Sawyer::Response. + def put(data = nil, options = nil) + options ||= {} + options[:method] = :put + call data, options + end + + # Public: Makes an API request with the curent Relation using PATCH. + # + # data - The Optional Hash or Resource body to be sent. + # options - Hash of option to configure the API request. + # :headers - Hash of API headers to set. + # :query - Hash of URL query params to set. + # :method - Symbol HTTP method. + # + # Returns a Sawyer::Response. + def patch(data = nil, options = nil) + options ||= {} + options[:method] = :patch + call data, options + end + + # Public: Makes an API request with the curent Relation using DELETE. + # + # data - The Optional Hash or Resource body to be sent. + # options - Hash of option to configure the API request. + # :headers - Hash of API headers to set. + # :query - Hash of URL query params to set. + # :method - Symbol HTTP method. + # + # Returns a Sawyer::Response. + def delete(data = nil, options = nil) + options ||= {} + options[:method] = :delete + call data, options + end + + # Public: Makes an API request with the curent Relation using OPTIONS. + # + # data - The Optional Hash or Resource body to be sent. + # options - Hash of option to configure the API request. + # :headers - Hash of API headers to set. + # :query - Hash of URL query params to set. + # :method - Symbol HTTP method. + # + # Returns a Sawyer::Response. + def options(data = nil, opt = nil) + opt ||= {} + opt[:method] = :options + call data, opt + end + + def href(options = nil) + return @href if @href_template.nil? + @href_template.expand(options || {}).to_s + end + + # Public: Makes an API request with the curent Relation. + # + # data - The Optional Hash or Resource body to be sent. :get or :head + # requests can have no body, so this can be the options Hash + # instead. + # options - Hash of option to configure the API request. + # :headers - Hash of API headers to set. + # :query - Hash of URL query params to set. + # :method - Symbol HTTP method. + # + # Raises ArgumentError if the :method value is not in @available_methods. + # Returns a Sawyer::Response. + def call(data = nil, options = nil) + m = options && options[:method] + if m && !@agent.allow_undefined_methods? && !@available_methods.include?(m == :head ? :get : m) + raise ArgumentError, "method #{m.inspect} is not available: #{@available_methods.to_a.inspect}" + end + + @agent.call m || @method, @href_template, data, options + end + + def inspect + %(#<#{self.class}: #{@name}: #{@method} #{@href_template}>) + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/lib/sawyer/resource.rb b/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/lib/sawyer/resource.rb new file mode 100644 index 0000000..a090c87 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/lib/sawyer/resource.rb @@ -0,0 +1,160 @@ +require 'forwardable' + +module Sawyer + class Resource + SPECIAL_METHODS = Set.new(%w(agent rels fields)) + attr_reader :_agent, :_rels, :_fields + attr_reader :attrs + include Enumerable + extend Forwardable + + # Initializes a Resource with the given data. + # + # agent - The Sawyer::Agent that made the API request. + # data - Hash of key/value properties. + def initialize(agent, data = {}) + @_agent = agent + data, links = agent.parse_links(data) + @_rels = Relation.from_links(agent, links) + @_fields = Set.new + @_metaclass = (class << self; self; end) + @attrs = {} + data.each do |key, value| + @_fields << key + @attrs[key.to_sym] = process_value(value) + end + @_metaclass.send(:attr_accessor, *data.keys) + end + + # Processes an individual value of this resource. Hashes get exploded + # into another Resource, and Arrays get their values processed too. + # + # value - An Object value of a Resource's data. + # + # Returns an Object to set as the value of a Resource key. + def process_value(value) + case value + when Hash then self.class.new(@_agent, value) + when Array then value.map { |v| process_value(v) } + else value + end + end + + # Checks to see if the given key is in this resource. + # + # key - A Symbol key. + # + # Returns true if the key exists, or false. + def key?(key) + @_fields.include? key + end + + # Allow fields to be retrieved via Hash notation + # + # method - key name + # + # Returns the value from attrs if exists + def [](method) + send(method.to_sym) + rescue NoMethodError + nil + end + + # Allow fields to be set via Hash notation + # + # method - key name + # value - value to set for the attr key + # + # Returns - value + def []=(method, value) + send("#{method}=", value) + rescue NoMethodError + nil + end + + def_delegators :attrs, :dig, :fetch + + ATTR_SETTER = '='.freeze + ATTR_PREDICATE = '?'.freeze + + # Provides access to a resource's attributes. + def method_missing(method, *args) + attr_name, suffix = method.to_s.scan(/([a-z0-9\_]+)(\?|\=)?$/i).first + if suffix == ATTR_SETTER + @_metaclass.send(:attr_accessor, attr_name) + @_fields << attr_name.to_sym + send(method, args.first) + elsif attr_name && @_fields.include?(attr_name.to_sym) + value = @attrs[attr_name.to_sym] + case suffix + when nil + @_metaclass.send(:attr_accessor, attr_name) + value + when ATTR_PREDICATE then !!value + end + elsif suffix.nil? && SPECIAL_METHODS.include?(attr_name) + instance_variable_get "@_#{attr_name}" + elsif attr_name && !@_fields.include?(attr_name.to_sym) + nil + else + super + end + end + + # Wire up accessor methods to pull from attrs + def self.attr_accessor(*attrs) + attrs.each do |attribute| + class_eval do + define_method attribute do + @attrs[attribute.to_sym] + end + + define_method "#{attribute}=" do |value| + @attrs[attribute.to_sym] = value + end + + define_method "#{attribute}?" do + !!@attrs[attribute.to_sym] + end + end + end + end + + def inspect + to_attrs.respond_to?(:pretty_inspect) ? to_attrs.pretty_inspect : to_attrs.inspect + end + + def each(&block) + @attrs.each(&block) + end + + # private + def to_yaml_properties + [:@attrs, :@_fields, :@_rels] + end + + def to_attrs + hash = self.attrs.clone + hash.keys.each do |k| + if hash[k].is_a?(Sawyer::Resource) + hash[k] = hash[k].to_attrs + elsif hash[k].is_a?(Array) && hash[k].all?{|el| el.is_a?(Sawyer::Resource)} + hash[k] = hash[k].collect{|el| el.to_attrs} + end + end + hash + end + + alias to_hash to_attrs + alias to_h to_attrs + + def marshal_dump + [@attrs, @_fields, @_rels] + end + + def marshal_load(dumped) + @attrs, @_fields, @_rels = *dumped.shift(3) + @_metaclass = (class << self; self; end) + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/lib/sawyer/response.rb b/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/lib/sawyer/response.rb new file mode 100644 index 0000000..684aca1 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/lib/sawyer/response.rb @@ -0,0 +1,72 @@ +module Sawyer + class Response + attr_reader :agent, + :status, + :headers, + :env, + :body, + :rels + + # Builds a Response after a completed request. + # + # agent - The Sawyer::Agent that is managing the API connection. + # res - A Faraday::Response. + def initialize(agent, res, options = {}) + @agent = agent + @status = res.status + @headers = res.headers + @env = res.env + @body = res.body + @rels = process_rels + @started = options[:sawyer_started] + @ended = options[:sawyer_ended] + end + + def data + @data ||= begin + return(body) unless (headers[:content_type] =~ /json|msgpack/) + process_data(agent.decode_body(body)) + end + end + + # Turns parsed contents from an API response into a Resource or + # collection of Resources. + # + # data - Either an Array or Hash parsed from JSON. + # + # Returns either a Resource or Array of Resources. + def process_data(data) + case data + when Hash then Resource.new(agent, data) + when Array then data.map { |hash| process_data(hash) } + when nil then nil + else data + end + end + + # Finds link relations from 'Link' response header + # + # Returns an array of Relations + def process_rels + links = ( @headers["Link"] || "" ).split(', ').map do |link| + href, name = link.match(/<(.*?)>; rel="(\w+)"/).captures + + [name.to_sym, Relation.from_link(@agent, name, :href => href)] + end + + Hash[*links.flatten] + end + + def timing + @timing ||= @ended - @started + end + + def time + @ended + end + + def inspect + %(#<#{self.class}: #{@status} @rels=#{@rels.inspect} @data=#{data.inspect}>) + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/lib/sawyer/serializer.rb b/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/lib/sawyer/serializer.rb new file mode 100644 index 0000000..b711de4 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/lib/sawyer/serializer.rb @@ -0,0 +1,129 @@ +require 'date' +require 'time' + +module Sawyer + class Serializer + def self.any_json + yajl || multi_json || json || begin + raise RuntimeError, "Sawyer requires a JSON gem: yajl, multi_json, or json" + end + end + + def self.yajl + require 'yajl' + new(Yajl) + rescue LoadError + end + + def self.json + require 'json' + new(JSON) + rescue LoadError + end + + def self.multi_json + require 'multi_json' + new(MultiJson) + rescue LoadError + end + + def self.message_pack + require 'msgpack' + new(MessagePack, :pack, :unpack) + rescue LoadError + end + + # Public: Wraps a serialization format for Sawyer. Nested objects are + # prepared for serialization (such as changing Times to ISO 8601 Strings). + # Any serialization format that responds to #dump and #load will work. + def initialize(format, dump_method_name = nil, load_method_name = nil) + @format = format + @dump = @format.method(dump_method_name || :dump) + @load = @format.method(load_method_name || :load) + end + + # Public: Encodes an Object (usually a Hash or Array of Hashes). + # + # data - Object to be encoded. + # + # Returns an encoded String. + def encode(data) + @dump.call(encode_object(data)) + end + + alias dump encode + + # Public: Decodes a String into an Object (usually a Hash or Array of + # Hashes). + # + # data - An encoded String. + # + # Returns a decoded Object. + def decode(data) + return nil if data.nil? || data.strip.empty? + decode_object(@load.call(data)) + end + + alias load decode + + def encode_object(data) + case data + when Hash then encode_hash(data) + when Array then data.map { |o| encode_object(o) } + else data + end + end + + def encode_hash(hash) + hash.keys.each do |key| + case value = hash[key] + when Date then hash[key] = value.to_time.utc.xmlschema + when Time then hash[key] = value.utc.xmlschema + when Hash then hash[key] = encode_hash(value) + end + end + hash + end + + def decode_object(data) + case data + when Hash then decode_hash(data) + when Array then data.map { |o| decode_object(o) } + else data + end + end + + def decode_hash(hash) + hash.keys.each do |key| + hash[key.to_sym] = decode_hash_value(key, hash.delete(key)) + end + hash + end + + def decode_hash_value(key, value) + if time_field?(key, value) + if value.is_a?(String) + begin + Time.parse(value) + rescue ArgumentError + value + end + elsif value.is_a?(Integer) || value.is_a?(Float) + Time.at(value) + else + value + end + elsif value.is_a?(Hash) + decode_hash(value) + elsif value.is_a?(Array) + value.map { |o| decode_hash_value(key, o) } + else + value + end + end + + def time_field?(key, value) + value && (key =~ /_(at|on)\z/ || key =~ /(\A|_)date\z/) + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/sawyer.gemspec b/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/sawyer.gemspec new file mode 100644 index 0000000..fe171d8 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/sawyer.gemspec @@ -0,0 +1,31 @@ +lib = "sawyer" +lib_file = File.expand_path("../lib/#{lib}.rb", __FILE__) +File.read(lib_file) =~ /\bVERSION\s*=\s*["'](.+?)["']/ +version = $1 + +Gem::Specification.new do |spec| + spec.specification_version = 2 if spec.respond_to? :specification_version= + spec.required_rubygems_version = Gem::Requirement.new(">= 1.3.5") if spec.respond_to? :required_rubygems_version= + + spec.name = lib + spec.version = version + + spec.summary = "Secret User Agent of HTTP" + + spec.authors = ["Rick Olson", "Wynn Netherland"] + spec.email = 'technoweenie@gmail.com' + spec.homepage = 'https://github.com/lostisland/sawyer' + spec.licenses = ['MIT'] + + spec.add_dependency 'faraday', '>= 0.17.3', '< 3' + spec.add_dependency 'addressable', ['>= 2.3.5'] + + spec.files = %w(Gemfile LICENSE.md README.md Rakefile) + spec.files << "#{lib}.gemspec" + spec.files += Dir.glob("lib/**/*.rb") + spec.files += Dir.glob("script/*") + + dev_null = File.exist?('/dev/null') ? '/dev/null' : 'NUL' + git_files = `git ls-files -z 2>#{dev_null}` + spec.files &= git_files.split("\0") if $?.success? +end diff --git a/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/script/bootstrap b/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/script/bootstrap new file mode 100755 index 0000000..6479ada --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/script/bootstrap @@ -0,0 +1,5 @@ +#!/bin/sh + +set -e + +bundle install --quiet "$@" diff --git a/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/script/console b/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/script/console new file mode 100755 index 0000000..8c28470 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/script/console @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +# Usage: script/console +# Starts an IRB console with this library loaded. + +gemspec="$(ls *.gemspec | head -1)" + +exec bundle exec irb -r "${gemspec%.*}" + diff --git a/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/script/package b/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/script/package new file mode 100755 index 0000000..3f59b50 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/script/package @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +# Usage: script/gem +# Updates the gemspec and builds a new gem in the pkg directory. + +mkdir -p pkg +gem build *.gemspec +mv *.gem pkg + diff --git a/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/script/release b/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/script/release new file mode 100755 index 0000000..a80bc5a --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/script/release @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +# Usage: script/release +# Build the package, tag a commit, push it to origin, and then release the +# package publicly. + +set -e + +version="$(script/package | grep Version: | awk '{print $2}')" +[ -n "$version" ] || exit 1 + +git commit --allow-empty -a -m "Release $version" +git tag "v$version" +git push origin +git push origin "v$version" +gem push pkg/*-${version}.gem + diff --git a/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/script/test b/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/script/test new file mode 100755 index 0000000..fa00f05 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/sawyer-0.9.3/script/test @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +# Usage: script/test +# Runs the library's test suite. + +script/bootstrap +bundle exec rake test diff --git a/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/.document b/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/.document new file mode 100644 index 0000000..1d07bdf --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/.document @@ -0,0 +1,5 @@ +BSDL +COPYING +README.md +docs/ +lib/ diff --git a/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/.rdoc_options b/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/.rdoc_options new file mode 100644 index 0000000..cd5e496 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/.rdoc_options @@ -0,0 +1,5 @@ +main_page: README.md +op_dir: _site +warn_missing_rdoc_ref: true +title: URI Documentation +visibility: :private diff --git a/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/BSDL b/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/BSDL new file mode 100644 index 0000000..66d9359 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/BSDL @@ -0,0 +1,22 @@ +Copyright (C) 1993-2013 Yukihiro Matsumoto. 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. diff --git a/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/COPYING b/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/COPYING new file mode 100644 index 0000000..48e5a96 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/COPYING @@ -0,0 +1,56 @@ +Ruby is copyrighted free software by Yukihiro Matsumoto <matz@netlab.jp>. +You can redistribute it and/or modify it under either the terms of the +2-clause BSDL (see the file BSDL), or the conditions below: + +1. You may make and give away verbatim copies of the source form of the + software without restriction, provided that you duplicate all of the + original copyright notices and associated disclaimers. + +2. You may modify your copy of the software in any way, provided that + you do at least ONE of the following: + + a. place your modifications in the Public Domain or otherwise + make them Freely Available, such as by posting said + modifications to Usenet or an equivalent medium, or by allowing + the author to include your modifications in the software. + + b. use the modified software only within your corporation or + organization. + + c. give non-standard binaries non-standard names, with + instructions on where to get the original software distribution. + + d. make other distribution arrangements with the author. + +3. You may distribute the software in object code or binary form, + provided that you do at least ONE of the following: + + a. distribute the binaries and library files of the software, + together with instructions (in the manual page or equivalent) + on where to get the original distribution. + + b. accompany the distribution with the machine-readable source of + the software. + + c. give non-standard binaries non-standard names, with + instructions on where to get the original software distribution. + + d. make other distribution arrangements with the author. + +4. You may modify and include the part of the software into any other + software (possibly commercial). But some files in the distribution + are not written by the author, so that they are not under these terms. + + For the list of those files and their copying conditions, see the + file LEGAL. + +5. The scripts and library files supplied as input to or produced as + output from the software do not automatically fall under the + copyright of the software, but belong to whomever generated them, + and may be sold commercially, and may be aggregated with this + software. + +6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE. diff --git a/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/README.md b/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/README.md new file mode 100644 index 0000000..5c7c0dd --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/README.md @@ -0,0 +1,55 @@ +# URI + +[![CI](https://github.com/ruby/uri/actions/workflows/test.yml/badge.svg)](https://github.com/ruby/uri/actions/workflows/test.yml) +[![Yard Docs](https://img.shields.io/badge/docs-exist-blue.svg)](https://ruby.github.io/uri/) + +URI is a module providing classes to handle Uniform Resource Identifiers [RFC3986](http://tools.ietf.org/html/rfc3986). + +## Features + +* Uniform way of handling URIs. +* Flexibility to introduce custom URI schemes. +* Flexibility to have an alternate URI::Parser (or just different patterns and regexp's). + +## Installation + +Add this line to your application's Gemfile: + +```ruby +gem 'uri' +``` + +And then execute: + + $ bundle + +Or install it yourself as: + + $ gem install uri + +## Usage + +```ruby +require 'uri' + +uri = URI("http://foo.com/posts?id=30&limit=5#time=1305298413") +#=> #<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" +``` + +## Development + +After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. + +To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). + +## Contributing + +Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/uri. diff --git a/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/docs/kernel.rb b/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/docs/kernel.rb new file mode 100644 index 0000000..68ed335 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/docs/kernel.rb @@ -0,0 +1,2 @@ +# :stopdoc: +module Kernel end diff --git a/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/lib/uri.rb b/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/lib/uri.rb new file mode 100644 index 0000000..dfdb052 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/lib/uri.rb @@ -0,0 +1,104 @@ +# frozen_string_literal: false +# URI is a module providing classes to handle Uniform Resource Identifiers +# (RFC2396[https://www.rfc-editor.org/rfc/rfc2396]). +# +# == Features +# +# * Uniform way of handling URIs. +# * Flexibility to introduce custom URI schemes. +# * Flexibility to have an alternate URI::Parser (or just different patterns +# and regexp's). +# +# == Basic example +# +# require 'uri' +# +# uri = URI("http://foo.com/posts?id=30&limit=5#time=1305298413") +# #=> #<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 URI +# class RSYNC < Generic +# DEFAULT_PORT = 873 +# end +# register_scheme 'RSYNC', RSYNC +# end +# #=> URI::RSYNC +# +# URI.scheme_list +# #=> {"FILE"=>URI::File, "FTP"=>URI::FTP, "HTTP"=>URI::HTTP, +# # "HTTPS"=>URI::HTTPS, "LDAP"=>URI::LDAP, "LDAPS"=>URI::LDAPS, +# # "MAILTO"=>URI::MailTo, "RSYNC"=>URI::RSYNC} +# +# uri = URI("rsync://rsync.foo.com") +# #=> #<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[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 +# +# - URI::Generic (in uri/generic.rb) +# - URI::File - (in uri/file.rb) +# - URI::FTP - (in uri/ftp.rb) +# - URI::HTTP - (in uri/http.rb) +# - URI::HTTPS - (in uri/https.rb) +# - URI::LDAP - (in uri/ldap.rb) +# - URI::LDAPS - (in uri/ldaps.rb) +# - URI::MailTo - (in uri/mailto.rb) +# - URI::Parser - (in uri/common.rb) +# - URI::REGEXP - (in uri/common.rb) +# - URI::REGEXP::PATTERN - (in uri/common.rb) +# - URI::Util - (in uri/common.rb) +# - URI::Error - (in uri/common.rb) +# - URI::InvalidURIError - (in uri/common.rb) +# - URI::InvalidComponentError - (in uri/common.rb) +# - 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 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/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/lib/uri/common.rb b/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/lib/uri/common.rb new file mode 100644 index 0000000..1800836 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/lib/uri/common.rb @@ -0,0 +1,922 @@ +# 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 URI for general documentation +# + +require_relative "rfc2396_parser" +require_relative "rfc3986_parser" + +module URI + # The default parser instance for RFC 2396. + RFC2396_PARSER = RFC2396_Parser.new + Ractor.make_shareable(RFC2396_PARSER) if defined?(Ractor) + + # The default parser instance for RFC 3986. + RFC3986_PARSER = RFC3986_Parser.new + Ractor.make_shareable(RFC3986_PARSER) if defined?(Ractor) + + # The default parser instance. + DEFAULT_PARSER = RFC3986_PARSER + Ractor.make_shareable(DEFAULT_PARSER) if defined?(Ractor) + + # Set the default parser instance. + def self.parser=(parser = RFC3986_PARSER) + remove_const(:Parser) if defined?(::URI::Parser) + const_set("Parser", parser.class) + + remove_const(:PARSER) if defined?(::URI::PARSER) + const_set("PARSER", parser) + + remove_const(:REGEXP) if defined?(::URI::REGEXP) + remove_const(:PATTERN) if defined?(::URI::PATTERN) + if Parser == RFC2396_Parser + const_set("REGEXP", URI::RFC2396_REGEXP) + const_set("PATTERN", URI::RFC2396_REGEXP::PATTERN) + end + + Parser.new.regexp.each_pair do |sym, str| + remove_const(sym) if const_defined?(sym, false) + const_set(sym, str) + end + end + self.parser = RFC3986_PARSER + + def self.const_missing(const) # :nodoc: + if const == :REGEXP + warn "URI::REGEXP is obsolete. Use URI::RFC2396_REGEXP explicitly.", uplevel: 1 if $VERBOSE + URI::RFC2396_REGEXP + elsif value = RFC2396_PARSER.regexp[const] + warn "URI::#{const} is obsolete. Use URI::RFC2396_PARSER.regexp[#{const.inspect}] explicitly.", uplevel: 1 if $VERBOSE + value + elsif value = RFC2396_Parser.const_get(const) + warn "URI::#{const} is obsolete. Use URI::RFC2396_Parser::#{const} explicitly.", uplevel: 1 if $VERBOSE + value + else + super + end + end + + 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 # :nodoc: + class << self + ReservedChars = ".+-" + EscapedChars = "\u01C0\u01C1\u01C2" + # Use Lo category chars as escaped chars for TruffleRuby, which + # does not allow Symbol categories as identifiers. + + def escape(name) + unless name and name.ascii_only? + return nil + end + name.upcase.tr(ReservedChars, EscapedChars) + end + + def unescape(name) + name.tr(EscapedChars, ReservedChars).encode(Encoding::US_ASCII).upcase + end + + def find(name) + const_get(name, false) if name and const_defined?(name, false) + end + + def register(name, klass) + unless scheme = escape(name) + raise ArgumentError, "invalid character as scheme - #{name}" + end + const_set(scheme, klass) + end + + def list + constants.map { |name| + [unescape(name.to_s), const_get(name)] + }.to_h + end + end + end + private_constant :Schemes + + # Registers the given +klass+ as the class to be instantiated + # when parsing a \URI with the given +scheme+: + # + # URI.register_scheme('MS_SEARCH', URI::Generic) # => URI::Generic + # URI.scheme_list['MS_SEARCH'] # => URI::Generic + # + # Note that after calling String#upcase on +scheme+, it must be a valid + # constant name. + def self.register_scheme(scheme, klass) + Schemes.register(scheme, klass) + end + + # Returns a hash of the defined schemes: + # + # URI.scheme_list + # # => + # {"MAILTO"=>URI::MailTo, + # "LDAPS"=>URI::LDAPS, + # "WS"=>URI::WS, + # "HTTP"=>URI::HTTP, + # "HTTPS"=>URI::HTTPS, + # "LDAP"=>URI::LDAP, + # "FILE"=>URI::File, + # "FTP"=>URI::FTP} + # + # Related: URI.register_scheme. + def self.scheme_list + Schemes.list + end + + # :stopdoc: + INITIAL_SCHEMES = scheme_list + private_constant :INITIAL_SCHEMES + Ractor.make_shareable(INITIAL_SCHEMES) if defined?(Ractor) + # :startdoc: + + # Returns a new object constructed from the given +scheme+, +arguments+, + # and +default+: + # + # - The new object is an instance of <tt>URI.scheme_list[scheme.upcase]</tt>. + # - The object is initialized by calling the class initializer + # using +scheme+ and +arguments+. + # See URI::Generic.new. + # + # Examples: + # + # values = ['john.doe', 'www.example.com', '123', nil, '/forum/questions/', nil, 'tag=networking&order=newest', 'top'] + # URI.for('https', *values) + # # => #<URI::HTTPS https://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top> + # URI.for('foo', *values, default: URI::HTTP) + # # => #<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 = Schemes.escape(scheme) + + uri_class = INITIAL_SCHEMES[const_name] + uri_class ||= Schemes.find(const_name) + uri_class ||= default + + return uri_class.new(scheme, *arguments) + end + + # + # Base class for all URI exceptions. + # + class Error < StandardError; end + # + # Not a URI. + # + class InvalidURIError < Error; end + # + # Not a URI component. + # + class InvalidComponentError < Error; end + # + # URI is valid, bad usage is not. + # + class BadURIError < Error; end + + # Returns a 9-element array representing the parts of the \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 = 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) + PARSER.split(uri) + end + + # Returns a new \URI object constructed from the given string +uri+: + # + # URI.parse('https://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top') + # # => #<URI::HTTPS https://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top> + # URI.parse('http://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top') + # # => #<URI::HTTP http://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top> + # + # It's recommended to first URI::RFC2396_PARSER.escape string +uri+ + # if it may contain invalid URI characters. + # + def self.parse(uri) + PARSER.parse(uri) + end + + # Merges the given URI strings +str+ + # per {RFC 2396}[https://www.rfc-editor.org/rfc/rfc2396.html]. + # + # Each string in +str+ is converted to an + # {RFC3986 URI}[https://www.rfc-editor.org/rfc/rfc3986.html] before being merged. + # + # Examples: + # + # URI.join("http://example.com/","main.rbx") + # # => #<URI::HTTP http://example.com/main.rbx> + # + # URI.join('http://example.com', 'foo') + # # => #<URI::HTTP http://example.com/foo> + # + # URI.join('http://example.com', '/foo', '/bar') + # # => #<URI::HTTP http://example.com/bar> + # + # URI.join('http://example.com', '/foo', 'bar') + # # => #<URI::HTTP http://example.com/bar> + # + # URI.join('http://example.com', '/foo/', 'bar') + # # => #<URI::HTTP http://example.com/foo/bar> + # + def self.join(*str) + DEFAULT_PARSER.join(*str) + end + + # + # == Synopsis + # + # URI::extract(str[, schemes][,&blk]) + # + # == Args + # + # +str+:: + # String to extract URIs from. + # +schemes+:: + # Limit 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 "uri" + # + # 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 "URI.extract is obsolete", uplevel: 1 if $VERBOSE + PARSER.extract(str, schemes, &block) + end + + # + # == Synopsis + # + # 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 URI-like strings. + # The Regexp object returned by this method includes arbitrary + # number of capture group (parentheses). Never rely on its number. + # + # == Usage + # + # require 'uri' + # + # # extract first URI from html_string + # html_string.slice(URI.regexp) + # + # # remove ftp URIs + # html_string.sub(URI.regexp(['ftp']), '') + # + # # You should not rely on the number of parentheses + # html_string.scan(URI.regexp) do |*matches| + # p $& + # end + # + def self.regexp(schemes = nil)# :nodoc: + warn "URI.regexp is obsolete", uplevel: 1 if $VERBOSE + PARSER.make_regexp(schemes) + end + + TBLENCWWWCOMP_ = {} # :nodoc: + 256.times do |i| + TBLENCWWWCOMP_[-i.chr] = -('%%%02X' % i) + end + TBLENCURICOMP_ = TBLENCWWWCOMP_.dup.freeze # :nodoc: + 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: + # + # 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: + # + # 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: 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: + # + # URI.decode_www_form_component('*.-_azAZ09') + # # => "*.-_azAZ09" + # + # - Converts: + # + # - Character <tt>'+'</tt> to character <tt>' '</tt>. + # - Each "percent notation" to an ASCII character. + # + # Example: + # + # URI.decode_www_form_component('Here+are+some+punctuation+characters%3A+%2C%3B%3F%3A') + # # => "Here are some punctuation characters: ,;?:" + # + # Related: 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 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 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 + + # Returns a string derived from the given string +str+ with + # URI-encoded characters matching +regexp+ according to +table+. + 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 + + # Returns a string decoding characters matching +regexp+ from the + # given \URL-encoded string +str+. + 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: + # + # URI.encode_www_form([['foo', 0], ['bar', 1], ['baz', 2]]) + # # => "foo=0&bar=1&baz=2" + # URI.encode_www_form({foo: 0, bar: 1, baz: 2}) + # # => "foo=0&bar=1&baz=2" + # + # The returned string is formed using method URI.encode_www_form_component, + # which converts certain characters: + # + # 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 = URI.encode_www_form_component(ele[0], enc) + # value = URI.encode_www_form_component(ele[1], enc) + # "#{name}=#{value}" + # + # Examples: + # + # URI.encode_www_form([%w[foo bar], %w[baz bat bah]]) + # # => "foo=bar&baz=bat" + # 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>: + # + # URI.encode_www_form_component(ele[0]) + # + # Example: + # + # URI.encode_www_form([['foo'], [:bar], [0]]) + # # => "foo&bar&0" + # + # - Otherwise the field is formed from +ele+: + # + # URI.encode_www_form_component(ele) + # + # Example: + # + # URI.encode_www_form(['foo', :bar, 0]) + # # => "foo&bar&0" + # + # The elements of an Array-like +enum+ may be mixture: + # + # 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 = URI.encode_www_form_component(key, enc) + # value = URI.encode_www_form_component(ele, enc) + # "#{name}=#{value}" + # + # Example: + # + # 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 = URI.encode_www_form_component(key, enc) + # value = URI.encode_www_form_component(value, enc) + # "#{name}=#{value}" + # + # Example: + # + # 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: + # + # 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: + # + # 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 URI.decode_www_form_component: + # + # 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: + # + # URI.decode_www_form('foo=0&&bar=1&&baz=2') + # # => [["foo", "0"], ["", ""], ["bar", "1"], ["", ""], ["baz", "2"]] + # + # A different separator may be specified: + # + # 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 URI + +module Kernel + + # + # Returns a \URI object derived from the given +uri+, + # which may be a \URI string or an existing \URI object: + # + # require 'uri' + # # Returns a new URI. + # uri = URI('http://github.com/ruby/ruby') + # # => #<URI::HTTP http://github.com/ruby/ruby> + # # Returns the given URI. + # URI(uri) + # # => #<URI::HTTP http://github.com/ruby/ruby> + # + # You must require 'uri' to use this method. + # + def URI(uri) + if uri.is_a?(URI::Generic) + uri + elsif uri = String.try_convert(uri) + URI.parse(uri) + else + raise ArgumentError, + "bad argument (expected URI object or URI string)" + end + end + module_function :URI +end diff --git a/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/lib/uri/file.rb b/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/lib/uri/file.rb new file mode 100644 index 0000000..47b5aef --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/lib/uri/file.rb @@ -0,0 +1,100 @@ +# frozen_string_literal: true + +require_relative 'generic' + +module URI + + # + # The "file" URI is defined by RFC8089. + # + class File < Generic + # A Default port of nil for URI::File. + DEFAULT_PORT = nil + + # + # An Array of the available components for URI::File. + # + COMPONENT = [ + :scheme, + :host, + :path + ].freeze + + # + # == Description + # + # Creates a new 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 'uri' + # + # uri1 = URI::File.build(['host.example.com', '/path/file.zip']) + # uri1.to_s # => "file://host.example.com/path/file.zip" + # + # uri2 = URI::File.build({:host => 'host.example.com', + # :path => '/ruby/src'}) + # uri2.to_s # => "file://host.example.com/ruby/src" + # + # uri3 = URI::File.build({:path => URI::RFC2396_PARSER.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 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 URI::InvalidURIError, "cannot set userinfo for file URI" + end + + # raise InvalidURIError + def check_user(user) + raise URI::InvalidURIError, "cannot set user for file URI" + end + + # raise InvalidURIError + def check_password(user) + raise URI::InvalidURIError, "cannot set password for file 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/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/lib/uri/ftp.rb b/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/lib/uri/ftp.rb new file mode 100644 index 0000000..1c75e24 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/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 URI for general documentation +# + +require_relative 'generic' + +module URI + + # + # FTP 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. + # https://datatracker.ietf.org/doc/html/draft-hoffman-ftp-uri-04 + # + class FTP < Generic + # A Default port of 21 for URI::FTP. + DEFAULT_PORT = 21 + + # + # An Array of the available components for 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 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 URI. + # + # Examples: + # + # require 'uri' + # + # uri1 = 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 = 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 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 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 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 URI::FTP.check_typecode. + # + # == Usage + # + # require 'uri' + # + # uri = URI.parse("ftp://john@ftp.example.com/my_file.img") + # #=> #<URI::FTP ftp://john@ftp.example.com/my_file.img> + # uri.typecode = "i" + # uri + # #=> #<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 URI. + # + # RFC 1738 specifically states that the path for an FTP URI does not + # include the / which separates the URI path from the URI host. Example: + # + # <code>ftp://ftp.example.com/pub/ruby</code> + # + # The above 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 URI::FTP. + def set_path(v) + super("/" + v.sub(/^\//, "%2F")) + end + protected :set_path + + # Returns a String representation of the 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/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/lib/uri/generic.rb b/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/lib/uri/generic.rb new file mode 100644 index 0000000..6fd0f7c --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/lib/uri/generic.rb @@ -0,0 +1,1592 @@ +# 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 URI for general documentation +# + +require_relative 'common' +autoload :IPSocket, 'socket' +autoload :IPAddr, 'ipaddr' + +module URI + + # + # Base class for all URI classes. + # Implements generic URI syntax as per RFC 2396. + # + class Generic + include URI + + # + # A Default port of nil for 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 URI::Generic. + # + COMPONENT = [ + :scheme, + :userinfo, :host, :port, :registry, + :path, :opaque, + :query, + :fragment + ].freeze + + # + # Components of the 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 URI::Generic instance using + # URI::Generic::build. But, if exception URI::InvalidComponentError is raised, + # then it does URI::RFC2396_PARSER.escape all 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) + URI::RFC2396_PARSER.escape(x) + else + x + end + }) + elsif args.kind_of?(Hash) + tmp = {} + args.each do |key, value| + tmp[key] = if value + URI::RFC2396_PARSER.escape(value) + else + value + end + end + return self.build(tmp) + end + end + end + + # + # == Synopsis + # + # See ::new. + # + # == Description + # + # Creates a new URI::Generic instance from components of 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 == ::URI::Generic::COMPONENT.size + tmp = args.dup + elsif args.kind_of?(Hash) + tmp = ::URI::Generic::COMPONENT.collect do |c| + if args.include?(c) + args[c] + else + nil + end + end + else + component = self.component rescue ::URI::Generic::COMPONENT + raise ArgumentError, + "expected Array of or Hash of components of #{self} (#{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 URI after '#' character. + # +parser+:: + # Parser for internal use [URI::DEFAULT_PARSER by default]. + # +arg_check+:: + # Check arguments [false by default]. + # + # == Description + # + # Creates a new 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.hostname = host + self.port = port + self.userinfo = userinfo + self.path = path + self.query = query + self.opaque = opaque + self.fragment = fragment + else + self.set_scheme(scheme) + self.set_host(host) + self.set_port(port) + self.set_userinfo(userinfo) + 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 URI. + # + # URI("http://foo/bar/baz").scheme #=> "http" + # + attr_reader :scheme + + # Returns the host component of the URI. + # + # URI("http://foo/bar/baz").host #=> "foo" + # + # It returns nil if no host component exists. + # + # URI("mailto:foo@example.org").host #=> nil + # + # The component does not contain the port number. + # + # 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. + # + # URI("http://[::1]/bar/baz").host #=> "[::1]" + # URI("http://[::1]/bar/baz").hostname #=> "::1" + # + attr_reader :host + + # Returns the port component of the URI. + # + # URI("http://foo/bar/baz").port #=> 80 + # URI("http://foo:8080/bar/baz").port #=> 8080 + # + attr_reader :port + + def registry # :nodoc: + nil + end + + # Returns the path component of the URI. + # + # URI("http://foo/bar/baz").path #=> "/bar/baz" + # + attr_reader :path + + # Returns the query component of the URI. + # + # URI("http://foo/bar/baz?search=FooBar").query #=> "search=FooBar" + # + attr_reader :query + + # Returns the opaque part of the URI. + # + # URI("mailto:foo@example.org").opaque #=> "foo@example.org" + # 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 URI. + # + # URI("http://foo/bar/baz?search=FooBar#ponies").fragment #=> "ponies" + # + attr_reader :fragment + + # Returns the parser to be used. + # + # Unless the +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 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 URI in the order. + # + def component + self.class.component + end + + # + # Checks the scheme +v+ component against the +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 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 URI::Generic.check_scheme. + # + # == Usage + # + # require 'uri' + # + # uri = 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 URI::Generic.split_userinfo, to + # pull +user+ and +password. + # + # See also URI::Generic.check_user, 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 +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, + "cannot 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 +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, + "cannot 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 URI::Generic.check_user. + # + # == Usage + # + # require 'uri' + # + # uri = 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 URI::Generic.check_password. + # + # == Usage + # + # require 'uri' + # + # uri = 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 URI::Generic.userinfo=. + # + def set_userinfo(user, password = nil) + unless password + user, password = split_userinfo(user) + end + @user = user + @password = password + + [@user, @password] + end + protected :set_userinfo + + # Protected setter for the user component +v+. + # + # See also URI::Generic.user=. + # + def set_user(v) + set_userinfo(v, nil) + v + end + protected :set_user + + # Protected setter for the password component +v+. + # + # See also 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 URI decoding). + def user + @user + end + + # Returns the password component (without URI decoding). + def password + @password + end + + # Returns the authority info (array of user, password, host and + # port), if any is set. Or returns +nil+. + def authority + return @user, @password, @host, @port if @user || @password || @host || @port + end + + # Returns the user component after URI decoding. + def decoded_user + URI.decode_uri_component(@user) if @user + end + + # Returns the password component after URI decoding. + def decoded_password + URI.decode_uri_component(@password) if @password + end + + # + # Checks the host +v+ component for RFC2396 compliance + # and against the +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, + "cannot 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 URI::Generic.host=. + # + def set_host(v) + @host = v + end + protected :set_host + + # Protected setter for the authority info (+user+, +password+, +host+ + # and +port+). If +port+ is +nil+, +default_port+ will be set. + # + protected def set_authority(user, password, host, port = nil) + @user, @password, @host, @port = user, password, host, port || self.default_port + end + + # + # == Args + # + # +v+:: + # String + # + # == Description + # + # Public setter for the host component +v+ + # (with validation). + # + # See also URI::Generic.check_host. + # + # == Usage + # + # require 'uri' + # + # uri = 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) + set_userinfo(nil) + v + end + + # Extract the host part of the URI and unwrap brackets for IPv6 addresses. + # + # This method is the same as URI::Generic#host except + # brackets for IPv6 (and future IP) addresses are removed. + # + # uri = 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 URI as the argument with brackets for IPv6 addresses. + # + # This method is the same as URI::Generic#host= except + # the argument can be a bare IPv6 address. + # + # uri = 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 +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, + "cannot 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 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 URI::Generic.check_port. + # + # == Usage + # + # require 'uri' + # + # uri = 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) + set_userinfo(nil) + port + end + + def check_registry(v) # :nodoc: + raise InvalidURIError, "cannot set registry" + end + private :check_registry + + def set_registry(v) # :nodoc: + raise InvalidURIError, "cannot set registry" + end + protected :set_registry + + def registry=(v) # :nodoc: + raise InvalidURIError, "cannot set registry" + end + + # + # Checks the path +v+ component for RFC2396 compliance + # and against the +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 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 URI::Generic.check_path. + # + # == Usage + # + # require 'uri' + # + # uri = 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 'uri' + # + # uri = 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 +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, + "cannot 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 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 URI::Generic.check_opaque. + # + def opaque=(v) + check_opaque(v) + set_opaque(v) + v + end + + # + # Checks the fragment +v+ component against the +parser+ Regexp for :FRAGMENT. + # + # + # == Args + # + # +v+:: + # String + # + # == Description + # + # Public setter for the fragment component +v+ + # (with validation). + # + # == Usage + # + # require 'uri' + # + # uri = 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 URI is hierarchical. + # + # == Description + # + # URI has components listed in order of decreasing significance from left to right, + # see RFC3986 https://www.rfc-editor.org/rfc/rfc3986 1.2.3. + # + # == Usage + # + # require 'uri' + # + # uri = URI.parse("http://my.example.com/") + # uri.hierarchical? + # #=> true + # uri = URI.parse("mailto:joe@example.com") + # uri.hierarchical? + # #=> false + # + def hierarchical? + if @path + true + else + false + end + end + + # + # Returns true if 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 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+:: + # URI or String + # + # == Description + # + # Destructive form of #merge. + # + # == Usage + # + # require 'uri' + # + # uri = 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+:: + # URI or String + # + # == Description + # + # Merges two URIs. + # + # == Usage + # + # require 'uri' + # + # uri = 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 URI are absolute" if absolute? + # hmm... should return oth for usability? + return rel + end + + unless self.absolute? + raise BadURIError, "both URI are relative" + end + + base = self.dup + + authority = rel.authority + + # 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_authority(*authority) + base.set_path(rel.path) + elsif base.path && rel.path + base.set_path(merge_path(base.path, rel.path)) + end + + # RFC2396, Section 5.2, 7) + 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 URI: #{self}" + end + if oth.relative? + raise BadURIError, + "relative URI: #{oth}" + end + + if self.scheme != oth.scheme + return self, self.dup + end + rel = URI::Generic.new(nil, # it is relative 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 cannot `oth'. + return oth, rel + end + private :route_from0 + # :startdoc: + + # + # == Args + # + # +oth+:: + # URI or String + # + # == Description + # + # Calculates relative path from oth to self. + # + # == Usage + # + # require 'uri' + # + # uri = URI.parse('http://my.example.com/main.rbx?page=1') + # uri.route_from('http://my.example.com') + # #=> #<URI::Generic /main.rbx?page=1> + # + def route_from(oth) + # you can modify `rel', but cannot `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+:: + # URI or String + # + # == Description + # + # Calculates relative path to oth from self. + # + # == Usage + # + # require 'uri' + # + # uri = URI.parse('http://my.example.com') + # uri.route_to('http://my.example.com/main.rbx?page=1') + # #=> #<URI::Generic /main.rbx?page=1> + # + def route_to(oth) + parser.__send__(:convert_to_uri, oth).route_from(self) + end + + # + # Returns normalized URI. + # + # require 'uri' + # + # URI("HTTP://my.EXAMPLE.com").normalize + # #=> #<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 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 + if (@host || @port) && !@path.empty? && !@path.start_with?('/') + str << '/' + 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 + + # Returns the hash value. + def hash + self.component_ary.hash + end + + # Compares with _oth_ for Hash. + def eql?(oth) + self.class == oth.class && + parser == oth.parser && + self.component_ary.eql?(oth.component_ary) + 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 URI::HTTP. + # + # == Description + # + # Selects specified components from URI. + # + # == Usage + # + # require 'uri' + # + # uri = 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 # :nodoc: + "#<#{self.class} #{self}>" + end + + # + # == Args + # + # +v+:: + # URI or String + # + # == Description + # + # Attempts to parse other URI +oth+, + # returns [parsed_oth, self]. + # + # == Usage + # + # require 'uri' + # + # uri = URI.parse("http://my.example.com") + # uri.coerce("http://foo.com") + # #=> [#<URI::HTTP http://foo.com>, #<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 URI. + # The proxy 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 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. Please use http_proxy instead.', 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 URI::Generic.use_proxy?(self.hostname, addr, self.port, no_proxy) + end + 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/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/lib/uri/http.rb b/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/lib/uri/http.rb new file mode 100644 index 0000000..3c41cd4 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/lib/uri/http.rb @@ -0,0 +1,137 @@ +# 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 URI for general documentation +# + +require_relative 'generic' + +module URI + + # + # The syntax of HTTP URIs is defined in RFC1738 section 3.3. + # + # Note that the Ruby 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 URI::HTTP. + DEFAULT_PORT = 80 + + # An Array of the available components for URI::HTTP. + COMPONENT = %i[ + scheme + userinfo host port + path + query + fragment + ].freeze + + # + # == Description + # + # Creates a new 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 = URI::HTTP.build(host: 'www.example.com', path: '/foo/bar') + # + # uri = 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 + + # Do not allow empty host names, as they are not allowed by RFC 3986. + def check_host(v) + ret = super + + if ret && v.empty? + raise InvalidComponentError, + "bad component(expected host component): #{v}" + end + + ret + end + + # + # == Description + # + # Returns the full path for an HTTP request, as required by Net::HTTP::Get. + # + # If the URI contains a query, the full path is URI#path + '?' + URI#query. + # Otherwise, the path is simply URI#path. + # + # Example: + # + # uri = 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://www.rfc-editor.org/rfc/rfc3986#section-3.2. + # + # + # Example: + # + # URI::HTTP.build(host: 'www.example.com', path: '/foo/bar').authority #=> "www.example.com" + # URI::HTTP.build(host: 'www.example.com', port: 8000, path: '/foo/bar').authority #=> "www.example.com:8000" + # 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://www.rfc-editor.org/rfc/rfc6454. + # + # + # Example: + # + # URI::HTTP.build(host: 'www.example.com', path: '/foo/bar').origin #=> "http://www.example.com" + # URI::HTTP.build(host: 'www.example.com', port: 8000, path: '/foo/bar').origin #=> "http://www.example.com:8000" + # URI::HTTP.build(host: 'www.example.com', port: 80, path: '/foo/bar').origin #=> "http://www.example.com" + # 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/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/lib/uri/https.rb b/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/lib/uri/https.rb new file mode 100644 index 0000000..50a5cab --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/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 URI for general documentation +# + +require_relative 'http' + +module 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 URI::HTTP. + class HTTPS < HTTP + # A Default port of 443 for URI::HTTPS + DEFAULT_PORT = 443 + end + + register_scheme 'HTTPS', HTTPS +end diff --git a/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/lib/uri/ldap.rb b/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/lib/uri/ldap.rb new file mode 100644 index 0000000..4544349 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/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:: +# 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 URI for general documentation +# + +require_relative 'generic' + +module URI + + # + # LDAP URI SCHEMA (described in RFC2255). + #-- + # ldap://<host>/<dn>[?<attrs>[?<scope>[?<filter>[?<extensions>]]]] + #++ + class LDAP < Generic + + # A Default port of 389 for URI::LDAP. + DEFAULT_PORT = 389 + + # An Array of the available components for 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 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 = URI::LDAP.build({:host => 'ldap.example.com', + # :dn => '/dc=example'}) + # + # uri = 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 URI::LDAP object from generic 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 = URI::LDAP.new("ldap", nil, "ldap.example.com", nil, nil, + # "/dc=example;dc=com", nil, "query", nil) + # + # See also 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 URI has a path. + # For URI::LDAP this will return +false+. + def hierarchical? + false + end + end + + register_scheme 'LDAP', LDAP +end diff --git a/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/lib/uri/ldaps.rb b/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/lib/uri/ldaps.rb new file mode 100644 index 0000000..58228f5 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/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 URI for general documentation +# + +require_relative 'ldap' + +module 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 URI::LDAP. + class LDAPS < LDAP + # A Default port of 636 for URI::LDAPS + DEFAULT_PORT = 636 + end + + register_scheme 'LDAPS', LDAPS +end diff --git a/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/lib/uri/mailto.rb b/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/lib/uri/mailto.rb new file mode 100644 index 0000000..cb8024f --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/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 URI for general documentation +# + +require_relative 'generic' + +module URI + + # + # RFC6068, the mailto URL scheme. + # + class MailTo < Generic + include RFC2396_REGEXP + + # A Default port of nil for URI::MailTo. + DEFAULT_PORT = nil + + # An Array of the available components for 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 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 'uri' + # + # m1 = URI::MailTo.build(['joe@example.com', 'subject=Ruby']) + # m1.to_s # => "mailto:joe@example.com?subject=Ruby" + # + # m2 = 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 = 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 URI::MailTo object from generic URL components with + # no syntax checking. + # + # This method is usually called from 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 URI #{addr.dump}" + end + + # check addr-spec + # don't s/\+/ /g + addr.gsub!(/%\h\h/, 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 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 'uri' + # + # uri = 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 = URI.decode_www_form_component(@to) + head = '' + body = '' + @headers.each do |x| + case x[0] + when 'body' + body = URI.decode_www_form_component(x[1]) + when 'to' + to << ', ' + URI.decode_www_form_component(x[1]) + else + head << URI.decode_www_form_component(x[0]).capitalize + ': ' + + 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/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/lib/uri/rfc2396_parser.rb b/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/lib/uri/rfc2396_parser.rb new file mode 100644 index 0000000..cefd126 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/lib/uri/rfc2396_parser.rb @@ -0,0 +1,547 @@ +# 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 URI for general documentation +# + +module URI + # + # Includes URI::REGEXP::PATTERN + # + module RFC2396_REGEXP + # + # Patterns used to parse URI's + # + module PATTERN + # :stopdoc: + + # RFC 2396 (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 URI's. + # + # It contains a Hash set of patterns and Regexp's that match and validate. + # + class RFC2396_Parser + include RFC2396_REGEXP + + # + # == Synopsis + # + # URI::RFC2396_Parser.new([opts]) + # + # == Args + # + # The constructor accepts a hash as options for parser. + # Keys of options are pattern names of 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 (URI::PATTERN::ESCAPED in default) + # * :UNRESERVED (URI::PATTERN::UNRESERVED in default) + # * :DOMLABEL (URI::PATTERN::DOMLABEL in default) + # * :TOPLABEL (URI::PATTERN::TOPLABEL in default) + # * :HOSTNAME (URI::PATTERN::HOSTNAME in default) + # + # == Examples + # + # p = URI::RFC2396_Parser.new(:ESCAPED => "(?:%[a-fA-F0-9]{2}|%u[a-fA-F0-9]{4})") + # u = p.parse("http://example.jp/%uABCD") #=> #<URI::HTTP http://example.jp/%uABCD> + # URI.parse(u.to_s) #=> raises URI::InvalidURIError + # + # s = "http://example.com/ABCD" + # u1 = p.parse(s) #=> #<URI::HTTP http://example.com/ABCD> + # u2 = URI.parse(s) #=> #<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 #initialize_pattern. + attr_reader :pattern + + # The Hash of Regexp. + # + # See also #initialize_regexp. + attr_reader :regexp + + # Returns a split 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] + + # 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 URI (absolute but no scheme): #{uri}" + end + if !opaque && (!path && (!host && !registry)) + raise InvalidURIError, + "bad 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 + + # 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 URI (is not 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 URI scheme object + # (File, FTP, HTTP, HTTPS, LDAP, LDAPS, or MailTo) or URI::Generic. + # + # == Usage + # + # URI::RFC2396_PARSER.parse("ldap://ldap.example.com/dc=example?user=john") + # #=> #<URI::LDAP ldap://ldap.example.com/dc=example?user=john> + # + def parse(uri) + 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 #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 + /(?=(?i:#{Regexp.union(*schemes).source}):)#{@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) # :nodoc: + if TO_S.respond_to?(:bind_call) + def inspect # :nodoc: + TO_S.bind_call(self) + end + else + def inspect # :nodoc: + 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 (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})?" + + # 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 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 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 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 + + # Returns +uri+ as-is if it is URI, or convert it to URI if it is + # a String. + def convert_to_uri(uri) + if uri.is_a?(URI::Generic) + uri + elsif uri = String.try_convert(uri) + parse(uri) + else + raise ArgumentError, + "bad argument (expected URI object or URI string)" + end + end + + end # class Parser + + # Backward compatibility for URI::REGEXP::PATTERN::* + RFC2396_Parser.new.pattern.each_pair do |sym, str| + unless RFC2396_REGEXP::PATTERN.const_defined?(sym, false) + RFC2396_REGEXP::PATTERN.const_set(sym, str) + end + end +end # module URI diff --git a/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/lib/uri/rfc3986_parser.rb b/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/lib/uri/rfc3986_parser.rb new file mode 100644 index 0000000..0b5f0c4 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/lib/uri/rfc3986_parser.rb @@ -0,0 +1,206 @@ +# frozen_string_literal: true +module URI + class RFC3986_Parser # :nodoc: + # 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} + (?<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 URI (is not URI?): #{uri.inspect}" + end + uri.ascii_only? or + raise InvalidURIError, "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 URI (is not URI?): #{uri.inspect}" + end + end + + def parse(uri) # :nodoc: + URI.for(*self.split(uri), self) + end + + def join(*uris) # :nodoc: + uris[0] = convert_to_uri(uris[0]) + uris.inject :merge + end + + # Compatibility for RFC2396 parser + def extract(str, schemes = nil, &block) # :nodoc: + warn "URI::RFC3986_PARSER.extract is obsolete. Use URI::RFC2396_PARSER.extract explicitly.", uplevel: 1 if $VERBOSE + RFC2396_PARSER.extract(str, schemes, &block) + end + + # Compatibility for RFC2396 parser + def make_regexp(schemes = nil) # :nodoc: + warn "URI::RFC3986_PARSER.make_regexp is obsolete. Use URI::RFC2396_PARSER.make_regexp explicitly.", uplevel: 1 if $VERBOSE + RFC2396_PARSER.make_regexp(schemes) + end + + # Compatibility for RFC2396 parser + def escape(str, unsafe = nil) # :nodoc: + warn "URI::RFC3986_PARSER.escape is obsolete. Use URI::RFC2396_PARSER.escape explicitly.", uplevel: 1 if $VERBOSE + unsafe ? RFC2396_PARSER.escape(str, unsafe) : RFC2396_PARSER.escape(str) + end + + # Compatibility for RFC2396 parser + def unescape(str, escaped = nil) # :nodoc: + warn "URI::RFC3986_PARSER.unescape is obsolete. Use URI::RFC2396_PARSER.unescape explicitly.", uplevel: 1 if $VERBOSE + escaped ? RFC2396_PARSER.unescape(str, escaped) : RFC2396_PARSER.unescape(str) + 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?(URI::Generic) + uri + elsif uri = String.try_convert(uri) + parse(uri) + else + raise ArgumentError, + "bad argument (expected URI object or URI string)" + end + end + + end # class Parser +end # module URI diff --git a/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/lib/uri/version.rb b/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/lib/uri/version.rb new file mode 100644 index 0000000..1f81060 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/lib/uri/version.rb @@ -0,0 +1,6 @@ +module URI + # :stopdoc: + VERSION = '1.1.1'.freeze + VERSION_CODE = VERSION.split('.').map{|s| s.rjust(2, '0')}.join.freeze + # :startdoc: +end diff --git a/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/lib/uri/ws.rb b/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/lib/uri/ws.rb new file mode 100644 index 0000000..ff3c554 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/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 URI for general documentation +# + +require_relative 'generic' + +module URI + + # + # The syntax of WS URIs is defined in RFC6455 section 3. + # + # Note that the Ruby 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 URI::WS. + DEFAULT_PORT = 80 + + # An Array of the available components for URI::WS. + COMPONENT = %i[ + scheme + userinfo host port + path + query + ].freeze + + # + # == Description + # + # Creates a new 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 = URI::WS.build(host: 'www.example.com', path: '/foo/bar') + # + # uri = 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 URI, as required by Net::HTTP::Get. + # + # If the URI contains a query, the full path is URI#path + '?' + URI#query. + # Otherwise, the path is simply URI#path. + # + # Example: + # + # uri = 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/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/lib/uri/wss.rb b/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/lib/uri/wss.rb new file mode 100644 index 0000000..7cea9d7 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/uri-1.1.1/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 URI for general documentation +# + +require_relative 'ws' + +module 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 URI::WS. + class WSS < WS + # A Default port of 443 for URI::WSS + DEFAULT_PORT = 443 + end + + register_scheme 'WSS', WSS +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/CHANGELOG.md b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/CHANGELOG.md new file mode 100644 index 0000000..3145f48 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/CHANGELOG.md @@ -0,0 +1,2134 @@ +# Changelog + +# 3.26.1 + + Fix compatibility with recent async-http versions + + Thanks to [Mikhail Doronin](https://github.com/misdoro) + +# 3.26.0 + +* Support Addressable::URI in request patterns + + Thanks to [Alexey Zapparov](https://github.com/ixti) + +# 3.25.2 + +* Return support for `em_http_request` + + Thanks to [Oleg](https://github.com/Koilanetroc) + +# 3.25.1 + +* Fix FrozenError in Typhoeus streaming response body + + Thanks to [Patrick Jaberg](https://github.com/patrickjaberg) + +# 3.25.0 + +* Resolve net-http adapter deprecation Ruby 3.4 + + Thanks to [Earlopain](https://github.com/Earlopain) + +# 3.24.0 + +* Ignore parsing errors when parsing invalid JSON or XML body to match against body pattern #1066 + + Thanks to [Christian Schmidt](https://github.com/c960657) + +* Added support for mocked HTTP::Connection#finished_request? method #1065 + + Thanks to [Christian Schmidt](https://github.com/c960657) + +* Detect if Patron is loaded by checking if Patron::Session constant is defined #1068 + + Thanks to [Rodrigo Argumedo](https://github.com/rodrigoargumedo) + +* Raising an ArgumentError when uri is passed as a Pathname object to stub_request or request expectation declaration. + +* Dropped support for em-http-request on Ruby 3.4. The current version of em-http-request (1.1.7) is incompatible with Ruby 3.4 due to an unresolved issue (https://github.com/igrigorik/em-http-request/issues/365). Support for em-http-request will be re-enabled once the compatibility issue is resolved. + + Thanks to [Christian Schmidt](https://github.com/c960657) + +# 3.23.1 + +* Added support for async-http version >= 0.65.0 [PR](https://github.com/bblimke/webmock/pull/1056) + + Thanks to [Jacob Frautschi](https://github.com/jakeonfire) + +# 3.23.0 + + * Fixed HTTP.rb adapter to support streaming real responses when WebMock is enabled. + + Thanks to [Viacheslav Nepomniashchikh](https://github.com/aka-nez) for reporting and investigating this [issue](https://github.com/bblimke/webmock/issues/1017). + +# 3.22.0 + + * Addressed an issue in the HTTPClient adapter where memoized stubbed responses and memoized request_signatures were incorrectly persisted between subsequent requests ([#1019](https://github.com/bblimke/webmock/issues/1019)). The implementation of a more robust thread-safety solution by [Tom Beauvais](https://github.com/tbeauvais) in [PR #300](https://github.com/bblimke/webmock/pull/300) not only resolved the memoization problem but also enhanced the overall thread safety of the adapter. This update ensures that stubbed responses and request signatures are correctly isolated to individual requests, improving both consistency and thread safety. + +# 3.21.2 + + * Corrected type checking in `WebMock::Response#assert_valid_body!` to accurately recognize `Hash` objects. Additionally, improved the clarity of the error message for unsupported body types, guiding users towards proper usage. + + Thanks to [Jake Robb](https://github.com/jakerobb) for reporting. + +# 3.21.1 + + * The stubbed Net::HTTPResponse#uri now returns request.uri, aligning it with the behavior of an actual Net::HTTPResponse. + + Thanks to [Abe Voelker](https://github.com/abevoelker) for reporting and to [Victor Maslov](https://github.com/Nakilon) and [Gio Lodi](https://github.com/mokagio) for the suggested solution. + +# 3.21.0 + + * Don't use deprecated Rack::VERSION for Rack >= 3 + + Thanks to [Étienne Barrié](https://github.com/etiennebarrie) + + * Updated HTTPClient adapter, to build request signature using the URI after filters have been applied. + + Thanks to [Matt Brown](https://github.com/mattbnz) + +# 3.20.0 + + * Optimised EmHttpRequestAdapter performance. + + Thanks to [Ricardo Trindade](https://github.com/RicardoTrindade) + + * Removed runtime dependency on base64. + + Thanks to [Earlopain](https://github.com/Earlopain) + + * Typhoeus::Response objects constructed from stubbed responses now have all timing attributes set to 0.0. + + Thanks to [James Brown](https://github.com/Roguelazer) + + * Optimised WebMock::Util::Headers by removing redundant freeze invocations. + + Thanks to [Kazuhiro NISHIYAMA](https://github.com/znz) + + * The default stubbed response body, which is an empty String, is unfrozen. + + * When building signatures of requests made by http.rb, the request body encoding is now preserved. + +# 3.19.1 + + * When passing a Proc or lambda as response body to `to_return_json`, the body is evaluated at the time of request and not at the time of `to_return_json` method invocation. + + Thanks to [Jason Karns](https://github.com/jasonkarns) for reporting. + +# 3.19.0 + + * Do not alter real (non-stubbed) request headers when handling em-http-request requests. + + Thanks to [Yoann Lecuyer](https://github.com/ylecuyer) + + * Fix matching stubs with HashExcludingMatcher + + Thanks to [Lucas Arnaud](https://github.com/lucasarnaud) + + * Remove development and test files from the gem package + + Thanks to [Orien Madgwick](https://github.com/orien) + + * Fix supported http client gem version checks. + + Thanks to [Marc Rohloff](https://github.com/marcrohloff) and [Roman Stražanec](https://github.com/romanstrazanec) + + * Non-string body passed to #to_return_json is now converted to JSON string. + + Thanks to [inkstak](https://github.com/inkstak) + + * `[::1]` is recognised as localhost e.g when passing `allow_localhost` option to `disable_net_connect!` + + Thanks to [Yuki Inoue](https://github.com/Yuki-Inoue) + + * Optimized `normalize_headers` for performance + + Thanks to [Brandon Weaver](https://github.com/baweaver) + + * Added Frozen string literal to files to optimise memory usage + + Thanks to [Josh Nichols](https://github.com/technicalpickles) + +# 3.18.1 + + * Reverted simplified connection handing in Net::HTTP adapter due to https://github.com/bblimke/webmock/issues/999 + +# 3.18.0 + + * Net::BufferedIO is not replaced anymore. + + Thanks to [Ray Zane](https://github.com/rzane) + + * Simplified connection handing in Net::HTTP adapter. + + Thanks to [Ray Zane](https://github.com/rzane) + +# 3.17.1 + + * Fixed Syntax Error + + Thanks to [Mark Spangler](https://github.com/mspangler) + +# 3.17.0 + + * Minimum required Ruby version is 2.3 + + Thanks to [Go Sueyoshi](https://github.com/sue445) + + * When using Net::HTTP, stubbed socket StubSocket#close and StubSocket#closed? behave more like the real sockets. + + Thanks to [Ray Zane](https://github.com/rzane) + + * Added `peeraddr`, `ssl_version` and `cipher` methods to stubbed sockets used by Net::HTTP. + + Thanks to [Ray Zane](https://github.com/rzane) + + * Added support for matching top-level array in JSON request body. + + E.g. + + ```` + stub_request(:post, 'www.example.com').with(body: [{a: 1}]) + ```` + + Thanks to [Cedric Sohrauer](https://github.com/cedrics) + +# 3.16.2 + + * Minimum required Ruby version is 2.0. + +# 3.16.0 (yanked) + + * Fix leaky file descriptors and reuse socket for persistent connections. + + Thanks to [Ray Zane](https://github.com/rzane) + + * Allow specifying for what URIs or hosts, Net::HTTP should connect on start. + + Thanks to [Ray Zane](https://github.com/rzane) + +# 3.15.2 + + * Minimum required Ruby version is 2.0. + +# 3.15.0 (yanked) + + * fixed async-http adapter on Windows + + Thanks to [Pavel Rosický](https://github.com/ahorek) + + * Support for http.rb >= 5.0.2 + + Thanks to [ojab](https://github.com/ojab) + + * Curb adapter supports headers with `:` character in the header value + + Thanks to [Giorgio Gambino](https://github.com/mrbuzz) + + * Support for matching body of JSON or application/x-www-form-urlencoded requests with content type header including charset. + + Thanks to [Timmitry](https://github.com/Timmitry) + + * Prevent double-wrapping http.rb features on non-stubbed requests + + Thanks to [Michael Fairley](https://github.com/michaelfairley) + +# 3.14.0 + + * Bump Addressable from 2.3.6 to 2.8.0 + + Thanks to [Eduardo Hernandez](https://github.com/EduardoGHdez) + +# 3.13.0 + + * Support http.rb 5.x + + Thanks to [Will Storey](https://github.com/horgh) + +# 3.12.2 + + * Fixed em-http-request adapter to avoid calling middleware twice. + + Thanks to [Alex Vondrak](https://github.com/ajvondrak) + +# 3.12.1 + + * Fixed handling of URIs with IPv6 addresses with square brackets when in Net::HTTP adapter. + + Thanks to [Johanna Hartmann](https://github.com/JohannaHartmann) + +# 3.12.0 + + * Added support for handling custom JSON and XML content types e.g. 'application/vnd.api+json' + +# 3.11.3 + + * Fixed async-http adapter to only considered requests as real if they are real. + + Thanks to [Tony Schneider](https://github.com/tonywok) and [Samuel Williams](https://github.com/ioquatix) + +# 3.11.2 + + * Fix for Manticore streaming mode + + Thanks to [Oleksiy Kovyrin](https://github.com/kovyrin) + +# 3.11.1 + + * Compatibility with async-http 0.54+ + + Thanks to [Jun Jiang](https://github.com/jasl) + +# 3.11.0 + + * Added support for `features` in http.rb adapter. + + Thanks to [Carl (ce07c3)](https://github.com/ce07c3) + +# 3.10.0 + + * Added option to global stubs to have lower priority than local stubs. + + WebMock.globally_stub_request(:after_local_stubs) do + { body: "global stub body" } + end + + stub_request(:get, "www.example.com").to_return(body: 'non-global stub body') + + expect(http_request(:get, "http://www.example.com/").body).to eq("non-global stub body") + + Thanks to [Marek Kasztelnik](https://github.com/mkasztelnik) + +# 3.9.5 + + * Prevent overwriting `teardown` method in Test::Unit + + Thanks to [Jesse Bowes](https://github.com/jessebs) + +# 3.9.4 + + * More intuitive error message when stubbed response body was provided as Hash + + Thanks to [Ben Koshy](https://github.com/BKSpurgeon) + +# 3.9.3 + + * Make httpclient_adapter thread-safe + + Thanks to [Adam Harwood](https://github.com/adam-harwood) + +# 3.9.2 + + * Made global stubs thread-safe + + Thanks to [Adam Harwood](https://github.com/adam-harwood) + +# 3.9.1 + + * Fixed support for passing `URI` objects as second argument of `stub_request` + + Thanks to [Ryan Kerr](https://github.com/leboshi) + +## 3.9.0 + + * Allow using a "callable" (like a proc) as URI pattern + + stub_request(:any, ->(uri) { true }) + + Thanks to [John Hawthorn](https://github.com/jhawthorn) + + * Added stubbed IO on stubbed socket in Net::HTTP adapter. + + Thanks to [Thilo Rusche](https://github.com/trusche) + + * When 'webmock/rspec' is required, reset WebMock after all after(:each/example) hooks + + Thanks to [Andrew Stuntz](https://github.com/drews256) + + * Fixed `net_connect_allowed?` when invoked with no arguments, when there were any allowed URIs passed to `disable_net_connect?`. + + Thanks to [Lucas Uyezu](https://github.com/lucasuyezu) + + * Fixed async-http adapter which caused Async::HTTP::Client or Async::HTTP::Internet to hang and never return a response. + + Thanks to [Bruno Sutic](https://github.com/bruno-) and [Samuel Williams](https://github.com/ioquatix) + + * Fixed warning when using async-http adapter + + Thanks to [Bruno Sutic](https://github.com/bruno-) + + * Dropped support for Ruby 2.3 - EOL date: 2019-03-31 + + * Dropped support for Ruby 2.4 - EOL date: 2020-03-31 + + * Handling matching of Addressable::Template patterns that have an ip address without port and patterns that have ip address and don’t have schema and path. + + Thanks to [Rafael França](https://github.com/rafaelfranca) and [guppy0356](https://github.com/guppy0356) + +## 3.8.3 + + * Fixed problem introduced in version 3.4.2, which caused matching against Addressable::Template representing host part of the URI to raise an error. + + Thanks to [Vesa Laakso](https://github.com/valscion) + +## 3.8.2 + + * Support correct encoding parameter for HTTP.rb 2.x and earlier + + Thanks to [Alex Coomans](https://github.com/drcapulet) + +## 3.8.1 + + * Added support for mocking non-ASCII bodies when making requests with HTTP.rb + + Thanks to [Patrik Ragnarsson](https://github.com/dentarg) + +## 3.8.0 + + * Fixed options handling when initialising Async::HTTP::Client + + Thanks to [Samuel Williams](https://github.com/ioquatix) + + * Ruby 2.7 support. + + Thanks to [Ryan Davis](https://github.com/zenspider) and [Brandur](https://github.com/brandur) + +## 3.7.6 + + * Suppressed keyword argument warnings in Ruby 2.7 in async-http adapter. + + Thanks to [Koichi ITO](https://github.com/koic) + +## 3.7.5 + + * Suppress Excon warning generated by extra key + + Thanks to [Marco Costa](https://github.com/marcotc) + +## 3.7.4 + + * Resetting memoized response fields in Curb adapter. + + Thanks to [Andrei Sidorov](https://github.com/heretge) + +## 3.7.3 + + * Fix for http.rb. Allow passing an output buffer to HTTP::Response::Body#readpartial + + Thanks to [George Claghorn](https://github.com/georgeclaghorn) + + * Fixed Manticore adapter to invoke Manticore failure handler on stubbed timeout + + Thanks to [Alex Junger](https://github.com/alexJunger) + + * Added project metadata to the gemspec + + Thanks to [Orien Madgwick](https://github.com/orien) + +## 3.7.2 + + * Fixed handling of non UTF-8 encoded urls + + Thanks to [Rafael França](https://github.com/rafaelfranca) + + * Fixed "shadowing outer local variable" warning + + Thanks to [y-yagi](https://github.com/y-yagi) + +## 3.7.1 + + * Fixed Async::HTTP::Client adapter code to not cause Ruby warning + + Thanks to [y-yagi](https://github.com/y-yagi) + +## 3.7.0 + + * Support for Async::HTTP::Client + + Thanks to [Andriy Yanko](https://github.com/ayanko) + +## 3.6.2 + + * Fixed Patron adapter to handle HTTP/2 status line. + + Thanks to [Fábio D. Batista](https://github.com/fabiob) + +## 3.6.1 + + * Fixed issue with matching Addressable::Template without a period in the domain + + Thanks to [Eike Send](https://github.com/eikes) + + * Support for `write_timeout` in Net::HTTP + + Thanks to [Claudio Poli](https://github.com/masterkain) + + * Fixed issue with handling urls with ":80" or ":443" in the path. + + Thanks to [Csaba Apagyi](https://github.com/thisismydesign) for reporting and to [Frederick Cheung](https://github.com/fcheung) for fixing the issue. + +## 3.6.0 + + * Compatibility with the latest version of hashdiff gem, with constant changed from HashDiff to Hashdiff + + Thanks to [Jeff Felchner](https://github.com/jfelchner) + + * Added a hint to the error message raised when `with` method is called without args or a block. + + Thanks to [Adam Sokolnicki](https://github.com/asok) + + * Resetting configured HTTP method in Curb adapter after each request + + Thanks to [tiendo1011](https://github.com/tiendo1011) + + * Added `WebMock.enable_net_connect!` as an alias for `WebMock.allow_net_connect!` + and `WebMock.disallow_net_connect!` as an alias for `WebMock.disable_net_connect!` + + Thanks to [SoonKhen OwYong](https://github.com/owyongsk) + + * Fixed handling of empty arrays as query params when using Faraday + + Thanks to [Ryan Moret](https://github.com/rcmoret) + +## 3.5.1 + + * Disabling TracePoint defined in Net::BufferedIO in case of exception being raised. + + Thanks to [Koichi Sasada](https://github.com/ko1) + + +## 3.5.0 + + * Ruby 2.6.0 support + + Thanks to [Arkadiy Tetelman](https://github.com/arkadiyt) + + * Added `WebMock.reset_executed_requests!` method. + + stub_get = stub_request(:get, "www.example.com") + Net::HTTP.get('www.example.com', '/') + WebMock::RequestRegistry.instance.times_executed(stub_get.request_pattern) # => 1 + reset_executed_requests! + WebMock::RequestRegistry.instance.times_executed(stub_get.request_pattern) # => 0 + + Thanks to [Olia Kremmyda](https://github.com/oliakremmyda) + + * Performance improvements + + Thanks to [Pavel Rosický](https://github.com/ahorek) + +## 3.4.2 + + * Fixed `rbuf_fill` in Net::HTTP adapter to be thread-safe + + Thanks to [Arkadiy Tetelman](https://github.com/arkadiyt) + + * Fix invalid scheme error with Addressable::Template + + Thanks to [Kazato Sugimoto](https://github.com/uiureo) + +## 3.4.1 + + * When comparing url encoded body to a body from request stub, which was declared as hash, only String, Numeric and boolean hash values are stringified before the comparison. + + Thanks to [Lukas Pokorny](https://github.com/luk4s) + +## 3.4.0 + + * Ruby 2.6 support. Prevent `Net/ReadTimeout` error in Ruby 2.6 + + Thanks to [Koichi ITO](https://github.com/koic) + + * Handling query params, which represent nested hashes with keys starting with non word characters. + + Thanks to [rapides](https://github.com/rapides) for reporting the issue. + + * Patron adapter handles PATCH requests with body. + + Thanks to [Mattia](https://github.com/iMacTia) for reporting the issue. + + * Allowing requests with url encoded body to be matched by request stubs declared with hash body with non-string values. + + stub_request(:post, "www.example.com").with(body: {"a" => 1, "b" => false}) + + RestClient.post('www.example.com', 'a=1&b=false', :content_type => 'application/x-www-form-urlencoded') # ===> Success + + Thanks to [Kenny Ortmann](https://github.com/yairgo) for suggesting this feature. + + * When request headers contain 'Accept'=>'application/json' and no registered stub matches the request, WebMock prints a suggested stub code with to_return body set to '{}'. + + Thanks to [redbar0n](https://github.com/redbar0n) + +* Improved suggested stub output when the request stub only contains headers declaration. + + Thanks to [Olia Kremmyda](https://github.com/oliakremmyda) + +* Fixed Curb adapter to handle `reset` method. + + Thanks tp [dinhhuydh](https://github.com/dinhhuydh) for reporting the issue. + Thanks to [Olia Kremmyda](https://github.com/oliakremmyda) for fixing it. + + +## 3.3.0 + + * Better formatting of outputted request stub snippets, displayed on failures due to unstubbed requests. + + Thanks to [Olia Kremmyda](https://github.com/oliakremmyda) + + +## 3.2.1 + + * Fixed Ruby warning under Ruby 2.5 + + Thanks to [Matt Brictson](https://github.com/mattbrictson) + + +## 3.2.0 + + * Automatically disable WebMock after Rspec suite + + Thanks to [Michał Matyas](https://github.com/d4rky-pl) + + * Fixed bug when handling redirection using Curb. + + Thanks to [Olia Kremmyda](https://github.com/oliakremmyda) + + +## 3.1.1 + + * Warning message is displayed only once when adding query params to URIAddressablePattern. + +## 3.1.0 + + * http.rb 3.0.0 compatibility + + Thanks to [Piotr Boniecki](https://github.com/Bonias) + + * Typhoeus 1.3.0 support + + Thanks to [NARUSE, Yui](https://github.com/nurse) + + * Added support for matching partial query params using hash_excluding + + stub_request(:get, "www.example.com"). + with(query: hash_excluding({"a" => "b"})) + + RestClient.get("http://www.example.com/?a=b") # ===> Failure + RestClient.get("http://www.example.com/?a=c") # ===> Success + + Thanks to [Olexandr Hoshylyk](https://github.com/Warrior109) + + * Added MRI 2.3+ frozen string literal compatibility + + Thanks to [Pat Allan](https://github.com/pat) + + * Ensured that HTTPClient adapter does not yield block on empty response body if a block is provided + + Thanks to [NARUSE, Yui](https://github.com/nurse) + + * Fixed issue with `to_timeout` incorrectly raising `HTTP::ConnectionError` instead of `HTTP::TimeoutError` when using http.rb + + Thanks to [Rick Song](https://github.com/RickCSong) + + * Fixed problem with `response.connection.close` method being undefined when using http.rb + + Thanks to [Janko Marohnić](https://github.com/janko-m) + + * Fixed problem with matching Net::HTTP request header values assigned as numbers. + + Thanks to [Felipe Constantino de Oliveira](https://github.com/felipecdo) for reporting the issue. + + * Fixed problem with Net::HTTP adapter converting empty response body to nil for non 204 responses. + + Thanks to [Jeffrey Charles](https://github.com/jeffcharles) for reporting the issue. + + +## 3.0.1 + + * Suppressed \`warning: \`&' interpreted as argument prefix\` + + Thanks to [Koichi ITO](https://github.com/koic) + +## 3.0.0 + + * Dropped support for Ruby 1.9.3 + + * Using Ruby >= 1.9 hash key syntax in stub suggestions + + Thanks to [Tarmo Tänav](https://github.com/tarmo) + + * Add at_least matchers for fakeweb-style expectations + + Thanks to [Joe Marty](https://github.com/mltsy) + + * Fix against "can't modify frozen String' error when Ruby option `frozen_string_literal` is enabled. + + Thanks to [Chris Thomson](https://github.com/christhomson) + + * Handling `read_timeout` option in Net::HTTP in Ruby >= 2.4 + + Thanks to [Christof Koenig](https://github.com/ckoenig) + + * `RequestRegistry` fix for `RuntimeError - can't add a new key into hash during iteration` + + Thanks to [Chung-Yi Chi](https://github.com/starsirius) + +## 2.3.2 + + * Restored support for Ruby 1.9.3 to comply with semantic versioning. + + Thanks to [Jordan Harband](https://github.com/ljharb) for reporting the problem. + +## 2.3.1 + + * Added support for Ruby 2.4 + + Thanks to [Koichi ITO](https://github.com/koic) + + * Dropped support for Ruby 1.9.3 + +## 2.2.0 + + * Added `refute_requested` as an alias for `assert_not_requested` + + Thanks to [Michael Grosser](https://github.com/grosser) + + * Raising `Net::OpenTimeout` instead of `Timeout::Error` if available when a request stub is declared `to_timeout` + + Thanks to [Gabe Martin-Dempesy](https://github.com/gabetax) + +## 2.1.1 + + * Added support for handling status messages in Excon responses. + + Thanks to [Tero Marttila](https://github.com/SpComb) for reporting the issue. + +## 2.1.0 + + * Added support for `on_debug` callback in Curb. + + Thanks to [Pavel Jurašek](https://github.com/pavel-jurasek-bcgdv-com) + + * Added support for PATCH requests using Curb. + + Thanks to [Pavel Jurašek](https://github.com/pavel-jurasek-bcgdv-com) + +## 2.0.3 + + * Handling headers passed as an Array to Curl::Easy + + Thanks to [Chelsea](https://github.com/grosscr) for reporting the issue. + + * Removed Ruby warnings. + + Thanks to [Aaron Kromer](https://github.com/cupakromer) + +## 2.0.2 + + * Using `Base64.strict_encode64` instead of `Base64.encode64` to handle long user:pass basic auth credentials + + Thanks to [Jonathan Schatz](https://github.com/modosc) + + * Fixed handling of Authorisation header provided as string instead of array when using em-http-request. + + Thanks to [Michael Richardson](https://github.com/TTransmit) for reporing the issue. + + * Ensured `WebMock.net_connect_explicit_allowed?` always returns boolean. + + Thanks tp [Jose Luis Honorato](https://github.com/jlhonora) + +## 2.0.1 + + * Added code responsible for loading em-http-request if available, which has been removed by mistake. + + Thanks to [Vasiliy](https://github.com/304) + + * WebMock loads "base64" if it's not yet loaded. + + Thanks to [Taiki Ono](https://github.com/taiki45). + +## 2.0.0 + + * `require 'webmock'` does not enable WebMock anymore. `gem 'webmock'` can now be safely added to a Gemfile and no http client libs will be modified when it's loaded. Call `WebMock.enable!` to enable WebMock. + + Please note that `require 'webmock/rspec'`, `require 'webmock/test_unit'`, `require 'webmock/minitest'` and `require 'webmock/cucumber'` still do enable WebMock. + + * Dropped support for Ruby < 1.9.3 + + * Dropped support for em-http-request < 1.0.0 + + * WebMock 2.0 does not match basic authentication credentials in the userinfo part of the url, with credentials passed in `Authorization: Basic ...` header anymore. + It now treats the Authorization header and credentials in the userinfo part of a url as two completely separate attributes of a request. + + The following stub declaration, which used to work in WebMock 1.x, is not going to work anymore + + stub_request(:get, "user:pass@www.example.com") + + Net::HTTP.start('www.example.com') do |http| + req = Net::HTTP::Get.new('/') + req.basic_auth 'user', 'pass' + http.request(req) + end # ===> Failure + + In order to stub a request with basic authentication credentials provided in the Authorization header, please use the following code: + + stub_request(:get, "www.example.com").with(basic_auth: ['user', 'pass']) + + or + + stub_request(:get, "www.example.com"). + with(headers: 'Authorization' => "Basic #{ Base64.strict_encode64('user:pass').chomp}") + + In order to stub a request with basic authentication credentials provided in the url, please use the following code: + + stub_request(:get, "user:pass@www.example.com") + + RestClient.get('user:pass@www.example.com') # ===> Success + +## 1.24.6 + + * Fixed issue with RUBY_VERSION comparison using old RubyGems. + + Thanks to [Chris Griego](https://github.com/cgriego). + + * Support for http.rb >= 2.0.0 + +## 1.24.4 + + * Fixed the issue with parsing query to a hash with nested array i.e. `a[][b][]=one&a[][c][]=two` + + Thanks to [Tim Diggins](https://github.com/timdiggins) for reporting the issue. + Thanks to [Cedric Pimenta](https://github.com/cedricpim) for finding the solution. + +## 1.24.3 + + * Allow Net:HTTP headers keys to be provided as symbols if `RUBY_VERSION` >= 2.3.0 + + Thanks to [Alex Kestner](https://github.com/akestner) + + * Added a clear message on an attempt to match a multipart encoded request body. + WebMock doesn't support requests with multipart body... yet. + + * `WebMock.disable_net_connect` `:allow` option, provided as regexp, matches https URIs correctly. + + * `WebMock.disable_net_connect` `:allow` option can be set as a url string with scheme, host and port. + + WebMock.disable_net_connect!(:allow => 'https://www.google.pl') + + Thanks to [Gabriel Chaney](https://github.com/gabrieljoelc) for reporting the issue. + +## 1.24.2 + + * Improve parsing of params on request + + Thanks to [Cedric Pimenta](https://github.com/cedricpim) + +## 1.24.1 + + * HTTPClient adapter supports reading basic authentication credentials directly from Authorization header. + + Thanks to [Michiel Karnebeek](https://github.com/mkarnebeek) + +## 1.24.0 + + * Enabled support for Curb > 0.8.6 + +## 1.23.0 + + * `WebMock.disable_net_connect` accepts `:allow` option with an object that responds to `#call`, receiving a `URI` object and returning a boolean: + + + denylist = ['google.com', 'facebook.com', 'apple.com'] + allowed_sites = lambda{|uri| + denylist.none?{|site| uri.host.include?(site) } + } + WebMock.disable_net_connect!(:allow => allowed_sites) + + RestClient.get('www.example.org', '/') # ===> Allowed + RestClient.get('www.facebook.com', '/') # ===> Failure + RestClient.get('apple.com', '/') # ===> Failure + + Thanks to [Pablo Brasero](https://github.com/pablobm) + + * Support for HTTPClient stream responses with body chunks + + Thanks to [Cedric Pimenta](https://github.com/cedricpim) + + +## 1.22.6 + + * Fixes [issue](https://github.com/bblimke/webmock/issues/568) around + WebMock restricting [Addressable](https://github.com/sporkmonger/addressable) + version, based on Ruby 1.8.7 for all versions of Ruby. + + This change inverts that, and forces Ruby 1.8.7 users to specify in thier + Gemfile an Addressable version < 2.4.0. + + Thanks to [PikachuEXE](https://github.com/PikachuEXE) and + [Matthew Rudy Jacobs](https://github.com/matthewrudy). + +## 1.22.5 + + * Fixes [bug](https://github.com/bblimke/webmock/issues/565) where WebMock tries + to alias a method that is deprecated in Ruby Versions > 1.9.2 ('sysread' for class 'StringIO') + + Thanks to [Marcos Acosta](https://github.com/mmaa) for discovering this bug. + +## 1.22.4 + + * Adds support for JSONClient (a subclass of HTTPClient) + + Thanks to [Andrew Kozin](https://github.com/nepalez) + + * Adds support for Ruby 2.3.0 + + Thanks to [Charles Pence](https://github.com/cpence) + + * Adds support for [http](https://github.com/httprb/http) versions >= 1.0.0 + + Thanks to [Alexey Zapparov](https://github.com/ixti) + + * Fixes support for Ruby 1.8.7 by restrciting Addressable version < 2.4.0 + + Thanks to [Matthew Rudy Jacobs](https://github.com/matthewrudy) + +## 1.22.3 + + * Return "effective_url" attribute in Typhoeus::Response + + Thanks to [Senya](https://github.com/cmrd-senya) + +## 1.22.2 + + * Fix: prevents adding an extra =true to urls with parameters without values + + Thanks to [David Begin](https://github.com/davidbegin) + +## 1.22.1 + + * Adds Rack as a development dependency and removes require rack/utils in main lib. + + Thanks to [Keenan Brock](https://github.com/kbrock) + +## 1.22.0 + + All the credit for preparing this release go to [David Begin](https://github.com/davidbegin)! + + * Adds [Manticore](https://github.com/cheald/manticore) support. + + Thanks to [Mike Knepper](https://github.com/mikeknep), [David Abdemoulaie](https://github.com/hobodave) + + * Update to Show a hash diff for requests that have stubs with a body. + + Thanks to [yurivm](https://github.com/yurivm) + + * Update to mirror Net::HTTP handling of headers as symbols + + * Update to ignore non-comparable-values error when sorting + query values, because sorting is just a convience. + + Thanks to [Magne Land](https://github.com/magneland) + + * Covert Boolean values to Strings when using them to define + the body of a request. + + Thanks to [Krzysztof Rygielski](https://github.com/riggy) + + * Fixes WebMock's parsing Multibyte characters + + Thanks to [Zhao Wen](https://github.com/VincentZhao) + + * Updates to be compatible with httpclient 2.6.0 + + * Converts keys from symbols to strings when for QueryMapper.to_query + + Thanks to [Ramon Tayag](https://github.com/ramontayag) + + * Restricts http.rb version to 0.7.3 for Ruby 1.8.7 + + * Fixes issue emulating em-http-request's handling of multiple requests. + + Thanks to [Matt Palmer](https://github.com/mpalmer) + + * WebMock requires only the necessary parts of crack to avoid pulling in safe_yaml + + Thanks to [Johannes Schlumberger](https://github.com/spjsschl) + +## 1.21.0 + + * Support for http.rb >= 0.8.0 + + Thanks to [Zachary Anker](https://github.com/zanker), [Aleksey V. Zapparov](https://github.com/ixti) + + * Support for http.rb 0.7.0 + + Thanks to [Mattias Putman](https://github.com/challengee) + + * Added support for RSpec3-like `and_return`, `and_raise`, `and_timeout` syntax. + + Thanks to [Franky Wahl](https://github.com/frankywahl) + + * Restricted Curb support up to version 0.8.6. WebMock specs fail with Curb 0.8.7. + +## 1.20.4 + + * Fixed support for `hash_including` matcher in RSpec 3 + +## 1.20.3 + + * `with` method raises error if provided without options hash and without block + + * `with` and `to_return` raise an error if invoked with invalid keys in options hash. + +## 1.20.2 + + * WebMock provides a helpful error message if an incompatible object is given as response body. + + Thanks to [Mark Lorenz](https://github.com/dapplebeforedawn) + +## 1.20.1 + + * `assert_requested` and `assert_not_requested` accept `at_least_times` and `at_most_times` options + + Thanks to [Dan Buettner](https://github.com/Capncavedan) + + * Silenced `instance variable undefined` warnings in Curb adapted. + + Thanks to [Sven Riedel](https://github.com/sriedel) + +## 1.20.0 + + * Add support for on_missing callback of Curb::Easy + + Thanks to [Tasos Stathopoulos](https://github.com/astathopoulos) + + * Add at_least_times and at_most_times matchers + + Thanks to [Dan Buettner](https://github.com/Capncavedan) + +## 1.19.0 + + * Fixed issue with Excon adapter giving warning message when redirects middleware was enabled. + + Thanks to [Theo Hultberg](https://github.com/iconara) for reporting that. + + * Fixed issue with `undefined method 'valid_request_keys' for Excon::Utils:Module` + + Thanks to [Pablo Jairala](https://github.com/davidjairala) + + * Fixed query mapper to encode `'one' => ['1','2']` as `'one[]=1&one[]=2'`. + + Thanks to [Insoo Buzz Jung](https://github.com/insoul) + + * Improved cookies support for em-http-request + + Thanks to [Carlos Alonso Pérez](https://github.com/calonso) + + * Fix HTTP Gem adapter to ensure uri attribute is set on response object. + + Thanks to [Aleksey V. Zapparov](https://github.com/ixti) + + * Fixed HTTPClient adapter. The response header now receives `request_method`, `request_uri`, and `request_query` transferred from request header + + Thanks to [trlorenz](https://github.com/trlorenz) + + * Query mapper supports nested data structures i.e. `{"first" => [{"two" => [{"three" => "four"}, "five"]}]}` + + Thanks to [Alexander Simonov](https://github.com/simonoff) + + * Fixed compatibility with latest versions of Excon which don't define `VALID_REQUEST_KEYS` anymore. + + Thanks to [Pablo Jairala](https://github.com/davidjairala) + + * Request method is always a symbol is request signatures. This fixes the issue of WebMock not matching Typhoeus requests with request method defined as string. + + Thanks to [Thorbjørn Hermanse](https://github.com/thhermansen) + + * Stubbing instructions which are displayed when no matching stub is found, can be disabled with `Config.instance.show_stubbing_instructions = false` + + Thanks to [Mark Lorenz](https://github.com/dapplebeforedawn) + + * Notation used for mapping query strings to data structure can be configured i.e. `WebMock::Config.instance.query_values_notation = :subscript`. This allows setting `:flat_array` notation which supports duplicated parameter names in query string. + + Thanks to [tjsousa](https://github.com/tjsousa) + +## 1.18.0 + +* Updated dependency on Addressable to versions >= 2.3.6 + +* Added support for matching uris using RFC 6570 (URI Templates) + + uri_template = Addressable::Template.new "www.example.com/{id}/" + stub_request(:any, uri_template) + + Thanks to [Max Lincoln](https://github.com/maxlinc) + +* Fixed content length calculation for Rack responses with UTF8 body + + Thanks to [Oleg Gritsenko](https://github.com/Claster) + +* Add missing Curl::Easy aliases + + Thanks to [Hwan-Joon Choi](https://github.com/hc5duke) + +* HTTP Gem >= 0.6.0 compatibility + + Thanks to [Aleksey V. Zapparov](https://github.com/ixti) + +* Minitest 4 and 5 compatibility. + + Thanks to [SHIBATA Hiroshi](https://github.com/hsbt) + +## 1.17.4 + +* Update matchers for RSpec 3's matcher protocol + + Thanks to [Rob Olson](https://github.com/robolson) + +## 1.17.3 + +* Fixed issue with Rack response removing 'Content-Type' header + + Thanks to [Bo Jeanes](https://github.com/bjeanes) and [Matthew Conway](https://github.com/mattonrails) + +## 1.17.2 + +* Support for chunked responses in Curb + + Thanks to [Zachary Belzer](https://github.com/zbelzer) + +* Fixed handling of request body passed as a hash to `Typhoeus.post` + + Thanks to [Mason Chang](https://github.com/changmason) for reporting. + +## 1.17.1 + +* Added missing license statements. + + Thanks to [Praveen Arimbrathodiyil](https://github.com/pravi) + +## 1.17.0 + +* HTTP gem support! + + Thanks to [Aleksey V. Zapparov](https://github.com/ixti) + +* Limited Excon gem requirement to version < 0.30 until the compatibility with version > 0.30.0 is fixed. + + Thanks to [Aleksey V. Zapparov](https://github.com/ixti) + +* Fixed issue where empty query key caused a `TypeError` + + Thanks to [Jon Rowe](https://github.com/JonRowe) + +* Handling Typhoeus `on_headers` and `on_body` params. + + Thanks to [Matt Burke](https://github.com/spraints) + +## 1.16.1 + +* Fixed "NameError: uninitialized constant WebMock::Response::Pathname" issue. + + Thanks to [Alex Stupakow and Karen Wang](https://github.com/stupakov) for the fix. + +## 1.16.0 + +* Allow a Pathname to be passed as a Response body + + stub_request(:get, /example.com/).to_return( + body: Rails.root.join('test/fixtures/foo.txt') + ) + + Thanks to [Ben Pickles](https://github.com/benpickles) + +* `hash_including` matcher can be initialized with empty keys to match any values. + + stub_request(:post, "www.example.com").with(:body => hash_including(:a, :b => {'c'})) + RestClient.post('www.example.com', '{"a":"1","b":"c"}', :content_type => 'application/json') + + Thanks to [Stefano Uliari](https://github.com/steookk) + +## 1.15.2 + +* Fixed `hash_including` to accept a splat of solitary keys. + + Thanks to [Tamir Duberstein](https://github.com/tamird) and [https://github.com/strongriley](https://github.com/strongriley) + +## 1.15.0 + +* Excon >= 0.27.5 compatibility. + + Thanks to [Brian D. Burns](https://github.com/burns) + +## 1.14.0 + +* Handling non UTF-8 characters in query params. + + Thanks to [Florian Dütsch](https://github.com/der-flo) for reporting the issue. + +* Added support for `request_block` param in Excon + + Thanks to [Dmitry Gutov](https://github.com/dgutov) for reporting the issue. + +* Fixed compatibility with latest Curb + + Thanks to [Ian Lesperance](https://github.com/elliterate) and [Matthew Horan](https://github.com/mhoran) + +* Triggering errbacks assynchronously in em-http-request adapter. + + Thanks to [Ian Lesperance](https://github.com/elliterate) and [Matthew Horan](https://github.com/mhoran) + +* Handling query params with a hashes nested inside arrays. + + Thanks to [Ian Asaff](https://github.com/montague) + +* Changed NetConnectNotAllowedError to inherit from Exception to allow it to bubble up into a test suite. + + Thanks to [Daniel van Hoesel](https://github.com/s0meone) + +* HTTPClient adapter is thread safe. + + Thanks to [Tom Beauvais](https://github.com/tbeauvais) + +## 1.13.0 + +* Net::HTTP::Persistent compatibility. + WebMock doesn't disconnect previously started connections upon a request anymore. + + +## 1.12.3 + +* Fixed issue with handling Addressable::URI with query params passed to `Net::HTTP.get_response` + + Thanks to [Leif Bladt](https://github.com/leifbladt) + +* Fixed HTTPClient adapter to not raise an error if a request with multipart body is executed. + +## 1.12.2 + +* Fixed issue with handling request.path when Addressable::URI is passed to #request instead of URI with Ruby 2.0. + + Thanks to [Leif Bladt](https://github.com/leifbladt) + +* Accept integers as query param values in request stubs + + i.e. `stub_request(:get, /.*/).with(:query => {"a" => 1})` + + Thanks to [Mitsutaka Mimura](https://github.com/takkanm) + +## 1.12.1 + +* Fixed Minitest < 5.0 compatibility + + Thanks to [Alex Tomlins](https://github.com/alext) for reporting the issue. + +## 1.12.0 + +* Not using Gem spec anymore to check loaded Curb version. + +* `WebMock.disable_net_connect!` now accepts array of regexps as allow param: + + i.e. `WebMock.disable_net_connect!(:allow => [/google.com/, /yahoo.com/])` + + Thanks to [Bastien Vaucher](https://github.com/bastien) + +* Fixed `on_header` Curb callback behaviour in Curb adapter + + Thanks to [Joel Chippindale](https://github.com/mocoso) + +* Fixed aws-sdk compatibility with Ruby 2.0, by supporting `continue_timeout` accessor on Net::HTTP socket. + + Thanks to [Lin Jen-Shin](https://github.com/godfat) + +* Fixed WebMock::Server to not give "log writing failed. can't be called from trap context" warning with Ruby 2.0 + + Thanks to [Murahashi Sanemat Kenichi](https://github.com/sanemat) + +* Added support for EM-HTTP-Request streaming data off disk feature. + + Thanks to [Lin Jen-Shin](https://github.com/godfat) + +* Added compatibility with Minitest 5 + + Thanks to [Tim Kurvers](https://github.com/timkurvers) + +* Excon >= 0.22 compatibility. + +* README has nice sytnax hightlighting and fixed code styling! + + Thanks to [Ilya Vassilevsky](https://github.com/vassilevsky) + +* Compatibility with Rails 4 `rack.session.options` + + Thanks to [gotwalt](https://github.com/gotwalt) + +## 1.11.0 + +* Excon >= 0.17 support. + + Thanks to [Nathan Sutton](https://github.com/nate) for reporting this issue and to [Wesley Beary](https://github.com/geemus) and [Myron Marston](https://github.com/myronmarston) for help. + +## 1.10.2 + +* '+' in request path is treated as plus, but in query params always as a space. + +## 1.10.1 + +* '+' in request body is still treated as a space. This fixes a bug introduced in previous version. + + Thanks to [Erik Michaels-Ober](https://github.com/sferik) for reporting this problem. + +* Fixed issue: response body declared as Proc was not evaluated again on subsequent requests. + + Thanks to [Rick Fletcher](https://github.com/rfletcher) for reporting this issue. + +## 1.10.0 + +* '+' in query params is not treated as space anymore and is encoded as %2B + + Thanks to [goblin](https://github.com/goblin) for reporting this issue. + +* added `remove_request_stub` method to the api to allow removing unused stubs i.e. + + stub_get = stub_request(:get, "www.example.com") + remove_request_stub(stub_get) + +* `assert_requested` and `assert_not_requested` raise an error if a stub object is provided together with a block. + +## 1.9.3 + +* Fixed issue with unavailable constant Mutex in Ruby < 1.9 + + Thanks to [Lucas Dohmen](https://github.com/moonglum) for reporting this issue. + +## 1.9.2 + +* Added support for Excon's :response_block parameter + + Thanks to [Myron Marston](https://github.com/myronmarston) for reporting this issue. + +## 1.9.1 + +* Fix 'rack.errors' not being set for Rack apps + + Thanks to [Alex Grant](https://github.com/grantovich) + +* Added support for minitest assertions count + + Thanks to [Mokevnin Kirill](https://github.com/mokevnin) + +* Fixed issues with registering http requests in multi-threaded environments + + Thanks to [Tom Beauvais](https://github.com/tbeauvais) + +* Bumped Crack version to >=0.3.2 + + Thanks to [Jake Benilov](https://github.com/benilovj) + +* Fixed issues in Typhoeus 0.6. Defaulted method to GET when no method specified. + + Thanks to [Hans Hasselberg](https://github.com/i0rek) + +* Add license information to the gemspec + + Thanks to [Jordi Massaguer Pla](https://github.com/jordimassaguerpla) and [Murahashi Sanemat Kenichi](https://github.com/sanemat) + +* Added support for :expects option in Excon adapter + + Thanks to [Evgeniy Dolzhenko](https://github.com/dolzenko) + +* Fixed Faye compatibility by treating StringIO in Net::HTTP adapter properly + + Thanks to [Pavel Forkert](https://github.com/fxposter) + +* Updated VCR link + + Thanks to [Rex Feng](https://github.com/xta) + +## 1.9.0 + +* Added support for Typhoeus >= 0.5.0 and removed support for Typhoeus < 0.5.0. + + Thanks to [Hans Hasselberg](https://github.com/i0rek) + +## 1.8.11 + +* Fix excon adapter to handle `:body => some_file_object` + + Thanks to [Myron Marston](https://github.com/myronmarston) + +## 1.8.10 + +* em-http-request fix. After request callbacks are correctly invoked for 3xx responses, + when :redirects option is set. + + Thanks to [Myron Marston](https://github.com/myronmarston) for reporting that issue. + +* Fixed compatibility with Net::HTTP::DigestAuth + + Thanks to [Jonathan Hyman](https://github.com/jonhyman) for reporting that issue. + +* Fixed problem in em-http-request 0.x appending the query to the client URI twice. + + Thanks to [Paweł Pierzchała](https://github.com/wrozka) + +## 1.8.9 + +* Fixed problem with caching nil responses when the same HTTPClient instance is used. + + Thanks to [Myron Marston](https://github.com/myronmarston) + +* Added support for Addressable >= 2.3.0. Addressable 2.3.0 removed support for multiple query value notations and broke backwards compatibility. + + https://github.com/sporkmonger/addressable/commit/f51e290b5f68a98293327a7da84eb9e2d5f21c62 + https://github.com/sporkmonger/addressable/issues/77 + + +## 1.8.8 + +* Fixed Net::HTTP adapter so that it returns `nil` for an empty body response. + + Thanks to [Myron Marston](https://github.com/myronmarston) + +* Gemspec defines compatibility with Addressable ~> 2.2.8, not >= 2.3.0 + +* Specs compatibility with Typhoeus 0.4.0 + + Thanks to [Hans Hasselberg](https://github.com/i0rek) + +* Handling content types that specify a charset + + Thanks to [Kevin Glowacz](https://github.com/kjg) + +* Fixed em-http-request adapter to correctly fetch authorization header from a request + + Thanks to [Julien Boyer](https://github.com/chatgris) + +* Fixing travis-ci image to report master's status + + Thanks to [Ryan Schlesinger](https://github.com/ryansch) + +* Fixed problem with em-http-request callback triggering if there were other EM::Deferred callbacks registered + + Thanks to [Jon Leighton](https://github.com/jonleighton) + +* Fixed problem with em-http-request appending the query to the URI a second time, and +the parameters are repeated. + + Thanks to [Jon Leighton](https://github.com/jonleighton) + +## 1.8.7 + +* Compatibility with RSpec >= 2.10 + + Thanks to [erwanlr](https://github.com/erwanlr) for reporting this issue. + +* Add missing required rack environment variable SCRIPT_NAME + + Thanks to [Eric Oestrich](https://github.com/oestrich) + +* Fixed warnings due to @query_params not being initialized + + Thanks to [Ben Bleything](https://github.com/bleything) + +## 1.8.6 + +* Pass through SERVER_PORT when stubbing to rack + + Thanks to [Eric Oestrich](https://github.com/oestrich) + +* Fixed problem with missing parenthesis in `WebMock#net_connect_allowed?` conditions. + + Thanks to [aindustries](https://github.com/aindustries) + +## 1.8.5 + +* WebMock::RackResponse supports basic auth + + Thanks to [jugyo](https://github.com/jugyo) + +## 1.8.4 + +* Warning message is printed when an unsupported version of a http library is loaded. + + Thanks to [Alexander Staubo](https://github.com/alexstaubo) for reporting the problem and to [Myron Marston](https://github.com/myronmarston) for a help with solution. + +## 1.8.3 + +* Fixed compatibility with latest em-http-request + + Thanks to [Paul Cortens](https://github.com/thoughtless) + +## 1.8.2 + +* Prevent Webmock `hash_including` from overriding RSpec version 1 `hash_including` method. + + Thanks to [Joe Karayusuf](https://github.com/karayusuf) + +* Ensured WebMock handles RSpec 1 `hash_including` matcher for matching query params and body. + +## 1.8.1 + +* Ensured WebMock doesn't interfere with `em-synchrony`, when `em-synchrony/em-http` is not included. + + Thanks to [Nick Recobra](https://github.com/oruen) + +* Improved README + + Thanks to [Jordan Elver](https://github.com/jordelver) + + +## 1.8.0 + +* Matching request body against partial hash. + + stub_http_request(:post, "www.example.com"). + with(:body => hash_including({:data => {:a => '1', :b => 'five'}})) + + RestClient.post('www.example.com', "data[a]=1&data[b]=five&x=1", + :content_type => 'application/x-www-form-urlencoded') # ===> Success + + request(:post, "www.example.com"). + with(:body => hash_including({:data => {:a => '1', :b => 'five'}}), + :headers => 'Content-Type' => 'application/json').should have_been_made # ===> Success + + Thanks to [Marnen Laibow-Koser](https://github.com/marnen) for help with this solution + +* Matching request query params against partial hash. + + stub_http_request(:get, "www.example.com").with(:query => hash_including({"a" => ["b", "c"]})) + + RestClient.get("http://www.example.com/?a[]=b&a[]=c&x=1") # ===> Success + + request(:get, "www.example.com"). + with(:query => hash_including({"a" => ["b", "c"]})).should have_been_made # ===> Success + +* Added support for Excon. + + Thanks to [Dimitrij Denissenko](https://github.com/dim) + +* Added support for setting expectations on the request stub with `assert_requested` + + stub_get = stub_request(:get, "www.example.com") + stub_post = stub_request(:post, "www.example.com") + + Net::HTTP.get('www.example.com', '/') + + assert_requested(stub_get) + assert_not_requested(stub_post) + + Thanks to [Nicolas Fouché](https://github.com/nfo) + +* `WebMock.disable_net_connect!` accepts `RegExp` as `:allow` parameter + + Thanks to [Frank Schumacher](https://github.com/thenoseman) + +* Ensure multiple values for the same header can be recorded and played back + + Thanks to [Myron Marston](https://github.com/myronmarston) + +* Updated dependency on Addressable to version >= 2.2.7 to handle nested hash query values. I.e. `?one[two][three][]=four&one[two][three][]=five` + +* Fixed compatibility with Curb >= 0.7.16 This breaks compatibility with Curb < 0.7.16 + +* Fix #to_rack to handle non-array response bodies. + + Thanks to [Tammer Saleh](https://github.com/tsaleh) + +* Added `read_timeout` accessor to StubSocket which fixes compatibility with aws-sdk + + Thanks to [Lin Jen-Shin](https://github.com/godfat) + +* Fix warning "instance variable @query_params not initialized" + + Thanks to [Joe Van Dyk](https://github.com/joevandyk) + +* Using bytesize of message instead of its length for content-length header in em-http-request adapter. + This fixes a problem with messages getting truncated in Ruby >= 1.9 + + Thanks to [Mark Abramov](https://github.com/markiz) + +* Fixed problem with body params being matched even if params were different. + + Thanks to [Evgeniy Dolzhenko](https://github.com/dolzenko) for reporting this issue. + +## 1.7.10 + +* Yanked 1.7.9 and rebuilt gem on 1.8.7 to deal with syck/psych incompatibilties in gemspec. + +## 1.7.9 + +* Fixed support for native Typhoeus timeouts. + + Thanks to [Albert Llop](https://github.com/mrsimo) + +* Fixed problem with WebMock and RSpec compatibility on TeamCity servers. See [this article](http://www.coding4streetcred.com/blog/post/Issue-RubyMine-31-Webmock-162-and-%E2%80%9CSpecconfigure%E2%80%9D-curse.aspx) for more details. + + Thanks to [Christopher Pickslay](https://github.com/chrispix) from [Two Bit Labs](https://github.com/twobitlabs) + + +## 1.7.8 + +* Fix each adapter so that it calls a `stub.with` block only once per + request. Previously, the block would be called two or three times per + request [Myron Marston](https://github.com/myronmarston). + +## 1.7.7 - RuPy 2011 release + +* Passing response object to a block passed to `HTTPClient#do_get_block`. This fixes `HTTPClient.get_content` failures. [issue 130](https://github.com/bblimke/webmock/pull/130) + + Thanks to [Chris McGrath](https://github.com/chrismcg) + +* Cleaned up ruby warnings when running WebMock code with `-w`. + + Thanks to [Stephen Celis](https://github.com/stephencelis) + +* Curb adapter now correctly calls on_failure for 4xx response codes. + + Thanks to [Eugene Pimenov](https://github.com/libc) + +## 1.7.6 + +* Support for the HTTPClient's request_filter feature + + Thanks to [Roman Shterenzon](https://github.com/romanbsd) + +## 1.7.5 + +* Added support for Patron 0.4.15. This change is not backward compatible so please upgrade Patron to version >= 0.4.15 if you want to use it with WebMock. + + Thanks to [Andreas Garnæs](https://github.com/andreas) + +## 1.7.4 + +* Added support for matching EM-HTTP-Request requests with body declared as a Hash + + Thanks to [David Yeu](https://github.com/daveyeu) + +## 1.7.3 + +* Added `Get`, `Post`, `Delete`, `Put`, `Head`, `Option` constants to replaced `Net::HTTP` to make it possible to marshal objects with these constants assigned to properties. This fixed problem with `tvdb_party` gem which serializes HTTParty responses. + + Thanks to [Klaus Hartl](https://github.com/carhartl) for reporting this issue. + +## 1.7.2 + +* Redefined `const_get` and `constants` methods on the replaced `Net::HTTP` to return same values as original `Net::HTTP` + +## 1.7.1 + +* Redefined `const_defined?` on the replaced `Net::HTTP` so that it returns true if constant is defined on the original `Net::HTTP`. This fixes problems with `"Net::HTTP::Get".constantize`. + + Thanks to [Cássio Marques](https://github.com/cassiomarques) for reporting the issue and to [Myron Marston](https://github.com/myronmarston) for help with the solution. + +## 1.7.0 + +* Fixed Net::HTTP adapter to not break normal Net::HTTP behaviour when network connections are allowed. This fixes **selenium-webdriver compatibility**!!! + +* Added support for EM-HTTP-Request 1.0.x and EM-Synchrony. Thanks to [Steve Hull](https://github.com/sdhull) + +* Added support for setting expectations to on a stub itself i.e. + + stub = stub_request(:get, "www.example.com") + # ... make requests ... + stub.should have_been_requested + + Thanks to [Aidan Feldman](https://github.com/afeld) + +* Minitest support! Thanks to [Peter Higgins](https://github.com/phiggins) + +* Added support for Typhoeus::Hydra + +* Added support for `Curb::Easy#http_post` and `Curb::Easy#http_post` with multiple arguments. Thanks to [Salvador Fuentes Jr](https://github.com/fuentesjr) and [Alex Rothenberg](https://github.com/alexrothenberg) + +* Rack support. Requests can be stubbed to respond with a Rack app i.e. + + class MyRackApp + def self.call(env) + [200, {}, ["Hello"]] + end + end + + stub_request(:get, "www.example.com").to_rack(MyRackApp) + + RestClient.get("www.example.com") # ===> "Hello" + + + Thanks to [Jay Adkisson](https://github.com/jayferd) + +* Added support for selective disabling and enabling of http lib adapters + + WebMock.disable! #disable WebMock (all adapters) + WebMock.disable!(:except => [:net_http]) #disable WebMock for all libs except Net::HTTP + WebMock.enable! #enable WebMock (all adapters) + WebMock.enable!(:except => [:patron]) #enable WebMock for all libs except Patron + +* The error message on an unstubbed request shows a code snippet with body as a hash when it was in url encoded form. + + > RestClient.post('www.example.com', "data[a]=1&data[b]=2", :content_type => 'application/x-www-form-urlencoded') + + WebMock::NetConnectNotAllowedError: Real HTTP connections are disabled.... + + You can stub this request with the following snippet: + + stub_request(:post, "http://www.example.com/"). + with(:body => {"data"=>{"a"=>"1", "b"=>"2"}}, + :headers => { 'Content-Type'=>'application/x-www-form-urlencoded' }). + to_return(:status => 200, :body => "", :headers => {}) + + Thanks to [Alex Rothenberg](https://github.com/alexrothenberg) + +* The error message on an unstubbed request shows currently registered request stubs. + + > stub_request(:get, "www.example.net") + > stub_request(:get, "www.example.org") + > RestClient.get("www.example.com") + WebMock::NetConnectNotAllowedError: Real HTTP connections are disabled.... + + You can stub this request with the following snippet: + + stub_request(:get, "http://www.example.com/"). + to_return(:status => 200, :body => "", :headers => {}) + + registered request stubs: + + stub_request(:get, "http://www.example.net/") + stub_request(:get, "http://www.example.org/") + + Thanks to [Lin Jen-Shin](https://github.com/godfat) for suggesting this feature. + +* Fixed problem with matching requests with json body, when json strings have date format. Thanks to [Joakim Ekberg](https://github.com/kalasjocke) for reporting this issue. + +* WebMock now attempts to require each http library before monkey patching it. This is to avoid problem when http library is required after WebMock is required. Thanks to [Myron Marston](https://github.com/myronmarston) for suggesting this change. + +* External requests can be disabled while allowing selected ports on selected hosts + + WebMock.disable_net_connect!(:allow => "www.example.com:8080") + RestClient.get("www.example.com:80") # ===> Failure + RestClient.get("www.example.com:8080") # ===> Allowed. + + Thanks to [Zach Dennis](https://github.com/zdennis) + +* Fixed syntax error in README examples, showing the ways of setting request expectations. Thanks to [Nikita Fedyashev](https://github.com/nfedyashev) + + +**Many thanks to WebMock co-maintainer [James Conroy-Finn](https://github.com/jcf) who did a great job maintaining WebMock on his own for the last couple of months.** + +## 1.6.4 + +This is a quick slip release to regenerate the gemspec. Apparently +jeweler inserts dependencies twice if you use the `gemspec` method in +your Gemfile and declare gem dependencies in your gemspec. + +https://github.com/technicalpickles/jeweler/issues/154 + +josevalim: + +> This just bit me. I just released a gem with the wrong dependencies +> because I have updated jeweler. This should have been opt-in, +> otherwise a bunch of people using jeweler are going to release gems +> with the wrong dependencies because you are automatically importing +> from the Gemfile. + +## 1.6.3 + +* Update the dependency on addressable to get around an issue in v2.2.5. + Thanks to [Peter Higgins](https://github.com/phiggins). + +* Add support for matching parameter values using a regular expression + as well as a string. Thanks to [Oleg M Prozorov](https://github.com/oleg). + +* Fix integration with httpclient as the internal API has changed. + Thanks to [Frank Prößdorf](https://github.com/endor). + +* Ensure Curl::Easy#content_type is always set. Thanks to [Peter + Higgins](https://github.com/phiggins). + +* Fix bug with em-http-request adapter stubbing responses that have a + chunked transfer encoding. Thanks to [Myron + Marston](https://github.com/myronmarston). + +* Fix a load of spec failures with Patron, httpclient, and specs that + depended on the behaviour of example.com. Thanks to [Alex + Grigorovich](https://github.com/grig). + +## 1.6.2 + +* Em-http-request adapter sets `last_effective_url` property. Thanks to [Sam Stokes](https://github.com/samstokes). + +* Curb adapter supports `Curb::Easy#http_post` and `Curb::Easy#http_put` without arguments (by setting `post_body` or `put_data` beforehand). Thanks to [Eugene Bolshakov](https://github.com/eugenebolshakov) + +## 1.6.1 + +* Fixed issue with `webmock/rspec` which didn't load correctly if `rspec/core` was already required but `rspec/expectations` not. + +## 1.6.0 + +* Simplified integration with Test::Unit, RSpec and Cucumber. Now only a single file has to be required i.e. + + require 'webmock/test_unit' + require 'webmock/rspec' + require 'webmock/cucumber' + +* The error message on unstubbed request now contains code snippet which can be used to stub this request. Thanks to Martyn Loughran for suggesting this feature. + +* The expectation failure message now contains a list of made requests. Thanks to Martyn Loughran for suggesting this feature. + +* Added `WebMock.print_executed_requests` method which can be useful to find out what requests were made until a given point. + +* em-http-request adapter is now activated by replacing EventMachine::HttpRequest constant, instead of monkeypatching the original class. + + This technique is borrowed from em-http-request native mocking module. It allows switching WebMock adapter on an off, and using it interchangeably with em-http-request native mocking i.e: + + EventMachine::WebMockHttpRequest.activate! + EventMachine::WebMockHttpRequest.deactivate! + + Thanks to Martyn Loughran for suggesting this feature. + +* `WebMock.reset_webmock` is deprecated in favour of new `WebMock.reset!` + +* Fixed integration with Cucumber. Previously documented example didn't work with new versions of Cucumber. + +* Fixed stubbing requests with body declared as a hash. Thanks to Erik Michaels-Ober for reporting the issue. + +* Fixed issue with em-http-request adapter which didn't work when :query option value was passed as a string, not a hash. Thanks to Chee Yeo for reporting the issue. + +* Fixed problem with assert_requested which didn't work if used outside rspec or test/unit + +* Removed dependency on json gem + +## 1.5.0 + +* Support for dynamically evaluated raw responses recorded with `curl -is` <br/> + i.e. + + `curl -is www.example.com > /tmp/www.example.com.txt` + stub_request(:get, "www.example.com").to_return(lambda { |request| File.new("/tmp/#{request.uri.host.to_s}.txt" })) + +* `:net_http_connect_on_start` option can be passed to `WebMock.allow_net_connect!` and `WebMock.disable_net_connect!` methods, i.e. + + WebMock.allow_net_connect!(:net_http_connect_on_start => true) + + This forces WebMock Net::HTTP adapter to always connect on `Net::HTTP.start`. Check 'Connecting on Net::HTTP.start' in README for more information. + + Thanks to Alastair Brunton for reporting the issue and for fix suggestions. + +* Fixed an issue where Patron spec tried to remove system temporary directory. + Thanks to Hans de Graaff + +* WebMock specs now use RSpec 2 + +* `rake spec NO_CONNECTION=true` can now be used to only run WebMock specs which do not make real network connections + +## 1.4.0 + +* Curb support!!! Thanks to the awesome work of Pete Higgins! + +* `include WebMock` is now deprecated to avoid method and constant name conflicts. Please `include WebMock::API` instead. + +* `WebMock::API#request` is renamed to `WebMock::API#a_request` to prevent method name conflicts with i.e. Rails controller specs. + WebMock.request is still available. + +* Deprecated `WebMock#request`, `WebMock#allow_net_connect!`, `WebMock#net_connect_allowed?`, `WebMock#registered_request?`, `WebMock#reset_callbacks`, `WebMock#after_request` instance methods. These methods are still available, but only as WebMock class methods. + +* Removed `WebMock.response_for_request` and `WebMock.assertion_failure` which were only used internally and were not documented. + +* :allow_localhost => true' now permits 0.0.0.0 in addition to 127.0.0.1 and 'localhost'. Thanks to Myron Marston and Mike Gehard for suggesting this. + +* Fixed issue with both RSpec 1.x and 2.x being available. + + WebMock now tries to use already loaded version of RSpec (1.x or 2.x). Previously it was loading RSpec 2.0 if available, even if RSpec 1.3 was already loaded. + + Thanks to Hans de Graaff for reporting this. + +* Changed runtime dependency on Addressable version 2.2.2 which fixes handling of percent-escaped '+' + +## 1.3.5 + +* External requests can be disabled while allowing selected hosts. Thanks to Charles Li and Ryan Bigg + + This feature was available before only for localhost with `:allow_localhost => true` + + WebMock.disable_net_connect!(:allow => "www.example.org") + + Net::HTTP.get('www.something.com', '/') # ===> Failure + + Net::HTTP.get('www.example.org', '/') # ===> Allowed. + +* Fixed Net::HTTP adapter so that it preserves the original behavior of Net::HTTP. + + When making a request with a block that calls #read_body on the request, + Net::HTTP causes the body to be set to a Net::ReadAdapter, but WebMock was causing the body to be set to a string. + +## 1.3.4 + +* Fixed Net::HTTP adapter to handle cases where a block with `read_body` call is passed to `request`. + This fixes compatibility with `open-uri`. Thanks to Mark Evans for reporting the issue. + +## 1.3.3 + +* Fixed handling of multiple values for the same response header for Net::HTTP. Thanks to Myron Marston for reporting the issue. + +## 1.3.2 + +* Fixed compatibility with EM-HTTP-Request >= 0.2.9. Thanks to Myron Marston for reporting the issue. + +## 1.3.1 + +* The less hacky way to get the stream behaviour working for em-http-request. Thanks to Martyn Loughran + +* Fixed issues where Net::HTTP was not accepting valid nil response body. Thanks to Muness Alrubaie + +## 1.3.0 + +* Added support for [em-http-request](http://github.com/igrigorik/em-http-request) + +* Matching query params using a hash + + stub_http_request(:get, "www.example.com").with(:query => {"a" => ["b", "c"]}) + + RestClient.get("http://www.example.com/?a[]=b&a[]=c") # ===> Success + + request(:get, "www.example.com").with(:query => {"a" => ["b", "c"]}).should have_been_made # ===> Success + +* Matching request body against a hash. Body can be URL-Encoded, JSON or XML. + + (Thanks to Steve Tooke for the idea and a solution for url-encoded bodies) + + stub_http_request(:post, "www.example.com"). + with(:body => {:data => {:a => '1', :b => 'five'}}) + + RestClient.post('www.example.com', "data[a]=1&data[b]=five", + :content_type => 'application/x-www-form-urlencoded') # ===> Success + + RestClient.post('www.example.com', '{"data":{"a":"1","b":"five"}}', + :content_type => 'application/json') # ===> Success + + RestClient.post('www.example.com', '<data a="1" b="five" />', + :content_type => 'application/xml' ) # ===> Success + + request(:post, "www.example.com"). + with(:body => {:data => {:a => '1', :b => 'five'}}, + :headers => 'Content-Type' => 'application/json').should have_been_made # ===> Success + +* Request callbacks (Thanks to Myron Marston for all suggestions) + + WebMock can now invoke callbacks for stubbed or real requests: + + WebMock.after_request do |request_signature, response| + puts "Request #{request_signature} was made and #{response} was returned" + end + + invoke callbacks for real requests only and except requests made with Patron client + + WebMock.after_request(:except => [:patron], :real_requests_only => true) do |request_signature, response| + puts "Request #{request_signature} was made and #{response} was returned" + end + +* `to_raise()` now accepts an exception instance or a string as argument in addition to an exception class + + stub_request(:any, 'www.example.net').to_raise(StandardError.new("some error")) + + stub_request(:any, 'www.example.net').to_raise("some error") + +* Matching requests based on a URI is 30% faster + +* Fixed constant namespace issues in HTTPClient adapter. Thanks to Nathaniel Bibler for submitting a patch. + +## 1.2.2 + +* Fixed problem where ArgumentError was raised if query params were made up of an array e.g. data[]=a&data[]=b. Thanks to Steve Tooke + +## 1.2.1 + +* Changed license from GPL to MIT + +* Fixed gemspec file. Thanks to Razic + +## 1.2.0 + +* RSpec 2 compatibility. Thanks to Sam Phillips! + +* :allow_localhost => true' now permits 127.0.0.1 as well as 'localhost'. Thanks to Mack Earnhardt + +* Request URI matching in now 2x faster! + + +## 1.1.0 + +* [VCR](http://github.com/myronmarston/vcr/) compatibility. Many thanks to Myron Marston for all suggestions. + +* Support for stubbing requests and returning responses with multiple headers with the same name. i.e multiple Accept headers. + + stub_http_request(:get, 'www.example.com'). + with(:headers => {'Accept' => ['image/png', 'image/jpeg']}). + to_return(:body => 'abc') + RestClient.get('www.example.com', + {"Accept" => ['image/png', 'image/jpeg']}) # ===> "abc\n" + +* When real net connections are disabled and unstubbed request is made, WebMock throws WebMock::NetConnectNotAllowedError instead of assertion error or StandardError. + +* Added WebMock.version() + + +## 1.0.0 + +* Added support for [Patron](http://toland.github.com/patron/) + +* Responses dynamically evaluated from block (idea and implementation by Tom Ward) + + stub_request(:any, 'www.example.net'). + to_return { |request| {:body => request.body} } + + RestClient.post('www.example.net', 'abc') # ===> "abc\n" + +* Responses dynamically evaluated from lambda (idea and implementation by Tom Ward) + + stub_request(:any, 'www.example.net'). + to_return(lambda { |request| {:body => request.body} }) + + RestClient.post('www.example.net', 'abc') # ===> "abc\n" + +* Response with custom status message + + stub_request(:any, "www.example.com").to_return(:status => [500, "Internal Server Error"]) + + req = Net::HTTP::Get.new("/") + Net::HTTP.start("www.example.com") { |http| http.request(req) }.message # ===> "Internal Server Error" + +* Raising timeout errors (suggested by Jeffrey Jones) (compatibility with Ruby 1.8.6 by Mack Earnhardt) + + stub_request(:any, 'www.example.net').to_timeout + + RestClient.post('www.example.net', 'abc') # ===> RestClient::RequestTimeout + +* External requests can be disabled while allowing localhost (idea and implementation by Mack Earnhardt) + + WebMock.disable_net_connect!(:allow_localhost => true) + + Net::HTTP.get('www.something.com', '/') # ===> Failure + + Net::HTTP.get('localhost:9887', '/') # ===> Allowed. Perhaps to Selenium? + + +### Bug fixes + +* Fixed issue where Net::HTTP adapter didn't work for requests with body responding to read (reported by Tekin Suleyman) +* Fixed issue where request stub with headers declared as nil was matching requests with non empty headers + +## 0.9.1 + +* Fixed issue where response status code was not read from raw (curl -is) responses + +## 0.9.0 + +* Matching requests against provided block (by Sergio Gil) + + stub_request(:post, "www.example.com").with { |request| request.body == "abc" }.to_return(:body => "def") + RestClient.post('www.example.com', 'abc') # ===> "def\n" + request(:post, "www.example.com").with { |req| req.body == "abc" }.should have_been_made + #or + assert_requested(:post, "www.example.com") { |req| req.body == "abc" } + +* Matching request body against regular expressions (suggested by Ben Pickles) + + stub_request(:post, "www.example.com").with(:body => /^.*world$/).to_return(:body => "abc") + RestClient.post('www.example.com', 'hello world') # ===> "abc\n" + +* Matching request headers against regular expressions (suggested by Ben Pickles) + + stub_request(:post, "www.example.com").with(:headers => {"Content-Type" => /image\/.+/}).to_return(:body => "abc") + RestClient.post('www.example.com', '', {'Content-Type' => 'image/png'}) # ===> "abc\n" + +* Replaying raw responses recorded with `curl -is` + + `curl -is www.example.com > /tmp/example_curl_-is_output.txt` + raw_response_file = File.new("/tmp/example_curl_-is_output.txt") + + from file + + stub_request(:get, "www.example.com").to_return(raw_response_file) + + or string + + stub_request(:get, "www.example.com").to_return(raw_response_file.read) + +* Multiple responses for repeated requests + + stub_request(:get, "www.example.com").to_return({:body => "abc"}, {:body => "def"}) + Net::HTTP.get('www.example.com', '/') # ===> "abc\n" + Net::HTTP.get('www.example.com', '/') # ===> "def\n" + +* Multiple responses using chained `to_return()` or `to_raise()` declarations + + stub_request(:get, "www.example.com"). + to_return({:body => "abc"}).then. #then() just is a syntactic sugar + to_return({:body => "def"}).then. + to_raise(MyException) + Net::HTTP.get('www.example.com', '/') # ===> "abc\n" + Net::HTTP.get('www.example.com', '/') # ===> "def\n" + Net::HTTP.get('www.example.com', '/') # ===> MyException raised + +* Specifying number of times given response should be returned + + stub_request(:get, "www.example.com"). + to_return({:body => "abc"}).times(2).then. + to_return({:body => "def"}) + + Net::HTTP.get('www.example.com', '/') # ===> "abc\n" + Net::HTTP.get('www.example.com', '/') # ===> "abc\n" + Net::HTTP.get('www.example.com', '/') # ===> "def\n" + +* Added support for `Net::HTTP::Post#body_stream` + + This fixes compatibility with new versions of RestClient + +* WebMock doesn't suppress default request headers added by http clients anymore. + + i.e. Net::HTTP adds `'Accept'=>'*/*'` to all requests by default + + + +## 0.8.2 + + * Fixed issue where WebMock was not closing IO object passed as response body after reading it. + * Ruby 1.9.2 compat: Use `File#expand_path` for require path because "." is not be included in LOAD_PATH since Ruby 1.9.2 + + +## 0.8.1 + + * Fixed HTTPClient adapter compatibility with Ruby 1.8.6 (reported by Piotr Usewicz) + * Net:HTTP adapter now handles request body assigned as Net::HTTP::Post#body attribute (fixed by Mack Earnhardt) + * Fixed issue where requests were not matching stubs with Accept header set.(reported by Piotr Usewicz) + * Fixed compatibility with Ruby 1.9.1, 1.9.2 and JRuby 1.3.1 (reported by Diego E. “Flameeyes” Pettenò) + * Fixed issue with response body declared as IO object and multiple requests (reported by Niels Meersschaert) + * Fixed "undefined method `assertion_failure'" error (reported by Nick Plante) + + +## 0.8.0 + + * Support for HTTPClient (sync and async requests) + * Support for dynamic responses. Response body and headers can be now declared as lambda. + (Thanks to Ivan Vega ( @ivanyv ) for suggesting this feature) + * Support for stubbing and expecting requests with empty body + * Executing non-stubbed request leads to failed expectation instead of error + + +### Bug fixes + + * Basic authentication now works correctly + * Fixed problem where WebMock didn't call a block with the response when block was provided + * Fixed problem where uris with single slash were not matching uris without path provided + + +## 0.7.3 + + * Clarified documentation + * Fixed some issues with loading of Webmock classes + * Test::Unit and RSpec adapters have to be required separately + + +## 0.7.2 + + * Added support for matching escaped and non escaped URLs diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/LICENSE b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/LICENSE new file mode 100644 index 0000000..9a6302d --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2009-2010 Bartosz Blimke + +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/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/README.md b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/README.md new file mode 100644 index 0000000..39fcf9e --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/README.md @@ -0,0 +1,1226 @@ +WebMock +======= +[![Gem Version](https://badge.fury.io/rb/webmock.svg)](http://badge.fury.io/rb/webmock) +[![Build Status](https://github.com/bblimke/webmock/actions/workflows/CI.yml/badge.svg?branch=master)](https://github.com/bblimke/webmock/actions) +[![Code Climate](https://codeclimate.com/github/bblimke/webmock/badges/gpa.svg)](https://codeclimate.com/github/bblimke/webmock) +[![Mentioned in Awesome Ruby](https://awesome.re/mentioned-badge.svg)](https://github.com/markets/awesome-ruby) + +Library for stubbing and setting expectations on HTTP requests in Ruby. + +Features +-------- + +* Stubbing HTTP requests at low http client lib level (no need to change tests when you change HTTP library) +* Setting and verifying expectations on HTTP requests +* Matching requests based on method, URI, headers and body +* Smart matching of the same URIs in different representations (also encoded and non encoded forms) +* Smart matching of the same headers in different representations. +* Support for Test::Unit +* Support for RSpec +* Support for MiniTest + +Supported HTTP libraries +------------------------ + +* [Async::HTTP::Client](https://github.com/socketry/async-http) +* [Curb](https://github.com/taf2/curb) (currently only Curb::Easy) +* [EM-HTTP-Request](https://github.com/igrigorik/em-http-request) +* [Excon](https://github.com/excon/excon) +* [HTTPClient](https://github.com/nahi/httpclient) +* [HTTP Gem (also known as http.rb)](https://github.com/httprb/http) +* [httpx](https://honeyryderchuck.gitlab.io/httpx/wiki/Webmock-Adapter) +* [Manticore](https://github.com/cheald/manticore) +* [Net::HTTP](https://ruby-doc.org/stdlib-2.7.0/libdoc/net/http/rdoc/Net/HTTP.html) and other libraries based on Net::HTTP, e.g.: + * [HTTParty](https://github.com/jnunemaker/httparty) + * [REST Client](https://github.com/rest-client/rest-client) +* [Patron](https://github.com/toland/patron) +* [Typhoeus](https://github.com/typhoeus/typhoeus) (currently only Typhoeus::Hydra) + +Supported Ruby Interpreters +--------------------------- +* MRI 2.6 +* MRI 2.7 +* MRI 3.0 +* MRI 3.1 +* MRI 3.2 +* MRI 3.3 +* MRI 3.4 +* JRuby + +## Installation + +```bash +gem install webmock +``` +or alternatively: + +```ruby +# add to your Gemfile +group :test do + gem "webmock" +end +``` + +### or to install the latest development version from github master + +```bash +git clone http://github.com/bblimke/webmock.git +cd webmock +rake install +``` + +## Upgrading from v1.x to v2.x + +WebMock 2.x has changed somewhat since version 1.x. Changes are listed in [CHANGELOG.md](CHANGELOG.md) + +### Cucumber + +Create a file `features/support/webmock.rb` with the following contents: + +```ruby +require 'webmock/cucumber' +``` + +### MiniTest + +Add the following code to `test/test_helper`: + +```ruby +require 'webmock/minitest' +``` + +### RSpec + +Add the following code to `spec/spec_helper`: + +```ruby +require 'webmock/rspec' +``` + +### Test::Unit + +Add the following code to `test/test_helper.rb` + +```ruby +require 'webmock/test_unit' +``` + +### Outside a test framework + +You can also use WebMock outside a test framework: + +```ruby +require 'webmock' +include WebMock::API + +WebMock.enable! +``` + +# Examples + +## Stubbing + +### Stubbed request based on uri only and with the default response + +```ruby +stub_request(:any, "www.example.com") + +Net::HTTP.get("www.example.com", "/") # ===> Success +``` + +### Stubbing requests based on method, uri, body and headers + +```ruby +stub_request(:post, "www.example.com"). + with(body: "abc", headers: { 'Content-Length' => 3 }) + +uri = URI.parse("http://www.example.com/") +req = Net::HTTP::Post.new(uri.path) +req['Content-Length'] = 3 + +res = Net::HTTP.start(uri.host, uri.port) do |http| + http.request(req, "abc") +end # ===> Success +``` + +### Matching request body and headers against regular expressions + +```ruby +stub_request(:post, "www.example.com"). + with(body: /world$/, headers: {"Content-Type" => /image\/.+/}). + to_return(body: "abc") + +uri = URI.parse('http://www.example.com/') +req = Net::HTTP::Post.new(uri.path) +req['Content-Type'] = 'image/png' + +res = Net::HTTP.start(uri.host, uri.port) do |http| + http.request(req, 'hello world') +end # ===> Success +``` + +### Matching request body against a hash. Body can be URL-Encoded, JSON or XML. + +```ruby +stub_request(:post, "www.example.com"). + with(body: {data: {a: '1', b: 'five'}}) + +RestClient.post('www.example.com', "data[a]=1&data[b]=five", + content_type: 'application/x-www-form-urlencoded') # ===> Success + +RestClient.post('www.example.com', '{"data":{"a":"1","b":"five"}}', + content_type: 'application/json') # ===> Success + +RestClient.post('www.example.com', '<data a="1" b="five" />', + content_type: 'application/xml') # ===> Success +``` + +### Matching request body against partial hash. + +```ruby +stub_request(:post, "www.example.com"). + with(body: hash_including({data: {a: '1', b: 'five'}})) + +RestClient.post('www.example.com', "data[a]=1&data[b]=five&x=1", +:content_type => 'application/x-www-form-urlencoded') # ===> Success +``` + +### Matching custom request headers + +```ruby +stub_request(:any, "www.example.com"). + with(headers:{ 'Header-Name' => 'Header-Value' }) + +uri = URI.parse('http://www.example.com/') +req = Net::HTTP::Post.new(uri.path) +req['Header-Name'] = 'Header-Value' + +res = Net::HTTP.start(uri.host, uri.port) do |http| + http.request(req, 'abc') +end # ===> Success +``` + +### Matching multiple headers with the same name + +```ruby +stub_request(:get, 'www.example.com'). + with(headers: {'Accept' => ['image/jpeg', 'image/png'] }) + +req = Net::HTTP::Get.new("/") +req['Accept'] = ['image/png'] +req.add_field('Accept', 'image/jpeg') +Net::HTTP.start("www.example.com") {|http| http.request(req) } # ===> Success +``` + +### Matching requests against provided block + +```ruby +stub_request(:post, "www.example.com").with { |request| request.body == "abc" } +RestClient.post('www.example.com', 'abc') # ===> Success +``` + +### Request with basic authentication header + +```ruby +stub_request(:get, "www.example.com").with(basic_auth: ['user', 'pass']) +# or +# stub_request(:get, "www.example.com"). +# with(headers: {'Authorization' => "Basic #{ Base64.strict_encode64('user:pass').chomp}"}) + +Net::HTTP.start('www.example.com') do |http| + req = Net::HTTP::Get.new('/') + req.basic_auth 'user', 'pass' + http.request(req) +end # ===> Success +``` + +##### Important! Since version 2.0.0, WebMock does not match credentials provided in Authorization header and credentials provided in the userinfo of a url. I.e. `stub_request(:get, "user:pass@www.example.com")` does not match a request with credentials provided in the Authorization header. + +### Request with basic authentication in the url + +```ruby +stub_request(:get, "user:pass@www.example.com") + +RestClient.get('user:pass@www.example.com') # ===> Success +``` + +### Matching uris using regular expressions + +```ruby +stub_request(:any, /example/) + +Net::HTTP.get('www.example.com', '/') # ===> Success +``` + +### Matching uris using lambda + +```ruby +stub_request(:any, ->(uri) { true }) +``` + +### Matching uris using RFC 6570 - Basic Example + +```ruby +uri_template = Addressable::Template.new "www.example.com/{id}/" +stub_request(:any, uri_template) + +Net::HTTP.get('www.example.com', '/webmock/') # ===> Success +``` + +### Matching uris using RFC 6570 - Advanced Example + +```ruby +uri_template = + Addressable::Template.new "www.example.com/thing/{id}.json{?x,y,z}{&other*}" +stub_request(:any, uri_template) + +Net::HTTP.get('www.example.com', + '/thing/5.json?x=1&y=2&z=3&anyParam=4') # ===> Success +``` + +### Matching query params using hash + +```ruby +stub_request(:get, "www.example.com").with(query: {"a" => ["b", "c"]}) + +RestClient.get("http://www.example.com/?a[]=b&a[]=c") # ===> Success +``` + +### Matching partial query params using hash + +```ruby +stub_request(:get, "www.example.com"). + with(query: hash_including({"a" => ["b", "c"]})) + +RestClient.get("http://www.example.com/?a[]=b&a[]=c&x=1") # ===> Success +``` + +### Matching partial query params using hash_excluding + +```ruby +stub_request(:get, "www.example.com"). + with(query: hash_excluding({"a" => "b"})) + +RestClient.get("http://www.example.com/?a=b") # ===> Failure +RestClient.get("http://www.example.com/?a=c") # ===> Success +``` + +### Stubbing with custom response + +```ruby +stub_request(:any, "www.example.com"). + to_return(body: "abc", status: 200, + headers: { 'Content-Length' => 3 }) + +Net::HTTP.get("www.example.com", '/') # ===> "abc" +``` + +Set appropriate Content-Type for HTTParty's `parsed_response`. + +```ruby +stub_request(:any, "www.example.com").to_return body: '{}', headers: {content_type: 'application/json'} +``` + +### Response with body specified as IO object + +```ruby +File.open('/tmp/response_body.txt', 'w') { |f| f.puts 'abc' } + +stub_request(:any, "www.example.com"). + to_return(body: File.new('/tmp/response_body.txt'), status: 200) + +Net::HTTP.get('www.example.com', '/') # ===> "abc\n" +``` + +### Response with JSON body + +```ruby + +stub_request(:any, "www.example.com"). + to_return_json(body: {foo: "bar"}) + +Net::HTTP.get('www.example.com', '/') # ===> "{\"foo\": \"bar\"}" +``` + +### Response with custom status message + +```ruby +stub_request(:any, "www.example.com"). + to_return(status: [500, "Internal Server Error"]) + +req = Net::HTTP::Get.new("/") +Net::HTTP.start("www.example.com") { |http| http.request(req) }. + message # ===> "Internal Server Error" +``` + +### Replaying raw responses recorded with `curl -is` + +``` +curl -is www.example.com > /tmp/example_curl_-is_output.txt +``` + +```ruby +raw_response_file = File.new("/tmp/example_curl_-is_output.txt") +``` + + from file + +```ruby +stub_request(:get, "www.example.com").to_return(raw_response_file) +``` + + or string + +```ruby +stub_request(:get, "www.example.com").to_return(raw_response_file.read) +``` + +### Responses dynamically evaluated from block + +```ruby +stub_request(:any, 'www.example.net'). + to_return { |request| {body: request.body} } + +RestClient.post('www.example.net', 'abc') # ===> "abc\n" +``` + +### Responses dynamically evaluated from lambda + +```ruby +stub_request(:any, 'www.example.net'). + to_return(lambda { |request| {body: request.body} }) + +RestClient.post('www.example.net', 'abc') # ===> "abc\n" +``` + +### Dynamically evaluated raw responses recorded with `curl -is` + +`curl -is www.example.com > /tmp/www.example.com.txt` + +```ruby +stub_request(:get, "www.example.com"). + to_return(lambda { |request| File.new("/tmp/#{request.uri.host.to_s}.txt") }) +``` + +### Responses with dynamically evaluated parts + +```ruby +stub_request(:any, 'www.example.net'). + to_return(body: lambda { |request| request.body }) + +RestClient.post('www.example.net', 'abc') # ===> "abc\n" +``` + +### Rack responses + +```ruby +class MyRackApp + def self.call(env) + [200, {}, ["Hello"]] + end +end + +stub_request(:get, "www.example.com").to_rack(MyRackApp) + +RestClient.post('www.example.com') # ===> "Hello" +``` + +### Raising errors + +#### Exception declared by class + +```ruby +stub_request(:any, 'www.example.net').to_raise(StandardError) + +RestClient.post('www.example.net', 'abc') # ===> StandardError +``` + +#### or by exception instance + +```ruby +stub_request(:any, 'www.example.net').to_raise(StandardError.new("some error")) +``` + +#### or by string + +```ruby +stub_request(:any, 'www.example.net').to_raise("some error") +``` + +### Raising timeout errors + +```ruby +stub_request(:any, 'www.example.net').to_timeout + +RestClient.post('www.example.net', 'abc') # ===> RestClient::RequestTimeout +``` + +### Multiple responses for repeated requests + +```ruby +stub_request(:get, "www.example.com"). + to_return({body: "abc"}, {body: "def"}) +Net::HTTP.get('www.example.com', '/') # ===> "abc\n" +Net::HTTP.get('www.example.com', '/') # ===> "def\n" + +#after all responses are used the last response will be returned infinitely + +Net::HTTP.get('www.example.com', '/') # ===> "def\n" +``` + +### Multiple responses using chained `to_return()`, `to_raise()` or `to_timeout` declarations + +```ruby +stub_request(:get, "www.example.com"). + to_return({body: "abc"}).then. #then() is just a syntactic sugar + to_return({body: "def"}).then. + to_raise(MyException) + +Net::HTTP.get('www.example.com', '/') # ===> "abc\n" +Net::HTTP.get('www.example.com', '/') # ===> "def\n" +Net::HTTP.get('www.example.com', '/') # ===> MyException raised +``` + +### Specifying number of times given response should be returned + +```ruby +stub_request(:get, "www.example.com"). + to_return({body: "abc"}).times(2).then. + to_return({body: "def"}) + +Net::HTTP.get('www.example.com', '/') # ===> "abc\n" +Net::HTTP.get('www.example.com', '/') # ===> "abc\n" +Net::HTTP.get('www.example.com', '/') # ===> "def\n" +``` + +### Removing unused stubs + +```ruby +stub_get = stub_request(:get, "www.example.com") +remove_request_stub(stub_get) +``` + +### Real requests to network can be allowed or disabled + +```ruby +WebMock.allow_net_connect! + +stub_request(:any, "www.example.com").to_return(body: "abc") + +Net::HTTP.get('www.example.com', '/') # ===> "abc" + +Net::HTTP.get('www.something.com', '/') # ===> /.+Something.+/ + +WebMock.disable_net_connect! + +Net::HTTP.get('www.something.com', '/') # ===> Failure +``` + +### External requests can be disabled while allowing localhost + +```ruby +WebMock.disable_net_connect!(allow_localhost: true) + +Net::HTTP.get('www.something.com', '/') # ===> Failure + +Net::HTTP.get('localhost:9887', '/') # ===> Allowed. Perhaps to Selenium? +``` + +### External requests can be disabled while allowing specific requests + +Allowed requests can be specified in a number of ways. + +With a `String` specifying a host name: + +```ruby +WebMock.disable_net_connect!(allow: 'www.example.org') + +RestClient.get('www.something.com', '/') # ===> Failure +RestClient.get('www.example.org', '/') # ===> Allowed +RestClient.get('www.example.org:8080', '/') # ===> Allowed +``` + +With a `String` specifying a host name and a port: + +```ruby +WebMock.disable_net_connect!(allow: 'www.example.org:8080') + +RestClient.get('www.something.com', '/') # ===> Failure +RestClient.get('www.example.org', '/') # ===> Failure +RestClient.get('www.example.org:8080', '/') # ===> Allowed +``` + +With a `Regexp` matching the URI: + +```ruby +WebMock.disable_net_connect!(allow: %r{ample\.org/foo}) + +RestClient.get('www.example.org', '/foo/bar') # ===> Allowed +RestClient.get('sample.org', '/foo') # ===> Allowed +RestClient.get('sample.org', '/bar') # ===> Failure +``` + +With an object that responds to `#call`, receiving a `URI` object and returning a boolean: + +```ruby +denylist = ['google.com', 'facebook.com', 'apple.com'] +allowed_sites = lambda{|uri| + denylist.none?{|site| uri.host.include?(site) } +} +WebMock.disable_net_connect!(allow: allowed_sites) + +RestClient.get('www.example.org', '/') # ===> Allowed +RestClient.get('www.facebook.com', '/') # ===> Failure +RestClient.get('apple.com', '/') # ===> Failure +``` + +With an `Array` of any of the above: + +```ruby +WebMock.disable_net_connect!(allow: [ + lambda{|uri| uri.host.length % 2 == 0 }, + /ample.org/, + 'bbc.co.uk', +]) + +RestClient.get('www.example.org', '/') # ===> Allowed +RestClient.get('bbc.co.uk', '/') # ===> Allowed +RestClient.get('bbc.com', '/') # ===> Allowed +RestClient.get('www.bbc.com', '/') # ===> Failure +``` + +## Connecting on Net::HTTP.start + +HTTP protocol has 3 steps: connect, request and response (or 4 with close). Most Ruby HTTP client libraries +treat connect as a part of request step, with the exception of `Net::HTTP` which +allows opening connection to the server separately to the request, by using `Net::HTTP.start`. + +WebMock API was also designed with connect being part of request step, and it only allows stubbing +requests, not connections. When `Net::HTTP.start` is called, WebMock doesn't know yet whether +a request is stubbed or not. WebMock by default delays a connection until the request is invoked, +so when there is no request, `Net::HTTP.start` doesn't do anything. +**This means that WebMock breaks the Net::HTTP behaviour by default!** + +To workaround this issue, WebMock offers `:net_http_connect_on_start` option, +which can be passed to `WebMock.allow_net_connect!` and `WebMock.disable_net_connect!` methods, i.e. + +```ruby +WebMock.allow_net_connect!(net_http_connect_on_start: true) +``` + +This forces WebMock Net::HTTP adapter to always connect on `Net::HTTP.start`. At the time of connection being made there is no information about the request or URL yet, therefore WebMock is not able to decide whether to stub a request or not and all connections are allowed. To enable connections only to a specific domain (e.g. your test server) use: + +```ruby +WebMock.allow_net_connect!(net_http_connect_on_start: "www.example.com") +``` + +## Setting Expectations + +### Setting expectations in Test::Unit + +```ruby +require 'webmock/test_unit' + +stub_request(:any, "www.example.com") + +uri = URI.parse('http://www.example.com/') +req = Net::HTTP::Post.new(uri.path) +req['Content-Length'] = 3 + +res = Net::HTTP.start(uri.host, uri.port) do |http| + http.request(req, 'abc') +end + +assert_requested :post, "http://www.example.com", + headers: {'Content-Length' => 3}, body: "abc", + times: 1 # ===> Success + +assert_not_requested :get, "http://www.something.com" # ===> Success + +assert_requested(:post, "http://www.example.com", + times: 1) { |req| req.body == "abc" } +``` + +### Expecting real (not stubbed) requests + +```ruby +WebMock.allow_net_connect! + +Net::HTTP.get('www.example.com', '/') # ===> Success + +assert_requested :get, "http://www.example.com" # ===> Success +``` + +### Setting expectations in Test::Unit on the stub + +```ruby +stub_get = stub_request(:get, "www.example.com") +stub_post = stub_request(:post, "www.example.com") + +Net::HTTP.get('www.example.com', '/') + +assert_requested(stub_get) +assert_not_requested(stub_post) +``` + + +### Setting expectations in RSpec on `WebMock` module + This style is borrowed from [fakeweb-matcher](http://github.com/pat/fakeweb-matcher) + +```ruby +require 'webmock/rspec' + +expect(WebMock).to have_requested(:get, "www.example.com"). + with(body: "abc", headers: {'Content-Length' => 3}).twice + +expect(WebMock).not_to have_requested(:get, "www.something.com") + +expect(WebMock).to have_requested(:post, "www.example.com"). + with { |req| req.body == "abc" } +# Note that the block with `do ... end` instead of curly brackets won't work! +# Why? See this comment https://github.com/bblimke/webmock/issues/174#issuecomment-34908908 + +expect(WebMock).to have_requested(:get, "www.example.com"). + with(query: {"a" => ["b", "c"]}) + +expect(WebMock).to have_requested(:get, "www.example.com"). + with(query: hash_including({"a" => ["b", "c"]})) + +expect(WebMock).to have_requested(:get, "www.example.com"). + with(body: {"a" => ["b", "c"]}, + headers: {'Content-Type' => 'application/json'}) +``` + +### Setting expectations in RSpec with `a_request` + +```ruby +expect(a_request(:post, "www.example.com"). + with(body: "abc", headers: {'Content-Length' => 3})). + to have_been_made.once + +expect(a_request(:post, "www.something.com")).to have_been_made.times(3) + +expect(a_request(:post, "www.something.com")).to have_been_made.at_least_once + +expect(a_request(:post, "www.something.com")). + to have_been_made.at_least_times(3) + +expect(a_request(:post, "www.something.com")).to have_been_made.at_most_twice + +expect(a_request(:post, "www.something.com")).to have_been_made.at_most_times(3) + +expect(a_request(:any, "www.example.com")).not_to have_been_made + +expect(a_request(:post, "www.example.com").with { |req| req.body == "abc" }). + to have_been_made + +expect(a_request(:get, "www.example.com").with(query: {"a" => ["b", "c"]})). + to have_been_made + +expect(a_request(:get, "www.example.com"). + with(query: hash_including({"a" => ["b", "c"]}))).to have_been_made + +expect(a_request(:post, "www.example.com"). + with(body: {"a" => ["b", "c"]}, + headers: {'Content-Type' => 'application/json'})).to have_been_made +``` + +### Setting expectations in RSpec on the stub + +```ruby +stub = stub_request(:get, "www.example.com") +# ... make requests ... +expect(stub).to have_been_requested +``` + +## Clearing stubs and request history + +If you want to reset all current stubs and history of requests use `WebMock.reset!` + +```ruby +stub_request(:any, "www.example.com") + +Net::HTTP.get('www.example.com', '/') # ===> Success + +WebMock.reset! + +Net::HTTP.get('www.example.com', '/') # ===> Failure + +assert_not_requested :get, "www.example.com" # ===> Success +``` + +## Clearing request counters + +If you want to reset **only** the counters of the executed requests use `WebMock.reset_executed_requests!` + +```ruby +stub = stub_request(:get, "www.example.com") +stub2 = stub_request(:get, "www.example2.com") + +Net::HTTP.get('www.example.com', '/') +Net::HTTP.get('www.example.com', '/') + +Net::HTTP.get('www.example2.com', '/') + +expect(stub).to have_been_requested.times(2) +expect(stub2).to have_been_requested.times(1) + +WebMock.reset_executed_requests! + +expect(stub).not_to have_been_requested +expect(stub2).not_to have_been_requested +``` + +## Disabling and enabling WebMock or only some http client adapters + +```ruby +# Disable WebMock (all adapters) +WebMock.disable! + +# Disable WebMock for all libs except Net::HTTP +WebMock.disable!(except: [:net_http]) + +# Enable WebMock (all adapters) +WebMock.enable! + +# Enable WebMock for all libs except Patron +WebMock.enable!(except: [:patron]) +``` + +## Matching requests + +An executed request matches stubbed request if it passes following criteria: + +- When request URI matches stubbed request URI string, Regexp pattern or RFC 6570 URI Template +- And request method is the same as stubbed request method or stubbed request method is :any +- And request body is the same as stubbed request body or stubbed request body is not specified +- And request headers match stubbed request headers, or stubbed request headers match a subset of request headers, or stubbed request headers are not specified +- And request matches provided block or block is not provided + +## Precedence of stubs + +Always the last declared stub matching the request will be applied i.e: + +```ruby +stub_request(:get, "www.example.com").to_return(body: "abc") +stub_request(:get, "www.example.com").to_return(body: "def") + +Net::HTTP.get('www.example.com', '/') # ====> "def" +``` + +## Matching URIs + +WebMock will match all different representations of the same URI. + +I.e all the following representations of the URI are equal: + +```ruby +"www.example.com" +"www.example.com/" +"www.example.com:80" +"www.example.com:80/" +"http://www.example.com" +"http://www.example.com/" +"http://www.example.com:80" +"http://www.example.com:80/" +``` + +The following URIs with userinfo are also equal for WebMock + +```ruby +"a b:pass@www.example.com" +"a b:pass@www.example.com/" +"a b:pass@www.example.com:80" +"a b:pass@www.example.com:80/" +"http://a b:pass@www.example.com" +"http://a b:pass@www.example.com/" +"http://a b:pass@www.example.com:80" +"http://a b:pass@www.example.com:80/" +"a%20b:pass@www.example.com" +"a%20b:pass@www.example.com/" +"a%20b:pass@www.example.com:80" +"a%20b:pass@www.example.com:80/" +"http://a%20b:pass@www.example.com" +"http://a%20b:pass@www.example.com/" +"http://a%20b:pass@www.example.com:80" +"http://a%20b:pass@www.example.com:80/" +``` + +or these + +```ruby +"www.example.com/my path/?a=my param&b=c" +"www.example.com/my%20path/?a=my%20param&b=c" +"www.example.com:80/my path/?a=my param&b=c" +"www.example.com:80/my%20path/?a=my%20param&b=c" +"http://www.example.com/my path/?a=my param&b=c" +"http://www.example.com/my%20path/?a=my%20param&b=c" +"http://www.example.com:80/my path/?a=my param&b=c" +"http://www.example.com:80/my%20path/?a=my%20param&b=c" +``` + +If you provide Regexp to match URI, WebMock will try to match it against every valid form of the same url. + +I.e `/my path/` will match `www.example.com/my%20path` because it is equivalent of `www.example.com/my path` + +## Matching with URI Templates + +If you use [Addressable::Template](https://github.com/sporkmonger/addressable#uri-templates) for matching, then WebMock will defer the matching rules to Addressable, which complies with [RFC 6570](http://tools.ietf.org/html/rfc6570). + +If you use any of the WebMock methods for matching query params, then Addressable will be used to match the base URI and WebMock will match the query params. If you do not, then WebMock will let Addressable match the full URI. + +## Matching headers + +WebMock will match request headers against stubbed request headers in the following situations: + +1. Stubbed request has headers specified and request headers are the same as stubbed headers <br/> +i.e stubbed headers: `{ 'Header1' => 'Value1', 'Header2' => 'Value2' }`, requested: `{ 'Header1' => 'Value1', 'Header2' => 'Value2' }` + +2. Stubbed request has headers specified and stubbed request headers are a subset of request headers <br/> +i.e stubbed headers: `{ 'Header1' => 'Value1' }`, requested: `{ 'Header1' => 'Value1', 'Header2' => 'Value2' }` + +3. Stubbed request has no headers <br/> +i.e stubbed headers: `nil`, requested: `{ 'Header1' => 'Value1', 'Header2' => 'Value2' }` + +WebMock normalises headers and treats all forms of same headers as equal: +i.e the following two sets of headers are equal: + +`{ "Header1" => "value1", content_length: 123, X_CuStOm_hEAder: :value }` + +`{ header1: "value1", "Content-Length" => 123, "x-cuSTOM-HeAder" => "value" }` + +## Recording real requests and responses and replaying them later + +To record your application's real HTTP interactions and replay them later in tests you can use [VCR](https://github.com/vcr/vcr) with WebMock. + +## Request callbacks + +#### WebMock can invoke callbacks stubbed or real requests: + +```ruby +WebMock.after_request do |request_signature, response| + puts "Request #{request_signature} was made and #{response} was returned" +end +``` + +#### invoke callbacks for real requests only and except requests made with Patron + +```ruby +WebMock.after_request(except: [:patron], + real_requests_only: true) do |req_signature, response| + puts "Request #{req_signature} was made and #{response} was returned" +end +``` + +## Bugs and Issues + +Please submit them here [http://github.com/bblimke/webmock/issues](http://github.com/bblimke/webmock/issues) + +## Issue triage [![Open Source Helpers](https://www.codetriage.com/bblimke/webmock/badges/users.svg)](https://www.codetriage.com/bblimke/webmock) + +You can contribute by triaging issues which may include reproducing bug reports or asking for vital information, such as version numbers or reproduction instructions. If you would like to start triaging issues, one easy way to get started is to [subscribe to webmock on CodeTriage](https://www.codetriage.com/bblimke/webmock). + +## Suggestions + +If you have any suggestions on how to improve WebMock please send an email to the mailing list [groups.google.com/group/webmock-users](http://groups.google.com/group/webmock-users) + +I'm particularly interested in how the DSL could be improved. + +## Development + +In order to work on Webmock you first need to fork and clone the repo. +Please do any work on a dedicated branch and rebase against master +before sending a pull request. + +## Credits + +The initial lines of this project were written during New Bamboo [Hack Day](http://blog.new-bamboo.co.uk/2009/11/13/hackday-results) +Thanks to my fellow [Bambinos](http://new-bamboo.co.uk/) for all the great suggestions! + +People who submitted patches and new features or suggested improvements. Many thanks to these people: + +* Ben Pickles +* Mark Evans +* Ivan Vega +* Piotr Usewicz +* Nick Plante +* Nick Quaranto +* Diego E. "Flameeyes" Pettenò +* Niels Meersschaert +* Mack Earnhardt +* Arvicco +* Sergio Gil +* Jeffrey Jones +* Tekin Suleyman +* Tom Ward +* Nadim Bitar +* Myron Marston +* Sam Phillips +* Jose Angel Cortinas +* Razic +* Steve Tooke +* Nathaniel Bibler +* Martyn Loughran +* Muness Alrubaie +* Charles Li +* Ryan Bigg +* Pete Higgins +* Hans de Graaff +* Alastair Brunton +* Sam Stokes +* Eugene Bolshakov +* James Conroy-Finn +* Salvador Fuentes Jr +* Alex Rothenberg +* Aidan Feldman +* Steve Hull +* Jay Adkisson +* Zach Dennis +* Nikita Fedyashev +* Lin Jen-Shin +* David Yeu +* Andreas Garnæs +* Roman Shterenzon +* Chris McGrath +* Stephen Celis +* Eugene Pimenov +* Albert Llop +* Christopher Pickslay +* Tammer Saleh +* Nicolas Fouché +* Joe Van Dyk +* Mark Abramov +* Frank Schumacher +* Dimitrij Denissenko +* Marnen Laibow-Koser +* Evgeniy Dolzhenko +* Nick Recobra +* Jordan Elver +* Joe Karayusuf +* Paul Cortens +* jugyo +* aindustries +* Eric Oestrich +* erwanlr +* Ben Bleything +* Jon Leighton +* Ryan Schlesinger +* Julien Boyer +* Kevin Glowacz +* Hans Hasselberg +* Andrew France +* Jonathan Hyman +* Rex Feng +* Pavel Forkert +* Jordi Massaguer Pla +* Jake Benilov +* Tom Beauvais +* Mokevnin Kirill +* Alex Grant +* Lucas Dohmen +* Bastien Vaucher +* Joost Baaij +* Joel Chippindale +* Murahashi Sanemat Kenichi +* Tim Kurvers +* Ilya Vassilevsky +* gotwalt +* Leif Bladt +* Alex Tomlins +* Mitsutaka Mimura +* Tomy Kaira +* Daniel van Hoesel +* Ian Asaff +* Ian Lesperance +* Matthew Horan +* Dmitry Gutov +* Florian Dütsch +* Manuel Meurer +* Brian D. Burns +* Riley Strong +* Tamir Duberstein +* Stefano Uliari +* Alex Stupakov +* Karen Wang +* Matt Burke +* Jon Rowe +* Aleksey V. Zapparov +* Praveen Arimbrathodiyil +* Bo Jeanes +* Matthew Conway +* Rob Olson +* Max Lincoln +* Oleg Gritsenko +* Hwan-Joon Choi +* SHIBATA Hiroshi +* Caleb Thompson +* Theo Hultberg +* Pablo Jairala +* Insoo Buzz Jung +* Carlos Alonso Pérez +* trlorenz +* Alexander Simonov +* Thorbjørn Hermanse +* Mark Lorenz +* tjsousa +* Tasos Stathopoulos +* Dan Buettner +* Sven Riedel +* Mark Lorenz +* Dávid Kovács +* fishermand46 +* Franky Wahl +* ChaYoung You +* Simon Russell +* Steve Mitchell +* Mattias Putman +* Zachary Anker +* Emmanuel Sambo +* Ramon Tayag +* Johannes Schlumberger +* Siôn Le Roux +* Matt Palmer +* Zhao Wen +* Krzysztof Rygielski +* Magne Land +* yurivm +* Mike Knepper +* Charles Pence +* Alexey Zapparov +* Pablo Brasero +* Cedric Pimenta +* Michiel Karnebeek +* Alex Kestner +* Manfred Stienstra +* Tim Diggins +* Gabriel Chaney +* Chris Griego +* Taiki Ono +* Jonathan Schatz +* Jose Luis Honorato +* Aaron Kromer +* Pavel Jurašek +* Jake Worth +* Gabe Martin-Dempesy +* Michael Grosser +* Aleksei Maridashvili +* Ville Lautanala +* Koichi ITO +* Jordan Harband +* Tarmo Tänav +* Joe Marty +* Chris Thomson +* Vít Ondruch +* George Ulmer +* Christof Koenig +* Chung-Yi Chi +* Olexandr Hoshylyk +* Janko Marohnić +* Pat Allan +* Rick Song +* NARUSE, Yui +* Piotr Boniecki +* Olia Kremmyda +* Michał Matyas +* Matt Brictson +* Kenny Ortmann +* redbar0n +* Lukas Pokorny +* Arkadiy Tetelman +* Kazato Sugimoto +* Olle Jonsson +* Pavel Rosický +* Geremia Taglialatela +* Koichi Sasada +* Yusuke Endoh +* Grey Baker +* SoonKhen OwYong +* Pavel Valena +* Adam Sokolnicki +* Jeff Felchner +* Eike Send +* Claudio Poli +* Csaba Apagyi +* Frederick Cheung +* Fábio D. Batista +* Andriy Yanko +* y-yagi +* Rafael França +* George Claghorn +* Alex Junger +* Orien Madgwick +* Andrei Sidorov +* Marco Costa +* Ryan Davis +* Brandur +* Samuel Williams +* Patrik Ragnarsson +* Alex Coomans +* Vesa Laakso +* John Hawthorn +* guppy0356 +* Thilo Rusche +* Andrew Stuntz +* Lucas Uyezu +* Bruno Sutic +* Ryan Kerr +* Adam Harwood +* Ben Koshy +* Jesse Bowes +* Marek Kasztelnik +* ce07c3 +* Jun Jiang +* Oleksiy Kovyrin +* Matt Larraz +* Tony Schneider +* Niklas Hösl +* Johanna Hartmann +* Alex Vondrak +* Will Storey +* Eduardo Hernandez +* ojab +* Giorgio Gambino +* Timmitry +* Michael Fairley +* Ray Zane +* Go Sueyoshi +* Cedric Sohrauer +* Akira Matsuda +* Mark Spangler +* Henrik Nyh +* Yoann Lecuyer +* Lucas Arnaud +* Marc Rohloff +* inkstak +* Yuki Inoue +* Brandon Weaver +* Josh Nichols +* Ricardo Trindade +* Earlopain +* James Brown +* Kazuhiro NISHIYAMA +* Étienne Barrié +* Matt Brown +* Victor Maslov +* Gio Lodi +* Ryan Brooks +* Jacob Frautschi +* Christian Schmidt +* Rodrigo Argumedo +* Patrick Jaberg +* Oleg +* Mikhail Doronin + +For a full list of contributors you can visit the +[contributors](https://github.com/bblimke/webmock/contributors) page. + +## Background + +Thank you Fakeweb! This library was inspired by [FakeWeb](https://github.com/chrisk/fakeweb). +I imported some solutions from that project to WebMock. I also copied some code i.e Net:HTTP adapter. +Fakeweb architecture unfortunately didn't allow me to extend it easily with the features I needed. +I also preferred some things to work differently i.e request stub precedence. + +## Copyright + +Copyright (c) 2009-2010 Bartosz Blimke. See LICENSE for details. diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock.rb new file mode 100644 index 0000000..894e818 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +require 'singleton' + +require 'addressable/uri' +require 'addressable/template' + +require_relative 'webmock/deprecation' +require_relative 'webmock/version' + +require_relative 'webmock/errors' + +require_relative 'webmock/util/query_mapper' +require_relative 'webmock/util/uri' +require_relative 'webmock/util/headers' +require_relative 'webmock/util/hash_counter' +require_relative 'webmock/util/hash_keys_stringifier' +require_relative 'webmock/util/values_stringifier' +require_relative 'webmock/util/parsers/json' +require_relative 'webmock/util/parsers/xml' +require_relative 'webmock/util/version_checker' +require_relative 'webmock/util/hash_validator' + +require_relative 'webmock/matchers/hash_argument_matcher' +require_relative 'webmock/matchers/hash_excluding_matcher' +require_relative 'webmock/matchers/hash_including_matcher' +require_relative 'webmock/matchers/any_arg_matcher' + +require_relative 'webmock/request_pattern' +require_relative 'webmock/request_signature' +require_relative 'webmock/responses_sequence' +require_relative 'webmock/request_stub' +require_relative 'webmock/response' +require_relative 'webmock/rack_response' + +require_relative 'webmock/stub_request_snippet' +require_relative 'webmock/request_signature_snippet' +require_relative 'webmock/request_body_diff' + +require_relative 'webmock/assertion_failure' +require_relative 'webmock/request_execution_verifier' +require_relative 'webmock/config' +require_relative 'webmock/callback_registry' +require_relative 'webmock/request_registry' +require_relative 'webmock/stub_registry' +require_relative 'webmock/api' + +require_relative 'webmock/http_lib_adapters/http_lib_adapter_registry' +require_relative 'webmock/http_lib_adapters/http_lib_adapter' +require_relative 'webmock/http_lib_adapters/net_http' +require_relative 'webmock/http_lib_adapters/http_rb_adapter' +require_relative 'webmock/http_lib_adapters/httpclient_adapter' +require_relative 'webmock/http_lib_adapters/patron_adapter' +require_relative 'webmock/http_lib_adapters/curb_adapter' +require_relative 'webmock/http_lib_adapters/em_http_request_adapter' +require_relative 'webmock/http_lib_adapters/typhoeus_hydra_adapter' +require_relative 'webmock/http_lib_adapters/excon_adapter' +require_relative 'webmock/http_lib_adapters/manticore_adapter' +require_relative 'webmock/http_lib_adapters/async_http_client_adapter' + +require_relative 'webmock/webmock' diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/api.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/api.rb new file mode 100644 index 0000000..4110c09 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/api.rb @@ -0,0 +1,111 @@ +# frozen_string_literal: true + +module WebMock + module API + extend self + + def stub_request(method, uri) + WebMock::StubRegistry.instance. + register_request_stub(WebMock::RequestStub.new(method, uri)) + end + + alias_method :stub_http_request, :stub_request + + def a_request(method, uri) + WebMock::RequestPattern.new(method, uri) + end + + class << self + alias :request :a_request + end + + def assert_requested(*args, &block) + if not args[0].is_a?(WebMock::RequestStub) + args = convert_uri_method_and_options_to_request_and_options(args[0], args[1], args[2], &block) + elsif block + raise ArgumentError, "assert_requested with a stub object, doesn't accept blocks" + end + assert_request_requested(*args) + end + + def assert_not_requested(*args, &block) + if not args[0].is_a?(WebMock::RequestStub) + args = convert_uri_method_and_options_to_request_and_options(args[0], args[1], args[2], &block) + elsif block + raise ArgumentError, "assert_not_requested with a stub object, doesn't accept blocks" + end + assert_request_not_requested(*args) + end + alias refute_requested assert_not_requested + + # Similar to RSpec::Mocks::ArgumentMatchers#hash_including() + # + # Matches a hash that includes the specified key(s) or key/value pairs. + # Ignores any additional keys. + # + # @example + # + # object.should_receive(:message).with(hash_including(:key => val)) + # object.should_receive(:message).with(hash_including(:key)) + # object.should_receive(:message).with(hash_including(:key, :key2 => val2)) + def hash_including(*args) + if defined?(super) + super + else + WebMock::Matchers::HashIncludingMatcher.new(anythingize_lonely_keys(*args)) + end + end + + def hash_excluding(*args) + if defined?(super) + super + else + WebMock::Matchers::HashExcludingMatcher.new(anythingize_lonely_keys(*args)) + end + end + + def remove_request_stub(stub) + WebMock::StubRegistry.instance.remove_request_stub(stub) + end + + def reset_executed_requests! + WebMock::RequestRegistry.instance.reset! + end + + private + + def convert_uri_method_and_options_to_request_and_options(method, uri, options, &block) + options ||= {} + options_for_pattern = options.dup + [:times, :at_least_times, :at_most_times].each { |key| options_for_pattern.delete(key) } + request = WebMock::RequestPattern.new(method, uri, options_for_pattern) + request = request.with(&block) if block + [request, options] + end + + def assert_request_requested(request, options = {}) + times = options.delete(:times) + at_least_times = options.delete(:at_least_times) + at_most_times = options.delete(:at_most_times) + times = 1 if times.nil? && at_least_times.nil? && at_most_times.nil? + verifier = WebMock::RequestExecutionVerifier.new(request, times, at_least_times, at_most_times) + WebMock::AssertionFailure.failure(verifier.failure_message) unless verifier.matches? + end + + def assert_request_not_requested(request, options = {}) + times = options.delete(:times) + at_least_times = options.delete(:at_least_times) + at_most_times = options.delete(:at_most_times) + verifier = WebMock::RequestExecutionVerifier.new(request, times, at_least_times, at_most_times) + WebMock::AssertionFailure.failure(verifier.failure_message_when_negated) unless verifier.does_not_match? + end + + #this is a based on RSpec::Mocks::ArgumentMatchers#anythingize_lonely_keys + def anythingize_lonely_keys(*args) + hash = args.last.class == Hash ? args.delete_at(-1) : {} + args.each { | arg | hash[arg] = WebMock::Matchers::AnyArgMatcher.new(nil) } + hash + end + + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/assertion_failure.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/assertion_failure.rb new file mode 100644 index 0000000..e2af6c9 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/assertion_failure.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module WebMock + class AssertionFailure + @error_class = RuntimeError + class << self + attr_accessor :error_class + def failure(message) + raise @error_class.new(message) + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/callback_registry.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/callback_registry.rb new file mode 100644 index 0000000..5b9c126 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/callback_registry.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module WebMock + class CallbackRegistry + @@callbacks = [] + + def self.add_callback(options, block) + @@callbacks << {options: options, block: block} + end + + def self.callbacks + @@callbacks + end + + def self.invoke_callbacks(options, request_signature, response) + return if @@callbacks.empty? + CallbackRegistry.callbacks.each do |callback| + except = callback[:options][:except] + real_only = callback[:options][:real_requests_only] + unless except && except.include?(options[:lib]) + if !real_only || options[:real_request] + callback[:block].call(request_signature, response) + end + end + end + end + + def self.reset + @@callbacks = [] + end + + def self.any_callbacks? + !@@callbacks.empty? + end + + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/config.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/config.rb new file mode 100644 index 0000000..965546c --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/config.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module WebMock + class Config + include Singleton + + def initialize + @show_stubbing_instructions = true + @show_body_diff = true + end + + attr_accessor :allow_net_connect + attr_accessor :allow_localhost + attr_accessor :allow + attr_accessor :net_http_connect_on_start + attr_accessor :show_stubbing_instructions + attr_accessor :query_values_notation + attr_accessor :show_body_diff + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/cucumber.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/cucumber.rb new file mode 100644 index 0000000..73ec9c8 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/cucumber.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require 'webmock' +require 'webmock/rspec/matchers' + +WebMock.enable! + +World(WebMock::API, WebMock::Matchers) + +After do + WebMock.reset! +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/deprecation.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/deprecation.rb new file mode 100644 index 0000000..2b1ebae --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/deprecation.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module WebMock + class Deprecation + class << self + def warning(message) + warn "WebMock deprecation warning: #{message}" + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/errors.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/errors.rb new file mode 100644 index 0000000..36b095a --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/errors.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module WebMock + + class NetConnectNotAllowedError < Exception + def initialize(request_signature) + request_signature_snippet = RequestSignatureSnippet.new(request_signature) + text = [ + "Real HTTP connections are disabled. Unregistered request: #{request_signature}", + request_signature_snippet.stubbing_instructions, + request_signature_snippet.request_stubs, + "="*60 + ].compact.join("\n\n") + super(text) + end + + end + +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/async_http_client_adapter.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/async_http_client_adapter.rb new file mode 100644 index 0000000..0591bb7 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/async_http_client_adapter.rb @@ -0,0 +1,228 @@ +# frozen_string_literal: true + +begin + require 'async' + require 'async/http' +rescue LoadError + # async-http not found +end + +if defined?(Async::HTTP) + module WebMock + module HttpLibAdapters + class AsyncHttpClientAdapter < HttpLibAdapter + adapter_for :async_http_client + + OriginalAsyncHttpClient = Async::HTTP::Client unless const_defined?(:OriginalAsyncHttpClient) + + class << self + def enable! + Async::HTTP.send(:remove_const, :Client) + Async::HTTP.send(:const_set, :Client, Async::HTTP::WebMockClientWrapper) + end + + def disable! + Async::HTTP.send(:remove_const, :Client) + Async::HTTP.send(:const_set, :Client, OriginalAsyncHttpClient) + end + end + end + end + end + + module Async + module HTTP + class WebMockClientWrapper < Client + def initialize( + endpoint, + protocol: endpoint.protocol, + scheme: endpoint.scheme, + authority: endpoint.authority, + **options + ) + webmock_endpoint = WebMockEndpoint.new(scheme, authority, protocol) + + @network_client = WebMockClient.new(endpoint, **options) + @webmock_client = WebMockClient.new(webmock_endpoint, **options) + + @endpoint = endpoint + @scheme = scheme + @authority = authority + end + + def call(request) + request.scheme ||= self.scheme + request.authority ||= self.authority + + request_signature = build_request_signature(request) + WebMock::RequestRegistry.instance.requested_signatures.put(request_signature) + webmock_response = WebMock::StubRegistry.instance.response_for_request(request_signature) + net_connect_allowed = WebMock.net_connect_allowed?(request_signature.uri) + real_request = false + + if webmock_response + webmock_response.raise_error_if_any + raise Async::TimeoutError, 'WebMock timeout error' if webmock_response.should_timeout + WebMockApplication.add_webmock_response(request, webmock_response) + response = @webmock_client.call(request) + elsif net_connect_allowed + response = @network_client.call(request) + real_request = true + else + raise WebMock::NetConnectNotAllowedError.new(request_signature) unless webmock_response + end + + if WebMock::CallbackRegistry.any_callbacks? + webmock_response ||= build_webmock_response(response) + WebMock::CallbackRegistry.invoke_callbacks( + { + lib: :async_http_client, + real_request: real_request + }, + request_signature, + webmock_response + ) + end + + response + end + + def close + @network_client.close + @webmock_client.close + end + + private + + def build_request_signature(request) + body = request.read + request.body = ::Protocol::HTTP::Body::Buffered.wrap(body) + WebMock::RequestSignature.new( + request.method.downcase.to_sym, + "#{request.scheme}://#{request.authority}#{request.path}", + headers: request.headers.to_h, + body: body + ) + end + + def build_webmock_response(response) + body = response.read + response.body = ::Protocol::HTTP::Body::Buffered.wrap(body) + + webmock_response = WebMock::Response.new + webmock_response.status = [ + response.status, + ::Protocol::HTTP1::Reason::DESCRIPTIONS[response.status] + ] + webmock_response.headers = build_webmock_response_headers(response) + webmock_response.body = body + webmock_response + end + + def build_webmock_response_headers(response) + response.headers.each.each_with_object({}) do |(k, v), o| + o[k] ||= [] + o[k] << v + end + end + end + + class WebMockClient < Client + end + + class WebMockEndpoint + def initialize(scheme, authority, protocol) + @scheme = scheme + @authority = authority + @protocol = protocol + end + + attr :scheme, :authority, :protocol + + def connect + server_socket, client_socket = create_connected_sockets + Async(transient: true) do + accept_socket(server_socket) + end + client_socket + end + + def inspect + "\#<#{self.class}> #{scheme}://#{authority} protocol=#{protocol}" + end + + private + + def socket_class + defined?(Async::IO::Socket) ? Async::IO::Socket : Socket + end + + def create_connected_sockets + pair = begin + socket_class.pair(Socket::AF_UNIX, Socket::SOCK_STREAM) + rescue Errno::EAFNOSUPPORT + socket_class.pair(Socket::AF_INET, Socket::SOCK_STREAM) + end + pair.tap do |sockets| + sockets.each do |socket| + socket.instance_variable_set :@alpn_protocol, nil + socket.instance_eval do + def alpn_protocol + nil # means HTTP11 will be used for HTTPS + end + end + end + end + end + + def accept_socket(socket) + server = Async::HTTP::Server.new(WebMockApplication, self) + server.accept(socket, socket.remote_address) + end + end + + module WebMockApplication + WEBMOCK_REQUEST_ID_HEADER = 'x-webmock-request-id'.freeze + + class << self + def call(request) + request.read + webmock_response = get_webmock_response(request) + build_response(webmock_response) + end + + def add_webmock_response(request, webmock_response) + webmock_request_id = request.object_id.to_s + request.headers.add(WEBMOCK_REQUEST_ID_HEADER, webmock_request_id) + webmock_responses[webmock_request_id] = webmock_response + end + + def get_webmock_response(request) + webmock_request_id = request.headers[WEBMOCK_REQUEST_ID_HEADER][0] + webmock_responses.fetch(webmock_request_id) + end + + private + + def webmock_responses + @webmock_responses ||= {} + end + + def build_response(webmock_response) + headers = (webmock_response.headers || {}).each_with_object([]) do |(k, value), o| + Array(value).each do |v| + o.push [k, v] unless k.downcase == 'content-length' # async-http appends the exact content-length automatically + end + end + + ::Protocol::HTTP::Response[ + webmock_response.status[0], + headers, + webmock_response.body + ] + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/curb_adapter.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/curb_adapter.rb new file mode 100644 index 0000000..8da5c97 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/curb_adapter.rb @@ -0,0 +1,353 @@ +# frozen_string_literal: true + +begin + require 'curb' +rescue LoadError + # curb not found +end + +if defined?(Curl) + WebMock::VersionChecker.new('Curb', Curl::CURB_VERSION, '0.7.16', '1.0.1', ['0.8.7']).check_version! + + module WebMock + module HttpLibAdapters + class CurbAdapter < HttpLibAdapter + adapter_for :curb + + OriginalCurlEasy = Curl::Easy unless const_defined?(:OriginalCurlEasy) + + def self.enable! + Curl.send(:remove_const, :Easy) + Curl.send(:const_set, :Easy, Curl::WebMockCurlEasy) + end + + def self.disable! + Curl.send(:remove_const, :Easy) + Curl.send(:const_set, :Easy, OriginalCurlEasy) + end + + # Borrowed from Patron: + # http://github.com/toland/patron/blob/master/lib/patron/response.rb + def self.parse_header_string(header_string) + status, headers = nil, {} + + header_string.split(/\r\n/).each do |header| + if header =~ %r|^HTTP/1.[01] \d\d\d (.*)| + status = $1 + else + parts = header.split(':', 2) + unless parts.empty? + parts[1].strip! unless parts[1].nil? + if headers.has_key?(parts[0]) + headers[parts[0]] = [headers[parts[0]]] unless headers[parts[0]].kind_of? Array + headers[parts[0]] << parts[1] + else + headers[parts[0]] = parts[1] + end + end + end + end + + return status, headers + end + end + end + end + + module Curl + class WebMockCurlEasy < Curl::Easy + def curb_or_webmock + request_signature = build_request_signature + WebMock::RequestRegistry.instance.requested_signatures.put(request_signature) + + if webmock_response = WebMock::StubRegistry.instance.response_for_request(request_signature) + build_curb_response(webmock_response) + WebMock::CallbackRegistry.invoke_callbacks( + {lib: :curb}, request_signature, webmock_response) + invoke_curb_callbacks + true + elsif WebMock.net_connect_allowed?(request_signature.uri) + res = yield + if WebMock::CallbackRegistry.any_callbacks? + webmock_response = build_webmock_response + WebMock::CallbackRegistry.invoke_callbacks( + {lib: :curb, real_request: true}, request_signature, + webmock_response) + end + res + else + raise WebMock::NetConnectNotAllowedError.new(request_signature) + end + end + + def build_request_signature + method = @webmock_method.to_s.downcase.to_sym + + uri = WebMock::Util::URI.heuristic_parse(self.url) + uri.path = uri.normalized_path.gsub("[^:]//","/") + + headers = headers_as_hash(self.headers).merge(basic_auth_headers) + + request_body = case method + when :post, :patch + self.post_body || @post_body + when :put + @put_data + else + nil + end + + if defined?( @on_debug ) + @on_debug.call("Trying 127.0.0.1...\r\n", 0) + @on_debug.call('Connected to ' + uri.hostname + "\r\n", 0) + @debug_method = method.upcase + @debug_path = uri.path + @debug_host = uri.hostname + http_request = ["#{@debug_method} #{@debug_path} HTTP/1.1"] + http_request << "Host: #{uri.hostname}" + headers.each do |name, value| + http_request << "#{name}: #{value}" + end + @on_debug.call(http_request.join("\r\n") + "\r\n\r\n", 2) + if request_body + @on_debug.call(request_body + "\r\n", 4) + @on_debug.call( + "upload completely sent off: #{request_body.bytesize}"\ + " out of #{request_body.bytesize} bytes\r\n", 0 + ) + end + end + + request_signature = WebMock::RequestSignature.new( + method, + uri.to_s, + body: request_body, + headers: headers + ) + request_signature + end + + def headers_as_hash(headers) + if headers.is_a?(Array) + headers.inject({}) {|hash, header| + name, value = header.split(":", 2).map(&:strip) + hash[name] = value + hash + } + else + headers + end + end + + def basic_auth_headers + if self.username + {'Authorization' => WebMock::Util::Headers.basic_auth_header(self.username, self.password)} + else + {} + end + end + + def build_curb_response(webmock_response) + raise Curl::Err::TimeoutError if webmock_response.should_timeout + webmock_response.raise_error_if_any + + @body_str = webmock_response.body + @response_code = webmock_response.status[0] + + @header_str = "HTTP/1.1 #{webmock_response.status[0]} #{webmock_response.status[1]}\r\n".dup + + @on_debug.call(@header_str, 1) if defined?( @on_debug ) + + if webmock_response.headers + @header_str << webmock_response.headers.map do |k,v| + header = "#{k}: #{v.is_a?(Array) ? v.join(", ") : v}" + @on_debug.call(header + "\r\n", 1) if defined?( @on_debug ) + header + end.join("\r\n") + @on_debug.call("\r\n", 1) if defined?( @on_debug ) + + location = webmock_response.headers['Location'] + if self.follow_location? && location + @last_effective_url = location + webmock_follow_location(location) + end + + @content_type = webmock_response.headers["Content-Type"] + @transfer_encoding = webmock_response.headers["Transfer-Encoding"] + end + + @last_effective_url ||= self.url + end + + def webmock_follow_location(location) + first_url = self.url + self.url = location + + curb_or_webmock do + send( :http, {'method' => @webmock_method} ) + end + + self.url = first_url + end + + def invoke_curb_callbacks + @on_progress.call(0.0,1.0,0.0,1.0) if defined?( @on_progress ) + self.header_str.lines.each { |header_line| @on_header.call header_line } if defined?( @on_header ) + if defined?( @on_body ) + if chunked_response? + self.body_str.each do |chunk| + @on_body.call(chunk) + end + else + @on_body.call(self.body_str) + end + end + @on_complete.call(self) if defined?( @on_complete ) + + case response_code + when 200..299 + @on_success.call(self) if defined?( @on_success ) + when 400..499 + @on_missing.call(self, self.response_code) if defined?( @on_missing ) + when 500..599 + @on_failure.call(self, self.response_code) if defined?( @on_failure ) + end + end + + def chunked_response? + defined?( @transfer_encoding ) && @transfer_encoding == 'chunked' && self.body_str.respond_to?(:each) + end + + def build_webmock_response + status, headers = + WebMock::HttpLibAdapters::CurbAdapter.parse_header_string(self.header_str) + + if defined?( @on_debug ) + http_response = ["HTTP/1.0 #{@debug_method} #{@debug_path}"] + headers.each do |name, value| + http_response << "#{name}: #{value}" + end + http_response << self.body_str + @on_debug.call(http_response.join("\r\n") + "\r\n", 3) + @on_debug.call("Connection #0 to host #{@debug_host} left intact\r\n", 0) + end + + webmock_response = WebMock::Response.new + webmock_response.status = [self.response_code, status] + webmock_response.body = self.body_str + webmock_response.headers = headers + webmock_response + end + + ### + ### Mocks of Curl::Easy methods below here. + ### + + def http(method) + @webmock_method = method + super + end + + %w[ get head delete ].each do |verb| + define_method "http_#{verb}" do + @webmock_method = verb + super() + end + end + + def http_put data = nil + @webmock_method = :put + @put_data = data if data + super + end + alias put http_put + + def http_post *data + @webmock_method = :post + @post_body = data.join('&') if data && !data.empty? + super + end + alias post http_post + + def perform + @webmock_method ||= :get + curb_or_webmock { super } + ensure + reset_webmock_method + end + + def put_data= data + @webmock_method = :put + @put_data = data + super + end + + def post_body= data + @webmock_method = :post + super + end + + def delete= value + @webmock_method = :delete if value + super + end + + def head= value + @webmock_method = :head if value + super + end + + def verbose=(verbose) + @verbose = verbose + end + + def verbose? + @verbose ||= false + end + + def body_str + @body_str ||= super + end + alias body body_str + + def response_code + @response_code ||= super + end + + def header_str + @header_str ||= super + end + alias head header_str + + def last_effective_url + @last_effective_url ||= super + end + + def content_type + @content_type ||= super + end + + %w[ success failure missing header body complete progress debug ].each do |callback| + class_eval <<-METHOD, __FILE__, __LINE__ + def on_#{callback} &block + @on_#{callback} = block + super + end + METHOD + end + + def reset_webmock_method + @webmock_method = :get + end + + def reset + instance_variable_set(:@body_str, nil) + instance_variable_set(:@content_type, nil) + instance_variable_set(:@header_str, nil) + instance_variable_set(:@last_effective_url, nil) + instance_variable_set(:@response_code, nil) + super + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/em_http_request_adapter.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/em_http_request_adapter.rb new file mode 100644 index 0000000..ec0b89f --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/em_http_request_adapter.rb @@ -0,0 +1,239 @@ +# frozen_string_literal: true + +begin + require 'em-http-request' +rescue LoadError + # em-http-request not found +end + +if defined?(EventMachine::HttpClient) + module WebMock + module HttpLibAdapters + class EmHttpRequestAdapter < HttpLibAdapter + adapter_for :em_http_request + + OriginalHttpClient = EventMachine::HttpClient unless const_defined?(:OriginalHttpClient) + OriginalHttpConnection = EventMachine::HttpConnection unless const_defined?(:OriginalHttpConnection) + + def self.enable! + EventMachine.send(:remove_const, :HttpConnection) + EventMachine.send(:const_set, :HttpConnection, EventMachine::WebMockHttpConnection) + EventMachine.send(:remove_const, :HttpClient) + EventMachine.send(:const_set, :HttpClient, EventMachine::WebMockHttpClient) + end + + def self.disable! + EventMachine.send(:remove_const, :HttpConnection) + EventMachine.send(:const_set, :HttpConnection, OriginalHttpConnection) + EventMachine.send(:remove_const, :HttpClient) + EventMachine.send(:const_set, :HttpClient, OriginalHttpClient) + end + end + end + end + + module EventMachine + if defined?(Synchrony) && HTTPMethods.instance_methods.include?(:aget) + # have to make the callbacks fire on the next tick in order + # to avoid the dreaded "double resume" exception + module HTTPMethods + %w[get head post delete put].each do |type| + class_eval %[ + def #{type}(options = {}, &blk) + f = Fiber.current + + conn = setup_request(:#{type}, options, &blk) + conn.callback { EM.next_tick { f.resume(conn) } } + conn.errback { EM.next_tick { f.resume(conn) } } + + Fiber.yield + end + ] + end + end + end + + class WebMockHttpConnection < HttpConnection + def activate_connection(client) + request_signature = client.request_signature + + if client.stubbed_webmock_response + conn = HttpStubConnection.new rand(10000) + post_init + + @deferred = false + @conn = conn + + conn.parent = self + conn.pending_connect_timeout = @connopts.connect_timeout + conn.comm_inactivity_timeout = @connopts.inactivity_timeout + + finalize_request(client) + @conn.set_deferred_status :succeeded + elsif WebMock.net_connect_allowed?(request_signature.uri) + super + else + raise WebMock::NetConnectNotAllowedError.new(request_signature) + end + end + + def drop_client + @clients.shift + end + end + + class WebMockHttpClient < EventMachine::HttpClient + include HttpEncoding + + def uri + @req.uri + end + + def setup(response, uri, error = nil) + @last_effective_url = @uri = uri + if error + on_error(error) + @conn.drop_client + fail(self) + else + @conn.receive_data(response) + succeed(self) + end + end + + def connection_completed + @state = :response_header + send_request(*headers_and_body_processed_by_middleware) + end + + def send_request(head, body) + WebMock::RequestRegistry.instance.requested_signatures.put(request_signature) + + if stubbed_webmock_response + WebMock::CallbackRegistry.invoke_callbacks({lib: :em_http_request}, request_signature, stubbed_webmock_response) + @uri ||= nil + EM.next_tick { + setup(make_raw_response(stubbed_webmock_response), @uri, + stubbed_webmock_response.should_timeout ? Errno::ETIMEDOUT : nil) + } + self + elsif WebMock.net_connect_allowed?(request_signature.uri) + super + else + raise WebMock::NetConnectNotAllowedError.new(request_signature) + end + end + + def unbind(reason = nil) + if !stubbed_webmock_response && WebMock::CallbackRegistry.any_callbacks? + webmock_response = build_webmock_response + WebMock::CallbackRegistry.invoke_callbacks( + {lib: :em_http_request, real_request: true}, + request_signature, + webmock_response) + end + @request_signature = nil + remove_instance_variable(:@stubbed_webmock_response) + + super + end + + def request_signature + @request_signature ||= build_request_signature + end + + def stubbed_webmock_response + unless defined?(@stubbed_webmock_response) + @stubbed_webmock_response = WebMock::StubRegistry.instance.response_for_request(request_signature) + end + + @stubbed_webmock_response + end + + def get_response_cookie(name) + name = name.to_s + + raw_cookie = response_header.cookie + raw_cookie = [raw_cookie] if raw_cookie.is_a? String + + cookie = raw_cookie.detect { |c| c.start_with? name } + cookie and cookie.split('=', 2)[1] + end + + private + + def build_webmock_response + webmock_response = WebMock::Response.new + webmock_response.status = [response_header.status, response_header.http_reason] + webmock_response.headers = response_header + webmock_response.body = response + webmock_response + end + + def headers_and_body_processed_by_middleware + @headers_and_body_processed_by_middleware ||= begin + head, body = build_request, @req.body + @conn.middleware.each do |m| + head, body = m.request(self, head, body) if m.respond_to?(:request) + end + [head, body] + end + end + + def build_request_signature + headers, body = headers_and_body_processed_by_middleware + + method = @req.method + uri = @req.uri.clone + query = @req.query + + uri.query = encode_query(@req.uri, query).slice(/\?(.*)/, 1) + + body = form_encode_body(body) if body.is_a?(Hash) + + if headers['authorization'] && headers['authorization'].is_a?(Array) + headers['Authorization'] = WebMock::Util::Headers.basic_auth_header(headers.delete('authorization')) + end + + WebMock::RequestSignature.new( + method.downcase.to_sym, + uri.to_s, + body: body || (@req.file && File.read(@req.file)), + headers: headers + ) + end + + def make_raw_response(response) + response.raise_error_if_any + + status, headers, body = response.status, response.headers, response.body + headers ||= {} + + response_string = [] + response_string << "HTTP/1.1 #{status[0]} #{status[1]}" + + headers["Content-Length"] = body.bytesize unless headers["Content-Length"] + headers.each do |header, value| + if header =~ /set-cookie/i + [value].flatten.each do |cookie| + response_string << "#{header}: #{cookie}" + end + else + value = value.join(", ") if value.is_a?(Array) + + # WebMock's internal processing will not handle the body + # correctly if the header indicates that it is chunked, unless + # we also create all the chunks. + # It's far easier just to remove the header. + next if header =~ /transfer-encoding/i && value =~/chunked/i + + response_string << "#{header}: #{value}" + end + end if headers + + response_string << "" << body + response_string.join("\n") + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/excon_adapter.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/excon_adapter.rb new file mode 100644 index 0000000..82734c7 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/excon_adapter.rb @@ -0,0 +1,167 @@ +# frozen_string_literal: true + +begin + require 'excon' +rescue LoadError + # excon not found +end + +if defined?(Excon) + WebMock::VersionChecker.new('Excon', Excon::VERSION, '0.27.5').check_version! + + module WebMock + module HttpLibAdapters + + class ExconAdapter < HttpLibAdapter + PARAMS_TO_DELETE = [:expects, :idempotent, + :instrumentor_name, :instrumentor, + :response_block, + :__construction_args, :stack, + :connection, :response] + + adapter_for :excon + + instance_exec do + @original_excon_mock_default = nil + @stub = nil + end + + def self.enable! + self.add_excon_stub + end + + def self.disable! + self.remove_excon_stub + end + + def self.add_excon_stub + if not @stub + @original_excon_mock_default = ::Excon.defaults[:mock] + ::Excon.defaults[:mock] = true + @stub = ::Excon.stub({}) do |params| + self.handle_request(params) + end + end + end + + def self.remove_excon_stub + ::Excon.defaults[:mock] = @original_excon_mock_default + @original_excon_mock_default = nil + Excon.stubs.delete(@stub) + @stub = nil + end + + def self.handle_request(params) + mock_request = self.build_request params.dup + WebMock::RequestRegistry.instance.requested_signatures.put(mock_request) + + if mock_response = WebMock::StubRegistry.instance.response_for_request(mock_request) + self.perform_callbacks(mock_request, mock_response, real_request: false) + response = self.real_response(mock_response) + response + elsif WebMock.net_connect_allowed?(mock_request.uri) + conn = new_excon_connection(params) + real_response = conn.request(request_params_from(params.merge(mock: false))) + + ExconAdapter.perform_callbacks(mock_request, ExconAdapter.mock_response(real_response), real_request: true) + + real_response.data + else + raise WebMock::NetConnectNotAllowedError.new(mock_request) + end + end + + def self.new_excon_connection(params) + # Ensure the connection is constructed with the exact same args + # that the orginal connection was constructed with. + args = params.fetch(:__construction_args) + ::Excon::Connection.new(connection_params_from args.merge(mock: false)) + end + + def self.connection_params_from(hash) + hash = hash.dup + PARAMS_TO_DELETE.each { |key| hash.delete(key) } + hash + end + + def self.request_params_from(hash) + hash = hash.dup + if defined?(Excon::VALID_REQUEST_KEYS) + hash.reject! {|key,_| !Excon::VALID_REQUEST_KEYS.include?(key) } + end + PARAMS_TO_DELETE.each { |key| hash.delete(key) } + hash + end + + def self.to_query(hash) + string = "".dup + for key, values in hash + if values.nil? + string << key.to_s << '&' + else + for value in [*values] + string << key.to_s << '=' << CGI.escape(value.to_s) << '&' + end + end + end + string.chop! # remove trailing '&' + end + + def self.build_request(params) + params = params.dup + params.delete(:user) + params.delete(:password) + method = (params.delete(:method) || :get).to_s.downcase.to_sym + params[:query] = to_query(params[:query]) if params[:query].is_a?(Hash) + uri = Addressable::URI.new(params).to_s + WebMock::RequestSignature.new method, uri, body: body_from(params), headers: params[:headers] + end + + def self.body_from(params) + body = params[:body] + return body unless body.respond_to?(:read) + + contents = body.read + body.rewind if body.respond_to?(:rewind) + contents + end + + def self.real_response(mock) + raise Excon::Errors::Timeout if mock.should_timeout + mock.raise_error_if_any + { + body: mock.body, + status: mock.status[0].to_i, + reason_phrase: mock.status[1], + headers: mock.headers || {} + } + end + + def self.mock_response(real) + mock = WebMock::Response.new + mock.status = [real.status, real.reason_phrase] + mock.headers = real.headers + mock.body = real.body.dup + mock + end + + def self.perform_callbacks(request, response, options = {}) + return unless WebMock::CallbackRegistry.any_callbacks? + WebMock::CallbackRegistry.invoke_callbacks(options.merge(lib: :excon), request, response) + end + end + end + end + + Excon::Connection.class_eval do + def self.new(args = {}) + args.delete(:__construction_args) + super(args).tap do |instance| + instance.data[:__construction_args] = args + end + end + end + + # Suppresses Excon connection argument validation warning + Excon::VALID_CONNECTION_KEYS << :__construction_args +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/http_lib_adapter.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/http_lib_adapter.rb new file mode 100644 index 0000000..cc85b84 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/http_lib_adapter.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +module WebMock + class HttpLibAdapter + def self.adapter_for(lib) + WebMock::HttpLibAdapterRegistry.instance.register(lib, self) + end + end +end \ No newline at end of file diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/http_lib_adapter_registry.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/http_lib_adapter_registry.rb new file mode 100644 index 0000000..eba352f --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/http_lib_adapter_registry.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module WebMock + class HttpLibAdapterRegistry + include Singleton + + attr_accessor :http_lib_adapters + + def initialize + @http_lib_adapters = {} + end + + def register(lib, adapter) + @http_lib_adapters[lib] = adapter + end + + def each_adapter(&block) + @http_lib_adapters.each(&block) + end + end +end \ No newline at end of file diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/http_rb/client.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/http_rb/client.rb new file mode 100644 index 0000000..ffa9404 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/http_rb/client.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module HTTP + class Client + alias_method :__perform__, :perform + + def perform(request, options) + return __perform__(request, options) unless webmock_enabled? + + WebMockPerform.new(request, options) { __perform__(request, options) }.exec + end + + def webmock_enabled? + ::WebMock::HttpLibAdapters::HttpRbAdapter.enabled? + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/http_rb/request.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/http_rb/request.rb new file mode 100644 index 0000000..32b36b7 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/http_rb/request.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module HTTP + class Request + def webmock_signature + request_body = nil + + if defined?(HTTP::Request::Body) + request_body = String.new + first_chunk_encoding = nil + body.each do |part| + request_body << part + first_chunk_encoding ||= part.encoding + end + + request_body.force_encoding(first_chunk_encoding) if first_chunk_encoding + request_body + else + request_body = body + end + + ::WebMock::RequestSignature.new(verb, uri.to_s, { + headers: headers.to_h, + body: request_body + }) + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/http_rb/response.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/http_rb/response.rb new file mode 100644 index 0000000..2e29b97 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/http_rb/response.rb @@ -0,0 +1,87 @@ +# frozen_string_literal: true + +module HTTP + class Response + def to_webmock + webmock_response = ::WebMock::Response.new + + webmock_response.status = [status.to_i, reason] + + webmock_response.body = body.to_s + # This call is used to reset the body of the response to enable it to be streamed if necessary. + # The `body.to_s` call above reads the body, which allows WebMock to trigger any registered callbacks. + # However, once the body is read to_s, it cannot be streamed again and attempting to do so + # will raise a "HTTP::StateError: body has already been consumed" error. + # To avoid this error, we replace the original body with a new one. + # The new body has its @stream attribute set to new Streamer, instead of the original Connection. + # Unfortunately, it's not possible to reset the original body to its initial streaming state. + # Therefore, this replacement is the best workaround currently available. + reset_body_to_allow_it_to_be_streamed!(webmock_response) + + webmock_response.headers = headers.to_h + webmock_response + end + + class << self + def from_webmock(request, webmock_response, request_signature = nil) + status = Status.new(webmock_response.status.first) + headers = webmock_response.headers || {} + uri = normalize_uri(request_signature && request_signature.uri) + + # HTTP.rb 3.0+ uses a keyword argument to pass the encoding, but 1.x + # and 2.x use a positional argument, and 0.x don't support supplying + # the encoding. + body = build_http_rb_response_body_from_webmock_response(webmock_response) + + return new(status, "1.1", headers, body, uri) if HTTP::VERSION < "1.0.0" + + # 5.0.0 had a breaking change to require request instead of uri. + if HTTP::VERSION < '5.0.0' + return new({ + status: status, + version: "1.1", + headers: headers, + body: body, + uri: uri + }) + end + + new({ + status: status, + version: "1.1", + headers: headers, + body: body, + request: request, + }) + end + + def build_http_rb_response_body_from_webmock_response(webmock_response) + if HTTP::VERSION < "1.0.0" + Body.new(Streamer.new(webmock_response.body)) + elsif HTTP::VERSION < "3.0.0" + Body.new(Streamer.new(webmock_response.body), webmock_response.body.encoding) + else + Body.new( + Streamer.new(webmock_response.body, encoding: webmock_response.body.encoding), + encoding: webmock_response.body.encoding + ) + end + end + + def normalize_uri(uri) + return unless uri + + uri = Addressable::URI.parse uri + uri.port = nil if uri.default_port && uri.port == uri.default_port + + uri + end + end + + private + + def reset_body_to_allow_it_to_be_streamed!(webmock_response) + @body = self.class.build_http_rb_response_body_from_webmock_response(webmock_response) + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/http_rb/streamer.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/http_rb/streamer.rb new file mode 100644 index 0000000..ab6186c --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/http_rb/streamer.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module HTTP + class Response + class Streamer + def initialize(str, encoding: Encoding::BINARY) + @io = StringIO.new str + @encoding = encoding + end + + def readpartial(size = nil, outbuf = nil) + unless size + if defined?(HTTP::Client::BUFFER_SIZE) + size = HTTP::Client::BUFFER_SIZE + elsif defined?(HTTP::Connection::BUFFER_SIZE) + size = HTTP::Connection::BUFFER_SIZE + end + end + + chunk = @io.read size, outbuf + chunk.force_encoding(@encoding) if chunk + end + + def close + @io.close + end + + def finished_request? + @io.eof? + end + + def sequence_id + -1 + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/http_rb/webmock.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/http_rb/webmock.rb new file mode 100644 index 0000000..56207f9 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/http_rb/webmock.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: true + +module HTTP + class WebMockPerform + def initialize(request, options, &perform) + @request = request + @options = options + @perform = perform + @request_signature = nil + end + + def exec + replay || perform || halt + end + + def request_signature + unless @request_signature + @request_signature = @request.webmock_signature + register_request(@request_signature) + end + + @request_signature + end + + protected + + def response_for_request(signature) + ::WebMock::StubRegistry.instance.response_for_request(signature) + end + + def register_request(signature) + ::WebMock::RequestRegistry.instance.requested_signatures.put(signature) + end + + def replay + webmock_response = response_for_request request_signature + + return unless webmock_response + + raise_timeout_error if webmock_response.should_timeout + webmock_response.raise_error_if_any + + invoke_callbacks(webmock_response, real_request: false) + response = ::HTTP::Response.from_webmock @request, webmock_response, request_signature + + @options.features.each { |_name, feature| response = feature.wrap_response(response) } + response + end + + def raise_timeout_error + raise Errno::ETIMEDOUT if HTTP::VERSION < "1.0.0" + raise HTTP::TimeoutError, "connection error: #{Errno::ETIMEDOUT.new}" + end + + def perform + return unless ::WebMock.net_connect_allowed?(request_signature.uri) + response = @perform.call + invoke_callbacks(response.to_webmock, real_request: true) + response + end + + def halt + raise ::WebMock::NetConnectNotAllowedError.new request_signature + end + + def invoke_callbacks(webmock_response, options = {}) + ::WebMock::CallbackRegistry.invoke_callbacks( + options.merge({ lib: :http_rb }), + request_signature, + webmock_response + ) + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/http_rb_adapter.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/http_rb_adapter.rb new file mode 100644 index 0000000..2d94860 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/http_rb_adapter.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +begin + require "http" +rescue LoadError + # HTTP gem not found +end + +if defined?(HTTP) && defined?(HTTP::VERSION) + WebMock::VersionChecker.new("HTTP Gem", HTTP::VERSION, "0.6.0").check_version! + + module WebMock + module HttpLibAdapters + class HttpRbAdapter < HttpLibAdapter + adapter_for :http_rb + + class << self + def enable! + @enabled = true + end + + def disable! + @enabled = false + end + + def enabled? + @enabled + end + end + end + end + end + + require_relative "http_rb/client" + require_relative "http_rb/request" + require_relative "http_rb/response" + require_relative "http_rb/streamer" + require_relative "http_rb/webmock" +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/httpclient_adapter.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/httpclient_adapter.rb new file mode 100644 index 0000000..809698e --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/httpclient_adapter.rb @@ -0,0 +1,260 @@ +# frozen_string_literal: true + +begin + require 'httpclient' + require 'jsonclient' # defined in 'httpclient' gem as well +rescue LoadError + # httpclient not found + # or jsonclient not defined (in old versions of httclient gem) +end + +if defined?(::HTTPClient) + + module WebMock + module HttpLibAdapters + class HTTPClientAdapter < HttpLibAdapter + adapter_for :httpclient + + unless const_defined?(:OriginalHttpClient) + OriginalHttpClient = ::HTTPClient + end + + unless const_defined?(:OriginalJsonClient) + OriginalJsonClient = ::JSONClient if defined?(::JSONClient) + end + + def self.enable! + Object.send(:remove_const, :HTTPClient) + Object.send(:const_set, :HTTPClient, WebMockHTTPClient) + if defined? ::JSONClient + Object.send(:remove_const, :JSONClient) + Object.send(:const_set, :JSONClient, WebMockJSONClient) + end + end + + def self.disable! + Object.send(:remove_const, :HTTPClient) + Object.send(:const_set, :HTTPClient, OriginalHttpClient) + if defined? ::JSONClient + Object.send(:remove_const, :JSONClient) + Object.send(:const_set, :JSONClient, OriginalJsonClient) + end + end + end + end + end + + module WebMockHTTPClients + WEBMOCK_HTTPCLIENT_RESPONSES = :webmock_httpclient_responses + WEBMOCK_HTTPCLIENT_REQUEST_SIGNATURES = :webmock_httpclient_request_signatures + + REQUEST_RESPONSE_LOCK = Mutex.new + + def do_get_block(req, proxy, conn, &block) + do_get(req, proxy, conn, false, &block) + end + + def do_get_stream(req, proxy, conn, &block) + do_get(req, proxy, conn, true, &block) + end + + def do_get(req, proxy, conn, stream = false, &block) + clear_thread_variables unless conn.async_thread + + request_signature = build_request_signature(req, :reuse_existing) + + WebMock::RequestRegistry.instance.requested_signatures.put(request_signature) + + if webmock_responses[request_signature] + webmock_response = webmock_responses.delete(request_signature) + response = build_httpclient_response(webmock_response, stream, req.header, &block) + @request_filter.each do |filter| + filter.filter_response(req, response) + end + res = conn.push(response) + WebMock::CallbackRegistry.invoke_callbacks( + {lib: :httpclient}, request_signature, webmock_response) + res + elsif WebMock.net_connect_allowed?(request_signature.uri) + # in case there is a nil entry in the hash... + webmock_responses.delete(request_signature) + + res = if stream + do_get_stream_without_webmock(req, proxy, conn, &block) + elsif block + body = '' + do_get_block_without_webmock(req, proxy, conn) do |http_res, chunk| + if chunk && chunk.bytesize > 0 + body += chunk + block.call(http_res, chunk) + end + end + else + do_get_block_without_webmock(req, proxy, conn) + end + res = conn.pop + conn.push(res) + if WebMock::CallbackRegistry.any_callbacks? + webmock_response = build_webmock_response(res, body) + WebMock::CallbackRegistry.invoke_callbacks( + {lib: :httpclient, real_request: true}, request_signature, + webmock_response) + end + res + else + raise WebMock::NetConnectNotAllowedError.new(request_signature) + end + end + + def do_request_async(method, uri, query, body, extheader) + clear_thread_variables + req = create_request(method, uri, query, body, extheader) + request_signature = build_request_signature(req) + webmock_request_signatures << request_signature + + if webmock_responses[request_signature] || WebMock.net_connect_allowed?(request_signature.uri) + conn = super + conn.async_thread[WEBMOCK_HTTPCLIENT_REQUEST_SIGNATURES] = Thread.current[WEBMOCK_HTTPCLIENT_REQUEST_SIGNATURES] + conn.async_thread[WEBMOCK_HTTPCLIENT_RESPONSES] = Thread.current[WEBMOCK_HTTPCLIENT_RESPONSES] + conn + else + raise WebMock::NetConnectNotAllowedError.new(request_signature) + end + end + + def build_httpclient_response(webmock_response, stream = false, req_header = nil, &block) + body = stream ? StringIO.new(webmock_response.body) : webmock_response.body + response = HTTP::Message.new_response(body, req_header) + response.header.init_response(webmock_response.status[0]) + response.reason=webmock_response.status[1] + webmock_response.headers.to_a.each { |name, value| response.header.set(name, value) } + + raise HTTPClient::TimeoutError if webmock_response.should_timeout + webmock_response.raise_error_if_any + + block.call(response, body) if block && body && body.bytesize > 0 + + response + end + + def build_webmock_response(httpclient_response, body = nil) + webmock_response = WebMock::Response.new + webmock_response.status = [httpclient_response.status, httpclient_response.reason] + + webmock_response.headers = {}.tap do |hash| + httpclient_response.header.all.each do |(key, value)| + if hash.has_key?(key) + hash[key] = Array(hash[key]) + [value] + else + hash[key] = value + end + end + end + + if body + webmock_response.body = body + elsif httpclient_response.content.respond_to?(:read) + webmock_response.body = httpclient_response.content.read + body = HTTP::Message::Body.new + body.init_response(StringIO.new(webmock_response.body)) + httpclient_response.body = body + else + webmock_response.body = httpclient_response.content + end + webmock_response + end + + def build_request_signature(req, reuse_existing = false) + @request_filter.each do |filter| + filter.filter_request(req) + end + + uri = WebMock::Util::URI.heuristic_parse(req.header.request_uri.to_s) + uri.query = WebMock::Util::QueryMapper.values_to_query(req.header.request_query, notation: WebMock::Config.instance.query_values_notation) if req.header.request_query + uri.port = req.header.request_uri.port + + headers = req.header.all.inject({}) do |hdrs, header| + hdrs[header[0]] ||= [] + hdrs[header[0]] << header[1] + hdrs + end + headers = headers_from_session(uri).merge(headers) + + signature = WebMock::RequestSignature.new( + req.header.request_method.downcase.to_sym, + uri.to_s, + body: req.http_body.dump, + headers: headers + ) + + # reuse a previous identical signature object if we stored one for later use + if reuse_existing && previous_signature = previous_signature_for(signature) + return previous_signature + end + + signature + end + + def webmock_responses + Thread.current[WEBMOCK_HTTPCLIENT_RESPONSES] ||= Hash.new do |hash, request_signature| + hash[request_signature] = WebMock::StubRegistry.instance.response_for_request(request_signature) + end + end + + def webmock_request_signatures + Thread.current[WEBMOCK_HTTPCLIENT_REQUEST_SIGNATURES] ||= [] + end + + def previous_signature_for(signature) + return nil unless index = webmock_request_signatures.index(signature) + webmock_request_signatures.delete_at(index) + end + + private + + # some of the headers sent by HTTPClient are derived from + # the client session + def headers_from_session(uri) + session_headers = HTTP::Message::Headers.new + @session_manager.send(:open, uri).send(:set_header, MessageMock.new(session_headers)) + session_headers.all.inject({}) do |hdrs, header| + hdrs[header[0]] = header[1] + hdrs + end + end + + def clear_thread_variables + Thread.current[WEBMOCK_HTTPCLIENT_REQUEST_SIGNATURES] = nil + Thread.current[WEBMOCK_HTTPCLIENT_RESPONSES] = nil + end + end + + class WebMockHTTPClient < HTTPClient + alias_method :do_get_block_without_webmock, :do_get_block + alias_method :do_get_stream_without_webmock, :do_get_stream + + include WebMockHTTPClients + end + + if defined? ::JSONClient + class WebMockJSONClient < JSONClient + alias_method :do_get_block_without_webmock, :do_get_block + alias_method :do_get_stream_without_webmock, :do_get_stream + + include WebMockHTTPClients + end + end + + + # Mocks a HTTPClient HTTP::Message + class MessageMock + attr_reader :header + + def initialize(headers) + @header = headers + end + + def http_version=(value);end + end + +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/manticore_adapter.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/manticore_adapter.rb new file mode 100644 index 0000000..bfedcbb --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/manticore_adapter.rb @@ -0,0 +1,147 @@ +# frozen_string_literal: true + +begin + require 'manticore' +rescue LoadError + # manticore not found +end + +if defined?(Manticore) + module WebMock + module HttpLibAdapters + class ManticoreAdapter < HttpLibAdapter + adapter_for :manticore + + OriginalManticoreClient = Manticore::Client + + def self.enable! + Manticore.send(:remove_const, :Client) + Manticore.send(:const_set, :Client, WebMockManticoreClient) + Manticore.instance_variable_set(:@manticore_facade, WebMockManticoreClient.new) + end + + def self.disable! + Manticore.send(:remove_const, :Client) + Manticore.send(:const_set, :Client, OriginalManticoreClient) + Manticore.instance_variable_set(:@manticore_facade, OriginalManticoreClient.new) + end + + class StubbedTimeoutResponse < Manticore::StubbedResponse + def call + @handlers[:failure].call(Manticore::ConnectTimeout.new("Too slow (mocked timeout)")) + end + end + + class WebMockManticoreClient < Manticore::Client + def request(klass, url, options={}, &block) + super(klass, WebMock::Util::URI.normalize_uri(url).to_s, format_options(options)) + end + + private + + def format_options(options) + return options unless headers = options[:headers] + + options.merge(headers: join_array_values(headers)) + end + + def join_array_values(headers) + headers.reduce({}) do |h, (k,v)| + v = v.join(', ') if v.is_a?(Array) + h.merge(k => v) + end + end + + def response_object_for(request, context, &block) + request_signature = generate_webmock_request_signature(request, context) + WebMock::RequestRegistry.instance.requested_signatures.put(request_signature) + + if webmock_response = registered_response_for(request_signature) + webmock_response.raise_error_if_any + manticore_response = generate_manticore_response(webmock_response) + manticore_response.on_success do + WebMock::CallbackRegistry.invoke_callbacks({lib: :manticore, real_request: false}, request_signature, webmock_response) + end + + elsif real_request_allowed?(request_signature.uri) + manticore_response = Manticore::Response.new(self, request, context, &block) + manticore_response.on_complete do |completed_response| + webmock_response = generate_webmock_response(completed_response) + WebMock::CallbackRegistry.invoke_callbacks({lib: :manticore, real_request: true}, request_signature, webmock_response) + end + + else + raise WebMock::NetConnectNotAllowedError.new(request_signature) + end + + manticore_response + end + + def registered_response_for(request_signature) + WebMock::StubRegistry.instance.response_for_request(request_signature) + end + + def real_request_allowed?(uri) + WebMock.net_connect_allowed?(uri) + end + + def generate_webmock_request_signature(request, context) + method = request.method.downcase + uri = request.uri.to_s + body = read_body(request) + headers = split_array_values(request.headers) + + if context.get_credentials_provider && credentials = context.get_credentials_provider.get_credentials(AuthScope::ANY) + headers['Authorization'] = WebMock::Util::Headers.basic_auth_header(credentials.get_user_name,credentials.get_password) + end + + WebMock::RequestSignature.new(method, uri, {body: body, headers: headers}) + end + + def read_body(request) + if request.respond_to?(:entity) && !request.entity.nil? + Manticore::EntityConverter.new.read_entity(request.entity) + end + end + + def split_array_values(headers = []) + headers.each_with_object({}) do |(k, v), h| + h[k] = case v + when /,/ then v.split(',').map(&:strip) + else v + end + end + end + + def generate_manticore_response(webmock_response) + if webmock_response.should_timeout + StubbedTimeoutResponse.new + else + Manticore::StubbedResponse.stub( + code: webmock_response.status[0], + body: webmock_response.body, + headers: webmock_response.headers, + cookies: {} + ) + end + end + + def generate_webmock_response(manticore_response) + webmock_response = WebMock::Response.new + webmock_response.status = [manticore_response.code, manticore_response.message] + webmock_response.headers = manticore_response.headers + + # The attempt to read the body could fail if manticore is used in a streaming mode + webmock_response.body = begin + manticore_response.body + rescue ::Manticore::StreamClosedException + nil + end + + webmock_response + end + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/net_http.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/net_http.rb new file mode 100644 index 0000000..32735cd --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/net_http.rb @@ -0,0 +1,310 @@ +# frozen_string_literal: true + +require 'net/http' +require 'net/https' +require 'stringio' +require File.join(File.dirname(__FILE__), 'net_http_response') + + +module WebMock + module HttpLibAdapters + class NetHttpAdapter < HttpLibAdapter + adapter_for :net_http + + OriginalNetHTTP = Net::HTTP unless const_defined?(:OriginalNetHTTP) + # This will be removed in Ruby 3.5. In Ruby 3.4, const_remove will warn. + HAS_LEGACY_CONSTANT = Net.const_defined?(:HTTPSession) + + def self.enable! + Net.send(:remove_const, :HTTP) + Net.send(:const_set, :HTTP, @webMockNetHTTP) + if HAS_LEGACY_CONSTANT + remove_silently(Net, :HTTPSession) + Net.send(:const_set, :HTTPSession, @webMockNetHTTP) + end + end + + def self.disable! + Net.send(:remove_const, :HTTP) + Net.send(:const_set, :HTTP, OriginalNetHTTP) + if HAS_LEGACY_CONSTANT + remove_silently(Net, :HTTPSession) + Net.send(:const_set, :HTTPSession, OriginalNetHTTP) + end + + #copy all constants from @webMockNetHTTP to original Net::HTTP + #in case any constants were added to @webMockNetHTTP instead of Net::HTTP + #after WebMock was enabled. + #i.e Net::HTTP::DigestAuth + @webMockNetHTTP.constants.each do |constant| + if !OriginalNetHTTP.constants.map(&:to_s).include?(constant.to_s) + OriginalNetHTTP.send(:const_set, constant, @webMockNetHTTP.const_get(constant)) + end + end + end + + def self.remove_silently(mod, const) #:nodoc: + # Don't warn on removing the deprecated constant + verbose, $VERBOSE = $VERBOSE, nil + mod.send(:remove_const, const) + ensure + $VERBOSE = verbose + end + + @webMockNetHTTP = Class.new(Net::HTTP) do + class << self + def socket_type + StubSocket + end + + if Module.method(:const_defined?).arity == 1 + def const_defined?(name) + super || self.superclass.const_defined?(name) + end + else + def const_defined?(name, inherit=true) + super || self.superclass.const_defined?(name, inherit) + end + end + + if Module.method(:const_get).arity != 1 + def const_get(name, inherit=true) + super + rescue NameError + self.superclass.const_get(name, inherit) + end + end + + if Module.method(:constants).arity != 0 + def constants(inherit=true) + (super + self.superclass.constants(inherit)).uniq + end + end + end + + def request(request, body = nil, &block) + request_signature = WebMock::NetHTTPUtility.request_signature_from_request(self, request, body) + + WebMock::RequestRegistry.instance.requested_signatures.put(request_signature) + + if webmock_response = WebMock::StubRegistry.instance.response_for_request(request_signature) + @socket = Net::HTTP.socket_type.new + WebMock::CallbackRegistry.invoke_callbacks( + {lib: :net_http}, request_signature, webmock_response) + build_net_http_response(webmock_response, request.uri, &block) + elsif WebMock.net_connect_allowed?(request_signature.uri) + check_right_http_connection + after_request = lambda do |response| + if WebMock::CallbackRegistry.any_callbacks? + webmock_response = build_webmock_response(response) + WebMock::CallbackRegistry.invoke_callbacks( + {lib: :net_http, real_request: true}, request_signature, webmock_response) + end + response.extend Net::WebMockHTTPResponse + block.call response if block + response + end + super_with_after_request = lambda { + response = super(request, nil, &nil) + after_request.call(response) + } + if started? + ensure_actual_connection + super_with_after_request.call + else + start_with_connect { + super_with_after_request.call + } + end + else + raise WebMock::NetConnectNotAllowedError.new(request_signature) + end + end + + def start_without_connect + raise IOError, 'HTTP session already opened' if @started + if block_given? + begin + @socket = Net::HTTP.socket_type.new + @started = true + return yield(self) + ensure + do_finish + end + end + @socket = Net::HTTP.socket_type.new + @started = true + self + end + + + def ensure_actual_connection + if @socket.is_a?(StubSocket) + @socket&.close + @socket = nil + do_start + end + end + + alias_method :start_with_connect, :start + + def start(&block) + uri = Addressable::URI.parse(WebMock::NetHTTPUtility.get_uri(self)) + + if WebMock.net_http_connect_on_start?(uri) + super(&block) + else + start_without_connect(&block) + end + end + + def build_net_http_response(webmock_response, request_uri, &block) + response = Net::HTTPResponse.send(:response_class, webmock_response.status[0].to_s).new("1.0", webmock_response.status[0].to_s, webmock_response.status[1]) + body = webmock_response.body + body = nil if webmock_response.status[0].to_s == '204' + + response.instance_variable_set(:@body, body) + webmock_response.headers.to_a.each do |name, values| + values = [values] unless values.is_a?(Array) + values.each do |value| + response.add_field(name, value) + end + end + + response.instance_variable_set(:@read, true) + + response.uri = request_uri + + response.extend Net::WebMockHTTPResponse + + if webmock_response.should_timeout + raise Net::OpenTimeout, "execution expired" + end + + webmock_response.raise_error_if_any + + yield response if block_given? + + response + end + + def build_webmock_response(net_http_response) + webmock_response = WebMock::Response.new + webmock_response.status = [ + net_http_response.code.to_i, + net_http_response.message] + webmock_response.headers = net_http_response.to_hash + webmock_response.body = net_http_response.body + webmock_response + end + + + def check_right_http_connection + unless @@already_checked_for_right_http_connection ||= false + WebMock::NetHTTPUtility.puts_warning_for_right_http_if_needed + @@already_checked_for_right_http_connection = true + end + end + end + @webMockNetHTTP.version_1_2 + [ + [:Get, Net::HTTP::Get], + [:Post, Net::HTTP::Post], + [:Put, Net::HTTP::Put], + [:Delete, Net::HTTP::Delete], + [:Head, Net::HTTP::Head], + [:Options, Net::HTTP::Options] + ].each do |c| + @webMockNetHTTP.const_set(c[0], c[1]) + end + end + end +end + +class StubSocket #:nodoc: + + attr_accessor :read_timeout, :continue_timeout, :write_timeout + + def initialize(*args) + @closed = false + end + + def closed? + @closed + end + + def close + @closed = true + nil + end + + def readuntil(*args) + end + + def io + @io ||= StubIO.new + end + + class StubIO + def setsockopt(*args); end + def peer_cert; end + def peeraddr; ["AF_INET", 443, "127.0.0.1", "127.0.0.1"] end + def ssl_version; "TLSv1.3" end + def cipher; ["TLS_AES_128_GCM_SHA256", "TLSv1.3", 128, 128] end + end +end + +module WebMock + module NetHTTPUtility + + def self.request_signature_from_request(net_http, request, body = nil) + path = request.path + + if path.respond_to?(:request_uri) #https://github.com/bblimke/webmock/issues/288 + path = path.request_uri + end + + path = WebMock::Util::URI.heuristic_parse(path).request_uri if path =~ /^http/ + + uri = get_uri(net_http, path) + method = request.method.downcase.to_sym + + headers = Hash[*request.to_hash.map {|k,v| [k, v]}.inject([]) {|r,x| r + x}] + + if request.body_stream + body = request.body_stream.read + request.body_stream = nil + end + + if body != nil && body.respond_to?(:read) + request.set_body_internal body.read + else + request.set_body_internal body + end + + WebMock::RequestSignature.new(method, uri, body: request.body, headers: headers) + end + + def self.get_uri(net_http, path = nil) + protocol = net_http.use_ssl? ? "https" : "http" + + hostname = net_http.address + hostname = "[#{hostname}]" if /\A\[.*\]\z/ !~ hostname && /:/ =~ hostname + + "#{protocol}://#{hostname}:#{net_http.port}#{path}" + end + + def self.check_right_http_connection + @was_right_http_connection_loaded = defined?(RightHttpConnection) + end + + def self.puts_warning_for_right_http_if_needed + if !@was_right_http_connection_loaded && defined?(RightHttpConnection) + $stderr.puts "\nWarning: RightHttpConnection has to be required before WebMock is required !!!\n" + end + end + + end +end + +WebMock::NetHTTPUtility.check_right_http_connection diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/net_http_response.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/net_http_response.rb new file mode 100644 index 0000000..c9139a7 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/net_http_response.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +# This code is entierly copied from VCR (http://github.com/myronmarston/vcr) by courtesy of Myron Marston + +# A Net::HTTP response that has already been read raises an IOError when #read_body +# is called with a destination string or block. +# +# This causes a problem when VCR records a response--it reads the body before yielding +# the response, and if the code that is consuming the HTTP requests uses #read_body, it +# can cause an error. +# +# This is a bit of a hack, but it allows a Net::HTTP response to be "re-read" +# after it has aleady been read. This attemps to preserve the behavior of +# #read_body, acting just as if it had never been read. + + +module Net + module WebMockHTTPResponse + def read_body(dest = nil, &block) + if !(defined?(@__read_body_previously_called).nil?) && @__read_body_previously_called + return super + end + return @body if dest.nil? && block.nil? + raise ArgumentError.new("both arg and block given for HTTP method") if dest && block + return nil if @body.nil? + + dest ||= ::Net::ReadAdapter.new(block) + dest << @body.dup + @body = dest + ensure + # allow subsequent calls to #read_body to proceed as normal, without our hack... + @__read_body_previously_called = true + end + end +end + diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/patron_adapter.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/patron_adapter.rb new file mode 100644 index 0000000..2c97f91 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/patron_adapter.rb @@ -0,0 +1,132 @@ +# frozen_string_literal: true + +begin + require 'patron' +rescue LoadError + # patron not found +end + +if defined?(::Patron::Session) + module WebMock + module HttpLibAdapters + class PatronAdapter < ::WebMock::HttpLibAdapter + adapter_for :patron + + OriginalPatronSession = ::Patron::Session unless const_defined?(:OriginalPatronSession) + + class WebMockPatronSession < ::Patron::Session + def handle_request(req) + request_signature = + WebMock::HttpLibAdapters::PatronAdapter.build_request_signature(req) + + WebMock::RequestRegistry.instance.requested_signatures.put(request_signature) + + if webmock_response = WebMock::StubRegistry.instance.response_for_request(request_signature) + WebMock::HttpLibAdapters::PatronAdapter. + handle_file_name(req, webmock_response) + res = WebMock::HttpLibAdapters::PatronAdapter. + build_patron_response(webmock_response, default_response_charset) + WebMock::CallbackRegistry.invoke_callbacks( + {lib: :patron}, request_signature, webmock_response) + res + elsif WebMock.net_connect_allowed?(request_signature.uri) + res = super + if WebMock::CallbackRegistry.any_callbacks? + webmock_response = WebMock::HttpLibAdapters::PatronAdapter. + build_webmock_response(res) + WebMock::CallbackRegistry.invoke_callbacks( + {lib: :patron, real_request: true}, request_signature, + webmock_response) + end + res + else + raise WebMock::NetConnectNotAllowedError.new(request_signature) + end + end + end + + def self.enable! + Patron.send(:remove_const, :Session) + Patron.send(:const_set, :Session, WebMockPatronSession) + end + + def self.disable! + Patron.send(:remove_const, :Session) + Patron.send(:const_set, :Session, OriginalPatronSession) + end + + def self.handle_file_name(req, webmock_response) + if req.action == :get && req.file_name + begin + File.open(req.file_name, "w") do |f| + f.write webmock_response.body + end + rescue Errno::EACCES + raise ArgumentError.new("Unable to open specified file.") + end + end + end + + def self.build_request_signature(req) + uri = WebMock::Util::URI.heuristic_parse(req.url) + uri.path = uri.normalized_path.gsub("[^:]//","/") + + if [:put, :post, :patch].include?(req.action) + if req.file_name + if !File.exist?(req.file_name) || !File.readable?(req.file_name) + raise ArgumentError.new("Unable to open specified file.") + end + request_body = File.read(req.file_name) + elsif req.upload_data + request_body = req.upload_data + else + raise ArgumentError.new("Must provide either data or a filename when doing a PUT or POST") + end + end + + headers = req.headers + + if req.credentials + headers['Authorization'] = WebMock::Util::Headers.basic_auth_header(req.credentials) + end + + request_signature = WebMock::RequestSignature.new( + req.action, + uri.to_s, + body: request_body, + headers: headers + ) + request_signature + end + + def self.build_patron_response(webmock_response, default_response_charset) + raise ::Patron::TimeoutError if webmock_response.should_timeout + webmock_response.raise_error_if_any + + header_fields = (webmock_response.headers || []).map { |(k, vs)| Array(vs).map { |v| "#{k}: #{v}" } }.flatten + status_line = "HTTP/1.1 #{webmock_response.status[0]} #{webmock_response.status[1]}" + header_data = ([status_line] + header_fields).join("\r\n") + + ::Patron::Response.new( + "".dup, + webmock_response.status[0], + 0, + header_data, + webmock_response.body.dup, + default_response_charset + ) + end + + def self.build_webmock_response(patron_response) + webmock_response = WebMock::Response.new + reason = patron_response.status_line. + scan(%r(\AHTTP/(\d+(?:\.\d+)?)\s+(\d\d\d)\s*([^\r\n]+)?))[0][2] + webmock_response.status = [patron_response.status, reason] + webmock_response.body = patron_response.body + webmock_response.headers = patron_response.headers + webmock_response + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/typhoeus_hydra_adapter.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/typhoeus_hydra_adapter.rb new file mode 100644 index 0000000..6a4bf1a --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/http_lib_adapters/typhoeus_hydra_adapter.rb @@ -0,0 +1,190 @@ +# frozen_string_literal: true + +begin + require 'typhoeus' +rescue LoadError + # typhoeus not found +end + +if defined?(Typhoeus) + WebMock::VersionChecker.new('Typhoeus', Typhoeus::VERSION, '0.3.2').check_version! + + module WebMock + module HttpLibAdapters + class TyphoeusAdapter < HttpLibAdapter + adapter_for :typhoeus + + def self.enable! + @disabled = false + add_before_callback + add_after_request_callback + ::Typhoeus::Config.block_connection = true + end + + def self.disable! + @disabled = true + remove_after_request_callback + remove_before_callback + ::Typhoeus::Config.block_connection = false + end + + def self.disabled? + !!@disabled + end + + def self.add_before_callback + unless Typhoeus.before.include?(BEFORE_CALLBACK) + Typhoeus.before << BEFORE_CALLBACK + end + end + + def self.remove_before_callback + Typhoeus.before.delete_if {|v| v == BEFORE_CALLBACK } + end + + def self.add_after_request_callback + unless Typhoeus.on_complete.include?(AFTER_REQUEST_CALLBACK) + Typhoeus.on_complete << AFTER_REQUEST_CALLBACK + end + end + + def self.remove_after_request_callback + Typhoeus.on_complete.delete_if {|v| v == AFTER_REQUEST_CALLBACK } + end + + def self.build_request_signature(req) + uri = WebMock::Util::URI.heuristic_parse(req.url) + uri.path = uri.normalized_path.gsub("[^:]//","/") + + headers = req.options[:headers] + + if req.options[:userpwd] + headers['Authorization'] = WebMock::Util::Headers.basic_auth_header(req.options[:userpwd]) + end + + body = req.options[:body] + + if body.is_a?(Hash) + body = WebMock::Util::QueryMapper.values_to_query(body) + end + + request_signature = WebMock::RequestSignature.new( + req.options[:method] || :get, + uri.to_s, + body: body, + headers: headers + ) + + req.instance_variable_set(:@__webmock_request_signature, request_signature) + + request_signature + end + + + def self.build_webmock_response(typhoeus_response) + webmock_response = WebMock::Response.new + webmock_response.status = [typhoeus_response.code, typhoeus_response.status_message] + webmock_response.body = typhoeus_response.body + webmock_response.headers = typhoeus_response.headers + webmock_response + end + + def self.generate_typhoeus_response(request_signature, webmock_response) + response = if webmock_response.should_timeout + ::Typhoeus::Response.new( + code: 0, + status_message: "", + body: "", + headers: {}, + return_code: :operation_timedout, + total_time: 0.0, + starttransfer_time: 0.0, + appconnect_time: 0.0, + pretransfer_time: 0.0, + connect_time: 0.0, + namelookup_time: 0.0, + redirect_time: 0.0 + ) + else + ::Typhoeus::Response.new( + code: webmock_response.status[0], + status_message: webmock_response.status[1], + body: webmock_response.body, + headers: webmock_response.headers, + effective_url: request_signature.uri, + total_time: 0.0, + starttransfer_time: 0.0, + appconnect_time: 0.0, + pretransfer_time: 0.0, + connect_time: 0.0, + namelookup_time: 0.0, + redirect_time: 0.0 + ) + end + response.mock = :webmock + response + end + + def self.request_hash(request_signature) + hash = {} + + hash[:body] = request_signature.body + hash[:headers] = request_signature.headers + + hash + end + + AFTER_REQUEST_CALLBACK = Proc.new do |response| + request = response.request + request_signature = request.instance_variable_get(:@__webmock_request_signature) + webmock_response = + ::WebMock::HttpLibAdapters::TyphoeusAdapter. + build_webmock_response(response) + if response.mock + WebMock::CallbackRegistry.invoke_callbacks( + {lib: :typhoeus}, + request_signature, + webmock_response + ) + else + WebMock::CallbackRegistry.invoke_callbacks( + {lib: :typhoeus, real_request: true}, + request_signature, + webmock_response + ) + end + end + + BEFORE_CALLBACK = Proc.new do |request| + Typhoeus::Expectation.all.delete_if {|e| e.from == :webmock } + res = true + + unless WebMock::HttpLibAdapters::TyphoeusAdapter.disabled? + request_signature = ::WebMock::HttpLibAdapters::TyphoeusAdapter.build_request_signature(request) + request.block_connection = false; + + ::WebMock::RequestRegistry.instance.requested_signatures.put(request_signature) + + if webmock_response = ::WebMock::StubRegistry.instance.response_for_request(request_signature) + # ::WebMock::HttpLibAdapters::TyphoeusAdapter.stub_typhoeus(request_signature, webmock_response, self) + response = ::WebMock::HttpLibAdapters::TyphoeusAdapter.generate_typhoeus_response(request_signature, webmock_response) + if request.respond_to?(:on_headers) + request.execute_headers_callbacks(response) + end + if request.respond_to?(:streaming?) && request.streaming? + response.options[:response_body] = "".dup + request.on_body.each { |callback| callback.call(webmock_response.body, response) } + end + request.finish(response) + webmock_response.raise_error_if_any + res = false + elsif !WebMock.net_connect_allowed?(request_signature.uri) + raise WebMock::NetConnectNotAllowedError.new(request_signature) + end + end + res + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/matchers/any_arg_matcher.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/matchers/any_arg_matcher.rb new file mode 100644 index 0000000..db01523 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/matchers/any_arg_matcher.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module WebMock + module Matchers + # this is a based on RSpec::Mocks::ArgumentMatchers::AnyArgMatcher + class AnyArgMatcher + def initialize(ignore) + end + + def ==(other) + true + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/matchers/hash_argument_matcher.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/matchers/hash_argument_matcher.rb new file mode 100644 index 0000000..b2dc22c --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/matchers/hash_argument_matcher.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module WebMock + module Matchers + # Base class for Hash matchers + # https://github.com/rspec/rspec-mocks/blob/master/lib/rspec/mocks/argument_matchers.rb + class HashArgumentMatcher + def initialize(expected) + @expected = Hash[WebMock::Util::HashKeysStringifier.stringify_keys!(expected, deep: true).sort] + end + + def ==(_actual, &block) + @expected.all?(&block) + rescue NoMethodError + false + end + + def self.from_rspec_matcher(matcher) + new(matcher.instance_variable_get(:@expected)) + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/matchers/hash_excluding_matcher.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/matchers/hash_excluding_matcher.rb new file mode 100644 index 0000000..f5028cc --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/matchers/hash_excluding_matcher.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module WebMock + module Matchers + # this is a based on RSpec::Mocks::ArgumentMatchers::HashExcludingMatcher + # https://github.com/rspec/rspec-mocks/blob/master/lib/rspec/mocks/argument_matchers.rb + class HashExcludingMatcher < HashArgumentMatcher + def ==(actual) + super { |key, value| !actual.key?(key) || value != actual[key] } + end + + def inspect + "hash_excluding(#{@expected.inspect})" + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/matchers/hash_including_matcher.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/matchers/hash_including_matcher.rb new file mode 100644 index 0000000..09daad6 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/matchers/hash_including_matcher.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module WebMock + module Matchers + # this is a based on RSpec::Mocks::ArgumentMatchers::HashIncludingMatcher + # https://github.com/rspec/rspec-mocks/blob/master/lib/rspec/mocks/argument_matchers.rb + class HashIncludingMatcher < HashArgumentMatcher + def ==(actual) + super { |key, value| actual.key?(key) && value === actual[key] } + rescue NoMethodError + false + end + + def inspect + "hash_including(#{@expected.inspect})" + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/minitest.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/minitest.rb new file mode 100644 index 0000000..7722bea --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/minitest.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +begin + require 'minitest/test' + test_class = Minitest::Test + assertions = "assertions" +rescue LoadError + require "minitest/unit" + test_class = MiniTest::Unit::TestCase + assertions = "_assertions" +end + +require 'webmock' + +WebMock.enable! + +test_class.class_eval do + include WebMock::API + + alias_method :teardown_without_webmock, :teardown + def teardown_with_webmock + teardown_without_webmock + WebMock.reset! + end + alias_method :teardown, :teardown_with_webmock + + [:assert_request_requested, :assert_request_not_requested].each do |name| + alias_method :"#{name}_without_assertions_count", name + define_method :"#{name}_with_assertions_count" do |*args| + self.send("#{assertions}=", self.send("#{assertions}") + 1) + send :"#{name}_without_assertions_count", *args + end + alias_method name, :"#{name}_with_assertions_count" + end +end + +begin + error_class = MiniTest::Assertion +rescue NameError + error_class = Minitest::Assertion +end + +WebMock::AssertionFailure.error_class = error_class diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/rack_response.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/rack_response.rb new file mode 100644 index 0000000..1688283 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/rack_response.rb @@ -0,0 +1,73 @@ +# frozen_string_literal: true + +module WebMock + class RackResponse < Response + def initialize(app) + @app = app + end + + def evaluate(request) + env = build_rack_env(request) + + status, headers, response = @app.call(env) + + Response.new( + body: body_from_rack_response(response), + headers: headers, + status: [status, Rack::Utils::HTTP_STATUS_CODES[status]] + ) + end + + def body_from_rack_response(response) + body = "".dup + response.each { |line| body << line } + response.close if response.respond_to?(:close) + return body + end + + def build_rack_env(request) + uri = request.uri + headers = (request.headers || {}).dup + body = request.body || '' + + env = { + # CGI variables specified by Rack + 'REQUEST_METHOD' => request.method.to_s.upcase, + 'CONTENT_TYPE' => headers.delete('Content-Type'), + 'CONTENT_LENGTH' => body.bytesize, + 'PATH_INFO' => uri.path, + 'QUERY_STRING' => uri.query || '', + 'SERVER_NAME' => uri.host, + 'SERVER_PORT' => uri.port, + 'SCRIPT_NAME' => "" + } + + env['HTTP_AUTHORIZATION'] = 'Basic ' + [uri.userinfo].pack('m').delete("\r\n") if uri.userinfo + + # Rack-specific variables + env['rack.input'] = StringIO.new(body) + env['rack.errors'] = $stderr + if !Rack.const_defined?(:RELEASE) || Rack::RELEASE < "3" + env['rack.version'] = Rack::VERSION + end + env['rack.url_scheme'] = uri.scheme + env['rack.run_once'] = true + env['rack.session'] = session + env['rack.session.options'] = session_options + + headers.each do |k, v| + env["HTTP_#{k.tr('-','_').upcase}"] = v + end + + env + end + + def session + @session ||= {} + end + + def session_options + @session_options ||= {} + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/request_body_diff.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/request_body_diff.rb new file mode 100644 index 0000000..3932f62 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/request_body_diff.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +require "hashdiff" +require "json" + +module WebMock + class RequestBodyDiff + + def initialize(request_signature, request_stub) + @request_signature = request_signature + @request_stub = request_stub + end + + def body_diff + return {} unless request_signature_diffable? && request_stub_diffable? + + Hashdiff.diff(request_signature_body_hash, request_stub_body_hash) + end + + attr_reader :request_signature, :request_stub + private :request_signature, :request_stub + + private + + def request_signature_diffable? + request_signature.json_headers? && request_signature_parseable_json? + end + + def request_stub_diffable? + request_stub_body.is_a?(Hash) || request_stub_parseable_json? + end + + def request_signature_body_hash + JSON.parse(request_signature.body) + end + + def request_stub_body_hash + return request_stub_body if request_stub_body.is_a?(Hash) + + JSON.parse(request_stub_body) + end + + def request_stub_body + request_stub.request_pattern && + request_stub.request_pattern.body_pattern && + request_stub.request_pattern.body_pattern.pattern + end + + def request_signature_parseable_json? + parseable_json?(request_signature.body) + end + + def request_stub_parseable_json? + parseable_json?(request_stub_body) + end + + def parseable_json?(body_pattern) + return false unless body_pattern.is_a?(String) + + JSON.parse(body_pattern) + true + rescue JSON::ParserError + false + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/request_execution_verifier.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/request_execution_verifier.rb new file mode 100644 index 0000000..97b4872 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/request_execution_verifier.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +module WebMock + class RequestExecutionVerifier + + attr_accessor :request_pattern, :expected_times_executed, :times_executed, :at_least_times_executed, :at_most_times_executed + + def initialize(request_pattern = nil, expected_times_executed = nil, at_least_times_executed = nil, at_most_times_executed = nil) + @request_pattern = request_pattern + @expected_times_executed = expected_times_executed + @at_least_times_executed = at_least_times_executed + @at_most_times_executed = at_most_times_executed + end + + def matches? + @times_executed = + RequestRegistry.instance.times_executed(@request_pattern) + + if @at_least_times_executed + @times_executed >= @at_least_times_executed + elsif @at_most_times_executed + @times_executed <= @at_most_times_executed + else + @times_executed == (@expected_times_executed || 1) + end + end + + def does_not_match? + @times_executed = + RequestRegistry.instance.times_executed(@request_pattern) + if @expected_times_executed + @times_executed != @expected_times_executed + else + @times_executed == 0 + end + end + + def description + "request #{request_pattern} #{quantity_phrase.strip}" + end + + def failure_message + failure_message_phrase(false) + end + + def failure_message_when_negated + failure_message_phrase(true) + end + + def self.executed_requests_message + "\n\nThe following requests were made:\n\n#{RequestRegistry.instance}\n" + "="*60 + end + + private + + def failure_message_phrase(is_negated=false) + negation = is_negated ? "was not" : "was" + "The request #{request_pattern} #{negation} expected to execute #{quantity_phrase(is_negated)}but it executed #{times(times_executed)}" + + self.class.executed_requests_message + end + + def quantity_phrase(is_negated=false) + if @at_least_times_executed + "at least #{times(@at_least_times_executed)} " + elsif @at_most_times_executed + "at most #{times(@at_most_times_executed)} " + elsif @expected_times_executed + "#{times(@expected_times_executed)} " + else + is_negated ? "" : "#{times(1)} " + end + end + + def times(times) + "#{times} time#{ (times == 1) ? '' : 's'}" + end + + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/request_pattern.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/request_pattern.rb new file mode 100644 index 0000000..f5484d6 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/request_pattern.rb @@ -0,0 +1,428 @@ +# frozen_string_literal: true + +module WebMock + + module RSpecMatcherDetector + def rSpecHashIncludingMatcher?(matcher) + matcher.class.name =~ /R?Spec::Mocks::ArgumentMatchers::HashIncludingMatcher/ + end + + def rSpecHashExcludingMatcher?(matcher) + matcher.class.name =~ /R?Spec::Mocks::ArgumentMatchers::HashExcludingMatcher/ + end + end + + class RequestPattern + + attr_reader :method_pattern, :uri_pattern, :body_pattern, :headers_pattern + + def initialize(method, uri, options = {}) + @method_pattern = MethodPattern.new(method) + @uri_pattern = create_uri_pattern(uri) + @body_pattern = nil + @headers_pattern = nil + @with_block = nil + assign_options(options) + end + + def with(options = {}, &block) + raise ArgumentError.new('#with method invoked with no arguments. Either options hash or block must be specified. Created a block with do..end? Try creating it with curly braces {} instead.') if options.empty? && !block_given? + assign_options(options) + @with_block = block + self + end + + def matches?(request_signature) + content_type = request_signature.headers['Content-Type'] if request_signature.headers + content_type = content_type.split(';').first if content_type + @method_pattern.matches?(request_signature.method) && + @uri_pattern.matches?(request_signature.uri) && + (@body_pattern.nil? || @body_pattern.matches?(request_signature.body, content_type || "")) && + (@headers_pattern.nil? || @headers_pattern.matches?(request_signature.headers)) && + (@with_block.nil? || @with_block.call(request_signature)) + end + + def to_s + string = "#{@method_pattern.to_s.upcase}".dup + string << " #{@uri_pattern.to_s}" + string << " with body #{@body_pattern.to_s}" if @body_pattern + string << " with headers #{@headers_pattern.to_s}" if @headers_pattern + string << " with given block" if @with_block + string + end + + private + + + def assign_options(options) + options = WebMock::Util::HashKeysStringifier.stringify_keys!(options, deep: true) + HashValidator.new(options).validate_keys('body', 'headers', 'query', 'basic_auth') + set_basic_auth_as_headers!(options) + @body_pattern = BodyPattern.new(options['body']) if options.has_key?('body') + @headers_pattern = HeadersPattern.new(options['headers']) if options.has_key?('headers') + @uri_pattern.add_query_params(options['query']) if options.has_key?('query') + end + + def set_basic_auth_as_headers!(options) + if basic_auth = options.delete('basic_auth') + validate_basic_auth!(basic_auth) + options['headers'] ||= {} + options['headers']['Authorization'] = WebMock::Util::Headers.basic_auth_header(basic_auth[0],basic_auth[1]) + end + end + + def validate_basic_auth!(basic_auth) + if !basic_auth.is_a?(Array) || basic_auth.map{|e| e.is_a?(String)}.uniq != [true] + raise "The basic_auth option value should be an array which contains 2 strings: username and password" + end + end + + def create_uri_pattern(uri) + if uri.is_a?(Regexp) + URIRegexpPattern.new(uri) + elsif uri.is_a?(Addressable::Template) + URIAddressablePattern.new(uri) + elsif uri.respond_to?(:call) + URICallablePattern.new(uri) + elsif uri.is_a?(::URI::Generic) + URIStringPattern.new(uri.to_s) + elsif uri.respond_to?(:to_str) + URIStringPattern.new(uri.to_str) + else + raise ArgumentError.new("URI should be a String, Regexp, Addressable::Template, a callable object, or respond to #to_str. Got: #{uri.class}") + end + end + end + + + class MethodPattern + def initialize(pattern) + @pattern = pattern + end + + def matches?(method) + @pattern == method || @pattern == :any + end + + def to_s + @pattern.to_s + end + end + + + class URIPattern + include RSpecMatcherDetector + + def initialize(pattern) + @pattern = if pattern.is_a?(Addressable::URI) \ + || pattern.is_a?(Addressable::Template) + pattern + elsif pattern.respond_to?(:call) + pattern + else + WebMock::Util::URI.normalize_uri(pattern) + end + @query_params = nil + end + + def add_query_params(query_params) + @query_params = if query_params.is_a?(Hash) + query_params + elsif query_params.is_a?(WebMock::Matchers::HashIncludingMatcher) \ + || query_params.is_a?(WebMock::Matchers::HashExcludingMatcher) + query_params + elsif rSpecHashIncludingMatcher?(query_params) + WebMock::Matchers::HashIncludingMatcher.from_rspec_matcher(query_params) + elsif rSpecHashExcludingMatcher?(query_params) + WebMock::Matchers::HashExcludingMatcher.from_rspec_matcher(query_params) + else + WebMock::Util::QueryMapper.query_to_values(query_params, notation: Config.instance.query_values_notation) + end + end + + def matches?(uri) + pattern_matches?(uri) && query_params_matches?(uri) + end + + def to_s + str = pattern_inspect + str += " with query params #{@query_params.inspect}" if @query_params + str + end + + private + + def pattern_inspect + @pattern.inspect + end + + def query_params_matches?(uri) + @query_params.nil? || @query_params == WebMock::Util::QueryMapper.query_to_values(uri.query, notation: Config.instance.query_values_notation) + end + end + + class URICallablePattern < URIPattern + private + + def pattern_matches?(uri) + @pattern.call(uri) + end + end + + class URIRegexpPattern < URIPattern + private + + def pattern_matches?(uri) + WebMock::Util::URI.variations_of_uri_as_strings(uri).any? { |u| u.match(@pattern) } + end + end + + class URIAddressablePattern < URIPattern + def add_query_params(query_params) + @@add_query_params_warned ||= false + if not @@add_query_params_warned + @@add_query_params_warned = true + warn "WebMock warning: ignoring query params in RFC 6570 template and checking them with WebMock" + end + super(query_params) + end + + private + + def pattern_matches?(uri) + if @query_params.nil? + # Let Addressable check the whole URI + matches_with_variations?(uri) + else + # WebMock checks the query, Addressable checks everything else + matches_with_variations?(uri.omit(:query)) + end + end + + def pattern_inspect + @pattern.pattern.inspect + end + + def matches_with_variations?(uri) + template = + begin + Addressable::Template.new(WebMock::Util::URI.heuristic_parse(@pattern.pattern)) + rescue Addressable::URI::InvalidURIError + Addressable::Template.new(@pattern.pattern) + end + WebMock::Util::URI.variations_of_uri_as_strings(uri).any? { |u| + template_matches_uri?(template, u) + } + end + + def template_matches_uri?(template, uri) + template.match(uri) + rescue Addressable::URI::InvalidURIError + false + end + end + + class URIStringPattern < URIPattern + def add_query_params(query_params) + super + if @query_params.is_a?(Hash) || @query_params.is_a?(String) + query_hash = (WebMock::Util::QueryMapper.query_to_values(@pattern.query, notation: Config.instance.query_values_notation) || {}).merge(@query_params) + @pattern.query = WebMock::Util::QueryMapper.values_to_query(query_hash, notation: WebMock::Config.instance.query_values_notation) + @query_params = nil + end + end + + private + + def pattern_matches?(uri) + if @pattern.is_a?(Addressable::URI) + if @query_params + uri.omit(:query) === @pattern + else + uri === @pattern + end + else + false + end + end + + def pattern_inspect + WebMock::Util::URI.strip_default_port_from_uri_string(@pattern.to_s) + end + end + + + class BodyPattern + include RSpecMatcherDetector + + BODY_FORMATS = { + 'text/xml' => :xml, + 'application/xml' => :xml, + 'application/json' => :json, + 'text/json' => :json, + 'application/javascript' => :json, + 'text/javascript' => :json, + 'text/html' => :html, + 'application/x-yaml' => :yaml, + 'text/yaml' => :yaml, + 'text/plain' => :plain + } + + attr_reader :pattern + + def initialize(pattern) + @pattern = if pattern.is_a?(Hash) + normalize_hash(pattern) + elsif rSpecHashIncludingMatcher?(pattern) + WebMock::Matchers::HashIncludingMatcher.from_rspec_matcher(pattern) + else + pattern + end + end + + def matches?(body, content_type = "") + assert_non_multipart_body(content_type) + + if (@pattern).is_a?(Hash) + return true if @pattern.empty? + matching_body_hashes?(body_as_hash(body, content_type), @pattern, content_type) + elsif (@pattern).is_a?(Array) + matching_body_array?(body_as_hash(body, content_type), @pattern, content_type) + elsif (@pattern).is_a?(WebMock::Matchers::HashArgumentMatcher) + @pattern == body_as_hash(body, content_type) + else + empty_string?(@pattern) && empty_string?(body) || + @pattern == body || + @pattern === body + end + end + + def to_s + @pattern.inspect + end + + private + + def body_as_hash(body, content_type) + case body_format(content_type) + when :json then + WebMock::Util::Parsers::JSON.parse(body) + when :xml then + WebMock::Util::Parsers::XML.parse(body) + else + WebMock::Util::QueryMapper.query_to_values(body, notation: Config.instance.query_values_notation) + end + rescue WebMock::Util::Parsers::ParseError + nil + end + + def body_format(content_type) + normalized_content_type = content_type.sub(/\A(application\/)[a-zA-Z0-9.-]+\+(json|xml)\Z/,'\1\2') + BODY_FORMATS[normalized_content_type] + end + + def assert_non_multipart_body(content_type) + if content_type =~ %r{^multipart/form-data} + raise ArgumentError.new("WebMock does not support matching body for multipart/form-data requests yet :(") + end + end + + # Compare two hashes for equality + # + # For two hashes to match they must have the same length and all + # values must match when compared using `#===`. + # + # The following hashes are examples of matches: + # + # {a: /\d+/} and {a: '123'} + # + # {a: '123'} and {a: '123'} + # + # {a: {b: /\d+/}} and {a: {b: '123'}} + # + # {a: {b: 'wow'}} and {a: {b: 'wow'}} + # + # @param [Hash] query_parameters typically the result of parsing + # JSON, XML or URL encoded parameters. + # + # @param [Hash] pattern which contains keys with a string, hash or + # regular expression value to use for comparison. + # + # @return [Boolean] true if the paramaters match the comparison + # hash, false if not. + def matching_body_hashes?(query_parameters, pattern, content_type) + return false unless query_parameters.is_a?(Hash) + return false unless query_parameters.keys.sort == pattern.keys.sort + + query_parameters.all? do |key, actual| + expected = pattern[key] + matching_values(actual, expected, content_type) + end + end + + def matching_body_array?(query_parameters, pattern, content_type) + return false unless query_parameters.is_a?(Array) + return false unless query_parameters.length == pattern.length + + query_parameters.each_with_index do |actual, index| + expected = pattern[index] + return false unless matching_values(actual, expected, content_type) + end + + true + end + + def matching_values(actual, expected, content_type) + return matching_body_hashes?(actual, expected, content_type) if actual.is_a?(Hash) && expected.is_a?(Hash) + return matching_body_array?(actual, expected, content_type) if actual.is_a?(Array) && expected.is_a?(Array) + + expected = WebMock::Util::ValuesStringifier.stringify_values(expected) if url_encoded_body?(content_type) + expected === actual + end + + def empty_string?(string) + string.nil? || string == "" + end + + def normalize_hash(hash) + Hash[WebMock::Util::HashKeysStringifier.stringify_keys!(hash, deep: true).sort] + end + + def url_encoded_body?(content_type) + content_type =~ %r{^application/x-www-form-urlencoded} + end + end + + class HeadersPattern + def initialize(pattern) + @pattern = WebMock::Util::Headers.normalize_headers(pattern) || {} + end + + def matches?(headers) + if empty_headers?(@pattern) + empty_headers?(headers) + else + return false if empty_headers?(headers) + @pattern.each do |key, value| + return false unless headers.has_key?(key) && value === headers[key] + end + true + end + end + + def to_s + WebMock::Util::Headers.sorted_headers_string(@pattern) + end + + def pp_to_s + WebMock::Util::Headers.pp_headers_string(@pattern) + end + + private + + def empty_headers?(headers) + headers.nil? || headers == {} + end + end + +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/request_registry.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/request_registry.rb new file mode 100644 index 0000000..e4618d3 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/request_registry.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module WebMock + + class RequestRegistry + include Singleton + + attr_accessor :requested_signatures + + def initialize + reset! + end + + def reset! + self.requested_signatures = Util::HashCounter.new + end + + def times_executed(request_pattern) + self.requested_signatures.select do |request_signature| + request_pattern.matches?(request_signature) + end.inject(0) { |sum, (_, times_executed)| sum + times_executed } + end + + def to_s + if requested_signatures.hash.empty? + "No requests were made." + else + text = "".dup + self.requested_signatures.each do |request_signature, times_executed| + text << "#{request_signature} was made #{times_executed} time#{times_executed == 1 ? '' : 's' }\n" + end + text + end + end + + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/request_signature.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/request_signature.rb new file mode 100644 index 0000000..27e62e9 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/request_signature.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +module WebMock + + class RequestSignature + + attr_accessor :method, :uri, :body + attr_reader :headers + + def initialize(method, uri, options = {}) + self.method = method.to_sym + self.uri = uri.is_a?(Addressable::URI) ? uri : WebMock::Util::URI.normalize_uri(uri) + assign_options(options) + end + + def to_s + string = "#{self.method.to_s.upcase}".dup + string << " #{WebMock::Util::URI.strip_default_port_from_uri_string(self.uri.to_s)}" + string << " with body '#{body.to_s}'" if body && body.to_s != '' + if headers && !headers.empty? + string << " with headers #{WebMock::Util::Headers.sorted_headers_string(headers)}" + end + string + end + + def headers=(headers) + @headers = WebMock::Util::Headers.normalize_headers(headers) + end + + def hash + self.to_s.hash + end + + def eql?(other) + self.to_s == other.to_s + end + alias == eql? + + def url_encoded? + !!(headers&.fetch('Content-Type', nil)&.start_with?('application/x-www-form-urlencoded')) + end + + def json_headers? + !!(headers&.fetch('Content-Type', nil)&.start_with?('application/json')) + end + + private + + def assign_options(options) + self.body = options[:body] if options.has_key?(:body) + self.headers = options[:headers] if options.has_key?(:headers) + end + + end + +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/request_signature_snippet.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/request_signature_snippet.rb new file mode 100644 index 0000000..5355780 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/request_signature_snippet.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +require "pp" + +module WebMock + class RequestSignatureSnippet + + attr_reader :request_signature, :request_stub + + def initialize(request_signature) + @request_signature = request_signature + @request_stub = RequestStub.from_request_signature(request_signature) + end + + def stubbing_instructions + return unless WebMock.show_stubbing_instructions? + + "You can stub this request with the following snippet:\n\n" + + WebMock::StubRequestSnippet.new(request_stub).to_s + end + + def request_stubs + return if WebMock::StubRegistry.instance.request_stubs.empty? + + text = "registered request stubs:\n".dup + WebMock::StubRegistry.instance.request_stubs.each do |stub| + text << "\n#{WebMock::StubRequestSnippet.new(stub).to_s(false)}" + add_body_diff(stub, text) if WebMock.show_body_diff? + end + text + end + + private + + def add_body_diff(stub, text) + body_diff_str = signature_stub_body_diff(stub) + text << "\n\n#{body_diff_str}" unless body_diff_str.empty? + end + + def signature_stub_body_diff(stub) + diff = RequestBodyDiff.new(request_signature, stub).body_diff + diff.empty? ? "" : "Body diff:\n #{pretty_print_to_string(diff)}" + end + + def request_params + @request_params ||= + if request_signature.json_headers? + JSON.parse(request_signature.body) + else + "" + end + end + + def pretty_print_to_string(string_to_print) + StringIO.open("".dup) do |stream| + PP.pp(string_to_print, stream) + stream.rewind + stream.read + end + end + + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/request_stub.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/request_stub.rb new file mode 100644 index 0000000..fa6395a --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/request_stub.rb @@ -0,0 +1,134 @@ +# frozen_string_literal: true + +module WebMock + class RequestStub + + attr_accessor :request_pattern + + def initialize(method, uri) + @request_pattern = RequestPattern.new(method, uri) + @responses_sequences = [] + self + end + + def with(params = {}, &block) + @request_pattern.with(params, &block) + self + end + + def to_return(*response_hashes, &block) + if block + @responses_sequences << ResponsesSequence.new([ResponseFactory.response_for(block)]) + else + @responses_sequences << ResponsesSequence.new([*response_hashes].flatten.map {|r| ResponseFactory.response_for(r)}) + end + self + end + alias_method :and_return, :to_return + + def to_return_json(*response_hashes) + raise ArgumentError, '#to_return_json does not support passing a block' if block_given? + + json_response_hashes = [*response_hashes].flatten.map do |resp_h| + headers, body = resp_h.values_at(:headers, :body) + + json_body = if body.respond_to?(:call) + ->(request_signature) { + b = if body.respond_to?(:arity) && body.arity == 1 + body.call(request_signature) + else + body.call + end + b = b.to_json unless b.is_a?(String) + b + } + elsif !body.is_a?(String) + body.to_json + else + body + end + + resp_h.merge( + headers: {content_type: 'application/json'}.merge(headers.to_h), + body: json_body + ) + end + + to_return(json_response_hashes) + end + alias_method :and_return_json, :to_return_json + + def to_rack(app, options={}) + @responses_sequences << ResponsesSequence.new([RackResponse.new(app)]) + end + + def to_raise(*exceptions) + @responses_sequences << ResponsesSequence.new([*exceptions].flatten.map {|e| + ResponseFactory.response_for(exception: e) + }) + self + end + alias_method :and_raise, :to_raise + + def to_timeout + @responses_sequences << ResponsesSequence.new([ResponseFactory.response_for(should_timeout: true)]) + self + end + alias_method :and_timeout, :to_timeout + + def response + if @responses_sequences.empty? + WebMock::Response.new + elsif @responses_sequences.length > 1 + @responses_sequences.shift if @responses_sequences.first.end? + @responses_sequences.first.next_response + else + @responses_sequences[0].next_response + end + end + + def has_responses? + !@responses_sequences.empty? + end + + def then + self + end + + def times(number) + raise "times(N) accepts integers >= 1 only" if !number.is_a?(Integer) || number < 1 + if @responses_sequences.empty? + raise "Invalid WebMock stub declaration." + + " times(N) can be declared only after response declaration." + end + @responses_sequences.last.times_to_repeat += number-1 + self + end + + def matches?(request_signature) + self.request_pattern.matches?(request_signature) + end + + def to_s + self.request_pattern.to_s + end + + def self.from_request_signature(signature) + stub = self.new(signature.method.to_sym, signature.uri.to_s) + + if signature.body.to_s != '' + body = if signature.url_encoded? + WebMock::Util::QueryMapper.query_to_values(signature.body, notation: Config.instance.query_values_notation) + else + signature.body + end + stub.with(body: body) + end + + if (signature.headers && !signature.headers.empty?) + stub.with(headers: signature.headers) + end + stub + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/response.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/response.rb new file mode 100644 index 0000000..7fe3763 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/response.rb @@ -0,0 +1,161 @@ +# frozen_string_literal: true + +require "pathname" + +module WebMock + + class ResponseFactory + def self.response_for(options) + if options.respond_to?(:call) + WebMock::DynamicResponse.new(options) + else + WebMock::Response.new(options) + end + end + end + + class Response + def initialize(options = {}) + case options + when IO, StringIO + self.options = read_raw_response(options) + when String + self.options = read_raw_response(StringIO.new(options)) + else + self.options = options + end + end + + def headers + @headers + end + + def headers=(headers) + @headers = headers + if @headers && !@headers.is_a?(Proc) + @headers = Util::Headers.normalize_headers(@headers) + end + end + + def body + @body || String.new("") + end + + def body=(body) + @body = body + assert_valid_body! + stringify_body! + end + + def status + @status || [200, ""] + end + + def status=(status) + @status = status.is_a?(Integer) ? [status, ""] : status + end + + def exception + @exception + end + + def exception=(exception) + @exception = case exception + when String then StandardError.new(exception) + when Class then exception.new('Exception from WebMock') + when Exception then exception + end + end + + def raise_error_if_any + raise @exception if @exception + end + + def should_timeout + @should_timeout == true + end + + def options=(options) + options = WebMock::Util::HashKeysStringifier.stringify_keys!(options) + HashValidator.new(options).validate_keys('headers', 'status', 'body', 'exception', 'should_timeout') + self.headers = options['headers'] + self.status = options['status'] + self.body = options['body'] + self.exception = options['exception'] + @should_timeout = options['should_timeout'] + end + + def evaluate(request_signature) + self.body = @body.call(request_signature) if @body.is_a?(Proc) + self.headers = @headers.call(request_signature) if @headers.is_a?(Proc) + self.status = @status.call(request_signature) if @status.is_a?(Proc) + @should_timeout = @should_timeout.call(request_signature) if @should_timeout.is_a?(Proc) + @exception = @exception.call(request_signature) if @exception.is_a?(Proc) + self + end + + def ==(other) + self.body == other.body && + self.headers === other.headers && + self.status == other.status && + self.exception == other.exception && + self.should_timeout == other.should_timeout + end + + private + + def stringify_body! + if @body.is_a?(IO) || @body.is_a?(Pathname) + io = @body + @body = io.read + io.close if io.respond_to?(:close) + end + end + + def assert_valid_body! + valid_types = [Proc, IO, Pathname, String, Array] + return if @body.nil? + return if valid_types.any? { |c| @body.is_a?(c) } + + if @body.is_a?(Hash) + raise InvalidBody, "must be one of: #{valid_types}, but you've used a #{@body.class}. " \ + "Please convert it by calling .to_json .to_xml, or otherwise convert it to a string." + else + raise InvalidBody, "must be one of: #{valid_types}. '#{@body.class}' given." + end + end + + def read_raw_response(io) + socket = ::Net::BufferedIO.new(io) + response = ::Net::HTTPResponse.read_new(socket) + transfer_encoding = response.delete('transfer-encoding') #chunks were already read by curl + response.reading_body(socket, true) {} + + options = {} + options[:headers] = {} + response.each_header {|name, value| options[:headers][name] = value} + options[:headers]['transfer-encoding'] = transfer_encoding if transfer_encoding + options[:body] = response.read_body + options[:status] = [response.code.to_i, response.message] + options + ensure + socket.close + end + + InvalidBody = Class.new(StandardError) + + end + + class DynamicResponse < Response + attr_accessor :responder + + def initialize(responder) + @responder = responder + end + + def evaluate(request_signature) + options = @responder.call(request_signature) + Response.new(options) + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/responses_sequence.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/responses_sequence.rb new file mode 100644 index 0000000..a49fbe8 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/responses_sequence.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +module WebMock + + class ResponsesSequence + + attr_accessor :times_to_repeat + + def initialize(responses) + @times_to_repeat = 1 + @responses = responses + @current_position = 0 + end + + def end? + @times_to_repeat == 0 + end + + def next_response + if @times_to_repeat > 0 + response = @responses[@current_position] + increase_position + response + else + @responses.last + end + end + + private + + def increase_position + if @current_position == (@responses.length - 1) + @current_position = 0 + @times_to_repeat -= 1 + else + @current_position += 1 + end + end + + end + +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/rspec.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/rspec.rb new file mode 100644 index 0000000..fc5d327 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/rspec.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +require 'webmock' + +# RSpec 1.x and 2.x compatibility +if defined?(RSpec::Expectations::ExpectationNotMetError) + RSPEC_NAMESPACE = RSPEC_CONFIGURER = RSpec +elsif defined?(Spec) && defined?(Spec.configure) + RSPEC_NAMESPACE = Spec + RSPEC_CONFIGURER = Spec::Runner +else + begin + require 'rspec/core' + require 'rspec/expectations' + RSPEC_NAMESPACE = RSPEC_CONFIGURER = RSpec + rescue LoadError + require 'spec' + RSPEC_NAMESPACE = Spec + RSPEC_CONFIGURER = Spec::Runner + end +end + +require 'webmock/rspec/matchers' + +RSPEC_CONFIGURER.configure { |config| + + config.include WebMock::API + config.include WebMock::Matchers + + config.before(:suite) do + WebMock.enable! + end + + config.after(:suite) do + WebMock.disable! + end + + config.around(:each) do |example| + example.run + WebMock.reset! + end +} + +WebMock::AssertionFailure.error_class = RSPEC_NAMESPACE::Expectations::ExpectationNotMetError diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/rspec/matchers.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/rspec/matchers.rb new file mode 100644 index 0000000..e069405 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/rspec/matchers.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require 'webmock' +require 'webmock/rspec/matchers/request_pattern_matcher' +require 'webmock/rspec/matchers/webmock_matcher' + +module WebMock + module Matchers + def have_been_made + WebMock::RequestPatternMatcher.new + end + + def have_been_requested + WebMock::RequestPatternMatcher.new + end + + def have_not_been_made + WebMock::RequestPatternMatcher.new.times(0) + end + + def have_requested(method, uri) + WebMock::WebMockMatcher.new(method, uri) + end + + def have_not_requested(method, uri) + WebMock::WebMockMatcher.new(method, uri).times(0) + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/rspec/matchers/request_pattern_matcher.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/rspec/matchers/request_pattern_matcher.rb new file mode 100644 index 0000000..9d0fd39 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/rspec/matchers/request_pattern_matcher.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: true + +module WebMock + class RequestPatternMatcher + + def initialize + @request_execution_verifier = RequestExecutionVerifier.new + end + + def once + @request_execution_verifier.expected_times_executed = 1 + self + end + + def twice + @request_execution_verifier.expected_times_executed = 2 + self + end + + def times(times) + @request_execution_verifier.expected_times_executed = times.to_i + self + end + + def at_least_once + @request_execution_verifier.at_least_times_executed = 1 + self + end + + def at_least_twice + @request_execution_verifier.at_least_times_executed = 2 + self + end + + def at_least_times(times) + @request_execution_verifier.at_least_times_executed = times.to_i + self + end + + def at_most_once + @request_execution_verifier.at_most_times_executed = 1 + self + end + + def at_most_twice + @request_execution_verifier.at_most_times_executed = 2 + self + end + + def at_most_times(times) + @request_execution_verifier.at_most_times_executed = times.to_i + self + end + + def matches?(request_pattern) + @request_execution_verifier.request_pattern = request_pattern + @request_execution_verifier.matches? + end + + def does_not_match?(request_pattern) + @request_execution_verifier.request_pattern = request_pattern + @request_execution_verifier.does_not_match? + end + + def failure_message + @request_execution_verifier.failure_message + end + + def failure_message_when_negated + @request_execution_verifier.failure_message_when_negated + end + + def description + @request_execution_verifier.description + end + + # RSpec 2 compatibility: + alias_method :negative_failure_message, :failure_message_when_negated + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/rspec/matchers/webmock_matcher.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/rspec/matchers/webmock_matcher.rb new file mode 100644 index 0000000..5a57885 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/rspec/matchers/webmock_matcher.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +module WebMock + class WebMockMatcher + + def initialize(method, uri) + @request_execution_verifier = RequestExecutionVerifier.new + @request_execution_verifier.request_pattern = RequestPattern.new(method, uri) + end + + def once + @request_execution_verifier.expected_times_executed = 1 + self + end + + def twice + @request_execution_verifier.expected_times_executed = 2 + self + end + + def at_least_once + @request_execution_verifier.at_least_times_executed = 1 + self + end + + def at_least_twice + @request_execution_verifier.at_least_times_executed = 2 + self + end + + def at_least_times(times) + @request_execution_verifier.at_least_times_executed = times + self + end + + def with(options = {}, &block) + @request_execution_verifier.request_pattern.with(options, &block) + self + end + + def times(times) + @request_execution_verifier.expected_times_executed = times.to_i + self + end + + def matches?(webmock) + @request_execution_verifier.matches? + end + + def does_not_match?(webmock) + @request_execution_verifier.does_not_match? + end + + def failure_message + @request_execution_verifier.failure_message + end + + def failure_message_when_negated + @request_execution_verifier.failure_message_when_negated + end + + def description + @request_execution_verifier.description + end + + # RSpec 2 compatibility: + alias_method :negative_failure_message, :failure_message_when_negated + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/stub_registry.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/stub_registry.rb new file mode 100644 index 0000000..56a0db6 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/stub_registry.rb @@ -0,0 +1,84 @@ +# frozen_string_literal: true + +module WebMock + + class StubRegistry + include Singleton + + attr_accessor :request_stubs + + def initialize + reset! + end + + def global_stubs + @global_stubs ||= Hash.new { |h, k| h[k] = [] } + end + + def reset! + self.request_stubs = [] + end + + def register_global_stub(order = :before_local_stubs, &block) + unless %i[before_local_stubs after_local_stubs].include?(order) + raise ArgumentError.new("Wrong order. Use :before_local_stubs or :after_local_stubs") + end + + # This hash contains the responses returned by the block, + # keyed by the exact request (using the object_id). + # That way, there's no race condition in case #to_return + # doesn't run immediately after stub.with. + responses = {} + response_lock = Mutex.new + + stub = ::WebMock::RequestStub.new(:any, ->(uri) { true }).with { |request| + update_response = -> { responses[request.object_id] = yield(request) } + + # The block can recurse, so only lock if we don't already own it + if response_lock.owned? + update_response.call + else + response_lock.synchronize(&update_response) + end + }.to_return(lambda { |request| + response_lock.synchronize { responses.delete(request.object_id) } + }) + + global_stubs[order].push stub + end + + def register_request_stub(stub) + request_stubs.insert(0, stub) + stub + end + + def remove_request_stub(stub) + if not request_stubs.delete(stub) + raise "Request stub \n\n #{stub.to_s} \n\n is not registered." + end + end + + def registered_request?(request_signature) + request_stub_for(request_signature) + end + + def response_for_request(request_signature) + stub = request_stub_for(request_signature) + stub ? evaluate_response_for_request(stub.response, request_signature) : nil + end + + private + + def request_stub_for(request_signature) + (global_stubs[:before_local_stubs] + request_stubs + global_stubs[:after_local_stubs]) + .detect { |registered_request_stub| + registered_request_stub.request_pattern.matches?(request_signature) + } + end + + def evaluate_response_for_request(response, request_signature) + response.dup.evaluate(request_signature) + end + + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/stub_request_snippet.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/stub_request_snippet.rb new file mode 100644 index 0000000..87485f6 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/stub_request_snippet.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +module WebMock + class StubRequestSnippet + def initialize(request_stub) + @request_stub = request_stub + end + + def body_pattern + request_pattern.body_pattern + end + + def to_s(with_response = true) + request_pattern = @request_stub.request_pattern + string = "stub_request(:#{request_pattern.method_pattern.to_s},".dup + string << " \"#{request_pattern.uri_pattern.to_s}\")" + + with = "".dup + + if (request_pattern.body_pattern) + with << "\n body: #{request_pattern.body_pattern.to_s}" + end + + if (request_pattern.headers_pattern) + with << "," unless with.empty? + + with << "\n headers: #{request_pattern.headers_pattern.pp_to_s}" + end + string << ".\n with(#{with})" unless with.empty? + if with_response + if request_pattern.headers_pattern && request_pattern.headers_pattern.matches?({ 'Accept' => "application/json" }) + string << ".\n to_return(status: 200, body: \"{}\", headers: {})" + else + string << ".\n to_return(status: 200, body: \"\", headers: {})" + end + end + string + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/test_unit.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/test_unit.rb new file mode 100644 index 0000000..eb6556b --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/test_unit.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +require 'test/unit' +require 'webmock' + +WebMock.enable! + +module Test + module Unit + class TestCase + include WebMock::API + + teardown + def teardown_with_webmock + WebMock.reset! + end + + end + end +end + +WebMock::AssertionFailure.error_class = Test::Unit::AssertionFailedError diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/util/hash_counter.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/util/hash_counter.rb new file mode 100644 index 0000000..ce7c059 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/util/hash_counter.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require 'thread' + +module WebMock + module Util + class HashCounter + attr_accessor :hash + + def initialize + self.hash = Hash.new(0) + @order = {} + @max = 0 + @lock = ::Mutex.new + end + + def put(key, num=1) + @lock.synchronize do + hash[key] += num + @order[key] = @max += 1 + end + end + + def get(key) + @lock.synchronize do + hash[key] + end + end + + def select(&block) + return unless block_given? + + @lock.synchronize do + hash.select(&block) + end + end + + def each(&block) + @order.to_a.sort_by { |a| a[1] }.each do |a| + yield(a[0], hash[a[0]]) + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/util/hash_keys_stringifier.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/util/hash_keys_stringifier.rb new file mode 100644 index 0000000..4ab4c80 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/util/hash_keys_stringifier.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module WebMock + module Util + class HashKeysStringifier + + def self.stringify_keys!(arg, options = {}) + case arg + when Array + arg.map { |elem| + options[:deep] ? stringify_keys!(elem, options) : elem + } + when Hash + Hash[ + *arg.map { |key, value| + k = key.is_a?(Symbol) ? key.to_s : key + v = (options[:deep] ? stringify_keys!(value, options) : value) + [k,v] + }.inject([]) {|r,x| r + x}] + else + arg + end + end + + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/util/hash_validator.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/util/hash_validator.rb new file mode 100644 index 0000000..45087d7 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/util/hash_validator.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module WebMock + class HashValidator + def initialize(hash) + @hash = hash + end + + #This code is based on https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/hash/keys.rb + def validate_keys(*valid_keys) + valid_keys.flatten! + @hash.each_key do |k| + unless valid_keys.include?(k) + raise ArgumentError.new("Unknown key: #{k.inspect}. Valid keys are: #{valid_keys.map(&:inspect).join(', ')}") + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/util/headers.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/util/headers.rb new file mode 100644 index 0000000..6ab0b01 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/util/headers.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +module WebMock + + module Util + + class Headers + + STANDARD_HEADER_DELIMITER = '-' + NONSTANDARD_HEADER_DELIMITER = '_' + JOIN = ', ' + + def self.normalize_headers(headers) + return nil unless headers + + headers.each_with_object({}) do |(name, value), new_headers| + new_headers[normalize_name(name)] = + case value + when Regexp then value + when Array then (value.size == 1) ? value.first.to_s : value.map(&:to_s).sort + else value.to_s + end + end + end + + def self.sorted_headers_string(headers) + headers = WebMock::Util::Headers.normalize_headers(headers) + str = '{'.dup + str << headers.map do |k,v| + v = case v + when Regexp then v.inspect + when Array then "["+v.map{|w| "'#{w.to_s}'"}.join(", ")+"]" + else "'#{v.to_s}'" + end + "'#{k}'=>#{v}" + end.sort.join(", ") + str << '}' + end + + def self.pp_headers_string(headers) + headers = WebMock::Util::Headers.normalize_headers(headers) + seperator = "\n\t " + str = "{#{seperator} ".dup + str << headers.map do |k,v| + v = case v + when Regexp then v.inspect + when Array then "["+v.map{|w| "'#{w.to_s}'"}.join(", ")+"]" + else "'#{v.to_s}'" + end + "'#{k}'=>#{v}" + end.sort.join(",#{seperator} ") + str << "\n }" + end + + def self.decode_userinfo_from_header(header) + header.sub(/^Basic /, "").unpack("m").first + end + + def self.basic_auth_header(*credentials) + strict_base64_encoded = [credentials.join(':')].pack("m0") + "Basic #{strict_base64_encoded.chomp}" + end + + def self.normalize_name(name) + name + .to_s + .tr(NONSTANDARD_HEADER_DELIMITER, STANDARD_HEADER_DELIMITER) + .split(STANDARD_HEADER_DELIMITER) + .map!(&:capitalize) + .join(STANDARD_HEADER_DELIMITER) + end + + end + + end + +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/util/parsers/json.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/util/parsers/json.rb new file mode 100644 index 0000000..98014ad --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/util/parsers/json.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +# This is a copy of https://github.com/jnunemaker/crack/blob/master/lib/crack/json.rb +# with date parsing removed +# Copyright (c) 2004-2008 David Heinemeier Hansson +# 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. + +require_relative "parse_error" + +module WebMock + module Util + module Parsers + class JSON + def self.parse(json) + yaml = unescape(convert_json_to_yaml(json)) + YAML.load(yaml) + rescue ArgumentError, Psych::SyntaxError => e + raise ParseError, "Invalid JSON string: #{yaml}, Error: #{e.inspect}" + end + + protected + + def self.unescape(str) + str.gsub(/\\u([0-9a-f]{4})/) { [$1.hex].pack("U") } + end + + # Ensure that ":" and "," are always followed by a space + def self.convert_json_to_yaml(json) #:nodoc: + scanner, quoting, marks, times = StringScanner.new(json), false, [], [] + while scanner.scan_until(/(\\['"]|['":,\\]|\\.)/) + case char = scanner[1] + when '"', "'" + if !quoting + quoting = char + elsif quoting == char + quoting = false + end + when ":","," + marks << scanner.pos - 1 unless quoting + when "\\" + scanner.skip(/\\/) + end + end + + if marks.empty? + json.gsub(/\\\//, '/') + else + left_pos = [-1].push(*marks) + right_pos = marks << json.bytesize + output = [] + + left_pos.each_with_index do |left, i| + if json.respond_to?(:byteslice) + output << json.byteslice(left.succ..right_pos[i]) + else + output << json[left.succ..right_pos[i]] + end + end + + output = output * " " + + times.each { |i| output[i-1] = ' ' } + output.gsub!(/\\\//, '/') + output + end + end + end + end + end +end \ No newline at end of file diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/util/parsers/parse_error.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/util/parsers/parse_error.rb new file mode 100644 index 0000000..378314d --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/util/parsers/parse_error.rb @@ -0,0 +1,7 @@ +module WebMock + module Util + module Parsers + class ParseError < StandardError; end + end + end +end \ No newline at end of file diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/util/parsers/xml.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/util/parsers/xml.rb new file mode 100644 index 0000000..4275541 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/util/parsers/xml.rb @@ -0,0 +1,16 @@ +require_relative "parse_error" +require "crack/xml" + +module WebMock + module Util + module Parsers + class XML + def self.parse(xml) + ::Crack::XML.parse(xml) + rescue ::REXML::ParseException => e + raise ParseError, "Invalid XML string: #{xml}, Error: #{e.inspect}" + end + end + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/util/query_mapper.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/util/query_mapper.rb new file mode 100644 index 0000000..c366840 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/util/query_mapper.rb @@ -0,0 +1,283 @@ +# frozen_string_literal: true + +module WebMock::Util + class QueryMapper + class << self + #This class is based on Addressable::URI pre 2.3.0 + + ## + # Converts the query component to a Hash value. + # + # @option [Symbol] notation + # May be one of <code>:flat</code>, <code>:dot</code>, or + # <code>:subscript</code>. The <code>:dot</code> notation is not + # supported for assignment. Default value is <code>:subscript</code>. + # + # @return [Hash, Array] The query string parsed as a Hash or Array object. + # + # @example + # WebMock::Util::QueryMapper.query_to_values("?one=1&two=2&three=3") + # #=> {"one" => "1", "two" => "2", "three" => "3"} + # WebMock::Util::QueryMapper("?one[two][three]=four").query_values + # #=> {"one" => {"two" => {"three" => "four"}}} + # WebMock::Util::QueryMapper.query_to_values("?one.two.three=four", + # :notation => :dot + # ) + # #=> {"one" => {"two" => {"three" => "four"}}} + # WebMock::Util::QueryMapper.query_to_values("?one[two][three]=four", + # :notation => :flat + # ) + # #=> {"one[two][three]" => "four"} + # WebMock::Util::QueryMapper.query_to_values("?one.two.three=four", + # :notation => :flat + # ) + # #=> {"one.two.three" => "four"} + # WebMock::Util::QueryMapper( + # "?one[two][three][]=four&one[two][three][]=five" + # ) + # #=> {"one" => {"two" => {"three" => ["four", "five"]}}} + # WebMock::Util::QueryMapper.query_to_values( + # "?one=two&one=three").query_values(:notation => :flat_array) + # #=> [['one', 'two'], ['one', 'three']] + def query_to_values(query, options={}) + return nil if query.nil? + query = query.dup.force_encoding('utf-8') if query.respond_to?(:force_encoding) + + options[:notation] ||= :subscript + + if ![:flat, :dot, :subscript, :flat_array].include?(options[:notation]) + raise ArgumentError, + 'Invalid notation. Must be one of: ' + + '[:flat, :dot, :subscript, :flat_array].' + end + + empty_accumulator = :flat_array == options[:notation] ? [] : {} + + query_array = collect_query_parts(query) + + query_hash = collect_query_hash(query_array, empty_accumulator, options) + + normalize_query_hash(query_hash, empty_accumulator, options) + end + + def normalize_query_hash(query_hash, empty_accumulator, options) + query_hash.inject(empty_accumulator.dup) do |accumulator, (key, value)| + if options[:notation] == :flat_array + accumulator << [key, value] + else + accumulator[key] = value.kind_of?(Hash) ? dehash(value) : value + end + accumulator + end + end + + def collect_query_parts(query) + query_parts = query.split('&').map do |pair| + pair.split('=', 2) if pair && !pair.empty? + end + query_parts.compact + end + + def collect_query_hash(query_array, empty_accumulator, options) + query_array.compact.inject(empty_accumulator.dup) do |accumulator, (key, value)| + value = if value.nil? + nil + else + ::Addressable::URI.unencode_component(value.tr('+', ' ')) + end + key = Addressable::URI.unencode_component(key) + key = key.dup.force_encoding(Encoding::ASCII_8BIT) if key.respond_to?(:force_encoding) + self.__send__("fill_accumulator_for_#{options[:notation]}", accumulator, key, value) + accumulator + end + end + + def fill_accumulator_for_flat(accumulator, key, value) + if accumulator[key] + raise ArgumentError, "Key was repeated: #{key.inspect}" + end + accumulator[key] = value + end + + def fill_accumulator_for_flat_array(accumulator, key, value) + accumulator << [key, value] + end + + def fill_accumulator_for_dot(accumulator, key, value) + array_value = false + subkeys = key.split(".") + current_hash = accumulator + subkeys[0..-2].each do |subkey| + current_hash[subkey] = {} unless current_hash[subkey] + current_hash = current_hash[subkey] + end + if array_value + if current_hash[subkeys.last] && !current_hash[subkeys.last].is_a?(Array) + current_hash[subkeys.last] = [current_hash[subkeys.last]] + end + current_hash[subkeys.last] = [] unless current_hash[subkeys.last] + current_hash[subkeys.last] << value + else + current_hash[subkeys.last] = value + end + end + + def fill_accumulator_for_subscript(accumulator, key, value) + current_node = accumulator + subkeys = key.split(/(?=\[[^\[\]]+)/) + subkeys[0..-2].each do |subkey| + node = subkey =~ /\[\]\z/ ? [] : {} + subkey = subkey.gsub(/[\[\]]/, '') + if current_node.is_a? Array + container = current_node.find { |n| n.is_a?(Hash) && n.has_key?(subkey) } + if container + current_node = container[subkey] + else + current_node << {subkey => node} + current_node = node + end + else + current_node[subkey] = node unless current_node[subkey] + current_node = current_node[subkey] + end + end + last_key = subkeys.last + array_value = !!(last_key =~ /\[\]$/) + last_key = last_key.gsub(/[\[\]]/, '') + if current_node.is_a? Array + last_container = current_node.select { |n| n.is_a?(Hash) }.last + if last_container && !last_container.has_key?(last_key) + if array_value + last_container[last_key] ||= [] + last_container[last_key] << value + else + last_container[last_key] = value + end + else + if array_value + current_node << {last_key => [value]} + else + current_node << {last_key => value} + end + end + else + if array_value + current_node[last_key] ||= [] + current_node[last_key] << value unless value.nil? + else + current_node[last_key] = value + end + end + end + + ## + # Sets the query component for this URI from a Hash object. + # This method produces a query string using the :subscript notation. + # An empty Hash will result in a nil query. + # + # @param [Hash, #to_hash, Array] new_query_values The new query values. + def values_to_query(new_query_values, options = {}) + options[:notation] ||= :subscript + return if new_query_values.nil? + + unless new_query_values.is_a?(Array) + unless new_query_values.respond_to?(:to_hash) + raise TypeError, + "Can't convert #{new_query_values.class} into Hash." + end + new_query_values = new_query_values.to_hash + new_query_values = new_query_values.inject([]) do |object, (key, value)| + key = key.to_s if key.is_a?(::Symbol) || key.nil? + if value.is_a?(Array) && value.empty? + object << [key.to_s + '[]'] + elsif value.is_a?(Array) + value.each { |v| object << [key.to_s + '[]', v] } + elsif value.is_a?(Hash) + value.each { |k, v| object << ["#{key.to_s}[#{k}]", v]} + else + object << [key.to_s, value] + end + object + end + # Useful default for OAuth and caching. + # Only to be used for non-Array inputs. Arrays should preserve order. + begin + new_query_values.sort! # may raise for non-comparable values + rescue NoMethodError, ArgumentError + # ignore + end + end + + buffer = ''.dup + new_query_values.each do |parent, value| + encoded_parent = ::Addressable::URI.encode_component( + parent.dup, ::Addressable::URI::CharacterClasses::UNRESERVED + ) + buffer << "#{to_query(encoded_parent, value, options)}&" + end + buffer.chop + end + + def dehash(hash) + hash.each do |(key, value)| + if value.is_a?(::Hash) + hash[key] = self.dehash(value) + end + end + if hash != {} && hash.keys.all? { |key| key =~ /^\d+$/ } + hash.sort.inject([]) do |accu, (_, value)| + accu << value; accu + end + else + hash + end + end + + ## + # Joins and converts parent and value into a properly encoded and + # ordered URL query. + # + # @private + # @param [String] parent an URI encoded component. + # @param [Array, Hash, Symbol, #to_str] value + # + # @return [String] a properly escaped and ordered URL query. + + # new_query_values have form [['key1', 'value1'], ['key2', 'value2']] + def to_query(parent, value, options = {}) + options[:notation] ||= :subscript + case value + when ::Hash + value = value.map do |key, val| + [ + ::Addressable::URI.encode_component(key.to_s.dup, ::Addressable::URI::CharacterClasses::UNRESERVED), + val + ] + end + value.sort! + buffer = ''.dup + value.each do |key, val| + new_parent = options[:notation] != :flat_array ? "#{parent}[#{key}]" : parent + buffer << "#{to_query(new_parent, val, options)}&" + end + buffer.chop + when ::Array + buffer = ''.dup + value.each_with_index do |val, i| + new_parent = options[:notation] != :flat_array ? "#{parent}[#{i}]" : parent + buffer << "#{to_query(new_parent, val, options)}&" + end + buffer.chop + when NilClass + parent + else + encoded_value = Addressable::URI.encode_component( + value.to_s.dup, Addressable::URI::CharacterClasses::UNRESERVED + ) + "#{parent}=#{encoded_value}" + end + end + end + + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/util/uri.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/util/uri.rb new file mode 100644 index 0000000..248b5d7 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/util/uri.rb @@ -0,0 +1,113 @@ +# frozen_string_literal: true + +module WebMock + + module Util + + class URI + module CharacterClasses + USERINFO = Addressable::URI::CharacterClasses::UNRESERVED + Addressable::URI::CharacterClasses::SUB_DELIMS + "\\:" + end + + ADDRESSABLE_URIS = Hash.new do |hash, key| + hash[key] = Addressable::URI.heuristic_parse(key) + end + + NORMALIZED_URIS = Hash.new do |hash, uri| + normalized_uri = WebMock::Util::URI.heuristic_parse(uri) + if normalized_uri.query_values + sorted_query_values = sort_query_values(WebMock::Util::QueryMapper.query_to_values(normalized_uri.query, notation: Config.instance.query_values_notation) || {}) + normalized_uri.query = WebMock::Util::QueryMapper.values_to_query(sorted_query_values, notation: WebMock::Config.instance.query_values_notation) + end + normalized_uri = normalized_uri.normalize #normalize! is slower + normalized_uri.query = normalized_uri.query.gsub("+", "%2B") if normalized_uri.query + normalized_uri.port = normalized_uri.inferred_port unless normalized_uri.port + hash[uri] = normalized_uri + end + + def self.heuristic_parse(uri) + ADDRESSABLE_URIS[uri].dup + end + + def self.normalize_uri(uri) + return uri if uri.is_a?(Regexp) + uri = 'http://' + uri unless uri.match('^https?://') if uri.is_a?(String) + NORMALIZED_URIS[uri].dup + end + + def self.variations_of_uri_as_strings(uri_object, only_with_scheme: false) + normalized_uri = normalize_uri(uri_object.dup).freeze + uris = [ normalized_uri ] + + if normalized_uri.path == '/' + uris = uris_with_trailing_slash_and_without(uris) + end + + if normalized_uri.port == Addressable::URI.port_mapping[normalized_uri.scheme] + uris = uris_with_inferred_port_and_without(uris) + end + + uris = uris_encoded_and_unencoded(uris) + + if normalized_uri.scheme == "http" && !only_with_scheme + uris = uris_with_scheme_and_without(uris) + end + + uris.map {|uri| uri.to_s.gsub(/^\/\//,'') }.uniq + end + + def self.strip_default_port_from_uri_string(uri_string) + case uri_string + when %r{^http://} then uri_string.sub(%r{:80(/|$)}, '\1') + when %r{^https://} then uri_string.sub(%r{:443(/|$)}, '\1') + else uri_string + end + end + + def self.encode_unsafe_chars_in_userinfo(userinfo) + Addressable::URI.encode_component(userinfo, WebMock::Util::URI::CharacterClasses::USERINFO) + end + + def self.is_uri_localhost?(uri) + uri.is_a?(Addressable::URI) && + %w(localhost 127.0.0.1 0.0.0.0 [::1]).include?(uri.host) + end + + private + + def self.sort_query_values(query_values) + sorted_query_values = query_values.sort + query_values.is_a?(Hash) ? Hash[*sorted_query_values.inject([]) { |values, pair| values + pair}] : sorted_query_values + end + + def self.uris_with_inferred_port_and_without(uris) + uris.map { |uri| + [ uri, uri.omit(:port)] + }.flatten + end + + def self.uris_encoded_and_unencoded(uris) + uris.map do |uri| + [ + uri.to_s.force_encoding(Encoding::ASCII_8BIT), + Addressable::URI.unencode(uri, String).force_encoding(Encoding::ASCII_8BIT).freeze + ] + end.flatten + end + + def self.uris_with_scheme_and_without(uris) + uris.map { |uri| + [ uri, uri.gsub(%r{^https?://},"").freeze ] + }.flatten + end + + def self.uris_with_trailing_slash_and_without(uris) + uris.map { |uri| + [ uri, uri.omit(:path).freeze ] + }.flatten + end + + end + end + +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/util/values_stringifier.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/util/values_stringifier.rb new file mode 100644 index 0000000..54959dc --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/util/values_stringifier.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +class WebMock::Util::ValuesStringifier + def self.stringify_values(value) + case value + when String, Numeric, TrueClass, FalseClass + value.to_s + when Hash + Hash[ + value.map do |k, v| + [k, stringify_values(v)] + end + ] + when Array + value.map do |v| + stringify_values(v) + end + else + value + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/util/version_checker.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/util/version_checker.rb new file mode 100644 index 0000000..82feb5d --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/util/version_checker.rb @@ -0,0 +1,113 @@ +# frozen_string_literal: true + +# This code was created based on https://github.com/myronmarston/vcr/blob/master/lib/vcr/util/version_checker.rb +# Thanks to @myronmarston + +# Copyright (c) 2010-2012 Myron Marston + +# 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. + +module WebMock + class VersionChecker + def initialize(library_name, library_version, min_patch_level, max_minor_version = nil, unsupported_versions = []) + @library_name, @library_version = library_name, library_version + @min_patch_level, @max_minor_version = min_patch_level, max_minor_version + @unsupported_versions = unsupported_versions || [] + + @major, @minor, @patch = parse_version(library_version) + @min_major, @min_minor, @min_patch = parse_version(min_patch_level) + if max_minor_version + @max_major, @max_minor = parse_version(max_minor_version) + else + @max_major, @max_minor = nil, nil + end + + @comparison_result = compare_version + end + + def check_version! + warn_about_too_low if too_low? + warn_about_too_high if too_high? + warn_about_unsupported_version if unsupported_version? + end + + private + + def too_low? + @comparison_result == :too_low + end + + def too_high? + @comparison_result == :too_high + end + + def unsupported_version? + @unsupported_versions.include?(@library_version) + end + + def warn_about_too_low + warn_in_red "You are using #{@library_name} #{@library_version}. " + + "WebMock supports version #{version_requirement}." + end + + def warn_about_too_high + warn_in_red "You are using #{@library_name} #{@library_version}. " + + "WebMock is known to work with #{@library_name} #{version_requirement}. " + + "It may not work with this version." + end + + def warn_about_unsupported_version + warn_in_red "You are using #{@library_name} #{@library_version}. " + + "WebMock does not support this version. " + + "WebMock supports versions #{version_requirement}." + end + + def warn_in_red(text) + Kernel.warn colorize(text, "\e[31m") + end + + def compare_version + case + when @major < @min_major then :too_low + when @major == @min_major && @minor < @min_minor then :too_low + when @major == @min_major && @minor == @min_minor && @patch < @min_patch then :too_low + when @max_major && @major > @max_major then :too_high + when @max_major && @major == @max_major && @max_minor && @minor > @max_minor then :too_high + + else :ok + end + end + + def version_requirement + req = ">= #{@min_patch_level}" + req += ", < #{@max_major}.#{@max_minor + 1}" if @max_minor + req += ", except versions #{@unsupported_versions.join(',')}" unless @unsupported_versions.empty? + req + end + + def parse_version(version) + version.split('.').map { |v| v.to_i } + end + + def colorize(text, color_code) + "#{color_code}#{text}\e[0m" + end + end +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/version.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/version.rb new file mode 100644 index 0000000..c9e70fa --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/version.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +module WebMock + VERSION = '3.26.1' unless defined?(::WebMock::VERSION) +end diff --git a/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/webmock.rb b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/webmock.rb new file mode 100644 index 0000000..24555a1 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/gems/webmock-3.26.1/lib/webmock/webmock.rb @@ -0,0 +1,175 @@ +# frozen_string_literal: true + +module WebMock + + def self.included(clazz) + WebMock::Deprecation.warning("include WebMock is deprecated. Please include WebMock::API instead") + if clazz.instance_methods.map(&:to_s).include?('request') + warn "WebMock#request was not included in #{clazz} to avoid name collision" + else + clazz.class_eval do + def request(method, uri) + WebMock::Deprecation.warning("WebMock#request is deprecated. Please use WebMock::API#a_request method instead") + WebMock.a_request(method, uri) + end + end + end + end + + include WebMock::API + extend WebMock::API + + class << self + alias :request :a_request + end + + def self.version + VERSION + end + + def self.disable!(options = {}) + except = [options[:except]].flatten.compact + HttpLibAdapterRegistry.instance.each_adapter do |name, adapter| + adapter.enable! + adapter.disable! unless except.include?(name) + end + end + + def self.enable!(options = {}) + except = [options[:except]].flatten.compact + HttpLibAdapterRegistry.instance.each_adapter do |name, adapter| + adapter.disable! + adapter.enable! unless except.include?(name) + end + end + + def self.allow_net_connect!(options = {}) + Config.instance.allow_net_connect = true + Config.instance.net_http_connect_on_start = options[:net_http_connect_on_start] + end + + def self.disable_net_connect!(options = {}) + Config.instance.allow_net_connect = false + Config.instance.allow_localhost = options[:allow_localhost] + Config.instance.allow = options[:allow] + Config.instance.net_http_connect_on_start = options[:net_http_connect_on_start] + end + + class << self + alias :enable_net_connect! :allow_net_connect! + alias :disallow_net_connect! :disable_net_connect! + end + + def self.net_connect_allowed?(uri = nil) + return !!Config.instance.allow_net_connect if uri.nil? + + if uri.is_a?(String) + uri = WebMock::Util::URI.normalize_uri(uri) + end + + !!Config.instance.allow_net_connect || + ( Config.instance.allow_localhost && WebMock::Util::URI.is_uri_localhost?(uri) || + Config.instance.allow && net_connect_explicit_allowed?(Config.instance.allow, uri) ) + end + + def self.net_http_connect_on_start?(uri) + allowed = Config.instance.net_http_connect_on_start || false + + if [true, false].include?(allowed) + allowed + else + net_connect_explicit_allowed?(allowed, uri) + end + end + + def self.net_connect_explicit_allowed?(allowed, uri=nil) + case allowed + when Array + allowed.any? { |allowed_item| net_connect_explicit_allowed?(allowed_item, uri) } + when Regexp + (uri.to_s =~ allowed) != nil || + (uri.omit(:port).to_s =~ allowed) != nil && uri.port == uri.default_port + when String + allowed == uri.to_s || + allowed == uri.host || + allowed == "#{uri.host}:#{uri.port}" || + allowed == "#{uri.scheme}://#{uri.host}:#{uri.port}" || + allowed == "#{uri.scheme}://#{uri.host}" && uri.port == uri.default_port + else + if allowed.respond_to?(:call) + allowed.call(uri) + end + end + end + + def self.show_body_diff! + Config.instance.show_body_diff = true + end + + def self.hide_body_diff! + Config.instance.show_body_diff = false + end + + def self.show_body_diff? + Config.instance.show_body_diff + end + + def self.hide_stubbing_instructions! + Config.instance.show_stubbing_instructions = false + end + + def self.show_stubbing_instructions! + Config.instance.show_stubbing_instructions = true + end + + def self.show_stubbing_instructions? + Config.instance.show_stubbing_instructions + end + + def self.reset! + WebMock::RequestRegistry.instance.reset! + WebMock::StubRegistry.instance.reset! + end + + def self.reset_webmock + WebMock::Deprecation.warning("WebMock.reset_webmock is deprecated. Please use WebMock.reset! method instead") + reset! + end + + def self.reset_callbacks + WebMock::CallbackRegistry.reset + end + + def self.after_request(options={}, &block) + WebMock::CallbackRegistry.add_callback(options, block) + end + + def self.registered_request?(request_signature) + WebMock::StubRegistry.instance.registered_request?(request_signature) + end + + def self.print_executed_requests + puts WebMock::RequestExecutionVerifier.executed_requests_message + end + + def self.globally_stub_request(order = :before_local_stubs, &block) + WebMock::StubRegistry.instance.register_global_stub(order, &block) + end + + %w( + allow_net_connect! + disable_net_connect! + net_connect_allowed? + reset_webmock + reset_callbacks + after_request + registered_request? + ).each do |method| + self.class_eval(%Q( + def #{method}(*args, &block) + WebMock::Deprecation.warning("WebMock##{method} instance method is deprecated. Please use WebMock.#{method} class method instead") + WebMock.#{method}(*args, &block) + end + )) + end +end diff --git a/vendor/bundle/ruby/3.3.0/specifications/addressable-2.8.8.gemspec b/vendor/bundle/ruby/3.3.0/specifications/addressable-2.8.8.gemspec new file mode 100644 index 0000000..2f37ff4 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/specifications/addressable-2.8.8.gemspec @@ -0,0 +1,30 @@ +# -*- encoding: utf-8 -*- +# stub: addressable 2.8.8 ruby lib + +Gem::Specification.new do |s| + s.name = "addressable".freeze + s.version = "2.8.8".freeze + + s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= + s.metadata = { "changelog_uri" => "https://github.com/sporkmonger/addressable/blob/main/CHANGELOG.md#v2.8.8" } if s.respond_to? :metadata= + s.require_paths = ["lib".freeze] + s.authors = ["Bob Aman".freeze] + s.date = "1980-01-02" + s.description = "Addressable is an alternative implementation to the URI implementation that is\npart of Ruby's standard library. It is flexible, offers heuristic parsing, and\nadditionally provides extensive support for IRIs and URI templates.\n".freeze + s.email = "bob@sporkmonger.com".freeze + s.extra_rdoc_files = ["README.md".freeze] + s.files = ["README.md".freeze] + s.homepage = "https://github.com/sporkmonger/addressable".freeze + s.licenses = ["Apache-2.0".freeze] + s.rdoc_options = ["--main".freeze, "README.md".freeze] + s.required_ruby_version = Gem::Requirement.new(">= 2.2".freeze) + s.rubygems_version = "3.6.9".freeze + s.summary = "URI Implementation".freeze + + s.installed_by_version = "3.5.22".freeze + + s.specification_version = 4 + + s.add_runtime_dependency(%q<public_suffix>.freeze, [">= 2.0.2".freeze, "< 8.0".freeze]) + s.add_development_dependency(%q<bundler>.freeze, [">= 1.0".freeze, "< 3.0".freeze]) +end diff --git a/vendor/bundle/ruby/3.3.0/specifications/bigdecimal-4.0.1.gemspec b/vendor/bundle/ruby/3.3.0/specifications/bigdecimal-4.0.1.gemspec new file mode 100644 index 0000000..4ede764 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/specifications/bigdecimal-4.0.1.gemspec @@ -0,0 +1,25 @@ +# -*- encoding: utf-8 -*- +# stub: bigdecimal 4.0.1 ruby lib +# stub: ext/bigdecimal/extconf.rb + +Gem::Specification.new do |s| + s.name = "bigdecimal".freeze + s.version = "4.0.1".freeze + + s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= + s.metadata = { "changelog_uri" => "https://github.com/ruby/bigdecimal/blob/master/CHANGES.md" } if s.respond_to? :metadata= + s.require_paths = ["lib".freeze] + s.authors = ["Kenta Murata".freeze, "Zachary Scott".freeze, "Shigeo Kobayashi".freeze] + s.date = "1980-01-02" + s.description = "This library provides arbitrary-precision decimal floating-point number class.".freeze + s.email = ["mrkn@mrkn.jp".freeze] + s.extensions = ["ext/bigdecimal/extconf.rb".freeze] + s.files = ["ext/bigdecimal/extconf.rb".freeze] + s.homepage = "https://github.com/ruby/bigdecimal".freeze + s.licenses = ["Ruby".freeze, "BSD-2-Clause".freeze] + s.required_ruby_version = Gem::Requirement.new(">= 2.5.0".freeze) + s.rubygems_version = "3.6.9".freeze + s.summary = "Arbitrary-precision decimal floating-point number library.".freeze + + s.installed_by_version = "3.5.22".freeze +end diff --git a/vendor/bundle/ruby/3.3.0/specifications/crack-1.0.1.gemspec b/vendor/bundle/ruby/3.3.0/specifications/crack-1.0.1.gemspec new file mode 100644 index 0000000..08c686e --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/specifications/crack-1.0.1.gemspec @@ -0,0 +1,27 @@ +# -*- encoding: utf-8 -*- +# stub: crack 1.0.1 ruby lib + +Gem::Specification.new do |s| + s.name = "crack".freeze + s.version = "1.0.1".freeze + + s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= + s.metadata = { "bug_tracker_uri" => "https://github.com/jnunemaker/crack/issues", "changelog_uri" => "https://github.com/jnunemaker/crack/blob/master/History", "rubygems_mfa_required" => "true", "source_code_uri" => "https://github.com/jnunemaker/crack" } if s.respond_to? :metadata= + s.require_paths = ["lib".freeze] + s.authors = ["John Nunemaker".freeze] + s.date = "2025-10-22" + s.description = "Really simple JSON and XML parsing, ripped from Merb and Rails.".freeze + s.email = ["nunemaker@gmail.com".freeze] + s.homepage = "https://github.com/jnunemaker/crack".freeze + s.licenses = ["MIT".freeze] + s.required_ruby_version = Gem::Requirement.new(">= 2.0".freeze) + s.rubygems_version = "3.3.7".freeze + s.summary = "Really simple JSON and XML parsing, ripped from Merb and Rails.".freeze + + s.installed_by_version = "3.5.22".freeze + + s.specification_version = 4 + + s.add_runtime_dependency(%q<bigdecimal>.freeze, [">= 0".freeze]) + s.add_runtime_dependency(%q<rexml>.freeze, [">= 0".freeze]) +end diff --git a/vendor/bundle/ruby/3.3.0/specifications/faraday-2.14.0.gemspec b/vendor/bundle/ruby/3.3.0/specifications/faraday-2.14.0.gemspec new file mode 100644 index 0000000..bf5fbd3 Binary files /dev/null and b/vendor/bundle/ruby/3.3.0/specifications/faraday-2.14.0.gemspec differ diff --git a/vendor/bundle/ruby/3.3.0/specifications/faraday-net_http-3.4.2.gemspec b/vendor/bundle/ruby/3.3.0/specifications/faraday-net_http-3.4.2.gemspec new file mode 100644 index 0000000..bc882de --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/specifications/faraday-net_http-3.4.2.gemspec @@ -0,0 +1,26 @@ +# -*- encoding: utf-8 -*- +# stub: faraday-net_http 3.4.2 ruby lib + +Gem::Specification.new do |s| + s.name = "faraday-net_http".freeze + s.version = "3.4.2".freeze + + s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= + s.metadata = { "changelog_uri" => "https://github.com/lostisland/faraday-net_http/releases/tag/v3.4.2", "homepage_uri" => "https://github.com/lostisland/faraday-net_http", "rubygems_mfa_required" => "true", "source_code_uri" => "https://github.com/lostisland/faraday-net_http" } if s.respond_to? :metadata= + s.require_paths = ["lib".freeze] + s.authors = ["Jan van der Pas".freeze] + s.date = "1980-01-02" + s.description = "Faraday adapter for Net::HTTP".freeze + s.email = ["janvanderpas@gmail.com".freeze] + s.homepage = "https://github.com/lostisland/faraday-net_http".freeze + s.licenses = ["MIT".freeze] + s.required_ruby_version = Gem::Requirement.new(">= 3.0.0".freeze) + s.rubygems_version = "3.6.7".freeze + s.summary = "Faraday adapter for Net::HTTP".freeze + + s.installed_by_version = "3.5.22".freeze + + s.specification_version = 4 + + s.add_runtime_dependency(%q<net-http>.freeze, ["~> 0.5".freeze]) +end diff --git a/vendor/bundle/ruby/3.3.0/specifications/hashdiff-1.2.1.gemspec b/vendor/bundle/ruby/3.3.0/specifications/hashdiff-1.2.1.gemspec new file mode 100644 index 0000000..abf57ca --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/specifications/hashdiff-1.2.1.gemspec @@ -0,0 +1,30 @@ +# -*- encoding: utf-8 -*- +# stub: hashdiff 1.2.1 ruby lib + +Gem::Specification.new do |s| + s.name = "hashdiff".freeze + s.version = "1.2.1".freeze + + s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= + s.metadata = { "bug_tracker_uri" => "https://github.com/liufengyun/hashdiff/issues", "changelog_uri" => "https://github.com/liufengyun/hashdiff/blob/master/changelog.md", "documentation_uri" => "https://www.rubydoc.info/gems/hashdiff", "homepage_uri" => "https://github.com/liufengyun/hashdiff", "source_code_uri" => "https://github.com/liufengyun/hashdiff" } if s.respond_to? :metadata= + s.require_paths = ["lib".freeze] + s.authors = ["Liu Fengyun".freeze] + s.date = "2025-09-06" + s.description = " Hashdiff is a diff lib to compute the smallest difference between two hashes. ".freeze + s.email = ["liufengyunchina@gmail.com".freeze] + s.homepage = "https://github.com/liufengyun/hashdiff".freeze + s.licenses = ["MIT".freeze] + s.required_ruby_version = Gem::Requirement.new(">= 2.0.0".freeze) + s.rubygems_version = "3.3.5".freeze + s.summary = "Hashdiff is a diff lib to compute the smallest difference between two hashes.".freeze + + s.installed_by_version = "3.5.22".freeze + + s.specification_version = 4 + + s.add_development_dependency(%q<bluecloth>.freeze, [">= 0".freeze]) + s.add_development_dependency(%q<rspec>.freeze, ["~> 3.5".freeze]) + s.add_development_dependency(%q<rubocop>.freeze, [">= 1.52.1".freeze]) + s.add_development_dependency(%q<rubocop-rspec>.freeze, ["> 1.16.0".freeze]) + s.add_development_dependency(%q<yard>.freeze, [">= 0".freeze]) +end diff --git a/vendor/bundle/ruby/3.3.0/specifications/json-2.18.0.gemspec b/vendor/bundle/ruby/3.3.0/specifications/json-2.18.0.gemspec new file mode 100644 index 0000000..524f779 Binary files /dev/null and b/vendor/bundle/ruby/3.3.0/specifications/json-2.18.0.gemspec differ diff --git a/vendor/bundle/ruby/3.3.0/specifications/logger-1.7.0.gemspec b/vendor/bundle/ruby/3.3.0/specifications/logger-1.7.0.gemspec new file mode 100644 index 0000000..161ffe7 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/specifications/logger-1.7.0.gemspec @@ -0,0 +1,22 @@ +# -*- encoding: utf-8 -*- +# stub: logger 1.7.0 ruby lib + +Gem::Specification.new do |s| + s.name = "logger".freeze + s.version = "1.7.0".freeze + + s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= + s.metadata = { "changelog_uri" => "https://github.com/ruby/logger/releases" } if s.respond_to? :metadata= + s.require_paths = ["lib".freeze] + s.authors = ["Naotoshi Seo".freeze, "SHIBATA Hiroshi".freeze] + s.date = "2025-03-27" + s.description = "Provides a simple logging utility for outputting messages.".freeze + s.email = ["sonots@gmail.com".freeze, "hsbt@ruby-lang.org".freeze] + s.homepage = "https://github.com/ruby/logger".freeze + s.licenses = ["Ruby".freeze, "BSD-2-Clause".freeze] + s.required_ruby_version = Gem::Requirement.new(">= 2.5.0".freeze) + s.rubygems_version = "3.5.11".freeze + s.summary = "Provides a simple logging utility for outputting messages.".freeze + + s.installed_by_version = "3.5.22".freeze +end diff --git a/vendor/bundle/ruby/3.3.0/specifications/minitest-6.0.1.gemspec b/vendor/bundle/ruby/3.3.0/specifications/minitest-6.0.1.gemspec new file mode 100644 index 0000000..7275aff --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/specifications/minitest-6.0.1.gemspec @@ -0,0 +1,34 @@ +# -*- encoding: utf-8 -*- +# stub: minitest 6.0.1 ruby lib + +Gem::Specification.new do |s| + s.name = "minitest".freeze + s.version = "6.0.1".freeze + + s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= + s.metadata = { "bug_tracker_uri" => "https://github.com/minitest/minitest/issues", "changelog_uri" => "https://github.com/minitest/minitest/blob/master/History.rdoc", "homepage_uri" => "https://minite.st/", "source_code_uri" => "https://github.com/minitest/minitest" } if s.respond_to? :metadata= + s.require_paths = ["lib".freeze] + s.authors = ["Ryan Davis".freeze] + s.cert_chain = ["-----BEGIN CERTIFICATE-----\nMIIDPjCCAiagAwIBAgIBCTANBgkqhkiG9w0BAQsFADBFMRMwEQYDVQQDDApyeWFu\nZC1ydWJ5MRkwFwYKCZImiZPyLGQBGRYJemVuc3BpZGVyMRMwEQYKCZImiZPyLGQB\nGRYDY29tMB4XDTI1MDEwNjIzMjcwMVoXDTI2MDEwNjIzMjcwMVowRTETMBEGA1UE\nAwwKcnlhbmQtcnVieTEZMBcGCgmSJomT8ixkARkWCXplbnNwaWRlcjETMBEGCgmS\nJomT8ixkARkWA2NvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALda\nb9DCgK+627gPJkB6XfjZ1itoOQvpqH1EXScSaba9/S2VF22VYQbXU1xQXL/WzCkx\ntaCPaLmfYIaFcHHCSY4hYDJijRQkLxPeB3xbOfzfLoBDbjvx5JxgJxUjmGa7xhcT\noOvjtt5P8+GSK9zLzxQP0gVLS/D0FmoE44XuDr3iQkVS2ujU5zZL84mMNqNB1znh\nGiadM9GHRaDiaxuX0cIUBj19T01mVE2iymf9I6bEsiayK/n6QujtyCbTWsAS9Rqt\nqhtV7HJxNKuPj/JFH0D2cswvzznE/a5FOYO68g+YCuFi5L8wZuuM8zzdwjrWHqSV\ngBEfoTEGr7Zii72cx+sCAwEAAaM5MDcwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAw\nHQYDVR0OBBYEFEfFe9md/r/tj/Wmwpy+MI8d9k/hMA0GCSqGSIb3DQEBCwUAA4IB\nAQAC0WQJcPOWPFwkojhzweilRVjTJ19UiLhiBTw3C1wJO3LVdBkWDmnnhAmKuX4D\nr7vjQvESlABGIPdutI1Yl7mrHQzTkfLfXvNN6MT0nLChPyIYauT6SZZxubwJrUfA\n7R0c2CJTIboZ0XaGpLsXqHEF1c29H7TV1QvVuqKAN2mCjh4N82QVn+ZKtys28AwT\n6GfQX2fqLoi4KSc7xIzHKaNzqxeOICmJofk9w5VZ2rRN6yes8jvFYwz9HR41wdj8\nbwfinv7Yp5fA6AysuZLhCykyfDuZVRrUp0Vb68YCKsLjJly/Theak+euNTxvHsB+\nal9oSgPPHICMEX65qvLywitx\n-----END CERTIFICATE-----\n".freeze] + s.date = "1980-01-02" + s.description = "minitest provides a complete suite of testing facilities supporting\nTDD, BDD, and benchmarking.\n\n \"I had a class with Jim Weirich on testing last week and we were\n allowed to choose our testing frameworks. Kirk Haines and I were\n paired up and we cracked open the code for a few test\n frameworks...\n\n I MUST say that minitest is *very* readable / understandable\n compared to the 'other two' options we looked at. Nicely done and\n thank you for helping us keep our mental sanity.\"\n\n -- Wayne E. Seguin\n\nminitest/test is a small and incredibly fast unit testing framework.\nIt provides a rich set of assertions to make your tests clean and\nreadable.\n\nminitest/spec is a functionally complete spec engine. It hooks onto\nminitest/test and seamlessly bridges test assertions over to spec\nexpectations.\n\nminitest/benchmark is an awesome way to assert the performance of your\nalgorithms in a repeatable manner. Now you can assert that your newb\nco-worker doesn't replace your linear algorithm with an exponential\none!\n\nminitest/pride shows pride in testing and adds coloring to your test\noutput. I guess it is an example of how to write IO pipes too. :P\n\nminitest/test is meant to have a clean implementation for language\nimplementors that need a minimal set of methods to bootstrap a working\ntest suite. For example, there is no magic involved for test-case\ndiscovery.\n\n \"Again, I can't praise enough the idea of a testing/specing\n framework that I can actually read in full in one sitting!\"\n\n -- Piotr Szotkowski\n\nComparing to rspec:\n\n rspec is a testing DSL. minitest is ruby.\n\n -- Adam Hawkins, \"Bow Before MiniTest\"\n\nminitest doesn't reinvent anything that ruby already provides, like:\nclasses, modules, inheritance, methods. This means you only have to\nlearn ruby to use minitest and all of your regular OO practices like\nextract-method refactorings still apply.".freeze + s.email = ["ryand-ruby@zenspider.com".freeze] + s.executables = ["minitest".freeze] + s.extra_rdoc_files = ["History.rdoc".freeze, "Manifest.txt".freeze, "README.rdoc".freeze] + s.files = ["History.rdoc".freeze, "Manifest.txt".freeze, "README.rdoc".freeze, "bin/minitest".freeze] + s.homepage = "https://minite.st/".freeze + s.licenses = ["MIT".freeze] + s.rdoc_options = ["--main".freeze, "README.rdoc".freeze] + s.required_ruby_version = Gem::Requirement.new(">= 3.2".freeze) + s.rubygems_version = "3.7.2".freeze + s.summary = "minitest provides a complete suite of testing facilities supporting TDD, BDD, and benchmarking".freeze + + s.installed_by_version = "3.5.22".freeze + + s.specification_version = 4 + + s.add_runtime_dependency(%q<prism>.freeze, ["~> 1.5".freeze]) + s.add_development_dependency(%q<rdoc>.freeze, [">= 6.0".freeze, "< 8".freeze]) + s.add_development_dependency(%q<simplecov>.freeze, ["~> 0.21".freeze]) + s.add_development_dependency(%q<hoe>.freeze, ["~> 4.5".freeze]) +end diff --git a/vendor/bundle/ruby/3.3.0/specifications/mocha-3.0.1.gemspec b/vendor/bundle/ruby/3.3.0/specifications/mocha-3.0.1.gemspec new file mode 100644 index 0000000..319bb27 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/specifications/mocha-3.0.1.gemspec @@ -0,0 +1,26 @@ +# -*- encoding: utf-8 -*- +# stub: mocha 3.0.1 ruby lib + +Gem::Specification.new do |s| + s.name = "mocha".freeze + s.version = "3.0.1".freeze + + s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= + s.metadata = { "bug_tracker_uri" => "https://github.com/freerange/mocha/issues", "changelog_uri" => "https://github.com/freerange/mocha/blob/main/RELEASE.md", "documentation_uri" => "https://mocha.jamesmead.org/", "funding_uri" => "https://github.com/sponsors/floehopper", "homepage_uri" => "https://mocha.jamesmead.org", "rubygems_mfa_required" => "true", "source_code_uri" => "https://github.com/freerange/mocha" } if s.respond_to? :metadata= + s.require_paths = ["lib".freeze] + s.authors = ["James Mead".freeze] + s.date = "2025-12-17" + s.description = "A library for mocking and stubbing with a unified, simple and readable syntax for both full & partial mocking. Built-in support for Minitest and Test::Unit. Supported by many other test frameworks, e.g. RSpec.".freeze + s.email = "mocha-developer@googlegroups.com".freeze + s.homepage = "https://mocha.jamesmead.org".freeze + s.licenses = ["MIT".freeze, "BSD-2-Clause".freeze] + s.required_ruby_version = Gem::Requirement.new(">= 2.2".freeze) + s.rubygems_version = "3.5.3".freeze + s.summary = "Mocking and stubbing library".freeze + + s.installed_by_version = "3.5.22".freeze + + s.specification_version = 4 + + s.add_runtime_dependency(%q<ruby2_keywords>.freeze, [">= 0.0.5".freeze]) +end diff --git a/vendor/bundle/ruby/3.3.0/specifications/net-http-0.9.1.gemspec b/vendor/bundle/ruby/3.3.0/specifications/net-http-0.9.1.gemspec new file mode 100644 index 0000000..1081bc6 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/specifications/net-http-0.9.1.gemspec @@ -0,0 +1,27 @@ +# -*- encoding: utf-8 -*- +# stub: net-http 0.9.1 ruby lib + +Gem::Specification.new do |s| + s.name = "net-http".freeze + s.version = "0.9.1".freeze + + s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= + s.metadata = { "changelog_uri" => "https://github.com/ruby/net-http/releases", "homepage_uri" => "https://github.com/ruby/net-http", "source_code_uri" => "https://github.com/ruby/net-http" } if s.respond_to? :metadata= + s.require_paths = ["lib".freeze] + s.authors = ["NARUSE, Yui".freeze] + s.bindir = "exe".freeze + s.date = "1980-01-02" + s.description = "HTTP client api for Ruby.".freeze + s.email = ["naruse@airemix.jp".freeze] + s.homepage = "https://github.com/ruby/net-http".freeze + s.licenses = ["Ruby".freeze, "BSD-2-Clause".freeze] + s.required_ruby_version = Gem::Requirement.new(">= 2.7.0".freeze) + s.rubygems_version = "3.6.9".freeze + s.summary = "HTTP client api for Ruby.".freeze + + s.installed_by_version = "3.5.22".freeze + + s.specification_version = 4 + + s.add_runtime_dependency(%q<uri>.freeze, [">= 0.11.1".freeze]) +end diff --git a/vendor/bundle/ruby/3.3.0/specifications/octokit-10.0.0.gemspec b/vendor/bundle/ruby/3.3.0/specifications/octokit-10.0.0.gemspec new file mode 100644 index 0000000..b10f893 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/specifications/octokit-10.0.0.gemspec @@ -0,0 +1,27 @@ +# -*- encoding: utf-8 -*- +# stub: octokit 10.0.0 ruby lib + +Gem::Specification.new do |s| + s.name = "octokit".freeze + s.version = "10.0.0".freeze + + s.required_rubygems_version = Gem::Requirement.new(">= 1.3.5".freeze) if s.respond_to? :required_rubygems_version= + s.metadata = { "rubygems_mfa_required" => "true" } if s.respond_to? :metadata= + s.require_paths = ["lib".freeze] + s.authors = ["Wynn Netherland".freeze, "Erik Michaels-Ober".freeze, "Clint Shryock".freeze] + s.date = "2025-04-24" + s.description = "Simple wrapper for the GitHub API".freeze + s.email = ["wynn.netherland@gmail.com".freeze, "sferik@gmail.com".freeze, "clint@ctshryock.com".freeze] + s.homepage = "https://github.com/octokit/octokit.rb".freeze + s.licenses = ["MIT".freeze] + s.required_ruby_version = Gem::Requirement.new(">= 2.7.0".freeze) + s.rubygems_version = "3.4.0.dev".freeze + s.summary = "Ruby toolkit for working with the GitHub API".freeze + + s.installed_by_version = "3.5.22".freeze + + s.specification_version = 4 + + s.add_runtime_dependency(%q<faraday>.freeze, [">= 1".freeze, "< 3".freeze]) + s.add_runtime_dependency(%q<sawyer>.freeze, ["~> 0.9".freeze]) +end diff --git a/vendor/bundle/ruby/3.3.0/specifications/prism-1.9.0.gemspec b/vendor/bundle/ruby/3.3.0/specifications/prism-1.9.0.gemspec new file mode 100644 index 0000000..89e1d5b --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/specifications/prism-1.9.0.gemspec @@ -0,0 +1,24 @@ +# -*- encoding: utf-8 -*- +# stub: prism 1.9.0 ruby lib +# stub: ext/prism/extconf.rb + +Gem::Specification.new do |s| + s.name = "prism".freeze + s.version = "1.9.0".freeze + + s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= + s.metadata = { "allowed_push_host" => "https://rubygems.org", "changelog_uri" => "https://github.com/ruby/prism/blob/main/CHANGELOG.md", "source_code_uri" => "https://github.com/ruby/prism" } if s.respond_to? :metadata= + s.require_paths = ["lib".freeze] + s.authors = ["Shopify".freeze] + s.date = "1980-01-02" + s.email = ["ruby@shopify.com".freeze] + s.extensions = ["ext/prism/extconf.rb".freeze] + s.files = ["ext/prism/extconf.rb".freeze] + s.homepage = "https://github.com/ruby/prism".freeze + s.licenses = ["MIT".freeze] + s.required_ruby_version = Gem::Requirement.new(">= 2.7.0".freeze) + s.rubygems_version = "3.6.9".freeze + s.summary = "Prism Ruby parser".freeze + + s.installed_by_version = "3.5.22".freeze +end diff --git a/vendor/bundle/ruby/3.3.0/specifications/public_suffix-7.0.2.gemspec b/vendor/bundle/ruby/3.3.0/specifications/public_suffix-7.0.2.gemspec new file mode 100644 index 0000000..3d41a62 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/specifications/public_suffix-7.0.2.gemspec @@ -0,0 +1,24 @@ +# -*- encoding: utf-8 -*- +# stub: public_suffix 7.0.2 ruby lib + +Gem::Specification.new do |s| + s.name = "public_suffix".freeze + s.version = "7.0.2".freeze + + s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= + s.metadata = { "bug_tracker_uri" => "https://github.com/weppos/publicsuffix-ruby/issues", "changelog_uri" => "https://github.com/weppos/publicsuffix-ruby/blob/master/CHANGELOG.md", "documentation_uri" => "https://rubydoc.info/gems/public_suffix/7.0.2", "funding_uri" => "https://github.com/sponsors/weppos", "homepage_uri" => "https://simonecarletti.com/code/publicsuffix-ruby", "source_code_uri" => "https://github.com/weppos/publicsuffix-ruby/tree/v7.0.2" } if s.respond_to? :metadata= + s.require_paths = ["lib".freeze] + s.authors = ["Simone Carletti".freeze] + s.date = "1980-01-02" + s.description = "PublicSuffix can parse and decompose a domain name into top level domain, domain and subdomains.".freeze + s.email = ["weppos@weppos.net".freeze] + s.extra_rdoc_files = ["LICENSE.txt".freeze] + s.files = ["LICENSE.txt".freeze] + s.homepage = "https://simonecarletti.com/code/publicsuffix-ruby".freeze + s.licenses = ["MIT".freeze] + s.required_ruby_version = Gem::Requirement.new(">= 3.2".freeze) + s.rubygems_version = "3.6.9".freeze + s.summary = "Domain name parser based on the Public Suffix List.".freeze + + s.installed_by_version = "3.5.22".freeze +end diff --git a/vendor/bundle/ruby/3.3.0/specifications/rake-13.3.1.gemspec b/vendor/bundle/ruby/3.3.0/specifications/rake-13.3.1.gemspec new file mode 100644 index 0000000..2743d27 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/specifications/rake-13.3.1.gemspec @@ -0,0 +1,26 @@ +# -*- encoding: utf-8 -*- +# stub: rake 13.3.1 ruby lib + +Gem::Specification.new do |s| + s.name = "rake".freeze + s.version = "13.3.1".freeze + + s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= + s.metadata = { "bug_tracker_uri" => "https://github.com/ruby/rake/issues", "changelog_uri" => "https://github.com/ruby/rake/releases", "documentation_uri" => "https://ruby.github.io/rake", "source_code_uri" => "https://github.com/ruby/rake" } if s.respond_to? :metadata= + s.require_paths = ["lib".freeze] + s.authors = ["Hiroshi SHIBATA".freeze, "Eric Hodel".freeze, "Jim Weirich".freeze] + s.bindir = "exe".freeze + s.date = "1980-01-02" + s.description = "Rake is a Make-like program implemented in Ruby. Tasks and dependencies are\nspecified in standard Ruby syntax.\nRake has the following features:\n * Rakefiles (rake's version of Makefiles) are completely defined in standard Ruby syntax.\n No XML files to edit. No quirky Makefile syntax to worry about (is that a tab or a space?)\n * Users can specify tasks with prerequisites.\n * Rake supports rule patterns to synthesize implicit tasks.\n * Flexible FileLists that act like arrays but know about manipulating file names and paths.\n * Supports parallel execution of tasks.\n".freeze + s.email = ["hsbt@ruby-lang.org".freeze, "drbrain@segment7.net".freeze, "".freeze] + s.executables = ["rake".freeze] + s.files = ["exe/rake".freeze] + s.homepage = "https://github.com/ruby/rake".freeze + s.licenses = ["MIT".freeze] + s.rdoc_options = ["--main".freeze, "README.rdoc".freeze] + s.required_ruby_version = Gem::Requirement.new(">= 2.3".freeze) + s.rubygems_version = "3.6.9".freeze + s.summary = "Rake is a Make-like program implemented in Ruby".freeze + + s.installed_by_version = "3.5.22".freeze +end diff --git a/vendor/bundle/ruby/3.3.0/specifications/rexml-3.4.4.gemspec b/vendor/bundle/ruby/3.3.0/specifications/rexml-3.4.4.gemspec new file mode 100644 index 0000000..39770be --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/specifications/rexml-3.4.4.gemspec @@ -0,0 +1,25 @@ +# -*- encoding: utf-8 -*- +# stub: rexml 3.4.4 ruby lib + +Gem::Specification.new do |s| + s.name = "rexml".freeze + s.version = "3.4.4".freeze + + s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= + s.metadata = { "changelog_uri" => "https://github.com/ruby/rexml/releases/tag/v3.4.4" } if s.respond_to? :metadata= + s.require_paths = ["lib".freeze] + s.authors = ["Kouhei Sutou".freeze] + s.date = "1980-01-02" + s.description = "An XML toolkit for Ruby".freeze + s.email = ["kou@cozmixng.org".freeze] + s.extra_rdoc_files = ["LICENSE.txt".freeze, "NEWS.md".freeze, "README.md".freeze, "doc/rexml/context.rdoc".freeze, "doc/rexml/tasks/rdoc/child.rdoc".freeze, "doc/rexml/tasks/rdoc/document.rdoc".freeze, "doc/rexml/tasks/rdoc/element.rdoc".freeze, "doc/rexml/tasks/rdoc/node.rdoc".freeze, "doc/rexml/tasks/rdoc/parent.rdoc".freeze, "doc/rexml/tasks/tocs/child_toc.rdoc".freeze, "doc/rexml/tasks/tocs/document_toc.rdoc".freeze, "doc/rexml/tasks/tocs/element_toc.rdoc".freeze, "doc/rexml/tasks/tocs/master_toc.rdoc".freeze, "doc/rexml/tasks/tocs/node_toc.rdoc".freeze, "doc/rexml/tasks/tocs/parent_toc.rdoc".freeze, "doc/rexml/tutorial.rdoc".freeze] + s.files = ["LICENSE.txt".freeze, "NEWS.md".freeze, "README.md".freeze, "doc/rexml/context.rdoc".freeze, "doc/rexml/tasks/rdoc/child.rdoc".freeze, "doc/rexml/tasks/rdoc/document.rdoc".freeze, "doc/rexml/tasks/rdoc/element.rdoc".freeze, "doc/rexml/tasks/rdoc/node.rdoc".freeze, "doc/rexml/tasks/rdoc/parent.rdoc".freeze, "doc/rexml/tasks/tocs/child_toc.rdoc".freeze, "doc/rexml/tasks/tocs/document_toc.rdoc".freeze, "doc/rexml/tasks/tocs/element_toc.rdoc".freeze, "doc/rexml/tasks/tocs/master_toc.rdoc".freeze, "doc/rexml/tasks/tocs/node_toc.rdoc".freeze, "doc/rexml/tasks/tocs/parent_toc.rdoc".freeze, "doc/rexml/tutorial.rdoc".freeze] + s.homepage = "https://github.com/ruby/rexml".freeze + s.licenses = ["BSD-2-Clause".freeze] + s.rdoc_options = ["--main".freeze, "README.md".freeze] + s.required_ruby_version = Gem::Requirement.new(">= 2.5.0".freeze) + s.rubygems_version = "3.6.9".freeze + s.summary = "An XML toolkit for Ruby".freeze + + s.installed_by_version = "3.5.22".freeze +end diff --git a/vendor/bundle/ruby/3.3.0/specifications/ruby2_keywords-0.0.5.gemspec b/vendor/bundle/ruby/3.3.0/specifications/ruby2_keywords-0.0.5.gemspec new file mode 100644 index 0000000..7e5f41d --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/specifications/ruby2_keywords-0.0.5.gemspec @@ -0,0 +1,22 @@ +# -*- encoding: utf-8 -*- +# stub: ruby2_keywords 0.0.5 ruby lib + +Gem::Specification.new do |s| + s.name = "ruby2_keywords".freeze + s.version = "0.0.5".freeze + + s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= + s.require_paths = ["lib".freeze] + s.authors = ["Nobuyoshi Nakada".freeze] + s.date = "2021-02-13" + s.extra_rdoc_files = ["LICENSE".freeze, "README.md".freeze, "ChangeLog".freeze, "logs/ChangeLog-0.0.0".freeze, "logs/ChangeLog-0.0.1".freeze, "logs/ChangeLog-0.0.2".freeze, "logs/ChangeLog-0.0.3".freeze, "logs/ChangeLog-0.0.4".freeze] + s.files = ["ChangeLog".freeze, "LICENSE".freeze, "README.md".freeze, "logs/ChangeLog-0.0.0".freeze, "logs/ChangeLog-0.0.1".freeze, "logs/ChangeLog-0.0.2".freeze, "logs/ChangeLog-0.0.3".freeze, "logs/ChangeLog-0.0.4".freeze] + s.homepage = "https://github.com/ruby/ruby2_keywords".freeze + s.licenses = ["Ruby".freeze, "BSD-2-Clause".freeze] + s.rdoc_options = ["--main".freeze, "README.md".freeze] + s.required_ruby_version = Gem::Requirement.new(">= 2.0.0".freeze) + s.rubygems_version = "3.3.0.dev".freeze + s.summary = "Shim library for Module#ruby2_keywords".freeze + + s.installed_by_version = "3.5.22".freeze +end diff --git a/vendor/bundle/ruby/3.3.0/specifications/sawyer-0.9.3.gemspec b/vendor/bundle/ruby/3.3.0/specifications/sawyer-0.9.3.gemspec new file mode 100644 index 0000000..5aca91e --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/specifications/sawyer-0.9.3.gemspec @@ -0,0 +1,24 @@ +# -*- encoding: utf-8 -*- +# stub: sawyer 0.9.3 ruby lib + +Gem::Specification.new do |s| + s.name = "sawyer".freeze + s.version = "0.9.3".freeze + + s.required_rubygems_version = Gem::Requirement.new(">= 1.3.5".freeze) if s.respond_to? :required_rubygems_version= + s.require_paths = ["lib".freeze] + s.authors = ["Rick Olson".freeze, "Wynn Netherland".freeze] + s.date = "1980-01-02" + s.email = "technoweenie@gmail.com".freeze + s.homepage = "https://github.com/lostisland/sawyer".freeze + s.licenses = ["MIT".freeze] + s.rubygems_version = "3.6.9".freeze + s.summary = "Secret User Agent of HTTP".freeze + + s.installed_by_version = "3.5.22".freeze + + s.specification_version = 2 + + s.add_runtime_dependency(%q<faraday>.freeze, [">= 0.17.3".freeze, "< 3".freeze]) + s.add_runtime_dependency(%q<addressable>.freeze, [">= 2.3.5".freeze]) +end diff --git a/vendor/bundle/ruby/3.3.0/specifications/uri-1.1.1.gemspec b/vendor/bundle/ruby/3.3.0/specifications/uri-1.1.1.gemspec new file mode 100644 index 0000000..e2ee2e5 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/specifications/uri-1.1.1.gemspec @@ -0,0 +1,23 @@ +# -*- encoding: utf-8 -*- +# stub: uri 1.1.1 ruby lib + +Gem::Specification.new do |s| + s.name = "uri".freeze + s.version = "1.1.1".freeze + + s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= + s.metadata = { "bug_tracker_uri" => "https://github.com/ruby/uri/issues", "changelog_uri" => "https://github.com/ruby/uri/releases", "documentation_uri" => "https://ruby.github.io/uri/", "homepage_uri" => "https://github.com/ruby/uri", "source_code_uri" => "https://github.com/ruby/uri" } if s.respond_to? :metadata= + s.require_paths = ["lib".freeze] + s.authors = ["Akira Yamada".freeze] + s.bindir = "exe".freeze + s.date = "1980-01-02" + s.description = "URI is a module providing classes to handle Uniform Resource Identifiers".freeze + s.email = ["akira@ruby-lang.org".freeze] + s.homepage = "https://github.com/ruby/uri".freeze + s.licenses = ["Ruby".freeze, "BSD-2-Clause".freeze] + s.required_ruby_version = Gem::Requirement.new(">= 2.5".freeze) + s.rubygems_version = "3.6.9".freeze + s.summary = "URI is a module providing classes to handle Uniform Resource Identifiers".freeze + + s.installed_by_version = "3.5.22".freeze +end diff --git a/vendor/bundle/ruby/3.3.0/specifications/webmock-3.26.1.gemspec b/vendor/bundle/ruby/3.3.0/specifications/webmock-3.26.1.gemspec new file mode 100644 index 0000000..a75c660 --- /dev/null +++ b/vendor/bundle/ruby/3.3.0/specifications/webmock-3.26.1.gemspec @@ -0,0 +1,44 @@ +# -*- encoding: utf-8 -*- +# stub: webmock 3.26.1 ruby lib + +Gem::Specification.new do |s| + s.name = "webmock".freeze + s.version = "3.26.1".freeze + + s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= + s.metadata = { "bug_tracker_uri" => "https://github.com/bblimke/webmock/issues", "changelog_uri" => "https://github.com/bblimke/webmock/blob/v3.26.1/CHANGELOG.md", "documentation_uri" => "https://www.rubydoc.info/gems/webmock/3.26.1", "source_code_uri" => "https://github.com/bblimke/webmock/tree/v3.26.1", "wiki_uri" => "https://github.com/bblimke/webmock/wiki" } if s.respond_to? :metadata= + s.require_paths = ["lib".freeze] + s.authors = ["Bartosz Blimke".freeze] + s.date = "2025-10-30" + s.description = "WebMock allows stubbing HTTP requests and setting expectations on HTTP requests.".freeze + s.email = ["bartosz.blimke@gmail.com".freeze] + s.homepage = "https://github.com/bblimke/webmock".freeze + s.licenses = ["MIT".freeze] + s.required_ruby_version = Gem::Requirement.new(">= 2.6".freeze) + s.rubygems_version = "3.5.16".freeze + s.summary = "Library for stubbing HTTP requests in Ruby.".freeze + + s.installed_by_version = "3.5.22".freeze + + s.specification_version = 4 + + s.add_runtime_dependency(%q<addressable>.freeze, [">= 2.8.0".freeze]) + s.add_runtime_dependency(%q<crack>.freeze, [">= 0.3.2".freeze]) + s.add_runtime_dependency(%q<hashdiff>.freeze, [">= 0.4.0".freeze, "< 2.0.0".freeze]) + s.add_development_dependency(%q<patron>.freeze, [">= 0.4.18".freeze]) + s.add_development_dependency(%q<curb>.freeze, [">= 0.7.16".freeze]) + s.add_development_dependency(%q<typhoeus>.freeze, [">= 0.5.0".freeze]) + s.add_development_dependency(%q<em-http-request>.freeze, [">= 1.0.2".freeze]) + s.add_development_dependency(%q<em-synchrony>.freeze, [">= 1.0.0".freeze]) + s.add_development_dependency(%q<async-http>.freeze, [">= 0.48.0".freeze]) + s.add_development_dependency(%q<http>.freeze, [">= 0.8.0".freeze]) + s.add_development_dependency(%q<rack>.freeze, ["> 1.6".freeze]) + s.add_development_dependency(%q<rspec>.freeze, [">= 3.1.0".freeze]) + s.add_development_dependency(%q<httpclient>.freeze, [">= 2.2.4".freeze]) + s.add_development_dependency(%q<excon>.freeze, [">= 0.27.5".freeze]) + s.add_development_dependency(%q<minitest>.freeze, [">= 5.0.0".freeze]) + s.add_development_dependency(%q<test-unit>.freeze, [">= 3.0.0".freeze]) + s.add_development_dependency(%q<rdoc>.freeze, ["> 3.5.0".freeze]) + s.add_development_dependency(%q<webrick>.freeze, [">= 0".freeze]) + s.add_development_dependency(%q<rspec-retry>.freeze, [">= 0".freeze]) +end